blob: 9ff37e93b151bd857eccf298b14bd7964a5eb73a [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
10#include "ErrorChecking.h"
11
12#include "llvm/ADT/SmallBitVector.h"
13#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
Zachary Turner1dfcf8d2017-05-19 05:57:45 +000014#include "llvm/DebugInfo/CodeView/RandomAccessTypeVisitor.h"
15#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
Zachary Turnerdd3a7392017-05-12 19:18:12 +000016#include "llvm/DebugInfo/CodeView/TypeRecord.h"
17#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
18#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
19#include "llvm/DebugInfo/CodeView/TypeServerHandler.h"
20#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
Zachary Turner1dfcf8d2017-05-19 05:57:45 +000021#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
Zachary Turnerdd3a7392017-05-12 19:18:12 +000022#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
23#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
24#include "llvm/Support/Allocator.h"
25#include "llvm/Support/BinaryItemStream.h"
26#include "llvm/Support/Error.h"
27
28#include "gtest/gtest.h"
29
30using namespace llvm;
31using namespace llvm::codeview;
32using namespace llvm::pdb;
33
34namespace llvm {
35namespace codeview {
36inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) {
37 if (R1.ElementType != R2.ElementType)
38 return false;
39 if (R1.IndexType != R2.IndexType)
40 return false;
41 if (R1.Name != R2.Name)
42 return false;
43 if (R1.Size != R2.Size)
44 return false;
45 return true;
46}
47inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) {
48 return !(R1 == R2);
49}
50
51inline bool operator==(const CVType &R1, const CVType &R2) {
52 if (R1.Type != R2.Type)
53 return false;
54 if (R1.RecordData != R2.RecordData)
55 return false;
56 return true;
57}
58inline bool operator!=(const CVType &R1, const CVType &R2) {
59 return !(R1 == R2);
60}
61}
62}
63
64namespace llvm {
65template <> struct BinaryItemTraits<CVType> {
66 static size_t length(const CVType &Item) { return Item.length(); }
67 static ArrayRef<uint8_t> bytes(const CVType &Item) { return Item.data(); }
68};
69}
70
71namespace {
72
73class MockCallbacks : public TypeVisitorCallbacks {
74public:
75 virtual Error visitTypeBegin(CVType &CVR, TypeIndex Index) {
76 Indices.push_back(Index);
77 return Error::success();
78 }
79 virtual Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) {
80 VisitedRecords.push_back(AR);
81 RawRecords.push_back(CVR);
82 return Error::success();
83 }
84
85 uint32_t count() const {
86 assert(Indices.size() == RawRecords.size());
87 assert(Indices.size() == VisitedRecords.size());
88 return Indices.size();
89 }
90 std::vector<TypeIndex> Indices;
91 std::vector<CVType> RawRecords;
92 std::vector<ArrayRecord> VisitedRecords;
93};
94
95class RandomAccessVisitorTest : public testing::Test {
96public:
97 RandomAccessVisitorTest() {}
98
99 static void SetUpTestCase() {
100 GlobalState = llvm::make_unique<GlobalTestState>();
101
102 TypeTableBuilder Builder(GlobalState->Allocator);
103
104 uint32_t Offset = 0;
105 for (int I = 0; I < 11; ++I) {
106 ArrayRecord AR(TypeRecordKind::Array);
107 AR.ElementType = TypeIndex::Int32();
108 AR.IndexType = TypeIndex::UInt32();
109 AR.Size = I;
110 std::string Name;
111 raw_string_ostream Stream(Name);
112 Stream << "Array [" << I << "]";
113 AR.Name = GlobalState->Strings.save(Stream.str());
114 GlobalState->Records.push_back(AR);
115 GlobalState->Indices.push_back(Builder.writeKnownType(AR));
116
117 CVType Type(TypeLeafKind::LF_ARRAY, Builder.records().back());
118 GlobalState->TypeVector.push_back(Type);
119
120 GlobalState->AllOffsets.push_back(
121 {GlobalState->Indices.back(), ulittle32_t(Offset)});
122 Offset += Type.length();
123 }
124
125 GlobalState->ItemStream.setItems(GlobalState->TypeVector);
126 GlobalState->TypeArray = VarStreamArray<CVType>(GlobalState->ItemStream);
127 }
128
129 static void TearDownTestCase() { GlobalState.reset(); }
130
131 void SetUp() override {
132 TestState = llvm::make_unique<PerTestState>();
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000133
134 TestState->Pipeline.addCallbackToPipeline(TestState->Deserializer);
135 TestState->Pipeline.addCallbackToPipeline(TestState->Callbacks);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000136 }
137
138 void TearDown() override { TestState.reset(); }
139
140protected:
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000141 bool ValidateDatabaseRecord(const RandomAccessTypeVisitor &Visitor,
142 uint32_t Index) {
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000143 TypeIndex TI = TypeIndex::fromArrayIndex(Index);
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000144 if (!Visitor.database().contains(TI))
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000145 return false;
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000146 if (GlobalState->TypeVector[Index] != Visitor.database().getTypeRecord(TI))
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000147 return false;
148 return true;
149 }
150
151 bool ValidateVisitedRecord(uint32_t VisitationOrder,
152 uint32_t GlobalArrayIndex) {
153 TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex);
154 if (TI != TestState->Callbacks.Indices[VisitationOrder])
155 return false;
156
157 if (GlobalState->TypeVector[TI.toArrayIndex()] !=
158 TestState->Callbacks.RawRecords[VisitationOrder])
159 return false;
160
161 if (GlobalState->Records[TI.toArrayIndex()] !=
162 TestState->Callbacks.VisitedRecords[VisitationOrder])
163 return false;
164
165 return true;
166 }
167
168 struct GlobalTestState {
169 GlobalTestState() : Strings(Allocator), ItemStream(llvm::support::little) {}
170
171 BumpPtrAllocator Allocator;
172 StringSaver Strings;
173
174 std::vector<ArrayRecord> Records;
175 std::vector<TypeIndex> Indices;
176 std::vector<TypeIndexOffset> AllOffsets;
177 std::vector<CVType> TypeVector;
178 BinaryItemStream<CVType> ItemStream;
179 VarStreamArray<CVType> TypeArray;
180
181 MutableBinaryByteStream Stream;
182 };
183
184 struct PerTestState {
185 FixedStreamArray<TypeIndexOffset> Offsets;
186
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000187 TypeVisitorCallbackPipeline Pipeline;
188 TypeDeserializer Deserializer;
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000189 MockCallbacks Callbacks;
190 };
191
192 FixedStreamArray<TypeIndexOffset>
193 createPartialOffsets(MutableBinaryByteStream &Storage,
194 std::initializer_list<uint32_t> Indices) {
195
196 uint32_t Count = Indices.size();
197 uint32_t Size = Count * sizeof(TypeIndexOffset);
198 uint8_t *Buffer = GlobalState->Allocator.Allocate<uint8_t>(Size);
199 MutableArrayRef<uint8_t> Bytes(Buffer, Size);
200 Storage = MutableBinaryByteStream(Bytes, support::little);
201 BinaryStreamWriter Writer(Storage);
202 for (const auto I : Indices)
203 consumeError(Writer.writeObject(GlobalState->AllOffsets[I]));
204
205 BinaryStreamReader Reader(Storage);
206 FixedStreamArray<TypeIndexOffset> Result;
207 consumeError(Reader.readArray(Result, Count));
208 return Result;
209 }
210
211 static std::unique_ptr<GlobalTestState> GlobalState;
212 std::unique_ptr<PerTestState> TestState;
213};
214
215std::unique_ptr<RandomAccessVisitorTest::GlobalTestState>
216 RandomAccessVisitorTest::GlobalState;
217}
218
219TEST_F(RandomAccessVisitorTest, MultipleVisits) {
220 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000221 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
222 GlobalState->TypeVector.size(),
223 TestState->Offsets);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000224
225 std::vector<uint32_t> IndicesToVisit = {5, 5, 5};
226
227 for (uint32_t I : IndicesToVisit) {
228 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000229 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000230 }
231
232 // [0,8) should be present
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000233 EXPECT_EQ(8u, Visitor.database().size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000234 for (uint32_t I = 0; I < 8; ++I)
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000235 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000236
237 // 5, 5, 5
Justin Bognerbce6d322017-05-13 00:11:39 +0000238 EXPECT_EQ(3u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000239 for (auto I : enumerate(IndicesToVisit))
240 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
241}
242
243TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) {
244 // Visit multiple items from the same "chunk" in reverse order. In this
245 // example, it's 7 then 4 then 2. At the end, all records from 0 to 7 should
246 // be known by the database, but only 2, 4, and 7 should have been visited.
247 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
248
249 std::vector<uint32_t> IndicesToVisit = {7, 4, 2};
250
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000251 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
252 GlobalState->TypeVector.size(),
253 TestState->Offsets);
254
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000255 for (uint32_t I : IndicesToVisit) {
256 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000257 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000258 }
259
260 // [0, 7]
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000261 EXPECT_EQ(8u, Visitor.database().size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000262 for (uint32_t I = 0; I < 8; ++I)
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000263 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000264
265 // 2, 4, 7
Justin Bognerbce6d322017-05-13 00:11:39 +0000266 EXPECT_EQ(3u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000267 for (auto I : enumerate(IndicesToVisit))
268 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
269}
270
271TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) {
272 // * Visit multiple items from the same chunk in ascending order, ensuring
273 // that intermediate items are not visited. In the below example, it's
274 // 5 -> 6 -> 7 which come from the [4,8) chunk.
275 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
276
277 std::vector<uint32_t> IndicesToVisit = {2, 4, 7};
278
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000279 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
280 GlobalState->TypeVector.size(),
281 TestState->Offsets);
282
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000283 for (uint32_t I : IndicesToVisit) {
284 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000285 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000286 }
287
288 // [0, 7]
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000289 EXPECT_EQ(8u, Visitor.database().size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000290 for (uint32_t I = 0; I < 8; ++I)
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000291 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000292
293 // 2, 4, 7
Justin Bognerbce6d322017-05-13 00:11:39 +0000294 EXPECT_EQ(3u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000295 for (auto &I : enumerate(IndicesToVisit))
296 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
297}
298
299TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) {
300 // * Don't visit the last item in one chunk, ensuring that visitation stops
301 // at the record you specify, and the chunk is only partially visited.
302 // In the below example, this is tested by visiting 0 and 1 but not 2,
303 // all from the [0,3) chunk.
304 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
305
306 std::vector<uint32_t> IndicesToVisit = {0, 1, 2};
307
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000308 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
309 GlobalState->TypeVector.size(),
310 TestState->Offsets);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000311
312 for (uint32_t I : IndicesToVisit) {
313 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000314 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000315 }
316
317 // [0, 8) should be visited.
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000318 EXPECT_EQ(8u, Visitor.database().size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000319 for (uint32_t I = 0; I < 8; ++I)
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000320 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000321
322 // [0, 2]
Justin Bognerbce6d322017-05-13 00:11:39 +0000323 EXPECT_EQ(3u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000324 for (auto I : enumerate(IndicesToVisit))
325 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
326}
327
328TEST_F(RandomAccessVisitorTest, InnerChunk) {
329 // Test that when a request comes from a chunk in the middle of the partial
330 // offsets array, that items from surrounding chunks are not visited or
331 // added to the database.
332 TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9});
333
334 std::vector<uint32_t> IndicesToVisit = {5, 7};
335
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000336 RandomAccessTypeVisitor Visitor(GlobalState->TypeArray,
337 GlobalState->TypeVector.size(),
338 TestState->Offsets);
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000339
340 for (uint32_t I : IndicesToVisit) {
341 TypeIndex TI = TypeIndex::fromArrayIndex(I);
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000342 EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000343 }
344
345 // [4, 9)
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000346 EXPECT_EQ(5u, Visitor.database().size());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000347 for (uint32_t I = 4; I < 9; ++I)
Zachary Turner1dfcf8d2017-05-19 05:57:45 +0000348 EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I));
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000349
350 // 5, 7
Justin Bognerbce6d322017-05-13 00:11:39 +0000351 EXPECT_EQ(2u, TestState->Callbacks.count());
Zachary Turnerdd3a7392017-05-12 19:18:12 +0000352 for (auto &I : enumerate(IndicesToVisit))
353 EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
354}