mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 12:12:47 +01:00
Revert "[CodeView] Provide a common interface for type collections."
This is a squash of ~5 reverts of, well, pretty much everything I did today. Something is seriously broken with lit on Windows right now, and as a result assertions that fire in tests are triggering failures. I've been breaking non-Windows bots all day which has seriously confused me because all my tests have been passing, and after running lit with -a to view the output even on successful runs, I find out that the tool is crashing and yet lit is still reporting it as a success! At this point I don't even know where to start, so rather than leave the tree broken for who knows how long, I will get this back to green, and then once lit is fixed on Windows, hopefully hopefully fix the remaining set of problems for real. llvm-svn: 303409
This commit is contained in:
parent
92c35d4ced
commit
4a5590fa6b
@ -32,10 +32,6 @@ public:
|
||||
uint32_t length() const { return RecordData.size(); }
|
||||
Kind kind() const { return Type; }
|
||||
ArrayRef<uint8_t> data() const { return RecordData; }
|
||||
StringRef str_data() const {
|
||||
return StringRef(reinterpret_cast<const char *>(RecordData.data()),
|
||||
RecordData.size());
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> content() const {
|
||||
return RecordData.drop_front(sizeof(RecordPrefix));
|
||||
|
61
include/llvm/DebugInfo/CodeView/CVTypeDumper.h
Normal file
61
include/llvm/DebugInfo/CodeView/CVTypeDumper.h
Normal file
@ -0,0 +1,61 @@
|
||||
//===-- CVTypeDumper.h - CodeView type info dumper --------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_CVTYPEDUMPER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_CVTYPEDUMPER_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
namespace codeview {
|
||||
|
||||
class TypeServerHandler;
|
||||
|
||||
/// Dumper for CodeView type streams found in COFF object files and PDB files.
|
||||
class CVTypeDumper {
|
||||
public:
|
||||
explicit CVTypeDumper(TypeDatabase &TypeDB,
|
||||
TypeServerHandler *Handler = nullptr)
|
||||
: TypeDB(TypeDB), Handler(Handler) {}
|
||||
|
||||
/// Dumps one type record. Returns false if there was a type parsing error,
|
||||
/// and true otherwise. This should be called in order, since the dumper
|
||||
/// maintains state about previous records which are necessary for cross
|
||||
/// type references.
|
||||
Error dump(const CVType &Record, TypeVisitorCallbacks &Dumper);
|
||||
|
||||
/// Dumps the type records in Types. Returns false if there was a type stream
|
||||
/// parse error, and true otherwise.
|
||||
Error dump(const CVTypeArray &Types, TypeVisitorCallbacks &Dumper);
|
||||
|
||||
/// Dumps the type records in Data. Returns false if there was a type stream
|
||||
/// parse error, and true otherwise. Use this method instead of the
|
||||
/// CVTypeArray overload when type records are laid out contiguously in
|
||||
/// memory.
|
||||
Error dump(ArrayRef<uint8_t> Data, TypeVisitorCallbacks &Dumper);
|
||||
|
||||
static void printTypeIndex(ScopedPrinter &Printer, StringRef FieldName,
|
||||
TypeIndex TI, TypeDatabase &DB);
|
||||
|
||||
private:
|
||||
TypeDatabase &TypeDB;
|
||||
TypeServerHandler *Handler;
|
||||
};
|
||||
|
||||
} // end namespace codeview
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_CODEVIEW_TYPEDUMPER_H
|
@ -10,15 +10,42 @@
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H
|
||||
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
class TypeCollection;
|
||||
class TypeServerHandler;
|
||||
class TypeVisitorCallbacks;
|
||||
|
||||
class CVTypeVisitor {
|
||||
public:
|
||||
explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks);
|
||||
|
||||
void addTypeServerHandler(TypeServerHandler &Handler);
|
||||
|
||||
Error visitTypeRecord(CVType &Record, TypeIndex Index);
|
||||
Error visitTypeRecord(CVType &Record);
|
||||
Error visitMemberRecord(CVMemberRecord Record);
|
||||
|
||||
/// Visits the type records in Data. Sets the error flag on parse failures.
|
||||
Error visitTypeStream(const CVTypeArray &Types);
|
||||
Error visitTypeStream(CVTypeRange Types);
|
||||
|
||||
Error visitFieldListMemberStream(ArrayRef<uint8_t> FieldList);
|
||||
Error visitFieldListMemberStream(BinaryStreamReader Reader);
|
||||
|
||||
private:
|
||||
Expected<bool> handleTypeServer(CVType &Record);
|
||||
Error finishVisitation(CVType &Record);
|
||||
|
||||
/// The interface to the class that gets notified of each visitation.
|
||||
TypeVisitorCallbacks &Callbacks;
|
||||
|
||||
TinyPtrVector<TypeServerHandler *> Handlers;
|
||||
};
|
||||
|
||||
enum VisitorDataSource {
|
||||
VDS_BytesPresent, // The record bytes are passed into the the visitation
|
||||
@ -49,8 +76,6 @@ Error visitTypeStream(const CVTypeArray &Types, TypeVisitorCallbacks &Callbacks,
|
||||
TypeServerHandler *TS = nullptr);
|
||||
Error visitTypeStream(CVTypeRange Types, TypeVisitorCallbacks &Callbacks,
|
||||
TypeServerHandler *TS = nullptr);
|
||||
Error visitTypeStream(TypeCollection &Types, TypeVisitorCallbacks &Callbacks,
|
||||
TypeServerHandler *TS = nullptr);
|
||||
|
||||
} // end namespace codeview
|
||||
} // end namespace llvm
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===- LazyRandomTypeCollection.h ---------------------------- *- C++ --*-===//
|
||||
//===- RandomAccessTypeVisitor.h ------------------------------ *- C++ --*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -7,10 +7,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_LAZYRANDOMTYPECOLLECTION_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_LAZYRANDOMTYPECOLLECTION_H
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_RANDOMACCESSTYPEVISITOR_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_RANDOMACCESSTYPEVISITOR_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
@ -21,6 +21,7 @@ namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class TypeDatabase;
|
||||
class TypeServerHandler;
|
||||
class TypeVisitorCallbacks;
|
||||
|
||||
/// \brief Provides amortized O(1) random access to a CodeView type stream.
|
||||
@ -39,48 +40,32 @@ class TypeVisitorCallbacks;
|
||||
/// consumer much better access time, because the consumer can find the nearest
|
||||
/// index in this array, and do a linear scan forward only from there.
|
||||
///
|
||||
/// LazyRandomTypeCollection implements this algorithm, but additionally goes
|
||||
/// one step further by caching offsets of every record that has been visited at
|
||||
/// RandomAccessTypeVisitor implements this algorithm, but additionally goes one
|
||||
/// step further by caching offsets of every record that has been visited at
|
||||
/// least once. This way, even repeated visits of the same record will never
|
||||
/// require more than one linear scan. For a type stream of N elements divided
|
||||
/// into M chunks of roughly equal size, this yields a worst case lookup time
|
||||
/// of O(N/M) and an amortized time of O(1).
|
||||
class LazyRandomTypeCollection : public TypeCollection {
|
||||
class RandomAccessTypeVisitor {
|
||||
typedef FixedStreamArray<TypeIndexOffset> PartialOffsetArray;
|
||||
|
||||
public:
|
||||
explicit LazyRandomTypeCollection(uint32_t RecordCountHint);
|
||||
LazyRandomTypeCollection(StringRef Data, uint32_t RecordCountHint);
|
||||
LazyRandomTypeCollection(ArrayRef<uint8_t> Data, uint32_t RecordCountHint);
|
||||
LazyRandomTypeCollection(const CVTypeArray &Types, uint32_t RecordCountHint,
|
||||
PartialOffsetArray PartialOffsets);
|
||||
LazyRandomTypeCollection(const CVTypeArray &Types, uint32_t RecordCountHint);
|
||||
RandomAccessTypeVisitor(const CVTypeArray &Types, uint32_t NumRecords,
|
||||
PartialOffsetArray PartialOffsets);
|
||||
|
||||
void reset(ArrayRef<uint8_t> Data);
|
||||
void reset(StringRef Data);
|
||||
Error visitTypeIndex(TypeIndex Index, TypeVisitorCallbacks &Callbacks);
|
||||
|
||||
CVType getType(TypeIndex Index) override;
|
||||
StringRef getTypeName(TypeIndex Index) override;
|
||||
bool contains(TypeIndex Index) override;
|
||||
uint32_t size() override;
|
||||
uint32_t capacity() override;
|
||||
TypeIndex getFirst() override;
|
||||
Optional<TypeIndex> getNext(TypeIndex Prev) override;
|
||||
const TypeDatabase &database() const { return Database; }
|
||||
|
||||
private:
|
||||
const TypeDatabase &database() const { return Database; }
|
||||
Error ensureTypeExists(TypeIndex Index);
|
||||
|
||||
Error visitRangeForType(TypeIndex TI);
|
||||
Error fullScanForType(TypeIndex TI);
|
||||
Error visitRange(TypeIndex Begin, uint32_t BeginOffset, TypeIndex End);
|
||||
Error visitOneRecord(TypeIndex TI, uint32_t Offset, CVType &Record);
|
||||
|
||||
/// Visited records get automatically added to the type database.
|
||||
TypeDatabase Database;
|
||||
|
||||
/// The type array to allow random access visitation of.
|
||||
CVTypeArray Types;
|
||||
const CVTypeArray &Types;
|
||||
|
||||
/// The database visitor which adds new records to the database.
|
||||
TypeDatabaseVisitor DatabaseVisitor;
|
||||
@ -100,4 +85,4 @@ private:
|
||||
} // end namespace codeview
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_CODEVIEW_LAZYRANDOMTYPECOLLECTION_H
|
||||
#endif // LLVM_DEBUGINFO_CODEVIEW_RANDOMACCESSTYPEVISITOR_H
|
@ -20,15 +20,15 @@ namespace llvm {
|
||||
class ScopedPrinter;
|
||||
|
||||
namespace codeview {
|
||||
class TypeCollection;
|
||||
class TypeDatabase;
|
||||
|
||||
/// Dumper for CodeView symbol streams found in COFF object files and PDB files.
|
||||
class CVSymbolDumper {
|
||||
public:
|
||||
CVSymbolDumper(ScopedPrinter &W, TypeCollection &Types,
|
||||
CVSymbolDumper(ScopedPrinter &W, TypeDatabase &TypeDB,
|
||||
std::unique_ptr<SymbolDumpDelegate> ObjDelegate,
|
||||
bool PrintRecordBytes)
|
||||
: W(W), Types(Types), ObjDelegate(std::move(ObjDelegate)),
|
||||
: W(W), TypeDB(TypeDB), ObjDelegate(std::move(ObjDelegate)),
|
||||
PrintRecordBytes(PrintRecordBytes) {}
|
||||
|
||||
/// Dumps one type record. Returns false if there was a type parsing error,
|
||||
@ -43,7 +43,7 @@ public:
|
||||
|
||||
private:
|
||||
ScopedPrinter &W;
|
||||
TypeCollection &Types;
|
||||
TypeDatabase &TypeDB;
|
||||
std::unique_ptr<SymbolDumpDelegate> ObjDelegate;
|
||||
|
||||
bool PrintRecordBytes;
|
||||
|
@ -1,38 +0,0 @@
|
||||
//===- TypeCollection.h - A collection of CodeView type records -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPECOLLECTION_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPECOLLECTION_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
class TypeCollection {
|
||||
public:
|
||||
virtual ~TypeCollection() = default;
|
||||
|
||||
bool empty() { return size() == 0; }
|
||||
|
||||
virtual TypeIndex getFirst() = 0;
|
||||
virtual Optional<TypeIndex> getNext(TypeIndex Prev) = 0;
|
||||
|
||||
virtual CVType getType(TypeIndex Index) = 0;
|
||||
virtual StringRef getTypeName(TypeIndex Index) = 0;
|
||||
virtual bool contains(TypeIndex Index) = 0;
|
||||
virtual uint32_t size() = 0;
|
||||
virtual uint32_t capacity() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -13,7 +13,6 @@
|
||||
#include "llvm/ADT/BitVector.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
@ -21,7 +20,7 @@
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
class TypeDatabase : public TypeCollection {
|
||||
class TypeDatabase {
|
||||
friend class RandomAccessTypeVisitor;
|
||||
|
||||
public:
|
||||
@ -42,31 +41,19 @@ public:
|
||||
CVType &getTypeRecord(TypeIndex Index);
|
||||
|
||||
bool contains(TypeIndex Index) const;
|
||||
|
||||
uint32_t size() const;
|
||||
uint32_t capacity() const;
|
||||
bool empty() const;
|
||||
|
||||
CVType getType(TypeIndex Index) override;
|
||||
StringRef getTypeName(TypeIndex Index) override;
|
||||
bool contains(TypeIndex Index) override;
|
||||
uint32_t size() override;
|
||||
uint32_t capacity() override;
|
||||
|
||||
TypeIndex getFirst() override;
|
||||
Optional<TypeIndex> getNext(TypeIndex Prev) override;
|
||||
|
||||
Optional<TypeIndex> largestTypeIndexLessThan(TypeIndex TI) const;
|
||||
|
||||
private:
|
||||
TypeIndex getAppendIndex() const;
|
||||
|
||||
private:
|
||||
void grow();
|
||||
void grow(TypeIndex Index);
|
||||
|
||||
BumpPtrAllocator Allocator;
|
||||
|
||||
uint32_t Count = 0;
|
||||
TypeIndex LargestTypeIndex;
|
||||
|
||||
/// All user defined type records in .debug$T live in here. Type indices
|
||||
/// greater than 0x1000 are user defined. Subtract 0x1000 from the index to
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
@ -21,20 +22,17 @@ class ScopedPrinter;
|
||||
|
||||
namespace codeview {
|
||||
|
||||
class TypeCollection;
|
||||
|
||||
/// Dumper for CodeView type streams found in COFF object files and PDB files.
|
||||
class TypeDumpVisitor : public TypeVisitorCallbacks {
|
||||
public:
|
||||
TypeDumpVisitor(TypeCollection &TpiTypes, ScopedPrinter *W,
|
||||
bool PrintRecordBytes)
|
||||
: W(W), PrintRecordBytes(PrintRecordBytes), TpiTypes(TpiTypes) {}
|
||||
TypeDumpVisitor(TypeDatabase &TypeDB, ScopedPrinter *W, bool PrintRecordBytes)
|
||||
: W(W), PrintRecordBytes(PrintRecordBytes), TypeDB(TypeDB) {}
|
||||
|
||||
/// When dumping types from an IPI stream in a PDB, a type index may refer to
|
||||
/// a type or an item ID. The dumper will lookup the "name" of the index in
|
||||
/// the item database if appropriate. If ItemDB is null, it will use TypeDB,
|
||||
/// which is correct when dumping types from an object file (/Z7).
|
||||
void setIpiTypes(TypeCollection &Types) { IpiTypes = &Types; }
|
||||
void setItemDB(TypeDatabase &DB) { ItemDB = &DB; }
|
||||
|
||||
void printTypeIndex(StringRef FieldName, TypeIndex TI) const;
|
||||
|
||||
@ -68,16 +66,14 @@ private:
|
||||
/// Get the database of indices for the stream that we are dumping. If ItemDB
|
||||
/// is set, then we must be dumping an item (IPI) stream. This will also
|
||||
/// always get the appropriate DB for printing item names.
|
||||
TypeCollection &getSourceTypes() const {
|
||||
return IpiTypes ? *IpiTypes : TpiTypes;
|
||||
}
|
||||
TypeDatabase &getSourceDB() const { return ItemDB ? *ItemDB : TypeDB; }
|
||||
|
||||
ScopedPrinter *W;
|
||||
|
||||
bool PrintRecordBytes = false;
|
||||
|
||||
TypeCollection &TpiTypes;
|
||||
TypeCollection *IpiTypes = nullptr;
|
||||
TypeDatabase &TypeDB;
|
||||
TypeDatabase *ItemDB = nullptr;
|
||||
};
|
||||
|
||||
} // end namespace codeview
|
||||
|
@ -15,13 +15,8 @@
|
||||
#include <cinttypes>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class ScopedPrinter;
|
||||
|
||||
namespace codeview {
|
||||
|
||||
class TypeCollection;
|
||||
|
||||
enum class SimpleTypeKind : uint32_t {
|
||||
None = 0x0000, // uncharacterized type (no type)
|
||||
Void = 0x0003, // void
|
||||
@ -243,11 +238,6 @@ public:
|
||||
return Result;
|
||||
}
|
||||
|
||||
friend inline uint32_t operator-(const TypeIndex &A, const TypeIndex &B) {
|
||||
assert(A >= B);
|
||||
return A.toArrayIndex() - B.toArrayIndex();
|
||||
}
|
||||
|
||||
private:
|
||||
support::ulittle32_t Index;
|
||||
};
|
||||
@ -259,9 +249,6 @@ struct TypeIndexOffset {
|
||||
TypeIndex Type;
|
||||
support::ulittle32_t Offset;
|
||||
};
|
||||
|
||||
void printTypeIndex(ScopedPrinter &Printer, StringRef FieldName, TypeIndex TI,
|
||||
TypeCollection &Types);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
//===- TypeTableCollection.h ---------------------------------- *- C++ --*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPETABLECOLLECTION_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPETABLECOLLECTION_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class TypeTableCollection : public TypeCollection {
|
||||
public:
|
||||
explicit TypeTableCollection(ArrayRef<MutableArrayRef<uint8_t>> Records);
|
||||
|
||||
TypeIndex getFirst() override;
|
||||
Optional<TypeIndex> getNext(TypeIndex Prev) override;
|
||||
|
||||
CVType getType(TypeIndex Index) override;
|
||||
StringRef getTypeName(TypeIndex Index) override;
|
||||
bool contains(TypeIndex Index) override;
|
||||
uint32_t size() override;
|
||||
uint32_t capacity() override;
|
||||
|
||||
private:
|
||||
bool hasCapacityFor(TypeIndex Index) const;
|
||||
void ensureTypeExists(TypeIndex Index);
|
||||
|
||||
ArrayRef<MutableArrayRef<uint8_t>> Records;
|
||||
TypeDatabase Database;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -17,6 +17,8 @@ namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class TypeVisitorCallbacks {
|
||||
friend class CVTypeVisitor;
|
||||
|
||||
public:
|
||||
virtual ~TypeVisitorCallbacks() = default;
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "CodeViewDebug.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/Line.h"
|
||||
@ -22,7 +23,6 @@
|
||||
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
@ -469,21 +469,17 @@ void CodeViewDebug::emitTypeInformation() {
|
||||
CommentPrefix += ' ';
|
||||
}
|
||||
|
||||
TypeTableCollection Table(TypeTable.records());
|
||||
Optional<TypeIndex> B = Table.getFirst();
|
||||
do {
|
||||
// This will fail if the record data is invalid.
|
||||
CVType Record = Table.getType(*B);
|
||||
|
||||
TypeDatabase TypeDB(TypeTable.records().size());
|
||||
CVTypeDumper CVTD(TypeDB);
|
||||
TypeTable.ForEachRecord([&](TypeIndex Index, ArrayRef<uint8_t> Record) {
|
||||
if (OS.isVerboseAsm()) {
|
||||
// Emit a block comment describing the type record for readability.
|
||||
SmallString<512> CommentBlock;
|
||||
raw_svector_ostream CommentOS(CommentBlock);
|
||||
ScopedPrinter SP(CommentOS);
|
||||
SP.setPrefix(CommentPrefix);
|
||||
TypeDumpVisitor TDV(Table, &SP, false);
|
||||
|
||||
Error E = codeview::visitTypeRecord(Record, *B, TDV);
|
||||
TypeDumpVisitor TDV(TypeDB, &SP, false);
|
||||
Error E = CVTD.dump(Record, TDV);
|
||||
if (E) {
|
||||
logAllUnhandledErrors(std::move(E), errs(), "error: ");
|
||||
llvm_unreachable("produced malformed type record");
|
||||
@ -493,9 +489,29 @@ void CodeViewDebug::emitTypeInformation() {
|
||||
// newline.
|
||||
OS.emitRawComment(
|
||||
CommentOS.str().drop_front(CommentPrefix.size() - 1).rtrim());
|
||||
} else {
|
||||
#ifndef NDEBUG
|
||||
// Assert that the type data is valid even if we aren't dumping
|
||||
// comments. The MSVC linker doesn't do much type record validation,
|
||||
// so the first link of an invalid type record can succeed while
|
||||
// subsequent links will fail with LNK1285.
|
||||
BinaryByteStream Stream(Record, llvm::support::little);
|
||||
CVTypeArray Types;
|
||||
BinaryStreamReader Reader(Stream);
|
||||
Error E = Reader.readArray(Types, Reader.getLength());
|
||||
if (!E) {
|
||||
TypeVisitorCallbacks C;
|
||||
E = codeview::visitTypeStream(Types, C);
|
||||
}
|
||||
if (E) {
|
||||
logAllUnhandledErrors(std::move(E), errs(), "error: ");
|
||||
llvm_unreachable("produced malformed type record");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
OS.EmitBinaryData(Record.str_data());
|
||||
} while ((B = Table.getNext(*B)));
|
||||
StringRef S(reinterpret_cast<const char *>(Record.data()), Record.size());
|
||||
OS.EmitBinaryData(S);
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -2,10 +2,10 @@ add_llvm_library(LLVMDebugInfoCodeView
|
||||
CodeViewError.cpp
|
||||
CodeViewRecordIO.cpp
|
||||
CVSymbolVisitor.cpp
|
||||
CVTypeDumper.cpp
|
||||
CVTypeVisitor.cpp
|
||||
EnumTables.cpp
|
||||
Formatters.cpp
|
||||
LazyRandomTypeCollection.cpp
|
||||
Line.cpp
|
||||
ModuleDebugFileChecksumFragment.cpp
|
||||
ModuleDebugFragment.cpp
|
||||
@ -13,6 +13,7 @@ add_llvm_library(LLVMDebugInfoCodeView
|
||||
ModuleDebugFragmentVisitor.cpp
|
||||
ModuleDebugInlineeLinesFragment.cpp
|
||||
ModuleDebugLineFragment.cpp
|
||||
RandomAccessTypeVisitor.cpp
|
||||
RecordSerialization.cpp
|
||||
StringTable.cpp
|
||||
SymbolRecordMapping.cpp
|
||||
@ -21,12 +22,10 @@ add_llvm_library(LLVMDebugInfoCodeView
|
||||
TypeDatabase.cpp
|
||||
TypeDatabaseVisitor.cpp
|
||||
TypeDumpVisitor.cpp
|
||||
TypeIndex.cpp
|
||||
TypeRecordMapping.cpp
|
||||
TypeSerializer.cpp
|
||||
TypeStreamMerger.cpp
|
||||
TypeTableCollection.cpp
|
||||
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/CodeView
|
||||
)
|
||||
|
61
lib/DebugInfo/CodeView/CVTypeDumper.cpp
Normal file
61
lib/DebugInfo/CodeView/CVTypeDumper.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
//===-- CVTypeDumper.cpp - CodeView type info dumper ------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
Error CVTypeDumper::dump(const CVType &Record, TypeVisitorCallbacks &Dumper) {
|
||||
TypeDatabaseVisitor DBV(TypeDB);
|
||||
TypeVisitorCallbackPipeline Pipeline;
|
||||
Pipeline.addCallbackToPipeline(DBV);
|
||||
Pipeline.addCallbackToPipeline(Dumper);
|
||||
|
||||
CVType RecordCopy = Record;
|
||||
return codeview::visitTypeRecord(RecordCopy, Pipeline, VDS_BytesPresent,
|
||||
Handler);
|
||||
}
|
||||
|
||||
Error CVTypeDumper::dump(const CVTypeArray &Types,
|
||||
TypeVisitorCallbacks &Dumper) {
|
||||
TypeDatabaseVisitor DBV(TypeDB);
|
||||
TypeVisitorCallbackPipeline Pipeline;
|
||||
Pipeline.addCallbackToPipeline(DBV);
|
||||
Pipeline.addCallbackToPipeline(Dumper);
|
||||
|
||||
return codeview::visitTypeStream(Types, Pipeline, Handler);
|
||||
}
|
||||
|
||||
Error CVTypeDumper::dump(ArrayRef<uint8_t> Data, TypeVisitorCallbacks &Dumper) {
|
||||
BinaryByteStream Stream(Data, llvm::support::little);
|
||||
CVTypeArray Types;
|
||||
BinaryStreamReader Reader(Stream);
|
||||
if (auto EC = Reader.readArray(Types, Reader.getLength()))
|
||||
return EC;
|
||||
|
||||
return dump(Types, Dumper);
|
||||
}
|
||||
|
||||
void CVTypeDumper::printTypeIndex(ScopedPrinter &Printer, StringRef FieldName,
|
||||
TypeIndex TI, TypeDatabase &DB) {
|
||||
StringRef TypeName;
|
||||
if (!TI.isNoneType())
|
||||
TypeName = DB.getTypeName(TI);
|
||||
if (!TypeName.empty())
|
||||
Printer.printHex(FieldName, TypeName, TI.getIndex());
|
||||
else
|
||||
Printer.printHex(FieldName, TI.getIndex());
|
||||
}
|
@ -9,9 +9,7 @@
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeViewError.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
@ -24,6 +22,8 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
|
||||
: Callbacks(Callbacks) {}
|
||||
|
||||
template <typename T>
|
||||
static Error visitKnownRecord(CVType &Record, TypeVisitorCallbacks &Callbacks) {
|
||||
@ -66,67 +66,6 @@ static Expected<TypeServer2Record> deserializeTypeServerRecord(CVType &Record) {
|
||||
return R;
|
||||
}
|
||||
|
||||
static Error visitMemberRecord(CVMemberRecord &Record,
|
||||
TypeVisitorCallbacks &Callbacks) {
|
||||
if (auto EC = Callbacks.visitMemberBegin(Record))
|
||||
return EC;
|
||||
|
||||
switch (Record.Kind) {
|
||||
default:
|
||||
if (auto EC = Callbacks.visitUnknownMember(Record))
|
||||
return EC;
|
||||
break;
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
case EnumName: { \
|
||||
if (auto EC = visitKnownMember<Name##Record>(Record, Callbacks)) \
|
||||
return EC; \
|
||||
break; \
|
||||
}
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
|
||||
MEMBER_RECORD(EnumVal, EnumVal, AliasName)
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name)
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
}
|
||||
|
||||
if (auto EC = Callbacks.visitMemberEnd(Record))
|
||||
return EC;
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class CVTypeVisitor {
|
||||
public:
|
||||
explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks);
|
||||
|
||||
void addTypeServerHandler(TypeServerHandler &Handler);
|
||||
|
||||
Error visitTypeRecord(CVType &Record, TypeIndex Index);
|
||||
Error visitTypeRecord(CVType &Record);
|
||||
|
||||
/// Visits the type records in Data. Sets the error flag on parse failures.
|
||||
Error visitTypeStream(const CVTypeArray &Types);
|
||||
Error visitTypeStream(CVTypeRange Types);
|
||||
Error visitTypeStream(TypeCollection &Types);
|
||||
|
||||
Error visitMemberRecord(CVMemberRecord Record);
|
||||
Error visitFieldListMemberStream(BinaryStreamReader &Stream);
|
||||
|
||||
private:
|
||||
Expected<bool> handleTypeServer(CVType &Record);
|
||||
Error finishVisitation(CVType &Record);
|
||||
|
||||
/// The interface to the class that gets notified of each visitation.
|
||||
TypeVisitorCallbacks &Callbacks;
|
||||
|
||||
TinyPtrVector<TypeServerHandler *> Handlers;
|
||||
};
|
||||
|
||||
CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
|
||||
: Callbacks(Callbacks) {}
|
||||
|
||||
void CVTypeVisitor::addTypeServerHandler(TypeServerHandler &Handler) {
|
||||
Handlers.push_back(&Handler);
|
||||
}
|
||||
@ -205,6 +144,35 @@ Error CVTypeVisitor::visitTypeRecord(CVType &Record) {
|
||||
return finishVisitation(Record);
|
||||
}
|
||||
|
||||
static Error visitMemberRecord(CVMemberRecord &Record,
|
||||
TypeVisitorCallbacks &Callbacks) {
|
||||
if (auto EC = Callbacks.visitMemberBegin(Record))
|
||||
return EC;
|
||||
|
||||
switch (Record.Kind) {
|
||||
default:
|
||||
if (auto EC = Callbacks.visitUnknownMember(Record))
|
||||
return EC;
|
||||
break;
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
case EnumName: { \
|
||||
if (auto EC = visitKnownMember<Name##Record>(Record, Callbacks)) \
|
||||
return EC; \
|
||||
break; \
|
||||
}
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
|
||||
MEMBER_RECORD(EnumVal, EnumVal, AliasName)
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name)
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
}
|
||||
|
||||
if (auto EC = Callbacks.visitMemberEnd(Record))
|
||||
return EC;
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error CVTypeVisitor::visitMemberRecord(CVMemberRecord Record) {
|
||||
return ::visitMemberRecord(Record, Callbacks);
|
||||
}
|
||||
@ -226,20 +194,12 @@ Error CVTypeVisitor::visitTypeStream(CVTypeRange Types) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error CVTypeVisitor::visitTypeStream(TypeCollection &Types) {
|
||||
if (Types.empty())
|
||||
return Error::success();
|
||||
Error CVTypeVisitor::visitFieldListMemberStream(BinaryStreamReader Reader) {
|
||||
FieldListDeserializer Deserializer(Reader);
|
||||
TypeVisitorCallbackPipeline Pipeline;
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Callbacks);
|
||||
|
||||
Optional<TypeIndex> I = Types.getFirst();
|
||||
do {
|
||||
CVType Type = Types.getType(*I);
|
||||
if (auto EC = visitTypeRecord(Type, *I))
|
||||
return EC;
|
||||
} while ((I = Types.getNext(*I)));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error CVTypeVisitor::visitFieldListMemberStream(BinaryStreamReader &Reader) {
|
||||
TypeLeafKind Leaf;
|
||||
while (!Reader.empty()) {
|
||||
if (auto EC = Reader.readEnum(Leaf))
|
||||
@ -247,13 +207,20 @@ Error CVTypeVisitor::visitFieldListMemberStream(BinaryStreamReader &Reader) {
|
||||
|
||||
CVMemberRecord Record;
|
||||
Record.Kind = Leaf;
|
||||
if (auto EC = ::visitMemberRecord(Record, Callbacks))
|
||||
if (auto EC = ::visitMemberRecord(Record, Pipeline))
|
||||
return EC;
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error CVTypeVisitor::visitFieldListMemberStream(ArrayRef<uint8_t> Data) {
|
||||
BinaryByteStream S(Data, llvm::support::little);
|
||||
BinaryStreamReader SR(S);
|
||||
return visitFieldListMemberStream(SR);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct FieldListVisitHelper {
|
||||
FieldListVisitHelper(TypeVisitorCallbacks &Callbacks, ArrayRef<uint8_t> Data,
|
||||
VisitorDataSource Source)
|
||||
@ -274,8 +241,11 @@ struct FieldListVisitHelper {
|
||||
};
|
||||
|
||||
struct VisitHelper {
|
||||
VisitHelper(TypeVisitorCallbacks &Callbacks, VisitorDataSource Source)
|
||||
VisitHelper(TypeVisitorCallbacks &Callbacks, VisitorDataSource Source,
|
||||
TypeServerHandler *TS)
|
||||
: Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) {
|
||||
if (TS)
|
||||
Visitor.addTypeServerHandler(*TS);
|
||||
if (Source == VDS_BytesPresent) {
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Callbacks);
|
||||
@ -292,57 +262,29 @@ Error llvm::codeview::visitTypeRecord(CVType &Record, TypeIndex Index,
|
||||
TypeVisitorCallbacks &Callbacks,
|
||||
VisitorDataSource Source,
|
||||
TypeServerHandler *TS) {
|
||||
VisitHelper V(Callbacks, Source);
|
||||
if (TS)
|
||||
V.Visitor.addTypeServerHandler(*TS);
|
||||
return V.Visitor.visitTypeRecord(Record, Index);
|
||||
VisitHelper Helper(Callbacks, Source, TS);
|
||||
return Helper.Visitor.visitTypeRecord(Record, Index);
|
||||
}
|
||||
|
||||
Error llvm::codeview::visitTypeRecord(CVType &Record,
|
||||
TypeVisitorCallbacks &Callbacks,
|
||||
VisitorDataSource Source,
|
||||
TypeServerHandler *TS) {
|
||||
VisitHelper V(Callbacks, Source);
|
||||
if (TS)
|
||||
V.Visitor.addTypeServerHandler(*TS);
|
||||
return V.Visitor.visitTypeRecord(Record);
|
||||
VisitHelper Helper(Callbacks, Source, TS);
|
||||
return Helper.Visitor.visitTypeRecord(Record);
|
||||
}
|
||||
|
||||
Error llvm::codeview::visitTypeStream(const CVTypeArray &Types,
|
||||
TypeVisitorCallbacks &Callbacks,
|
||||
TypeServerHandler *TS) {
|
||||
VisitHelper V(Callbacks, VDS_BytesPresent);
|
||||
if (TS)
|
||||
V.Visitor.addTypeServerHandler(*TS);
|
||||
return V.Visitor.visitTypeStream(Types);
|
||||
}
|
||||
|
||||
Error llvm::codeview::visitTypeStream(CVTypeRange Types,
|
||||
TypeVisitorCallbacks &Callbacks,
|
||||
TypeServerHandler *TS) {
|
||||
VisitHelper V(Callbacks, VDS_BytesPresent);
|
||||
if (TS)
|
||||
V.Visitor.addTypeServerHandler(*TS);
|
||||
return V.Visitor.visitTypeStream(Types);
|
||||
}
|
||||
|
||||
Error llvm::codeview::visitTypeStream(TypeCollection &Types,
|
||||
TypeVisitorCallbacks &Callbacks,
|
||||
TypeServerHandler *TS) {
|
||||
// When the internal visitor calls Types.getType(Index) the interface is
|
||||
// required to return a CVType with the bytes filled out. So we can assume
|
||||
// that the bytes will be present when individual records are visited.
|
||||
VisitHelper V(Callbacks, VDS_BytesPresent);
|
||||
if (TS)
|
||||
V.Visitor.addTypeServerHandler(*TS);
|
||||
return V.Visitor.visitTypeStream(Types);
|
||||
Error llvm::codeview::visitMemberRecordStream(ArrayRef<uint8_t> FieldList,
|
||||
TypeVisitorCallbacks &Callbacks) {
|
||||
CVTypeVisitor Visitor(Callbacks);
|
||||
return Visitor.visitFieldListMemberStream(FieldList);
|
||||
}
|
||||
|
||||
Error llvm::codeview::visitMemberRecord(CVMemberRecord Record,
|
||||
TypeVisitorCallbacks &Callbacks,
|
||||
VisitorDataSource Source) {
|
||||
FieldListVisitHelper V(Callbacks, Record.Data, Source);
|
||||
return V.Visitor.visitMemberRecord(Record);
|
||||
FieldListVisitHelper Helper(Callbacks, Record.Data, Source);
|
||||
return Helper.Visitor.visitMemberRecord(Record);
|
||||
}
|
||||
|
||||
Error llvm::codeview::visitMemberRecord(TypeLeafKind Kind,
|
||||
@ -354,8 +296,16 @@ Error llvm::codeview::visitMemberRecord(TypeLeafKind Kind,
|
||||
return visitMemberRecord(R, Callbacks, VDS_BytesPresent);
|
||||
}
|
||||
|
||||
Error llvm::codeview::visitMemberRecordStream(ArrayRef<uint8_t> FieldList,
|
||||
TypeVisitorCallbacks &Callbacks) {
|
||||
FieldListVisitHelper V(Callbacks, FieldList, VDS_BytesPresent);
|
||||
return V.Visitor.visitFieldListMemberStream(V.Reader);
|
||||
Error llvm::codeview::visitTypeStream(const CVTypeArray &Types,
|
||||
TypeVisitorCallbacks &Callbacks,
|
||||
TypeServerHandler *TS) {
|
||||
VisitHelper Helper(Callbacks, VDS_BytesPresent, TS);
|
||||
return Helper.Visitor.visitTypeStream(Types);
|
||||
}
|
||||
|
||||
Error llvm::codeview::visitTypeStream(CVTypeRange Types,
|
||||
TypeVisitorCallbacks &Callbacks,
|
||||
TypeServerHandler *TS) {
|
||||
VisitHelper Helper(Callbacks, VDS_BytesPresent, TS);
|
||||
return Helper.Visitor.visitTypeStream(Types);
|
||||
}
|
||||
|
@ -1,226 +0,0 @@
|
||||
//===- LazyRandomTypeCollection.cpp ---------------------------- *- C++--*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeViewError.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
static void error(Error &&EC) {
|
||||
assert(!static_cast<bool>(EC));
|
||||
if (EC)
|
||||
consumeError(std::move(EC));
|
||||
}
|
||||
|
||||
LazyRandomTypeCollection::LazyRandomTypeCollection(uint32_t RecordCountHint)
|
||||
: LazyRandomTypeCollection(CVTypeArray(), RecordCountHint,
|
||||
PartialOffsetArray()) {}
|
||||
|
||||
LazyRandomTypeCollection::LazyRandomTypeCollection(
|
||||
const CVTypeArray &Types, uint32_t RecordCountHint,
|
||||
PartialOffsetArray PartialOffsets)
|
||||
: Database(RecordCountHint), Types(Types), DatabaseVisitor(Database),
|
||||
PartialOffsets(PartialOffsets) {
|
||||
KnownOffsets.resize(Database.capacity());
|
||||
}
|
||||
|
||||
LazyRandomTypeCollection::LazyRandomTypeCollection(ArrayRef<uint8_t> Data,
|
||||
uint32_t RecordCountHint)
|
||||
: LazyRandomTypeCollection(RecordCountHint) {
|
||||
reset(Data);
|
||||
}
|
||||
|
||||
LazyRandomTypeCollection::LazyRandomTypeCollection(StringRef Data,
|
||||
uint32_t RecordCountHint)
|
||||
: LazyRandomTypeCollection(
|
||||
makeArrayRef(Data.bytes_begin(), Data.bytes_end()), RecordCountHint) {
|
||||
}
|
||||
|
||||
LazyRandomTypeCollection::LazyRandomTypeCollection(const CVTypeArray &Types,
|
||||
uint32_t NumRecords)
|
||||
: LazyRandomTypeCollection(Types, NumRecords, PartialOffsetArray()) {}
|
||||
|
||||
void LazyRandomTypeCollection::reset(StringRef Data) {
|
||||
reset(makeArrayRef(Data.bytes_begin(), Data.bytes_end()));
|
||||
}
|
||||
|
||||
void LazyRandomTypeCollection::reset(ArrayRef<uint8_t> Data) {
|
||||
PartialOffsets = PartialOffsetArray();
|
||||
|
||||
BinaryStreamReader Reader(Data, support::little);
|
||||
error(Reader.readArray(Types, Reader.getLength()));
|
||||
|
||||
KnownOffsets.resize(Database.capacity());
|
||||
}
|
||||
|
||||
CVType LazyRandomTypeCollection::getType(TypeIndex Index) {
|
||||
error(ensureTypeExists(Index));
|
||||
return Database.getTypeRecord(Index);
|
||||
}
|
||||
|
||||
StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) {
|
||||
if (!Index.isSimple()) {
|
||||
// Try to make sure the type exists. Even if it doesn't though, it may be
|
||||
// because we're dumping a symbol stream with no corresponding type stream
|
||||
// present, in which case we still want to be able to print <unknown UDT>
|
||||
// for the type names.
|
||||
consumeError(ensureTypeExists(Index));
|
||||
}
|
||||
|
||||
return Database.getTypeName(Index);
|
||||
}
|
||||
|
||||
bool LazyRandomTypeCollection::contains(TypeIndex Index) {
|
||||
return Database.contains(Index);
|
||||
}
|
||||
|
||||
uint32_t LazyRandomTypeCollection::size() { return Database.size(); }
|
||||
|
||||
uint32_t LazyRandomTypeCollection::capacity() { return Database.capacity(); }
|
||||
|
||||
Error LazyRandomTypeCollection::ensureTypeExists(TypeIndex TI) {
|
||||
if (!Database.contains(TI)) {
|
||||
if (auto EC = visitRangeForType(TI))
|
||||
return EC;
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error LazyRandomTypeCollection::visitRangeForType(TypeIndex TI) {
|
||||
if (PartialOffsets.empty())
|
||||
return fullScanForType(TI);
|
||||
|
||||
auto Next = std::upper_bound(PartialOffsets.begin(), PartialOffsets.end(), TI,
|
||||
[](TypeIndex Value, const TypeIndexOffset &IO) {
|
||||
return Value < IO.Type;
|
||||
});
|
||||
|
||||
assert(Next != PartialOffsets.begin());
|
||||
auto Prev = std::prev(Next);
|
||||
|
||||
TypeIndex TIB = Prev->Type;
|
||||
if (Database.contains(TIB)) {
|
||||
// They've asked us to fetch a type index, but the entry we found in the
|
||||
// partial offsets array has already been visited. Since we visit an entire
|
||||
// block every time, that means this record should have been previously
|
||||
// discovered. Ultimately, this means this is a request for a non-existant
|
||||
// type index.
|
||||
return make_error<CodeViewError>("Invalid type index");
|
||||
}
|
||||
|
||||
TypeIndex TIE;
|
||||
if (Next == PartialOffsets.end()) {
|
||||
TIE = TypeIndex::fromArrayIndex(Database.capacity());
|
||||
} else {
|
||||
TIE = Next->Type;
|
||||
}
|
||||
|
||||
if (auto EC = visitRange(TIB, Prev->Offset, TIE))
|
||||
return EC;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
TypeIndex LazyRandomTypeCollection::getFirst() {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(0);
|
||||
error(ensureTypeExists(TI));
|
||||
return TI;
|
||||
}
|
||||
|
||||
Optional<TypeIndex> LazyRandomTypeCollection::getNext(TypeIndex Prev) {
|
||||
// We can't be sure how long this type stream is, given that the initial count
|
||||
// given to the constructor is just a hint. So just try to make sure the next
|
||||
// record exists, and if anything goes wrong, we must be at the end.
|
||||
if (auto EC = ensureTypeExists(Prev + 1)) {
|
||||
consumeError(std::move(EC));
|
||||
return None;
|
||||
}
|
||||
|
||||
return Prev + 1;
|
||||
}
|
||||
|
||||
Error LazyRandomTypeCollection::fullScanForType(TypeIndex TI) {
|
||||
assert(PartialOffsets.empty());
|
||||
|
||||
TypeIndex CurrentTI = TypeIndex::fromArrayIndex(0);
|
||||
uint32_t Offset = 0;
|
||||
auto Begin = Types.begin();
|
||||
|
||||
if (!Database.empty()) {
|
||||
// In the case of type streams which we don't know the number of records of,
|
||||
// it's possible to search for a type index triggering a full scan, but then
|
||||
// later additional records are added since we didn't know how many there
|
||||
// would be until we did a full visitation, then you try to access the new
|
||||
// type triggering another full scan. To avoid this, we assume that if the
|
||||
// database has some records, this must be what's going on. So we ask the
|
||||
// database for the largest type index less than the one we're searching for
|
||||
// and only do the forward scan from there.
|
||||
auto Prev = Database.largestTypeIndexLessThan(TI);
|
||||
assert(Prev.hasValue() && "Empty database with valid types?");
|
||||
Offset = KnownOffsets[Prev->toArrayIndex()];
|
||||
CurrentTI = *Prev;
|
||||
++CurrentTI;
|
||||
Begin = Types.at(Offset);
|
||||
++Begin;
|
||||
Offset = Begin.offset();
|
||||
}
|
||||
|
||||
auto End = Types.end();
|
||||
while (Begin != End) {
|
||||
if (auto EC = visitOneRecord(CurrentTI, Offset, *Begin))
|
||||
return EC;
|
||||
|
||||
Offset += Begin.getRecordLength();
|
||||
++Begin;
|
||||
++CurrentTI;
|
||||
}
|
||||
if (CurrentTI <= TI) {
|
||||
return make_error<CodeViewError>("Type Index does not exist!");
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error LazyRandomTypeCollection::visitRange(TypeIndex Begin,
|
||||
uint32_t BeginOffset,
|
||||
TypeIndex End) {
|
||||
|
||||
auto RI = Types.at(BeginOffset);
|
||||
assert(RI != Types.end());
|
||||
|
||||
while (Begin != End) {
|
||||
if (auto EC = visitOneRecord(Begin, BeginOffset, *RI))
|
||||
return EC;
|
||||
|
||||
BeginOffset += RI.getRecordLength();
|
||||
++Begin;
|
||||
++RI;
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error LazyRandomTypeCollection::visitOneRecord(TypeIndex TI, uint32_t Offset,
|
||||
CVType &Record) {
|
||||
assert(!Database.contains(TI));
|
||||
if (auto EC = codeview::visitTypeRecord(Record, TI, DatabaseVisitor))
|
||||
return EC;
|
||||
// Keep the KnownOffsets array the same size as the Database's capacity. Since
|
||||
// we don't always know how many records are in the type stream, we need to be
|
||||
// prepared for the database growing and receicing a type index that can't fit
|
||||
// in our current buffer.
|
||||
if (KnownOffsets.size() < Database.capacity())
|
||||
KnownOffsets.resize(Database.capacity());
|
||||
KnownOffsets[TI.toArrayIndex()] = Offset;
|
||||
return Error::success();
|
||||
}
|
89
lib/DebugInfo/CodeView/RandomAccessTypeVisitor.cpp
Normal file
89
lib/DebugInfo/CodeView/RandomAccessTypeVisitor.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
//===- RandomAccessTypeVisitor.cpp ---------------------------- *- C++ --*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/RandomAccessTypeVisitor.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
RandomAccessTypeVisitor::RandomAccessTypeVisitor(
|
||||
const CVTypeArray &Types, uint32_t NumRecords,
|
||||
PartialOffsetArray PartialOffsets)
|
||||
: Database(NumRecords), Types(Types), DatabaseVisitor(Database),
|
||||
PartialOffsets(PartialOffsets) {
|
||||
|
||||
KnownOffsets.resize(Database.capacity());
|
||||
}
|
||||
|
||||
Error RandomAccessTypeVisitor::visitTypeIndex(TypeIndex TI,
|
||||
TypeVisitorCallbacks &Callbacks) {
|
||||
assert(TI.toArrayIndex() < Database.capacity());
|
||||
|
||||
if (!Database.contains(TI)) {
|
||||
if (auto EC = visitRangeForType(TI))
|
||||
return EC;
|
||||
}
|
||||
|
||||
assert(Database.contains(TI));
|
||||
auto &Record = Database.getTypeRecord(TI);
|
||||
return codeview::visitTypeRecord(Record, TI, Callbacks);
|
||||
}
|
||||
|
||||
Error RandomAccessTypeVisitor::visitRangeForType(TypeIndex TI) {
|
||||
if (PartialOffsets.empty()) {
|
||||
TypeIndex TIB(TypeIndex::FirstNonSimpleIndex);
|
||||
TypeIndex TIE = TIB + Database.capacity();
|
||||
return visitRange(TIB, 0, TIE);
|
||||
}
|
||||
|
||||
auto Next = std::upper_bound(PartialOffsets.begin(), PartialOffsets.end(), TI,
|
||||
[](TypeIndex Value, const TypeIndexOffset &IO) {
|
||||
return Value < IO.Type;
|
||||
});
|
||||
|
||||
assert(Next != PartialOffsets.begin());
|
||||
auto Prev = std::prev(Next);
|
||||
|
||||
TypeIndex TIB = Prev->Type;
|
||||
TypeIndex TIE;
|
||||
if (Next == PartialOffsets.end()) {
|
||||
TIE = TypeIndex::fromArrayIndex(Database.capacity());
|
||||
} else {
|
||||
TIE = Next->Type;
|
||||
}
|
||||
|
||||
if (auto EC = visitRange(TIB, Prev->Offset, TIE))
|
||||
return EC;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error RandomAccessTypeVisitor::visitRange(TypeIndex Begin, uint32_t BeginOffset,
|
||||
TypeIndex End) {
|
||||
|
||||
auto RI = Types.at(BeginOffset);
|
||||
assert(RI != Types.end());
|
||||
|
||||
while (Begin != End) {
|
||||
assert(!Database.contains(Begin));
|
||||
if (auto EC = codeview::visitTypeRecord(*RI, Begin, DatabaseVisitor))
|
||||
return EC;
|
||||
KnownOffsets[Begin.toArrayIndex()] = BeginOffset;
|
||||
|
||||
BeginOffset += RI.getRecordLength();
|
||||
++Begin;
|
||||
++RI;
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/EnumTables.h"
|
||||
#include "llvm/DebugInfo/CodeView/StringTable.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
||||
@ -32,9 +33,9 @@ namespace {
|
||||
/// the visitor out of SymbolDumper.h.
|
||||
class CVSymbolDumperImpl : public SymbolVisitorCallbacks {
|
||||
public:
|
||||
CVSymbolDumperImpl(TypeCollection &Types, SymbolDumpDelegate *ObjDelegate,
|
||||
CVSymbolDumperImpl(TypeDatabase &TypeDB, SymbolDumpDelegate *ObjDelegate,
|
||||
ScopedPrinter &W, bool PrintRecordBytes)
|
||||
: Types(Types), ObjDelegate(ObjDelegate), W(W),
|
||||
: TypeDB(TypeDB), ObjDelegate(ObjDelegate), W(W),
|
||||
PrintRecordBytes(PrintRecordBytes), InFunctionScope(false) {}
|
||||
|
||||
/// CVSymbolVisitor overrides.
|
||||
@ -53,7 +54,7 @@ private:
|
||||
void printLocalVariableAddrGap(ArrayRef<LocalVariableAddrGap> Gaps);
|
||||
void printTypeIndex(StringRef FieldName, TypeIndex TI);
|
||||
|
||||
TypeCollection &Types;
|
||||
TypeDatabase &TypeDB;
|
||||
SymbolDumpDelegate *ObjDelegate;
|
||||
ScopedPrinter &W;
|
||||
|
||||
@ -82,7 +83,7 @@ void CVSymbolDumperImpl::printLocalVariableAddrGap(
|
||||
}
|
||||
|
||||
void CVSymbolDumperImpl::printTypeIndex(StringRef FieldName, TypeIndex TI) {
|
||||
codeview::printTypeIndex(W, FieldName, TI, Types);
|
||||
CVTypeDumper::printTypeIndex(W, FieldName, TI, TypeDB);
|
||||
}
|
||||
|
||||
Error CVSymbolDumperImpl::visitSymbolBegin(CVSymbol &CVR) {
|
||||
@ -669,7 +670,7 @@ Error CVSymbolDumperImpl::visitUnknownSymbol(CVSymbol &CVR) {
|
||||
Error CVSymbolDumper::dump(CVRecord<SymbolKind> &Record) {
|
||||
SymbolVisitorCallbackPipeline Pipeline;
|
||||
SymbolDeserializer Deserializer(ObjDelegate.get());
|
||||
CVSymbolDumperImpl Dumper(Types, ObjDelegate.get(), W, PrintRecordBytes);
|
||||
CVSymbolDumperImpl Dumper(TypeDB, ObjDelegate.get(), W, PrintRecordBytes);
|
||||
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Dumper);
|
||||
@ -680,7 +681,7 @@ Error CVSymbolDumper::dump(CVRecord<SymbolKind> &Record) {
|
||||
Error CVSymbolDumper::dump(const CVSymbolArray &Symbols) {
|
||||
SymbolVisitorCallbackPipeline Pipeline;
|
||||
SymbolDeserializer Deserializer(ObjDelegate.get());
|
||||
CVSymbolDumperImpl Dumper(Types, ObjDelegate.get(), W, PrintRecordBytes);
|
||||
CVSymbolDumperImpl Dumper(TypeDB, ObjDelegate.get(), W, PrintRecordBytes);
|
||||
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Dumper);
|
||||
|
@ -72,20 +72,16 @@ TypeDatabase::TypeDatabase(uint32_t Capacity) : TypeNameStorage(Allocator) {
|
||||
}
|
||||
|
||||
TypeIndex TypeDatabase::appendType(StringRef Name, const CVType &Data) {
|
||||
LargestTypeIndex = getAppendIndex();
|
||||
if (LargestTypeIndex.toArrayIndex() >= capacity())
|
||||
TypeIndex TI;
|
||||
TI = getAppendIndex();
|
||||
if (TI.toArrayIndex() >= capacity())
|
||||
grow();
|
||||
recordType(Name, LargestTypeIndex, Data);
|
||||
return LargestTypeIndex;
|
||||
recordType(Name, TI, Data);
|
||||
return TI;
|
||||
}
|
||||
|
||||
void TypeDatabase::recordType(StringRef Name, TypeIndex Index,
|
||||
const CVType &Data) {
|
||||
LargestTypeIndex = empty() ? Index : std::max(Index, LargestTypeIndex);
|
||||
|
||||
if (LargestTypeIndex.toArrayIndex() >= capacity())
|
||||
grow(Index);
|
||||
|
||||
uint32_t AI = Index.toArrayIndex();
|
||||
|
||||
assert(!contains(Index));
|
||||
@ -148,65 +144,19 @@ uint32_t TypeDatabase::size() const { return Count; }
|
||||
|
||||
uint32_t TypeDatabase::capacity() const { return TypeRecords.size(); }
|
||||
|
||||
CVType TypeDatabase::getType(TypeIndex Index) { return getTypeRecord(Index); }
|
||||
|
||||
StringRef TypeDatabase::getTypeName(TypeIndex Index) {
|
||||
return static_cast<const TypeDatabase *>(this)->getTypeName(Index);
|
||||
}
|
||||
|
||||
bool TypeDatabase::contains(TypeIndex Index) {
|
||||
return static_cast<const TypeDatabase *>(this)->contains(Index);
|
||||
}
|
||||
|
||||
uint32_t TypeDatabase::size() {
|
||||
return static_cast<const TypeDatabase *>(this)->size();
|
||||
}
|
||||
|
||||
uint32_t TypeDatabase::capacity() {
|
||||
return static_cast<const TypeDatabase *>(this)->capacity();
|
||||
}
|
||||
|
||||
void TypeDatabase::grow() { grow(LargestTypeIndex + 1); }
|
||||
|
||||
void TypeDatabase::grow(TypeIndex NewIndex) {
|
||||
uint32_t NewSize = NewIndex.toArrayIndex() + 1;
|
||||
|
||||
if (NewSize <= capacity())
|
||||
return;
|
||||
|
||||
uint32_t NewCapacity = NewSize * 3 / 2;
|
||||
|
||||
TypeRecords.resize(NewCapacity);
|
||||
CVUDTNames.resize(NewCapacity);
|
||||
ValidRecords.resize(NewCapacity);
|
||||
void TypeDatabase::grow() {
|
||||
TypeRecords.emplace_back();
|
||||
CVUDTNames.emplace_back();
|
||||
ValidRecords.resize(ValidRecords.size() + 1);
|
||||
}
|
||||
|
||||
bool TypeDatabase::empty() const { return size() == 0; }
|
||||
|
||||
Optional<TypeIndex> TypeDatabase::largestTypeIndexLessThan(TypeIndex TI) const {
|
||||
uint32_t AI = TI.toArrayIndex();
|
||||
int N = ValidRecords.find_prev(AI);
|
||||
if (N == -1)
|
||||
return None;
|
||||
return TypeIndex::fromArrayIndex(N);
|
||||
}
|
||||
|
||||
TypeIndex TypeDatabase::getAppendIndex() const {
|
||||
if (empty())
|
||||
return TypeIndex::fromArrayIndex(0);
|
||||
|
||||
return LargestTypeIndex + 1;
|
||||
}
|
||||
|
||||
TypeIndex TypeDatabase::getFirst() {
|
||||
int N = ValidRecords.find_first();
|
||||
assert(N != -1);
|
||||
return TypeIndex::fromArrayIndex(N);
|
||||
}
|
||||
|
||||
Optional<TypeIndex> TypeDatabase::getNext(TypeIndex Prev) {
|
||||
int N = ValidRecords.find_next(Prev.toArrayIndex());
|
||||
if (N == -1)
|
||||
return None;
|
||||
return TypeIndex::fromArrayIndex(N);
|
||||
int Index = ValidRecords.find_last();
|
||||
assert(Index != -1);
|
||||
return TypeIndex::fromArrayIndex(Index) + 1;
|
||||
}
|
||||
|
@ -10,13 +10,15 @@
|
||||
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
|
||||
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/Formatters.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
@ -163,15 +165,16 @@ static StringRef getLeafTypeName(TypeLeafKind LT) {
|
||||
}
|
||||
|
||||
void TypeDumpVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI) const {
|
||||
codeview::printTypeIndex(*W, FieldName, TI, TpiTypes);
|
||||
CVTypeDumper::printTypeIndex(*W, FieldName, TI, TypeDB);
|
||||
}
|
||||
|
||||
void TypeDumpVisitor::printItemIndex(StringRef FieldName, TypeIndex TI) const {
|
||||
codeview::printTypeIndex(*W, FieldName, TI, getSourceTypes());
|
||||
CVTypeDumper::printTypeIndex(*W, FieldName, TI, getSourceDB());
|
||||
}
|
||||
|
||||
Error TypeDumpVisitor::visitTypeBegin(CVType &Record) {
|
||||
return visitTypeBegin(Record, TypeIndex::fromArrayIndex(TpiTypes.size()));
|
||||
TypeIndex TI = getSourceDB().getAppendIndex();
|
||||
return visitTypeBegin(Record, TI);
|
||||
}
|
||||
|
||||
Error TypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
|
||||
@ -242,7 +245,7 @@ Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, StringListRecord &Strs) {
|
||||
W->printNumber("NumStrings", Size);
|
||||
ListScope Arguments(*W, "Strings");
|
||||
for (uint32_t I = 0; I < Size; ++I) {
|
||||
printItemIndex("String", Indices[I]);
|
||||
printTypeIndex("String", Indices[I]);
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
//===-- TypeIndex.cpp - CodeView type index ---------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeCollection.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
void llvm::codeview::printTypeIndex(ScopedPrinter &Printer, StringRef FieldName,
|
||||
TypeIndex TI, TypeCollection &Types) {
|
||||
StringRef TypeName;
|
||||
if (!TI.isNoneType())
|
||||
TypeName = Types.getTypeName(TI);
|
||||
if (!TypeName.empty())
|
||||
Printer.printHex(FieldName, TypeName, TI.getIndex());
|
||||
else
|
||||
Printer.printHex(FieldName, TI.getIndex());
|
||||
}
|
@ -11,9 +11,11 @@
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
|
@ -1,82 +0,0 @@
|
||||
//===- TypeTableCollection.cpp -------------------------------- *- C++ --*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableCollection.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
static void error(Error &&EC) {
|
||||
assert(!static_cast<bool>(EC));
|
||||
if (EC)
|
||||
consumeError(std::move(EC));
|
||||
}
|
||||
|
||||
TypeTableCollection::TypeTableCollection(
|
||||
ArrayRef<MutableArrayRef<uint8_t>> Records)
|
||||
: Records(Records), Database(Records.size()) {}
|
||||
|
||||
TypeIndex TypeTableCollection::getFirst() {
|
||||
assert(!empty());
|
||||
return TypeIndex::fromArrayIndex(0);
|
||||
}
|
||||
|
||||
Optional<TypeIndex> TypeTableCollection::getNext(TypeIndex Prev) {
|
||||
++Prev;
|
||||
assert(Prev.toArrayIndex() <= size());
|
||||
if (Prev.toArrayIndex() == size())
|
||||
return None;
|
||||
return Prev;
|
||||
}
|
||||
|
||||
void TypeTableCollection::ensureTypeExists(TypeIndex Index) {
|
||||
assert(hasCapacityFor(Index));
|
||||
|
||||
if (Database.contains(Index))
|
||||
return;
|
||||
|
||||
BinaryByteStream Bytes(Records[Index.toArrayIndex()], support::little);
|
||||
|
||||
CVType Type;
|
||||
uint32_t Len;
|
||||
error(VarStreamArrayExtractor<CVType>::extract(Bytes, Len, Type));
|
||||
|
||||
TypeDatabaseVisitor DBV(Database);
|
||||
error(codeview::visitTypeRecord(Type, Index, DBV));
|
||||
assert(Database.contains(Index));
|
||||
}
|
||||
|
||||
CVType TypeTableCollection::getType(TypeIndex Index) {
|
||||
ensureTypeExists(Index);
|
||||
return Database.getTypeRecord(Index);
|
||||
}
|
||||
|
||||
StringRef TypeTableCollection::getTypeName(TypeIndex Index) {
|
||||
if (!Index.isSimple())
|
||||
ensureTypeExists(Index);
|
||||
return Database.getTypeName(Index);
|
||||
}
|
||||
|
||||
bool TypeTableCollection::contains(TypeIndex Index) {
|
||||
return Database.contains(Index);
|
||||
}
|
||||
|
||||
uint32_t TypeTableCollection::size() { return Records.size(); }
|
||||
|
||||
uint32_t TypeTableCollection::capacity() { return Records.size(); }
|
||||
|
||||
bool TypeTableCollection::hasCapacityFor(TypeIndex Index) const {
|
||||
return Index.toArrayIndex() < Records.size();
|
||||
}
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeViewError.h"
|
||||
#include "llvm/DebugInfo/PDB/GenericError.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
||||
|
@ -9,7 +9,10 @@
|
||||
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h"
|
||||
|
@ -326,7 +326,7 @@
|
||||
; EMPTY-NEXT: TypeLeafKind: LF_SUBSTR_LIST (0x1604)
|
||||
; EMPTY-NEXT: NumStrings: 1
|
||||
; EMPTY-NEXT: Strings [
|
||||
; EMPTY-NEXT: String: -Zi -MT -I"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE" -I"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\ATLMFC\INCLUDE" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared" -I"C:\Program Files (x86)\Windows
|
||||
; EMPTY-NEXT: String: __vc_attributes::threadingAttribute (0x100B)
|
||||
; EMPTY-NEXT: ]
|
||||
; EMPTY-NEXT: }
|
||||
; EMPTY-NEXT: Bytes (
|
||||
@ -1253,7 +1253,7 @@
|
||||
; ALL: TypeLeafKind: LF_SUBSTR_LIST (0x1604)
|
||||
; ALL: NumStrings: 1
|
||||
; ALL: Strings [
|
||||
; ALL: String: -Zi -MT -I"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE" -I"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\ATLMFC\INCLUDE" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared" -I"C:\Program Files (x86)\Windows (0x100B)
|
||||
; ALL: String: __vc_attributes::threadingAttribute (0x100B)
|
||||
; ALL: ]
|
||||
; ALL: }
|
||||
; ALL: }
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
|
@ -29,15 +29,15 @@ static StringRef getLeafName(TypeLeafKind K) {
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
CompactTypeDumpVisitor::CompactTypeDumpVisitor(TypeCollection &Types,
|
||||
CompactTypeDumpVisitor::CompactTypeDumpVisitor(TypeDatabase &TypeDB,
|
||||
ScopedPrinter *W)
|
||||
: CompactTypeDumpVisitor(Types, TypeIndex(TypeIndex::FirstNonSimpleIndex),
|
||||
: CompactTypeDumpVisitor(TypeDB, TypeIndex(TypeIndex::FirstNonSimpleIndex),
|
||||
W) {}
|
||||
|
||||
CompactTypeDumpVisitor::CompactTypeDumpVisitor(TypeCollection &Types,
|
||||
CompactTypeDumpVisitor::CompactTypeDumpVisitor(TypeDatabase &TypeDB,
|
||||
TypeIndex FirstTI,
|
||||
ScopedPrinter *W)
|
||||
: W(W), TI(FirstTI), Offset(0), Types(Types) {}
|
||||
: W(W), TI(FirstTI), Offset(0), TypeDB(TypeDB) {}
|
||||
|
||||
Error CompactTypeDumpVisitor::visitTypeBegin(CVType &Record) {
|
||||
return Error::success();
|
||||
@ -46,7 +46,7 @@ Error CompactTypeDumpVisitor::visitTypeBegin(CVType &Record) {
|
||||
Error CompactTypeDumpVisitor::visitTypeEnd(CVType &Record) {
|
||||
uint32_t I = TI.getIndex();
|
||||
StringRef Leaf = getLeafName(Record.Type);
|
||||
StringRef Name = Types.getTypeName(TI);
|
||||
StringRef Name = TypeDB.getTypeName(TI);
|
||||
W->printString(
|
||||
llvm::formatv("Index: {0:x} ({1:N} bytes, offset {2:N}) {3} \"{4}\"", I,
|
||||
Record.length(), Offset, Leaf, Name)
|
||||
|
@ -17,7 +17,7 @@
|
||||
namespace llvm {
|
||||
class ScopedPrinter;
|
||||
namespace codeview {
|
||||
class TypeCollection;
|
||||
class TypeDatabase;
|
||||
}
|
||||
|
||||
namespace pdb {
|
||||
@ -26,8 +26,8 @@ namespace pdb {
|
||||
/// Dumps records on a single line, and ignores member records.
|
||||
class CompactTypeDumpVisitor : public codeview::TypeVisitorCallbacks {
|
||||
public:
|
||||
CompactTypeDumpVisitor(codeview::TypeCollection &Types, ScopedPrinter *W);
|
||||
CompactTypeDumpVisitor(codeview::TypeCollection &Types,
|
||||
CompactTypeDumpVisitor(codeview::TypeDatabase &TypeDB, ScopedPrinter *W);
|
||||
CompactTypeDumpVisitor(codeview::TypeDatabase &TypeDB,
|
||||
codeview::TypeIndex FirstTI, ScopedPrinter *W);
|
||||
|
||||
/// Paired begin/end actions for all types. Receives all record data,
|
||||
@ -40,7 +40,7 @@ private:
|
||||
|
||||
codeview::TypeIndex TI;
|
||||
uint32_t Offset;
|
||||
codeview::TypeCollection &Types;
|
||||
codeview::TypeDatabase &TypeDB;
|
||||
};
|
||||
|
||||
} // end namespace pdb
|
||||
|
@ -14,9 +14,9 @@
|
||||
#include "StreamUtil.h"
|
||||
#include "llvm-pdbdump.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/EnumTables.h"
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/Line.h"
|
||||
#include "llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h"
|
||||
#include "llvm/DebugInfo/CodeView/ModuleDebugFragmentVisitor.h"
|
||||
@ -25,6 +25,7 @@
|
||||
#include "llvm/DebugInfo/CodeView/ModuleDebugUnknownFragment.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
||||
@ -83,7 +84,7 @@ struct PageStats {
|
||||
|
||||
class C13RawVisitor : public C13DebugFragmentVisitor {
|
||||
public:
|
||||
C13RawVisitor(ScopedPrinter &P, PDBFile &F, LazyRandomTypeCollection &IPI)
|
||||
C13RawVisitor(ScopedPrinter &P, PDBFile &F, TypeDatabase &IPI)
|
||||
: C13DebugFragmentVisitor(F), P(P), IPI(IPI) {}
|
||||
|
||||
Error handleLines() override {
|
||||
@ -159,7 +160,7 @@ public:
|
||||
if (auto EC = printFileName("FileName", L.Header->FileID))
|
||||
return EC;
|
||||
|
||||
if (auto EC = dumpTypeRecord("Function", L.Header->Inlinee))
|
||||
if (auto EC = dumpTypeRecord("Function", IPI, L.Header->Inlinee))
|
||||
return EC;
|
||||
P.printNumber("SourceLine", L.Header->SourceLineNum);
|
||||
if (IL.hasExtraFiles()) {
|
||||
@ -175,11 +176,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Error dumpTypeRecord(StringRef Label, TypeIndex Index) {
|
||||
CompactTypeDumpVisitor CTDV(IPI, Index, &P);
|
||||
Error dumpTypeRecord(StringRef Label, TypeDatabase &DB, TypeIndex Index) {
|
||||
CompactTypeDumpVisitor CTDV(DB, Index, &P);
|
||||
DictScope D(P, Label);
|
||||
if (IPI.contains(Index)) {
|
||||
CVType Type = IPI.getType(Index);
|
||||
if (DB.contains(Index)) {
|
||||
CVType &Type = DB.getTypeRecord(Index);
|
||||
if (auto EC = codeview::visitTypeRecord(Type, CTDV))
|
||||
return EC;
|
||||
} else {
|
||||
@ -198,7 +199,7 @@ private:
|
||||
}
|
||||
|
||||
ScopedPrinter &P;
|
||||
LazyRandomTypeCollection &IPI;
|
||||
TypeDatabase &IPI;
|
||||
};
|
||||
}
|
||||
|
||||
@ -608,19 +609,14 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
|
||||
VerLabel = "IPI Version";
|
||||
}
|
||||
|
||||
if (!DumpRecordBytes && !DumpRecords && !DumpTpiHash)
|
||||
return Error::success();
|
||||
|
||||
auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
|
||||
: File.getPDBIpiStream();
|
||||
if (!Tpi)
|
||||
return Tpi.takeError();
|
||||
|
||||
auto ExpectedTypes = initializeTypeDatabase(StreamIdx);
|
||||
if (!ExpectedTypes)
|
||||
return ExpectedTypes.takeError();
|
||||
auto &Types = *ExpectedTypes;
|
||||
|
||||
if (!DumpRecordBytes && !DumpRecords && !DumpTpiHash)
|
||||
return Error::success();
|
||||
|
||||
std::unique_ptr<DictScope> StreamScope;
|
||||
std::unique_ptr<ListScope> RecordScope;
|
||||
|
||||
@ -628,19 +624,25 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
|
||||
P.printNumber(VerLabel, Tpi->getTpiVersion());
|
||||
P.printNumber("Record count", Tpi->getNumTypeRecords());
|
||||
|
||||
Optional<TypeDatabase> &StreamDB = (StreamIdx == StreamTPI) ? TypeDB : ItemDB;
|
||||
|
||||
std::vector<std::unique_ptr<TypeVisitorCallbacks>> Visitors;
|
||||
|
||||
if (!StreamDB.hasValue()) {
|
||||
StreamDB.emplace(Tpi->getNumTypeRecords());
|
||||
Visitors.push_back(make_unique<TypeDatabaseVisitor>(*StreamDB));
|
||||
}
|
||||
// If we're in dump mode, add a dumper with the appropriate detail level.
|
||||
if (DumpRecords) {
|
||||
std::unique_ptr<TypeVisitorCallbacks> Dumper;
|
||||
if (opts::raw::CompactRecords)
|
||||
Dumper = make_unique<CompactTypeDumpVisitor>(Types, &P);
|
||||
Dumper = make_unique<CompactTypeDumpVisitor>(*StreamDB, &P);
|
||||
else {
|
||||
assert(TpiTypes);
|
||||
assert(TypeDB.hasValue());
|
||||
|
||||
auto X = make_unique<TypeDumpVisitor>(*TpiTypes, &P, false);
|
||||
auto X = make_unique<TypeDumpVisitor>(*TypeDB, &P, false);
|
||||
if (StreamIdx == StreamIPI)
|
||||
X->setIpiTypes(*IpiTypes);
|
||||
X->setItemDB(*ItemDB);
|
||||
Dumper = std::move(X);
|
||||
}
|
||||
Visitors.push_back(std::move(Dumper));
|
||||
@ -658,17 +660,23 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
|
||||
if (DumpRecords || DumpRecordBytes)
|
||||
RecordScope = llvm::make_unique<ListScope>(P, "Records");
|
||||
|
||||
Optional<TypeIndex> I = Types.getFirst();
|
||||
do {
|
||||
bool HadError = false;
|
||||
|
||||
TypeIndex T(TypeIndex::FirstNonSimpleIndex);
|
||||
for (auto Type : Tpi->types(&HadError)) {
|
||||
std::unique_ptr<DictScope> OneRecordScope;
|
||||
|
||||
if ((DumpRecords || DumpRecordBytes) && !opts::raw::CompactRecords)
|
||||
OneRecordScope = llvm::make_unique<DictScope>(P, "");
|
||||
|
||||
auto T = Types.getType(*I);
|
||||
if (auto EC = codeview::visitTypeRecord(T, *I, Pipeline))
|
||||
if (auto EC = codeview::visitTypeRecord(Type, Pipeline))
|
||||
return EC;
|
||||
} while ((I = Types.getNext(*I)));
|
||||
|
||||
++T;
|
||||
}
|
||||
if (HadError)
|
||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
||||
"TPI stream contained corrupt record");
|
||||
|
||||
if (DumpTpiHash) {
|
||||
DictScope DD(P, "Hash");
|
||||
@ -703,26 +711,35 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<codeview::LazyRandomTypeCollection &>
|
||||
LLVMOutputStyle::initializeTypeDatabase(uint32_t SN) {
|
||||
auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes;
|
||||
Error LLVMOutputStyle::buildTypeDatabase(uint32_t SN) {
|
||||
assert(SN == StreamIPI || SN == StreamTPI);
|
||||
|
||||
auto &DB = (SN == StreamIPI) ? ItemDB : TypeDB;
|
||||
|
||||
if (DB.hasValue())
|
||||
return Error::success();
|
||||
|
||||
auto Tpi =
|
||||
(SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream();
|
||||
|
||||
if (!Tpi)
|
||||
return Tpi.takeError();
|
||||
|
||||
if (!TypeCollection) {
|
||||
// Initialize the type collection, even if we're not going to dump it. This
|
||||
// way if some other part of the dumper decides it wants to use some or all
|
||||
// of the records for whatever purposes, it can still access them lazily.
|
||||
auto &Types = Tpi->typeArray();
|
||||
uint32_t Count = Tpi->getNumTypeRecords();
|
||||
auto Offsets = Tpi->getTypeIndexOffsets();
|
||||
TypeCollection =
|
||||
llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets);
|
||||
}
|
||||
DB.emplace(Tpi->getNumTypeRecords());
|
||||
|
||||
return *TypeCollection;
|
||||
TypeDatabaseVisitor DBV(*DB);
|
||||
|
||||
auto HashValues = Tpi->getHashValues();
|
||||
if (HashValues.empty())
|
||||
return codeview::visitTypeStream(Tpi->typeArray(), DBV);
|
||||
|
||||
TypeVisitorCallbackPipeline Pipeline;
|
||||
Pipeline.addCallbackToPipeline(DBV);
|
||||
|
||||
TpiHashVerifier HashVerifier(HashValues, Tpi->getNumHashBuckets());
|
||||
Pipeline.addCallbackToPipeline(HashVerifier);
|
||||
|
||||
return codeview::visitTypeStream(Tpi->typeArray(), Pipeline);
|
||||
}
|
||||
|
||||
Error LLVMOutputStyle::dumpDbiStream() {
|
||||
@ -797,13 +814,11 @@ Error LLVMOutputStyle::dumpDbiStream() {
|
||||
return EC;
|
||||
|
||||
if (ShouldDumpSymbols) {
|
||||
auto ExpectedTypes = initializeTypeDatabase(StreamTPI);
|
||||
if (!ExpectedTypes)
|
||||
return ExpectedTypes.takeError();
|
||||
auto &Types = *ExpectedTypes;
|
||||
if (auto EC = buildTypeDatabase(StreamTPI))
|
||||
return EC;
|
||||
|
||||
ListScope SS(P, "Symbols");
|
||||
codeview::CVSymbolDumper SD(P, Types, nullptr, false);
|
||||
codeview::CVSymbolDumper SD(P, *TypeDB, nullptr, false);
|
||||
bool HadError = false;
|
||||
for (auto S : ModS.symbols(&HadError)) {
|
||||
DictScope LL(P, "");
|
||||
@ -824,11 +839,10 @@ Error LLVMOutputStyle::dumpDbiStream() {
|
||||
}
|
||||
if (opts::raw::DumpLineInfo) {
|
||||
ListScope SS(P, "LineInfo");
|
||||
auto ExpectedTypes = initializeTypeDatabase(StreamIPI);
|
||||
if (!ExpectedTypes)
|
||||
return ExpectedTypes.takeError();
|
||||
auto &IpiItems = *ExpectedTypes;
|
||||
C13RawVisitor V(P, File, IpiItems);
|
||||
if (auto EC = buildTypeDatabase(StreamIPI))
|
||||
return EC;
|
||||
|
||||
C13RawVisitor V(P, File, *ItemDB);
|
||||
if (auto EC = codeview::visitModuleDebugFragments(
|
||||
ModS.linesAndChecksums(), V))
|
||||
return EC;
|
||||
@ -946,12 +960,10 @@ Error LLVMOutputStyle::dumpPublicsStream() {
|
||||
P.printList("Section Offsets", Publics->getSectionOffsets(),
|
||||
printSectionOffset);
|
||||
ListScope L(P, "Symbols");
|
||||
auto ExpectedTypes = initializeTypeDatabase(StreamTPI);
|
||||
if (!ExpectedTypes)
|
||||
return ExpectedTypes.takeError();
|
||||
auto &Tpi = *ExpectedTypes;
|
||||
if (auto EC = buildTypeDatabase(StreamTPI))
|
||||
return EC;
|
||||
|
||||
codeview::CVSymbolDumper SD(P, Tpi, nullptr, false);
|
||||
codeview::CVSymbolDumper SD(P, *TypeDB, nullptr, false);
|
||||
bool HadError = false;
|
||||
for (auto S : Publics->getSymbols(&HadError)) {
|
||||
DictScope DD(P, "");
|
||||
|
@ -21,11 +21,6 @@
|
||||
|
||||
namespace llvm {
|
||||
class BitVector;
|
||||
|
||||
namespace codeview {
|
||||
class LazyRandomTypeCollection;
|
||||
}
|
||||
|
||||
namespace pdb {
|
||||
class LLVMOutputStyle : public OutputStyle {
|
||||
public:
|
||||
@ -34,8 +29,7 @@ public:
|
||||
Error dump() override;
|
||||
|
||||
private:
|
||||
Expected<codeview::LazyRandomTypeCollection &>
|
||||
initializeTypeDatabase(uint32_t SN);
|
||||
Error buildTypeDatabase(uint32_t SN);
|
||||
|
||||
Error dumpFileHeaders();
|
||||
Error dumpStreamSummary();
|
||||
@ -60,8 +54,8 @@ private:
|
||||
|
||||
PDBFile &File;
|
||||
ScopedPrinter P;
|
||||
std::unique_ptr<codeview::LazyRandomTypeCollection> TpiTypes;
|
||||
std::unique_ptr<codeview::LazyRandomTypeCollection> IpiTypes;
|
||||
Optional<codeview::TypeDatabase> TypeDB;
|
||||
Optional<codeview::TypeDatabase> ItemDB;
|
||||
SmallVector<std::string, 32> StreamPurposes;
|
||||
};
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
@ -370,6 +371,7 @@ void MappingContextTraits<PdbInlineeInfo, SerializationContext>::mapping(
|
||||
void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>::
|
||||
mapping(IO &IO, pdb::yaml::PdbTpiRecord &Obj,
|
||||
pdb::yaml::SerializationContext &Context) {
|
||||
|
||||
if (IO.outputting()) {
|
||||
// For PDB to Yaml, deserialize into a high level record type, then dump it.
|
||||
consumeError(codeview::visitTypeRecord(Obj.Record, Context.Dumper));
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "OutputStyle.h"
|
||||
#include "PdbYaml.h"
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeDumper.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/EnumTables.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Config/config.h"
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h"
|
||||
#include "llvm/DebugInfo/CodeView/ModuleDebugInlineeLinesFragment.h"
|
||||
#include "llvm/DebugInfo/CodeView/ModuleDebugLineFragment.h"
|
||||
@ -886,7 +885,7 @@ static void mergePdbs() {
|
||||
DestIpi.addTypeRecord(Data, None);
|
||||
});
|
||||
|
||||
SmallString<64> OutFile(opts::merge::PdbOutputFile);
|
||||
SmallString<64> OutFile = opts::merge::PdbOutputFile;
|
||||
if (OutFile.empty()) {
|
||||
OutFile = opts::merge::InputFilenames[0];
|
||||
llvm::sys::path::replace_extension(OutFile, "merged.pdb");
|
||||
|
@ -22,9 +22,8 @@
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/Line.h"
|
||||
#include "llvm/DebugInfo/CodeView/ModuleDebugFileChecksumFragment.h"
|
||||
#include "llvm/DebugInfo/CodeView/ModuleDebugInlineeLinesFragment.h"
|
||||
@ -35,13 +34,11 @@
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableCollection.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
@ -73,7 +70,7 @@ class COFFDumper : public ObjDumper {
|
||||
public:
|
||||
friend class COFFObjectDumpDelegate;
|
||||
COFFDumper(const llvm::object::COFFObjectFile *Obj, ScopedPrinter &Writer)
|
||||
: ObjDumper(Writer), Obj(Obj), Writer(Writer), Types(100) {}
|
||||
: ObjDumper(Writer), Obj(Obj), Writer(Writer), TypeDB(100) {}
|
||||
|
||||
void printFileHeaders() override;
|
||||
void printSections() override;
|
||||
@ -109,7 +106,7 @@ private:
|
||||
void printFileNameForOffset(StringRef Label, uint32_t FileOffset);
|
||||
void printTypeIndex(StringRef FieldName, TypeIndex TI) {
|
||||
// Forward to CVTypeDumper for simplicity.
|
||||
codeview::printTypeIndex(Writer, FieldName, TI, Types);
|
||||
CVTypeDumper::printTypeIndex(Writer, FieldName, TI, TypeDB);
|
||||
}
|
||||
|
||||
void printCodeViewSymbolsSubsection(StringRef Subsection,
|
||||
@ -162,8 +159,7 @@ private:
|
||||
StringTableRef CVStringTable;
|
||||
|
||||
ScopedPrinter &Writer;
|
||||
BinaryByteStream TypeContents;
|
||||
LazyRandomTypeCollection Types;
|
||||
TypeDatabase TypeDB;
|
||||
};
|
||||
|
||||
class COFFObjectDumpDelegate : public SymbolDumpDelegate {
|
||||
@ -979,7 +975,9 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection,
|
||||
Subsection.bytes_end());
|
||||
auto CODD = llvm::make_unique<COFFObjectDumpDelegate>(*this, Section, Obj,
|
||||
SectionContents);
|
||||
CVSymbolDumper CVSD(W, Types, std::move(CODD), opts::CodeViewSubsectionBytes);
|
||||
|
||||
CVSymbolDumper CVSD(W, TypeDB, std::move(CODD),
|
||||
opts::CodeViewSubsectionBytes);
|
||||
CVSymbolArray Symbols;
|
||||
BinaryStreamReader Reader(BinaryData, llvm::support::little);
|
||||
if (auto EC = Reader.readArray(Symbols, Reader.getLength())) {
|
||||
@ -1096,11 +1094,12 @@ void COFFDumper::printCodeViewTypeSection(StringRef SectionName,
|
||||
if (Magic != COFF::DEBUG_SECTION_MAGIC)
|
||||
return error(object_error::parse_failed);
|
||||
|
||||
Types.reset(Data);
|
||||
|
||||
TypeDumpVisitor TDV(Types, &W, opts::CodeViewSubsectionBytes);
|
||||
error(codeview::visitTypeStream(Types, TDV));
|
||||
W.flush();
|
||||
CVTypeDumper CVTD(TypeDB);
|
||||
TypeDumpVisitor TDV(TypeDB, &W, opts::CodeViewSubsectionBytes);
|
||||
if (auto EC = CVTD.dump({Data.bytes_begin(), Data.bytes_end()}, TDV)) {
|
||||
W.flush();
|
||||
error(llvm::errorToErrorCode(std::move(EC)));
|
||||
}
|
||||
}
|
||||
|
||||
void COFFDumper::printSections() {
|
||||
@ -1640,22 +1639,35 @@ void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer,
|
||||
TypeBuf.append(Record.begin(), Record.end());
|
||||
});
|
||||
|
||||
TypeTableCollection TpiTypes(CVTypes.records());
|
||||
TypeDatabase TypeDB(CVTypes.records().size());
|
||||
{
|
||||
ListScope S(Writer, "MergedTypeStream");
|
||||
TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes);
|
||||
error(codeview::visitTypeStream(TpiTypes, TDV));
|
||||
Writer.flush();
|
||||
CVTypeDumper CVTD(TypeDB);
|
||||
TypeDumpVisitor TDV(TypeDB, &Writer, opts::CodeViewSubsectionBytes);
|
||||
if (auto EC = CVTD.dump(
|
||||
{TypeBuf.str().bytes_begin(), TypeBuf.str().bytes_end()}, TDV)) {
|
||||
Writer.flush();
|
||||
error(std::move(EC));
|
||||
}
|
||||
}
|
||||
|
||||
// Flatten the id stream and print it next. The ID stream refers to names from
|
||||
// the type stream.
|
||||
TypeTableCollection IpiTypes(IDTable.records());
|
||||
SmallString<0> IDBuf;
|
||||
IDTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Record) {
|
||||
IDBuf.append(Record.begin(), Record.end());
|
||||
});
|
||||
|
||||
{
|
||||
ListScope S(Writer, "MergedIDStream");
|
||||
TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes);
|
||||
TDV.setIpiTypes(IpiTypes);
|
||||
error(codeview::visitTypeStream(IpiTypes, TDV));
|
||||
Writer.flush();
|
||||
TypeDatabase IDDB(IDTable.records().size());
|
||||
CVTypeDumper CVTD(IDDB);
|
||||
TypeDumpVisitor TDV(TypeDB, &Writer, opts::CodeViewSubsectionBytes);
|
||||
TDV.setItemDB(IDDB);
|
||||
if (auto EC = CVTD.dump(
|
||||
{IDBuf.str().bytes_begin(), IDBuf.str().bytes_end()}, TDV)) {
|
||||
Writer.flush();
|
||||
error(std::move(EC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,12 +11,14 @@
|
||||
|
||||
#include "llvm/ADT/SmallBitVector.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/RandomAccessTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
@ -128,16 +130,20 @@ public:
|
||||
|
||||
void SetUp() override {
|
||||
TestState = llvm::make_unique<PerTestState>();
|
||||
|
||||
TestState->Pipeline.addCallbackToPipeline(TestState->Deserializer);
|
||||
TestState->Pipeline.addCallbackToPipeline(TestState->Callbacks);
|
||||
}
|
||||
|
||||
void TearDown() override { TestState.reset(); }
|
||||
|
||||
protected:
|
||||
bool ValidateDatabaseRecord(LazyRandomTypeCollection &Types, uint32_t Index) {
|
||||
bool ValidateDatabaseRecord(const RandomAccessTypeVisitor &Visitor,
|
||||
uint32_t Index) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(Index);
|
||||
if (!Types.contains(TI))
|
||||
if (!Visitor.database().contains(TI))
|
||||
return false;
|
||||
if (GlobalState->TypeVector[Index] != Types.getType(TI))
|
||||
if (GlobalState->TypeVector[Index] != Visitor.database().getTypeRecord(TI))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -178,6 +184,8 @@ protected:
|
||||
struct PerTestState {
|
||||
FixedStreamArray<TypeIndexOffset> Offsets;
|
||||
|
||||
TypeVisitorCallbackPipeline Pipeline;
|
||||
TypeDeserializer Deserializer;
|
||||
MockCallbacks Callbacks;
|
||||
};
|
||||
|
||||
@ -210,22 +218,21 @@ std::unique_ptr<RandomAccessVisitorTest::GlobalTestState>
|
||||
|
||||
TEST_F(RandomAccessVisitorTest, MultipleVisits) {
|
||||
TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
|
||||
std::vector<uint32_t> IndicesToVisit = {5, 5, 5};
|
||||
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_NO_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks));
|
||||
EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
|
||||
}
|
||||
|
||||
// [0,8) should be present
|
||||
EXPECT_EQ(8u, Types.size());
|
||||
EXPECT_EQ(8u, Visitor.database().size());
|
||||
for (uint32_t I = 0; I < 8; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
|
||||
|
||||
// 5, 5, 5
|
||||
EXPECT_EQ(3u, TestState->Callbacks.count());
|
||||
@ -241,19 +248,19 @@ TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) {
|
||||
|
||||
std::vector<uint32_t> IndicesToVisit = {7, 4, 2};
|
||||
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_NO_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks));
|
||||
EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
|
||||
}
|
||||
|
||||
// [0, 7]
|
||||
EXPECT_EQ(8u, Types.size());
|
||||
EXPECT_EQ(8u, Visitor.database().size());
|
||||
for (uint32_t I = 0; I < 8; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
|
||||
|
||||
// 2, 4, 7
|
||||
EXPECT_EQ(3u, TestState->Callbacks.count());
|
||||
@ -269,19 +276,19 @@ TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) {
|
||||
|
||||
std::vector<uint32_t> IndicesToVisit = {2, 4, 7};
|
||||
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_NO_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks));
|
||||
EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
|
||||
}
|
||||
|
||||
// [0, 7]
|
||||
EXPECT_EQ(8u, Types.size());
|
||||
EXPECT_EQ(8u, Visitor.database().size());
|
||||
for (uint32_t I = 0; I < 8; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
|
||||
|
||||
// 2, 4, 7
|
||||
EXPECT_EQ(3u, TestState->Callbacks.count());
|
||||
@ -298,20 +305,19 @@ TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) {
|
||||
|
||||
std::vector<uint32_t> IndicesToVisit = {0, 1, 2};
|
||||
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_NO_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks));
|
||||
EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
|
||||
}
|
||||
|
||||
// [0, 8) should be visited.
|
||||
EXPECT_EQ(8u, Types.size());
|
||||
EXPECT_EQ(8u, Visitor.database().size());
|
||||
for (uint32_t I = 0; I < 8; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
|
||||
|
||||
// [0, 2]
|
||||
EXPECT_EQ(3u, TestState->Callbacks.count());
|
||||
@ -327,20 +333,19 @@ TEST_F(RandomAccessVisitorTest, InnerChunk) {
|
||||
|
||||
std::vector<uint32_t> IndicesToVisit = {5, 7};
|
||||
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_NO_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks));
|
||||
EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
|
||||
}
|
||||
|
||||
// [4, 9)
|
||||
EXPECT_EQ(5u, Types.size());
|
||||
EXPECT_EQ(5u, Visitor.database().size());
|
||||
for (uint32_t I = 4; I < 9; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
|
||||
|
||||
// 5, 7
|
||||
EXPECT_EQ(2u, TestState->Callbacks.count());
|
||||
|
Loading…
Reference in New Issue
Block a user