blob: 92134513b75b97338c97b227305151bfbb351e46 [file] [log] [blame]
Zachary Turnerdd3a7392017-05-12 19:18:12 +00001//===- llvm/unittest/DebugInfo/CodeView/RandomAccessVisitorTest.cpp -------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Zachary Turnerdd3a7392017-05-12 19:18:12 +000010#include "llvm/ADT/SmallBitVector.h"
11#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
Zachary Turner526f4f22017-05-19 19:26:58 +000012#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
Zachary Turnerdd3a7392017-05-12 19:18:12 +000013#include "llvm/DebugInfo/CodeView/TypeRecord.h"
14#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
15#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
16#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
17#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
Zachary Turnerdd3a7392017-05-12 19:18:12 +000018#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
19#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
20#include "llvm/Support/Allocator.h"
21#include "llvm/Support/BinaryItemStream.h"
22#include "llvm/Support/Error.h"
Zachary Turnercb30e702017-06-14 16:41:50 +000023#include "llvm/Testing/Support/Error.h"
Zachary Turnerdd3a7392017-05-12 19:18:12 +000024
25#include "gtest/gtest.h"
26
27using namespace llvm;
28using namespace llvm::codeview;
29using namespace llvm::pdb;
30
31namespace llvm {
32namespace codeview {
33inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) {
34 if (R1.ElementType != R2.ElementType)
35 return false;
36 if (R1.IndexType != R2.IndexType)
37 return false;
38 if (R1.Name != R2.Name)
39 return false;
40 if (R1.Size != R2.Size)
41 return false;
42 return true;
43}
44inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) {
45 return !(R1 == R2);
46}
47
48inline bool operator==(const CVType &R1, const CVType &R2) {
49 if (R1.Type != R2.Type)
50 return false;
51 if (R1.RecordData != R2.RecordData)
52 return false;
53 return true;
54}
55inline bool operator!=(const CVType &R1, const CVType &R2) {
56 return !(R1 == R2);
57}
58}
59}
60
61namespace llvm {
62template <> struct BinaryItemTraits<CVType> {
63 static size_t length(const CVType &Item) { return Item.length(); }
64 static ArrayRef<uint8_t> bytes(const CVType &Item) { return Item.data(); }
65};
66}
67
68namespace {
69
70class MockCallbacks : public TypeVisitorCallbacks {
71public:
72 virtual Error visitTypeBegin(CVType &CVR, TypeIndex Index) {
73 Indices.push_back(Index);
74 return Error::success();
75 }
76 virtual Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) {
77 VisitedRecords.push_back(AR);
78 RawRecords.push_back(CVR);
79 return Error::success();
80 }
81
82 uint32_t count() const {
83 assert(Indices.size() == RawRecords.size());
84 assert(Indices.size() == VisitedRecords.size());
85 return Indices.size();
86 }
87 std::vector<TypeIndex> Indices;
88 std::vector<CVType> RawRecords;
89 std::vector<ArrayRecord> VisitedRecords;
90};
91
92class RandomAccessVisitorTest : public testing::Test {
93public:
94 RandomAccessVisitorTest() {}
95
96 static void SetUpTestCase() {
97 GlobalState = llvm::make_unique<GlobalTestState>();
98
99 TypeTableBuilder Builder(GlobalState->Allocator);
100
101 uint32_t Offset = 0;
102 for (int I = 0; I < 11; ++I) {
103 ArrayRecord AR(TypeRecordKind::Array);
104 AR.ElementType = TypeIndex::Int32();
105 AR.IndexType = TypeIndex::UInt32();
106 AR.Size = I;
107 std::string Name;
108 raw_string_ostream Stream(Name);
109 Stream << "Array [" << I << "]";
110 AR.Name = GlobalState->Strings.save(Stream.str());
111 GlobalState->Records.push_back(AR);
112 GlobalState->Indices.push_back(Builder.writeKnownType(AR));
113
114 CVType Type(TypeLeafKind::LF_ARRAY, Builder.records().back());
115 GlobalState->TypeVector.push_back(Type);
116
117 GlobalState->AllOffsets.push_back(
118 {GlobalState->Indices.back(), ulittle32_t(Offset)});
119 Offset += Type.length();
120 }
121
122 GlobalState->ItemStream.setItems(GlobalState->TypeVector);
123 GlobalState->TypeArray = VarStreamArray<CVType>(GlobalState->ItemStream);
124 }
125
126 static void TearDownTestCase() { GlobalState.reset(); }
127
128 void SetUp() override {
129 TestState = llvm::make_unique<PerTestState>();
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000130 }
131
132 void TearDown() override { TestState.reset(); }
133
134protected:
Zachary Turner526f4f22017-05-19 19:26:58 +0000135 bool ValidateDatabaseRecord(LazyRandomTypeCollection &Types, uint32_t Index) {
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000136 TypeIndex TI = TypeIndex::fromArrayIndex(Index);
Zachary Turner526f4f22017-05-19 19:26:58 +0000137 if (!Types.contains(TI))
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000138 return false;
Zachary Turner526f4f22017-05-19 19:26:58 +0000139 if (GlobalState->TypeVector[Index] != Types.getType(TI))
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000140 return false;
141 return true;
142 }
143
144 bool ValidateVisitedRecord(uint32_t VisitationOrder,
145 uint32_t GlobalArrayIndex) {
146 TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex);
147 if (TI != TestState->Callbacks.Indices[VisitationOrder])
148 return false;
149
150 if (GlobalState->TypeVector[TI.toArrayIndex()] !=
151 TestState->Callbacks.RawRecords[VisitationOrder])
152 return false;
153
154 if (GlobalState->Records[TI.toArrayIndex()] !=
155 TestState->Callbacks.VisitedRecords[VisitationOrder])
156 return false;
157
158 return true;
159 }
160
161 struct GlobalTestState {
162 GlobalTestState() : Strings(Allocator), ItemStream(llvm::support::little) {}
163
164 BumpPtrAllocator Allocator;
165 StringSaver Strings;
166
167 std::vector<ArrayRecord> Records;
168 std::vector<TypeIndex> Indices;
169 std::vector<TypeIndexOffset> AllOffsets;
170 std::vector<CVType> TypeVector;
171 BinaryItemStream<CVType> ItemStream;
172 VarStreamArray<CVType> TypeArray;
173
174 MutableBinaryByteStream Stream;
175 };
176
177 struct PerTestState {
178 FixedStreamArray<TypeIndexOffset> Offsets;
179
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000180 MockCallbacks Callbacks;
181 };
182
183 FixedStreamArray<TypeIndexOffset>
184 createPartialOffsets(MutableBinaryByteStream &Storage,
185 std::initializer_list<uint32_t> Indices) {
186
187 uint32_t Count = Indices.size();
188 uint32_t Size = Count * sizeof(TypeIndexOffset);
189 uint8_t *Buffer = GlobalState->Allocator.Allocate<uint8_t>(Size);
190 MutableArrayRef<uint8_t> Bytes(Buffer, Size);
191 Storage = MutableBinaryByteStream(Bytes, support::little);
192 BinaryStreamWriter Writer(Storage);
193 for (const auto I : Indices)
194 consumeError(Writer.writeObject(GlobalState->AllOffsets[I]));
195
196 BinaryStreamReader Reader(Storage);
197 FixedStreamArray<TypeIndexOffset> Result;
198 consumeError(Reader.readArray(Result, Count));
199 return Result;
200 }
201
202 static std::unique_ptr<GlobalTestState> GlobalState;
203 std::unique_ptr<PerTestState> TestState;
204};
205
206std::unique_ptr<RandomAccessVisitorTest::GlobalTestState>
207 RandomAccessVisitorTest::GlobalState;
208}
209
210TEST_F(RandomAccessVisitorTest, MultipleVisits) {
211 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
Zachary Turner526f4f22017-05-19 19:26:58 +0000212 LazyRandomTypeCollection Types(GlobalState->TypeArray,
213 GlobalState->TypeVector.size(),
214 TestState->Offsets);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000215
216 std::vector<uint32_t> IndicesToVisit = {5, 5, 5};
217
218 for (uint32_t I : IndicesToVisit) {
219 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner526f4f22017-05-19 19:26:58 +0000220 CVType T = Types.getType(TI);
Zachary Turnercb30e702017-06-14 16:41:50 +0000221 EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
222 Succeeded());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000223 }
224
225 // [0,8) should be present
Zachary Turner526f4f22017-05-19 19:26:58 +0000226 EXPECT_EQ(8u, Types.size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000227 for (uint32_t I = 0; I < 8; ++I)
Zachary Turner526f4f22017-05-19 19:26:58 +0000228 EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000229
230 // 5, 5, 5
Justin Bognerbce6d322017-05-13 00:11:39 +0000231 EXPECT_EQ(3u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000232 for (auto I : enumerate(IndicesToVisit))
233 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
234}
235
236TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) {
237 // Visit multiple items from the same "chunk" in reverse order. In this
238 // example, it's 7 then 4 then 2. At the end, all records from 0 to 7 should
239 // be known by the database, but only 2, 4, and 7 should have been visited.
240 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
241
242 std::vector<uint32_t> IndicesToVisit = {7, 4, 2};
243
Zachary Turner526f4f22017-05-19 19:26:58 +0000244 LazyRandomTypeCollection Types(GlobalState->TypeArray,
245 GlobalState->TypeVector.size(),
246 TestState->Offsets);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000247 for (uint32_t I : IndicesToVisit) {
248 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner526f4f22017-05-19 19:26:58 +0000249 CVType T = Types.getType(TI);
Zachary Turnercb30e702017-06-14 16:41:50 +0000250 EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
251 Succeeded());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000252 }
253
254 // [0, 7]
Zachary Turner526f4f22017-05-19 19:26:58 +0000255 EXPECT_EQ(8u, Types.size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000256 for (uint32_t I = 0; I < 8; ++I)
Zachary Turner526f4f22017-05-19 19:26:58 +0000257 EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000258
259 // 2, 4, 7
Justin Bognerbce6d322017-05-13 00:11:39 +0000260 EXPECT_EQ(3u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000261 for (auto I : enumerate(IndicesToVisit))
262 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
263}
264
265TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) {
266 // * Visit multiple items from the same chunk in ascending order, ensuring
267 // that intermediate items are not visited. In the below example, it's
268 // 5 -> 6 -> 7 which come from the [4,8) chunk.
269 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
270
271 std::vector<uint32_t> IndicesToVisit = {2, 4, 7};
272
Zachary Turner526f4f22017-05-19 19:26:58 +0000273 LazyRandomTypeCollection Types(GlobalState->TypeArray,
274 GlobalState->TypeVector.size(),
275 TestState->Offsets);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000276 for (uint32_t I : IndicesToVisit) {
277 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner526f4f22017-05-19 19:26:58 +0000278 CVType T = Types.getType(TI);
Zachary Turnercb30e702017-06-14 16:41:50 +0000279 EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
280 Succeeded());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000281 }
282
283 // [0, 7]
Zachary Turner526f4f22017-05-19 19:26:58 +0000284 EXPECT_EQ(8u, Types.size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000285 for (uint32_t I = 0; I < 8; ++I)
Zachary Turner526f4f22017-05-19 19:26:58 +0000286 EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000287
288 // 2, 4, 7
Justin Bognerbce6d322017-05-13 00:11:39 +0000289 EXPECT_EQ(3u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000290 for (auto &I : enumerate(IndicesToVisit))
291 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
292}
293
294TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) {
295 // * Don't visit the last item in one chunk, ensuring that visitation stops
296 // at the record you specify, and the chunk is only partially visited.
297 // In the below example, this is tested by visiting 0 and 1 but not 2,
298 // all from the [0,3) chunk.
299 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
300
301 std::vector<uint32_t> IndicesToVisit = {0, 1, 2};
302
Zachary Turner526f4f22017-05-19 19:26:58 +0000303 LazyRandomTypeCollection Types(GlobalState->TypeArray,
304 GlobalState->TypeVector.size(),
305 TestState->Offsets);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000306
307 for (uint32_t I : IndicesToVisit) {
308 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner526f4f22017-05-19 19:26:58 +0000309 CVType T = Types.getType(TI);
Zachary Turnercb30e702017-06-14 16:41:50 +0000310 EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
311 Succeeded());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000312 }
313
314 // [0, 8) should be visited.
Zachary Turner526f4f22017-05-19 19:26:58 +0000315 EXPECT_EQ(8u, Types.size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000316 for (uint32_t I = 0; I < 8; ++I)
Zachary Turner526f4f22017-05-19 19:26:58 +0000317 EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000318
319 // [0, 2]
Justin Bognerbce6d322017-05-13 00:11:39 +0000320 EXPECT_EQ(3u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000321 for (auto I : enumerate(IndicesToVisit))
322 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
323}
324
325TEST_F(RandomAccessVisitorTest, InnerChunk) {
326 // Test that when a request comes from a chunk in the middle of the partial
327 // offsets array, that items from surrounding chunks are not visited or
328 // added to the database.
329 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9});
330
331 std::vector<uint32_t> IndicesToVisit = {5, 7};
332
Zachary Turner526f4f22017-05-19 19:26:58 +0000333 LazyRandomTypeCollection Types(GlobalState->TypeArray,
334 GlobalState->TypeVector.size(),
335 TestState->Offsets);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000336
337 for (uint32_t I : IndicesToVisit) {
338 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner526f4f22017-05-19 19:26:58 +0000339 CVType T = Types.getType(TI);
Zachary Turnercb30e702017-06-14 16:41:50 +0000340 EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
341 Succeeded());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000342 }
343
344 // [4, 9)
Zachary Turner526f4f22017-05-19 19:26:58 +0000345 EXPECT_EQ(5u, Types.size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000346 for (uint32_t I = 4; I < 9; ++I)
Zachary Turner526f4f22017-05-19 19:26:58 +0000347 EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000348
349 // 5, 7
Justin Bognerbce6d322017-05-13 00:11:39 +0000350 EXPECT_EQ(2u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000351 for (auto &I : enumerate(IndicesToVisit))
352 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
353}
Zachary Turnerad859bd2017-06-16 23:42:44 +0000354
355TEST_F(RandomAccessVisitorTest, CrossChunkName) {
356 TypeTableBuilder Builder(GlobalState->Allocator);
357
358 // TypeIndex 0
359 ClassRecord Class(TypeRecordKind::Class);
360 Class.Name = "FooClass";
361 Class.Options = ClassOptions::None;
362 Class.MemberCount = 0;
Zachary Turnera56e4ee2017-06-19 21:59:09 +0000363 Class.Size = 4U;
Zachary Turnerad859bd2017-06-16 23:42:44 +0000364 Class.DerivationList = TypeIndex::fromArrayIndex(0);
365 Class.FieldList = TypeIndex::fromArrayIndex(0);
366 Class.VTableShape = TypeIndex::fromArrayIndex(0);
367 TypeIndex IndexZero = Builder.writeKnownType(Class);
368
369 // TypeIndex 1 refers to type index 0.
370 ModifierRecord Modifier(TypeRecordKind::Modifier);
371 Modifier.ModifiedType = TypeIndex::fromArrayIndex(0);
372 Modifier.Modifiers = ModifierOptions::Const;
373 TypeIndex IndexOne = Builder.writeKnownType(Modifier);
374
375 // set up a type stream that refers to the above two serialized records.
376 std::vector<CVType> TypeArray;
377 TypeArray.push_back(
378 CVType(static_cast<TypeLeafKind>(Class.Kind), Builder.records()[0]));
379 TypeArray.push_back(
380 CVType(static_cast<TypeLeafKind>(Modifier.Kind), Builder.records()[1]));
381 BinaryItemStream<CVType> ItemStream(llvm::support::little);
382 ItemStream.setItems(TypeArray);
383 VarStreamArray<CVType> TypeStream(ItemStream);
384
385 // Figure out the byte offset of the second item.
386 auto ItemOneIter = TypeStream.begin();
387 ++ItemOneIter;
388
389 // Set up a partial offsets buffer that contains the first and second items
390 // in separate chunks.
391 std::vector<TypeIndexOffset> TIO;
392 TIO.push_back({IndexZero, ulittle32_t(0u)});
393 TIO.push_back({IndexOne, ulittle32_t(ItemOneIter.offset())});
394 ArrayRef<uint8_t> Buffer(reinterpret_cast<const uint8_t *>(TIO.data()),
395 TIO.size() * sizeof(TypeIndexOffset));
396
397 BinaryStreamReader Reader(Buffer, llvm::support::little);
398 FixedStreamArray<TypeIndexOffset> PartialOffsets;
399 ASSERT_THAT_ERROR(Reader.readArray(PartialOffsets, 2), Succeeded());
400
401 LazyRandomTypeCollection Types(TypeStream, 2, PartialOffsets);
402
403 StringRef Name = Types.getTypeName(IndexOne);
404 EXPECT_EQ("const FooClass", Name);
405}