llvm.org GIT mirror llvm / 337b2d8
[CodeView] Add a random access type visitor. This adds a visitor that is capable of accessing type records randomly and caching intermediate results that it learns about during partial linear scans. This yields amortized O(1) access to a type stream even though type streams cannot normally be indexed. Differential Revision: https://reviews.llvm.org/D33009 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@302936 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 2 years ago
22 changed file(s) with 730 addition(s) and 139 deletion(s). Raw diff Collapse all Expand all
0 //===- RandomAccessTypeVisitor.h ------------------------------ *- C++ --*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_DEBUGINFO_CODEVIEW_RANDOMACCESSTYPEVISITOR_H
10 #define LLVM_DEBUGINFO_CODEVIEW_RANDOMACCESSTYPEVISITOR_H
11
12 #include "llvm/ADT/TinyPtrVector.h"
13 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
14 #include "llvm/DebugInfo/CodeView/TypeDatabase.h"
15 #include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
16 #include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
17 #include "llvm/DebugInfo/CodeView/TypeIndex.h"
18 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
19 #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
20 #include "llvm/Support/Error.h"
21
22 namespace llvm {
23 namespace codeview {
24
25 class TypeDatabase;
26 class TypeServerHandler;
27 class TypeVisitorCallbacks;
28
29 /// \brief Provides amortized O(1) random access to a CodeView type stream.
30 /// Normally to access a type from a type stream, you must know its byte
31 /// offset into the type stream, because type records are variable-lengthed.
32 /// However, this is not the way we prefer to access them. For example, given
33 /// a symbol record one of the fields may be the TypeIndex of the symbol's
34 /// type record. Or given a type record such as an array type, there might
35 /// be a TypeIndex for the element type. Sequential access is perfect when
36 /// we're just dumping every entry, but it's very poor for real world usage.
37 ///
38 /// Type streams in PDBs contain an additional field which is a list of pairs
39 /// containing indices and their corresponding offsets, roughly every ~8KB of
40 /// record data. This general idea need not be confined to PDBs though. By
41 /// supplying such an array, the producer of a type stream can allow the
42 /// consumer much better access time, because the consumer can find the nearest
43 /// index in this array, and do a linear scan forward only from there.
44 ///
45 /// RandomAccessTypeVisitor implements this algorithm, but additionally goes one
46 /// step further by caching offsets of every record that has been visited at
47 /// least once. This way, even repeated visits of the same record will never
48 /// require more than one linear scan. For a type stream of N elements divided
49 /// into M chunks of roughly equal size, this yields a worst case lookup time
50 /// of O(N/M) and an amortized time of O(1).
51 class RandomAccessTypeVisitor {
52 typedef FixedStreamArray PartialOffsetArray;
53
54 public:
55 RandomAccessTypeVisitor(const CVTypeArray &Types, uint32_t NumRecords,
56 PartialOffsetArray PartialOffsets);
57
58 Error visitTypeIndex(TypeIndex Index, TypeVisitorCallbacks &Callbacks);
59
60 const TypeDatabase &database() const { return Database; }
61
62 private:
63 Error visitRangeForType(TypeIndex TI);
64 Error visitRange(TypeIndex Begin, uint32_t BeginOffset, TypeIndex End);
65
66 /// Visited records get automatically added to the type database.
67 TypeDatabase Database;
68
69 /// The type array to allow random access visitation of.
70 const CVTypeArray &Types;
71
72 /// The database visitor which adds new records to the database.
73 TypeDatabaseVisitor DatabaseVisitor;
74
75 /// The deserializer which deserializes new records.
76 TypeDeserializer Deserializer;
77
78 /// The visitation callback pipeline to use. By default this contains a
79 /// deserializer and a type database visitor. But the callback specified
80 /// in the constructor is also added.
81 TypeVisitorCallbackPipeline Pipeline;
82
83 /// The visitor used to visit the internal pipeline for deserialization and
84 /// database maintenance.
85 CVTypeVisitor InternalVisitor;
86
87 /// A vector mapping type indices to type offset. For every record that has
88 /// been visited, contains the absolute offset of that record in the record
89 /// array.
90 std::vector KnownOffsets;
91
92 /// An array of index offsets for the given type stream, allowing log(N)
93 /// lookups of a type record by index. Similar to KnownOffsets but only
94 /// contains offsets for some type indices, some of which may not have
95 /// ever been visited.
96 PartialOffsetArray PartialOffsets;
97 };
98
99 } // end namespace codeview
100 } // end namespace llvm
101
102 #endif // LLVM_DEBUGINFO_CODEVIEW_RANDOMACCESSTYPEVISITOR_H
2323 friend class RandomAccessTypeVisitor;
2424
2525 public:
26 explicit TypeDatabase(uint32_t ExpectedSize);
27
28 /// Gets the type index for the next type record.
29 TypeIndex getNextTypeIndex() const;
26 explicit TypeDatabase(uint32_t Capacity);
3027
3128 /// Records the name of a type, and reserves its type index.
32 void recordType(StringRef Name, const CVType &Data);
29 TypeIndex appendType(StringRef Name, const CVType &Data);
30
31 /// Records the name of a type, and reserves its type index.
32 void recordType(StringRef Name, TypeIndex Index, const CVType &Data);
3333
3434 /// Saves the name in a StringSet and creates a stable StringRef.
3535 StringRef saveTypeName(StringRef TypeName);
3939 const CVType &getTypeRecord(TypeIndex Index) const;
4040 CVType &getTypeRecord(TypeIndex Index);
4141
42 bool containsTypeIndex(TypeIndex Index) const;
42 bool contains(TypeIndex Index) const;
4343
4444 uint32_t size() const;
45 uint32_t capacity() const;
46 bool empty() const;
4547
46 protected:
47 uint32_t toArrayIndex(TypeIndex Index) const;
48 TypeIndex getAppendIndex() const;
49
50 private:
51 void grow();
4852
4953 BumpPtrAllocator Allocator;
54
55 uint32_t Count = 0;
5056
5157 /// All user defined type records in .debug$T live in here. Type indices
5258 /// greater than 0x1000 are user defined. Subtract 0x1000 from the index to
5561 SmallVector TypeRecords;
5662
5763 StringSaver TypeNameStorage;
58 };
5964
60 class RandomAccessTypeDatabase : private TypeDatabase {
61 public:
62 explicit RandomAccessTypeDatabase(uint32_t ExpectedSize);
63
64 /// Records the name of a type, and reserves its type index.
65 void recordType(StringRef Name, TypeIndex Index, const CVType &Data);
66
67 using TypeDatabase::saveTypeName;
68
69 StringRef getTypeName(TypeIndex Index) const;
70
71 const CVType &getTypeRecord(TypeIndex Index) const;
72 CVType &getTypeRecord(TypeIndex Index);
73
74 bool containsTypeIndex(TypeIndex Index) const;
75
76 uint32_t size() const;
77
78 private:
7965 BitVector ValidRecords;
8066 };
8167 }
2323 class TypeDatabaseVisitor : public TypeVisitorCallbacks {
2424 public:
2525 explicit TypeDatabaseVisitor(TypeDatabase &TypeDB) : TypeDB(&TypeDB) {}
26 explicit TypeDatabaseVisitor(RandomAccessTypeDatabase &TypeDB)
27 : TypeDB(&TypeDB) {}
2826
2927 /// Paired begin/end actions for all types. Receives all record data,
3028 /// including the fixed-length record prefix.
5250 StringRef Name;
5351 /// Current type index. Only valid before visitTypeEnd, and if we are
5452 /// visiting a random access type database.
55 TypeIndex CurrentTypeIndex;
53 Optional CurrentTypeIndex;
5654
57 PointerUnion TypeDB;
55 TypeDatabase *TypeDB;
5856 };
5957
6058 } // end namespace codeview
4343 assert(!Mapping && "Already in a type mapping!");
4444 Mapping = llvm::make_unique(Record.content());
4545 return Mapping->Mapping.visitTypeBegin(Record);
46 }
47
48 Error visitTypeBegin(CVType &Record, TypeIndex Index) override {
49 return visitTypeBegin(Record);
4650 }
4751
4852 Error visitTypeEnd(CVType &Record) override {
4444 /// Paired begin/end actions for all types. Receives all record data,
4545 /// including the fixed-length record prefix.
4646 Error visitTypeBegin(CVType &Record) override;
47 Error visitTypeBegin(CVType &Record, TypeIndex Index) override;
4748 Error visitTypeEnd(CVType &Record) override;
4849 Error visitMemberBegin(CVMemberRecord &Record) override;
4950 Error visitMemberEnd(CVMemberRecord &Record) override;
241241 support::ulittle32_t Index;
242242 };
243243
244 // Used for pseudo-indexing an array of type records. An array of such records
245 // sorted by TypeIndex can allow log(N) lookups even though such a type record
246 // stream does not provide random access.
247 struct TypeIndexOffset {
248 TypeIndex Type;
249 support::ulittle32_t Offset;
250 };
244251 }
245252 }
246253
7272 support::ulittle32_t SecByteLength; // Byte count of the segment or group.
7373 };
7474
75 // Used for serialized hash table in TPI stream.
76 // In the reference, it is an array of TI and cbOff pair.
77 struct TypeIndexOffset {
78 codeview::TypeIndex Type;
79 support::ulittle32_t Offset;
80 };
81
8275 /// Some of the values are stored in bitfields. Since this needs to be portable
8376 /// across compilers and architectures (big / little endian in particular) we
8477 /// can't use the actual structures below, but must instead do the shifting
4646 uint32_t getHashKeySize() const;
4747 uint32_t getNumHashBuckets() const;
4848 FixedStreamArray getHashValues() const;
49 FixedStreamArray<TypeIndexOffset> getTypeIndexOffsets() const;
49 FixedStreamArray<codeview::TypeIndexOffset> getTypeIndexOffsets() const;
5050 HashTable &getHashAdjusters();
5151
5252 codeview::CVTypeRange types(bool *HadError) const;
6161
6262 std::unique_ptr HashStream;
6363 FixedStreamArray HashValues;
64 FixedStreamArray<TypeIndexOffset> TypeIndexOffsets;
64 FixedStreamArray<codeview::TypeIndexOffset> TypeIndexOffsets;
6565 HashTable HashAdjusters;
6666
6767 const TpiStreamHeader *Header;
7474 Optional VerHeader;
7575 std::vector> TypeRecords;
7676 std::vector TypeHashes;
77 std::vector<TypeIndexOffset> TypeIndexOffsets;
77 std::vector<codeview::TypeIndexOffset> TypeIndexOffsets;
7878 uint32_t HashStreamIndex = kInvalidStreamIndex;
7979 std::unique_ptr HashValueStream;
8080
138138 }
139139
140140 uint32_t offset() const { return AbsOffset; }
141 uint32_t getRecordLength() const { return ThisLen; }
141142
142143 private:
143144 void moveToEnd() {
293294 friend class FixedStreamArrayIterator;
294295
295296 public:
297 typedef FixedStreamArrayIterator Iterator;
298
296299 FixedStreamArray() = default;
297300 explicit FixedStreamArray(BinaryStreamRef Stream) : Stream(Stream) {
298301 assert(Stream.getLength() % sizeof(T) == 0);
370373 }
371374
372375 FixedStreamArrayIterator &operator-=(std::ptrdiff_t N) {
373 assert(Index >= N);
376 assert(std::ptrdiff_t(Index) >= N);
374377 Index -= N;
375378 return *this;
376379 }
1212 ModuleDebugFragmentVisitor.cpp
1313 ModuleDebugInlineeLinesFragment.cpp
1414 ModuleDebugLineFragment.cpp
15 RandomAccessTypeVisitor.cpp
1516 RecordSerialization.cpp
1617 StringTable.cpp
1718 SymbolRecordMapping.cpp
2525 : Callbacks(Callbacks) {}
2626
2727 template
28 static Error visitKnownRecord(CVTypeVisitor &Visitor, CVType &Record,
29 TypeVisitorCallbacks &Callbacks) {
28 static Error visitKnownRecord(CVType &Record, TypeVisitorCallbacks &Callbacks) {
3029 TypeRecordKind RK = static_cast(Record.Type);
3130 T KnownRecord(RK);
3231 if (auto EC = Callbacks.visitKnownRecord(Record, KnownRecord))
106105 break;
107106 #define TYPE_RECORD(EnumName, EnumVal, Name) \
108107 case EnumName: { \
109 if (auto EC = visitKnownRecord(*this, Record, Callbacks)) \
108 if (auto EC = visitKnownRecord(Record, Callbacks)) \
110109 return EC; \
111110 break; \
112111 }
0 //===- RandomAccessTypeVisitor.cpp ---------------------------- *- C++ --*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/DebugInfo/CodeView/RandomAccessTypeVisitor.h"
10
11 #include "llvm/DebugInfo/CodeView/TypeDatabase.h"
12 #include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
13 #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
14
15 using namespace llvm;
16 using namespace llvm::codeview;
17
18 RandomAccessTypeVisitor::RandomAccessTypeVisitor(
19 const CVTypeArray &Types, uint32_t NumRecords,
20 PartialOffsetArray PartialOffsets)
21 : Database(NumRecords), Types(Types), DatabaseVisitor(Database),
22 InternalVisitor(Pipeline), PartialOffsets(PartialOffsets) {
23 Pipeline.addCallbackToPipeline(Deserializer);
24 Pipeline.addCallbackToPipeline(DatabaseVisitor);
25
26 KnownOffsets.resize(Database.capacity());
27 }
28
29 Error RandomAccessTypeVisitor::visitTypeIndex(TypeIndex TI,
30 TypeVisitorCallbacks &Callbacks) {
31 assert(TI.toArrayIndex() < Database.capacity());
32
33 if (!Database.contains(TI)) {
34 if (auto EC = visitRangeForType(TI))
35 return EC;
36 }
37
38 assert(Database.contains(TI));
39 auto &Record = Database.getTypeRecord(TI);
40 CVTypeVisitor V(Callbacks);
41 return V.visitTypeRecord(Record, TI);
42 }
43
44 Error RandomAccessTypeVisitor::visitRangeForType(TypeIndex TI) {
45 if (PartialOffsets.empty()) {
46 TypeIndex TIB(TypeIndex::FirstNonSimpleIndex);
47 TypeIndex TIE = TIB + Database.capacity();
48 return visitRange(TIB, 0, TIE);
49 }
50
51 auto Next = std::upper_bound(PartialOffsets.begin(), PartialOffsets.end(), TI,
52 [](TypeIndex Value, const TypeIndexOffset &IO) {
53 return Value < IO.Type;
54 });
55
56 assert(Next != PartialOffsets.begin());
57 auto Prev = std::prev(Next);
58
59 TypeIndex TIB = Prev->Type;
60 TypeIndex TIE;
61 if (Next == PartialOffsets.end()) {
62 TIE = TypeIndex::fromArrayIndex(Database.capacity());
63 } else {
64 TIE = Next->Type;
65 }
66
67 if (auto EC = visitRange(TIB, Prev->Offset, TIE))
68 return EC;
69 return Error::success();
70 }
71
72 Error RandomAccessTypeVisitor::visitRange(TypeIndex Begin, uint32_t BeginOffset,
73 TypeIndex End) {
74
75 auto RI = Types.at(BeginOffset);
76 assert(RI != Types.end());
77
78 while (Begin != End) {
79 assert(!Database.contains(Begin));
80 if (auto EC = InternalVisitor.visitTypeRecord(*RI, Begin))
81 return EC;
82 KnownOffsets[Begin.toArrayIndex()] = BeginOffset;
83
84 BeginOffset += RI.getRecordLength();
85 ++Begin;
86 ++RI;
87 }
88
89 return Error::success();
90 }
6464 {"__bool64*", SimpleTypeKind::Boolean64},
6565 };
6666
67 TypeDatabase::TypeDatabase(uint32_t ExpectedSize) : TypeNameStorage(Allocator) {
68 CVUDTNames.reserve(ExpectedSize);
69 TypeRecords.reserve(ExpectedSize);
67 TypeDatabase::TypeDatabase(uint32_t Capacity) : TypeNameStorage(Allocator) {
68 CVUDTNames.resize(Capacity);
69 TypeRecords.resize(Capacity);
70 ValidRecords.resize(Capacity);
7071 }
7172
72 /// Gets the type index for the next type record.
73 TypeIndex TypeDatabase::getNextTypeIndex() const {
74 return TypeIndex(TypeIndex::FirstNonSimpleIndex + CVUDTNames.size());
73 TypeIndex TypeDatabase::appendType(StringRef Name, const CVType &Data) {
74 TypeIndex TI;
75 TI = getAppendIndex();
76 if (TI.toArrayIndex() >= capacity())
77 grow();
78 recordType(Name, TI, Data);
79 return TI;
7580 }
7681
77 /// Records the name of a type, and reserves its type index.
78 void TypeDatabase::recordType(StringRef Name, const CVType &Data) {
79 CVUDTNames.push_back(Name);
80 TypeRecords.push_back(Data);
82 void TypeDatabase::recordType(StringRef Name, TypeIndex Index,
83 const CVType &Data) {
84 uint32_t AI = Index.toArrayIndex();
85
86 assert(!contains(Index));
87 assert(AI < capacity());
88
89 CVUDTNames[AI] = Name;
90 TypeRecords[AI] = Data;
91 ValidRecords.set(AI);
92 ++Count;
8193 }
8294
8395 /// Saves the name in a StringSet and creates a stable StringRef.
103115 return "";
104116 }
105117
106 uint32_t I = Index.getIndex() - TypeIndex::FirstNonSimpleIndex;
107 if (I < CVUDTNames.size())
108 return CVUDTNames[I];
118 if (contains(Index))
119 return CVUDTNames[Index.toArrayIndex()];
109120
110121 return "";
111122 }
112123
113124 const CVType &TypeDatabase::getTypeRecord(TypeIndex Index) const {
114 return TypeRecords[toArrayIndex(Index)];
125 assert(contains(Index));
126 return TypeRecords[Index.toArrayIndex()];
115127 }
116128
117129 CVType &TypeDatabase::getTypeRecord(TypeIndex Index) {
118 return TypeRecords[toArrayIndex(Index)];
130 assert(contains(Index));
131 return TypeRecords[Index.toArrayIndex()];
119132 }
120133
121 bool TypeDatabase::containsTypeIndex(TypeIndex Index) const {
122 return toArrayIndex(Index) < CVUDTNames.size();
134 bool TypeDatabase::contains(TypeIndex Index) const {
135 uint32_t AI = Index.toArrayIndex();
136 if (AI >= capacity())
137 return false;
138
139 return ValidRecords.test(AI);
123140 }
124141
125 uint32_t TypeDatabase::size() const { return CVUDTNames.size(); }
142 uint32_t TypeDatabase::size() const { return Count; }
126143
127 uint32_t TypeDatabase::toArrayIndex(TypeIndex Index) const {
128 assert(Index.getIndex() >= TypeIndex::FirstNonSimpleIndex);
129 return Index.getIndex() - TypeIndex::FirstNonSimpleIndex;
144 uint32_t TypeDatabase::capacity() const { return TypeRecords.size(); }
145
146 void TypeDatabase::grow() {
147 TypeRecords.emplace_back();
148 CVUDTNames.emplace_back();
149 ValidRecords.resize(ValidRecords.size() + 1);
130150 }
131151
132 RandomAccessTypeDatabase::RandomAccessTypeDatabase(uint32_t ExpectedSize)
133 : TypeDatabase(ExpectedSize) {
134 ValidRecords.resize(ExpectedSize);
135 CVUDTNames.resize(ExpectedSize);
136 TypeRecords.resize(ExpectedSize);
152 bool TypeDatabase::empty() const { return size() == 0; }
153
154 TypeIndex TypeDatabase::getAppendIndex() const {
155 if (empty())
156 return TypeIndex::fromArrayIndex(0);
157
158 int Index = ValidRecords.find_last();
159 assert(Index != -1);
160 return TypeIndex::fromArrayIndex(Index) + 1;
137161 }
138
139 void RandomAccessTypeDatabase::recordType(StringRef Name, TypeIndex Index,
140 const CVType &Data) {
141 assert(!containsTypeIndex(Index));
142 uint32_t ZI = Index.getIndex() - TypeIndex::FirstNonSimpleIndex;
143
144 CVUDTNames[ZI] = Name;
145 TypeRecords[ZI] = Data;
146 ValidRecords.set(ZI);
147 }
148
149 StringRef RandomAccessTypeDatabase::getTypeName(TypeIndex Index) const {
150 assert(containsTypeIndex(Index));
151 return TypeDatabase::getTypeName(Index);
152 }
153
154 const CVType &RandomAccessTypeDatabase::getTypeRecord(TypeIndex Index) const {
155 assert(containsTypeIndex(Index));
156 return TypeDatabase::getTypeRecord(Index);
157 }
158
159 CVType &RandomAccessTypeDatabase::getTypeRecord(TypeIndex Index) {
160 assert(containsTypeIndex(Index));
161 return TypeDatabase::getTypeRecord(Index);
162 }
163
164 bool RandomAccessTypeDatabase::containsTypeIndex(TypeIndex Index) const {
165 if (Index.isSimple())
166 return true;
167
168 return ValidRecords.test(toArrayIndex(Index));
169 }
170
171 uint32_t RandomAccessTypeDatabase::size() const { return ValidRecords.count(); }
1515 using namespace llvm::codeview;
1616
1717 Error TypeDatabaseVisitor::visitTypeBegin(CVType &Record) {
18 assert(TypeDB.is());
19
2018 assert(!IsInFieldList);
2119 // Reset Name to the empty string. If the visitor sets it, we know it.
2220 Name = "";
2927 return Error::success();
3028 }
3129
32 StringRef TypeDatabaseVisitor::getTypeName(TypeIndex Index) const {
33 if (auto DB = TypeDB.get())
34 return DB->getTypeName(Index);
35 else if (auto DB = TypeDB.get())
36 return DB->getTypeName(Index);
37
38 llvm_unreachable("Invalid TypeDB Kind!");
39 }
40
41 StringRef TypeDatabaseVisitor::saveTypeName(StringRef Name) {
42 if (auto DB = TypeDB.get())
43 return DB->saveTypeName(Name);
44 else if (auto DB = TypeDB.get())
45 return DB->saveTypeName(Name);
46
47 llvm_unreachable("Invalid TypeDB Kind!");
48 }
49
5030 Error TypeDatabaseVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
51 assert(TypeDB.is());
52
5331 if (auto EC = visitTypeBegin(Record))
5432 return EC;
5533
5634 CurrentTypeIndex = Index;
5735 return Error::success();
36 }
37
38 StringRef TypeDatabaseVisitor::getTypeName(TypeIndex Index) const {
39 return TypeDB->getTypeName(Index);
40 }
41
42 StringRef TypeDatabaseVisitor::saveTypeName(StringRef Name) {
43 return TypeDB->saveTypeName(Name);
5844 }
5945
6046 Error TypeDatabaseVisitor::visitTypeEnd(CVType &CVR) {
6854 // CVUDTNames is indexed by type index, and must have one entry for every
6955 // type. Field list members are not recorded, and are only referenced by
7056 // their containing field list record.
71 if (auto DB = TypeDB.get())
72 DB->recordType(Name, CVR);
73 else if (auto DB = TypeDB.get())
74 DB->recordType(Name, CurrentTypeIndex, CVR);
75
57 if (CurrentTypeIndex)
58 TypeDB->recordType(Name, *CurrentTypeIndex, CVR);
59 else
60 TypeDB->appendType(Name, CVR);
61
62 CurrentTypeIndex.reset();
7663 return Error::success();
7764 }
7865
172172 }
173173
174174 Error TypeDumpVisitor::visitTypeBegin(CVType &Record) {
175 TypeIndex TI = getSourceDB().getAppendIndex();
176 return visitTypeBegin(Record, TI);
177 }
178
179 Error TypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
175180 W->startLine() << getLeafTypeName(Record.Type);
176 W->getOStream() << " ("
177 << HexNumber(getSourceDB().getNextTypeIndex().getIndex())
178 << ")";
181 W->getOStream() << " (" << HexNumber(Index.getIndex()) << ")";
179182 W->getOStream() << " {\n";
180183 W->indent();
181184 W->printEnum("TypeLeafKind", unsigned(Record.Type),
108108 }
109109
110110 uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const {
111 return TypeIndexOffsets.size() * sizeof(TypeIndexOffset);
111 return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset);
112112 }
113113
114114 Error TpiStreamBuilder::finalizeMsfLayout() {
179179 CompactTypeDumpVisitor CTDV(DB, Index, &P);
180180 CVTypeVisitor Visitor(CTDV);
181181 DictScope D(P, Label);
182 if (DB.containsTypeIndex(Index)) {
182 if (DB.contains(Index)) {
183183 CVType &Type = DB.getTypeRecord(Index);
184184 if (auto EC = Visitor.visitTypeRecord(Type))
185185 return EC;
None
0 add_subdirectory(CodeView)
11 add_subdirectory(DWARF)
22 add_subdirectory(PDB)
0 set(LLVM_LINK_COMPONENTS
1 DebugInfoCodeView
2 )
3
4 set(DebugInfoCodeViewSources
5 RandomAccessVisitorTest.cpp
6 )
7
8 add_llvm_unittest(DebugInfoCodeViewTests
9 ${DebugInfoCodeViewSources}
10 )
0 //===- ErrorChecking.h - Helpers for verifying llvm::Errors -----*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_UNITTESTS_DEBUGINFO_CODEVIEW_ERRORCHECKING_H
10 #define LLVM_UNITTESTS_DEBUGINFO_CODEVIEW_ERRORCHECKING_H
11
12 #define EXPECT_NO_ERROR(Err) \
13 { \
14 auto E = Err; \
15 EXPECT_FALSE(static_cast(E)); \
16 if (E) \
17 consumeError(std::move(E)); \
18 }
19
20 #define EXPECT_ERROR(Err) \
21 { \
22 auto E = Err; \
23 EXPECT_TRUE(static_cast(E)); \
24 if (E) \
25 consumeError(std::move(E)); \
26 }
27
28 #define EXPECT_EXPECTED(Exp) \
29 { \
30 auto E = Exp.takeError(); \
31 EXPECT_FALSE(static_cast(E)); \
32 if (E) { \
33 consumeError(std::move(E)); \
34 return; \
35 } \
36 }
37
38 #define EXPECT_EXPECTED_EQ(Val, Exp) \
39 { \
40 auto Result = Exp; \
41 auto E = Result.takeError(); \
42 EXPECT_FALSE(static_cast(E)); \
43 if (E) { \
44 consumeError(std::move(E)); \
45 return; \
46 } \
47 EXPECT_EQ(Val, *Result); \
48 }
49
50 #define EXPECT_UNEXPECTED(Exp) \
51 { \
52 auto E = Exp.takeError(); \
53 EXPECT_TRUE(static_cast(E)); \
54 if (E) { \
55 consumeError(std::move(E)); \
56 return; \
57 } \
58 }
59
60 #endif
0 //===- llvm/unittest/DebugInfo/CodeView/RandomAccessVisitorTest.cpp -------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "ErrorChecking.h"
10
11 #include "llvm/ADT/SmallBitVector.h"
12 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
13 #include "llvm/DebugInfo/CodeView/RandomAccessTypeVisitor.h"
14 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
15 #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
16 #include "llvm/DebugInfo/CodeView/TypeSerializer.h"
17 #include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
18 #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
19 #include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
20 #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
21 #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
22 #include "llvm/Support/Allocator.h"
23 #include "llvm/Support/BinaryItemStream.h"
24 #include "llvm/Support/Error.h"
25
26 #include "gtest/gtest.h"
27
28 using namespace llvm;
29 using namespace llvm::codeview;
30 using namespace llvm::pdb;
31
32 namespace llvm {
33 namespace codeview {
34 inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) {
35 if (R1.ElementType != R2.ElementType)
36 return false;
37 if (R1.IndexType != R2.IndexType)
38 return false;
39 if (R1.Name != R2.Name)
40 return false;
41 if (R1.Size != R2.Size)
42 return false;
43 return true;
44 }
45 inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) {
46 return !(R1 == R2);
47 }
48
49 inline bool operator==(const CVType &R1, const CVType &R2) {
50 if (R1.Type != R2.Type)
51 return false;
52 if (R1.RecordData != R2.RecordData)
53 return false;
54 return true;
55 }
56 inline bool operator!=(const CVType &R1, const CVType &R2) {
57 return !(R1 == R2);
58 }
59 }
60 }
61
62 namespace llvm {
63 template <> struct BinaryItemTraits {
64 static size_t length(const CVType &Item) { return Item.length(); }
65 static ArrayRef bytes(const CVType &Item) { return Item.data(); }
66 };
67 }
68
69 namespace {
70
71 class MockCallbacks : public TypeVisitorCallbacks {
72 public:
73 virtual Error visitTypeBegin(CVType &CVR, TypeIndex Index) {
74 Indices.push_back(Index);
75 return Error::success();
76 }
77 virtual Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) {
78 VisitedRecords.push_back(AR);
79 RawRecords.push_back(CVR);
80 return Error::success();
81 }
82
83 uint32_t count() const {
84 assert(Indices.size() == RawRecords.size());
85 assert(Indices.size() == VisitedRecords.size());
86 return Indices.size();
87 }
88 std::vector Indices;
89 std::vector RawRecords;
90 std::vector VisitedRecords;
91 };
92
93 class RandomAccessVisitorTest : public testing::Test {
94 public:
95 RandomAccessVisitorTest() {}
96
97 static void SetUpTestCase() {
98 GlobalState = llvm::make_unique();
99
100 TypeTableBuilder Builder(GlobalState->Allocator);
101
102 uint32_t Offset = 0;
103 for (int I = 0; I < 11; ++I) {
104 ArrayRecord AR(TypeRecordKind::Array);
105 AR.ElementType = TypeIndex::Int32();
106 AR.IndexType = TypeIndex::UInt32();
107 AR.Size = I;
108 std::string Name;
109 raw_string_ostream Stream(Name);
110 Stream << "Array [" << I << "]";
111 AR.Name = GlobalState->Strings.save(Stream.str());
112 GlobalState->Records.push_back(AR);
113 GlobalState->Indices.push_back(Builder.writeKnownType(AR));
114
115 CVType Type(TypeLeafKind::LF_ARRAY, Builder.records().back());
116 GlobalState->TypeVector.push_back(Type);
117
118 GlobalState->AllOffsets.push_back(
119 {GlobalState->Indices.back(), ulittle32_t(Offset)});
120 Offset += Type.length();
121 }
122
123 GlobalState->ItemStream.setItems(GlobalState->TypeVector);
124 GlobalState->TypeArray = VarStreamArray(GlobalState->ItemStream);
125 }
126
127 static void TearDownTestCase() { GlobalState.reset(); }
128
129 void SetUp() override {
130 TestState = llvm::make_unique();
131
132 TestState->Pipeline.addCallbackToPipeline(TestState->Deserializer);
133 TestState->Pipeline.addCallbackToPipeline(TestState->Callbacks);
134 }
135
136 void TearDown() override { TestState.reset(); }
137
138 protected:
139 bool ValidateDatabaseRecord(const RandomAccessTypeVisitor &Visitor,
140 uint32_t Index) {
141 TypeIndex TI = TypeIndex::fromArrayIndex(Index);
142 if (!Visitor.database().contains(TI))
143 return false;
144 if (GlobalState->TypeVector[Index] != Visitor.database().getTypeRecord(TI))
145 return false;
146 return true;
147 }
148
149 bool ValidateVisitedRecord(uint32_t VisitationOrder,
150 uint32_t GlobalArrayIndex) {
151 TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex);
152 if (TI != TestState->Callbacks.Indices[VisitationOrder])
153 return false;
154
155 if (GlobalState->TypeVector[TI.toArrayIndex()] !=
156 TestState->Callbacks.RawRecords[VisitationOrder])
157 return false;
158
159 if (GlobalState->Records[TI.toArrayIndex()] !=
160 TestState->Callbacks.VisitedRecords[VisitationOrder])
161 return false;
162
163 return true;
164 }
165
166 struct GlobalTestState {
167 GlobalTestState() : Strings(Allocator), ItemStream(llvm::support::little) {}
168
169 BumpPtrAllocator Allocator;
170 StringSaver Strings;
171
172 std::vector Records;
173 std::vector Indices;
174 std::vector AllOffsets;
175 std::vector TypeVector;
176 BinaryItemStream ItemStream;
177 VarStreamArray TypeArray;
178
179 MutableBinaryByteStream Stream;
180 };
181
182 struct PerTestState {
183 FixedStreamArray Offsets;
184
185 TypeVisitorCallbackPipeline Pipeline;
186 TypeDeserializer Deserializer;
187 MockCallbacks Callbacks;
188 };
189
190 FixedStreamArray
191 createPartialOffsets(MutableBinaryByteStream &Storage,
192 std::initializer_list Indices) {
193
194 uint32_t Count = Indices.size();
195 uint32_t Size = Count * sizeof(TypeIndexOffset);
196 uint8_t *Buffer = GlobalState->Allocator.Allocate(Size);
197 MutableArrayRef Bytes(Buffer, Size);
198 Storage = MutableBinaryByteStream(Bytes, support::little);
199 BinaryStreamWriter Writer(Storage);
200 for (const auto I : Indices)
201 consumeError(Writer.writeObject(GlobalState->AllOffsets[I]));
202
203 BinaryStreamReader Reader(Storage);
204 FixedStreamArray Result;
205 consumeError(Reader.readArray(Result, Count));
206 return Result;
207 }
208
209 static std::unique_ptr GlobalState;
210 std::unique_ptr TestState;
211 };
212
213 std::unique_ptr
214 RandomAccessVisitorTest::GlobalState;
215 }
216
217 TEST_F(RandomAccessVisitorTest, MultipleVisits) {
218 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
219 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
220 GlobalState->TypeVector.size(),
221 TestState->Offsets);
222
223 std::vector IndicesToVisit = {5, 5, 5};
224
225 for (uint32_t I : IndicesToVisit) {
226 TypeIndex TI = TypeIndex::fromArrayIndex(I);
227 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
228 }
229
230 // [0,8) should be present
231 EXPECT_EQ(8, Visitor.database().size());
232 for (uint32_t I = 0; I < 8; ++I)
233 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
234
235 // 5, 5, 5
236 EXPECT_EQ(3, TestState->Callbacks.count());
237 for (auto I : enumerate(IndicesToVisit))
238 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
239 }
240
241 TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) {
242 // Visit multiple items from the same "chunk" in reverse order. In this
243 // example, it's 7 then 4 then 2. At the end, all records from 0 to 7 should
244 // be known by the database, but only 2, 4, and 7 should have been visited.
245 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
246
247 std::vector IndicesToVisit = {7, 4, 2};
248
249 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
250 GlobalState->TypeVector.size(),
251 TestState->Offsets);
252
253 for (uint32_t I : IndicesToVisit) {
254 TypeIndex TI = TypeIndex::fromArrayIndex(I);
255 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
256 }
257
258 // [0, 7]
259 EXPECT_EQ(8, Visitor.database().size());
260 for (uint32_t I = 0; I < 8; ++I)
261 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
262
263 // 2, 4, 7
264 EXPECT_EQ(3, TestState->Callbacks.count());
265 for (auto I : enumerate(IndicesToVisit))
266 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
267 }
268
269 TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) {
270 // * Visit multiple items from the same chunk in ascending order, ensuring
271 // that intermediate items are not visited. In the below example, it's
272 // 5 -> 6 -> 7 which come from the [4,8) chunk.
273 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
274
275 std::vector IndicesToVisit = {2, 4, 7};
276
277 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
278 GlobalState->TypeVector.size(),
279 TestState->Offsets);
280
281 for (uint32_t I : IndicesToVisit) {
282 TypeIndex TI = TypeIndex::fromArrayIndex(I);
283 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
284 }
285
286 // [0, 7]
287 EXPECT_EQ(8, Visitor.database().size());
288 for (uint32_t I = 0; I < 8; ++I)
289 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
290
291 // 2, 4, 7
292 EXPECT_EQ(3, TestState->Callbacks.count());
293 for (auto &I : enumerate(IndicesToVisit))
294 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
295 }
296
297 TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) {
298 // * Don't visit the last item in one chunk, ensuring that visitation stops
299 // at the record you specify, and the chunk is only partially visited.
300 // In the below example, this is tested by visiting 0 and 1 but not 2,
301 // all from the [0,3) chunk.
302 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
303
304 std::vector IndicesToVisit = {0, 1, 2};
305
306 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
307 GlobalState->TypeVector.size(),
308 TestState->Offsets);
309
310 for (uint32_t I : IndicesToVisit) {
311 TypeIndex TI = TypeIndex::fromArrayIndex(I);
312 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
313 }
314
315 // [0, 8) should be visited.
316 EXPECT_EQ(8, Visitor.database().size());
317 for (uint32_t I = 0; I < 8; ++I)
318 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
319
320 // [0, 2]
321 EXPECT_EQ(3, TestState->Callbacks.count());
322 for (auto I : enumerate(IndicesToVisit))
323 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
324 }
325
326 TEST_F(RandomAccessVisitorTest, InnerChunk) {
327 // Test that when a request comes from a chunk in the middle of the partial
328 // offsets array, that items from surrounding chunks are not visited or
329 // added to the database.
330 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9});
331
332 std::vector IndicesToVisit = {5, 7};
333
334 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
335 GlobalState->TypeVector.size(),
336 TestState->Offsets);
337
338 for (uint32_t I : IndicesToVisit) {
339 TypeIndex TI = TypeIndex::fromArrayIndex(I);
340 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
341 }
342
343 // [4, 9)
344 EXPECT_EQ(5, Visitor.database().size());
345 for (uint32_t I = 4; I < 9; ++I)
346 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
347
348 // 5, 7
349 EXPECT_EQ(2, TestState->Callbacks.count());
350 for (auto &I : enumerate(IndicesToVisit))
351 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
352 }