RandomAccessVisitorTest.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. //===- llvm/unittest/DebugInfo/CodeView/RandomAccessVisitorTest.cpp -------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"
  9. #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
  10. #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
  11. #include "llvm/DebugInfo/CodeView/TypeRecord.h"
  12. #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
  13. #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
  14. #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
  15. #include "llvm/Support/Allocator.h"
  16. #include "llvm/Support/BinaryItemStream.h"
  17. #include "llvm/Support/Error.h"
  18. #include "llvm/Testing/Support/Error.h"
  19. #include "gtest/gtest.h"
  20. using namespace llvm;
  21. using namespace llvm::codeview;
  22. using namespace llvm::pdb;
  23. namespace llvm {
  24. namespace codeview {
  25. inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) {
  26. if (R1.ElementType != R2.ElementType)
  27. return false;
  28. if (R1.IndexType != R2.IndexType)
  29. return false;
  30. if (R1.Name != R2.Name)
  31. return false;
  32. if (R1.Size != R2.Size)
  33. return false;
  34. return true;
  35. }
  36. inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) {
  37. return !(R1 == R2);
  38. }
  39. inline bool operator==(const CVType &R1, const CVType &R2) {
  40. if (R1.RecordData != R2.RecordData)
  41. return false;
  42. return true;
  43. }
  44. inline bool operator!=(const CVType &R1, const CVType &R2) {
  45. return !(R1 == R2);
  46. }
  47. }
  48. }
  49. namespace llvm {
  50. template <> struct BinaryItemTraits<CVType> {
  51. static size_t length(const CVType &Item) { return Item.length(); }
  52. static ArrayRef<uint8_t> bytes(const CVType &Item) { return Item.data(); }
  53. };
  54. }
  55. namespace {
  56. class MockCallbacks : public TypeVisitorCallbacks {
  57. public:
  58. virtual Error visitTypeBegin(CVType &CVR, TypeIndex Index) {
  59. Indices.push_back(Index);
  60. return Error::success();
  61. }
  62. virtual Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) {
  63. VisitedRecords.push_back(AR);
  64. RawRecords.push_back(CVR);
  65. return Error::success();
  66. }
  67. uint32_t count() const {
  68. assert(Indices.size() == RawRecords.size());
  69. assert(Indices.size() == VisitedRecords.size());
  70. return Indices.size();
  71. }
  72. std::vector<TypeIndex> Indices;
  73. std::vector<CVType> RawRecords;
  74. std::vector<ArrayRecord> VisitedRecords;
  75. };
  76. class RandomAccessVisitorTest : public testing::Test {
  77. public:
  78. RandomAccessVisitorTest() {}
  79. static void SetUpTestCase() {
  80. GlobalState = std::make_unique<GlobalTestState>();
  81. AppendingTypeTableBuilder Builder(GlobalState->Allocator);
  82. uint32_t Offset = 0;
  83. for (int I = 0; I < 11; ++I) {
  84. ArrayRecord AR(TypeRecordKind::Array);
  85. AR.ElementType = TypeIndex::Int32();
  86. AR.IndexType = TypeIndex::UInt32();
  87. AR.Size = I;
  88. std::string Name;
  89. raw_string_ostream Stream(Name);
  90. Stream << "Array [" << I << "]";
  91. AR.Name = GlobalState->Strings.save(Stream.str());
  92. GlobalState->Records.push_back(AR);
  93. GlobalState->Indices.push_back(Builder.writeLeafType(AR));
  94. CVType Type(Builder.records().back());
  95. GlobalState->TypeVector.push_back(Type);
  96. GlobalState->AllOffsets.push_back(
  97. {GlobalState->Indices.back(), ulittle32_t(Offset)});
  98. Offset += Type.length();
  99. }
  100. GlobalState->ItemStream.setItems(GlobalState->TypeVector);
  101. GlobalState->TypeArray = VarStreamArray<CVType>(GlobalState->ItemStream);
  102. }
  103. static void TearDownTestCase() { GlobalState.reset(); }
  104. void SetUp() override {
  105. TestState = std::make_unique<PerTestState>();
  106. }
  107. void TearDown() override { TestState.reset(); }
  108. protected:
  109. bool ValidateDatabaseRecord(LazyRandomTypeCollection &Types, uint32_t Index) {
  110. TypeIndex TI = TypeIndex::fromArrayIndex(Index);
  111. if (!Types.contains(TI))
  112. return false;
  113. if (GlobalState->TypeVector[Index] != Types.getType(TI))
  114. return false;
  115. return true;
  116. }
  117. bool ValidateVisitedRecord(uint32_t VisitationOrder,
  118. uint32_t GlobalArrayIndex) {
  119. TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex);
  120. if (TI != TestState->Callbacks.Indices[VisitationOrder])
  121. return false;
  122. if (GlobalState->TypeVector[TI.toArrayIndex()] !=
  123. TestState->Callbacks.RawRecords[VisitationOrder])
  124. return false;
  125. if (GlobalState->Records[TI.toArrayIndex()] !=
  126. TestState->Callbacks.VisitedRecords[VisitationOrder])
  127. return false;
  128. return true;
  129. }
  130. struct GlobalTestState {
  131. GlobalTestState() : Strings(Allocator), ItemStream(llvm::support::little) {}
  132. BumpPtrAllocator Allocator;
  133. StringSaver Strings;
  134. std::vector<ArrayRecord> Records;
  135. std::vector<TypeIndex> Indices;
  136. std::vector<TypeIndexOffset> AllOffsets;
  137. std::vector<CVType> TypeVector;
  138. BinaryItemStream<CVType> ItemStream;
  139. VarStreamArray<CVType> TypeArray;
  140. MutableBinaryByteStream Stream;
  141. };
  142. struct PerTestState {
  143. FixedStreamArray<TypeIndexOffset> Offsets;
  144. MockCallbacks Callbacks;
  145. };
  146. FixedStreamArray<TypeIndexOffset>
  147. createPartialOffsets(MutableBinaryByteStream &Storage,
  148. std::initializer_list<uint32_t> Indices) {
  149. uint32_t Count = Indices.size();
  150. uint32_t Size = Count * sizeof(TypeIndexOffset);
  151. uint8_t *Buffer = GlobalState->Allocator.Allocate<uint8_t>(Size);
  152. MutableArrayRef<uint8_t> Bytes(Buffer, Size);
  153. Storage = MutableBinaryByteStream(Bytes, support::little);
  154. BinaryStreamWriter Writer(Storage);
  155. for (const auto I : Indices)
  156. consumeError(Writer.writeObject(GlobalState->AllOffsets[I]));
  157. BinaryStreamReader Reader(Storage);
  158. FixedStreamArray<TypeIndexOffset> Result;
  159. consumeError(Reader.readArray(Result, Count));
  160. return Result;
  161. }
  162. static std::unique_ptr<GlobalTestState> GlobalState;
  163. std::unique_ptr<PerTestState> TestState;
  164. };
  165. std::unique_ptr<RandomAccessVisitorTest::GlobalTestState>
  166. RandomAccessVisitorTest::GlobalState;
  167. }
  168. TEST_F(RandomAccessVisitorTest, MultipleVisits) {
  169. TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
  170. LazyRandomTypeCollection Types(GlobalState->TypeArray,
  171. GlobalState->TypeVector.size(),
  172. TestState->Offsets);
  173. std::vector<uint32_t> IndicesToVisit = {5, 5, 5};
  174. for (uint32_t I : IndicesToVisit) {
  175. TypeIndex TI = TypeIndex::fromArrayIndex(I);
  176. CVType T = Types.getType(TI);
  177. EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
  178. Succeeded());
  179. }
  180. // [0,8) should be present
  181. EXPECT_EQ(8u, Types.size());
  182. for (uint32_t I = 0; I < 8; ++I)
  183. EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
  184. // 5, 5, 5
  185. EXPECT_EQ(3u, TestState->Callbacks.count());
  186. for (auto I : enumerate(IndicesToVisit))
  187. EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
  188. }
  189. TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) {
  190. // Visit multiple items from the same "chunk" in reverse order. In this
  191. // example, it's 7 then 4 then 2. At the end, all records from 0 to 7 should
  192. // be known by the database, but only 2, 4, and 7 should have been visited.
  193. TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
  194. std::vector<uint32_t> IndicesToVisit = {7, 4, 2};
  195. LazyRandomTypeCollection Types(GlobalState->TypeArray,
  196. GlobalState->TypeVector.size(),
  197. TestState->Offsets);
  198. for (uint32_t I : IndicesToVisit) {
  199. TypeIndex TI = TypeIndex::fromArrayIndex(I);
  200. CVType T = Types.getType(TI);
  201. EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
  202. Succeeded());
  203. }
  204. // [0, 7]
  205. EXPECT_EQ(8u, Types.size());
  206. for (uint32_t I = 0; I < 8; ++I)
  207. EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
  208. // 2, 4, 7
  209. EXPECT_EQ(3u, TestState->Callbacks.count());
  210. for (auto I : enumerate(IndicesToVisit))
  211. EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
  212. }
  213. TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) {
  214. // * Visit multiple items from the same chunk in ascending order, ensuring
  215. // that intermediate items are not visited. In the below example, it's
  216. // 5 -> 6 -> 7 which come from the [4,8) chunk.
  217. TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
  218. std::vector<uint32_t> IndicesToVisit = {2, 4, 7};
  219. LazyRandomTypeCollection Types(GlobalState->TypeArray,
  220. GlobalState->TypeVector.size(),
  221. TestState->Offsets);
  222. for (uint32_t I : IndicesToVisit) {
  223. TypeIndex TI = TypeIndex::fromArrayIndex(I);
  224. CVType T = Types.getType(TI);
  225. EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
  226. Succeeded());
  227. }
  228. // [0, 7]
  229. EXPECT_EQ(8u, Types.size());
  230. for (uint32_t I = 0; I < 8; ++I)
  231. EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
  232. // 2, 4, 7
  233. EXPECT_EQ(3u, TestState->Callbacks.count());
  234. for (auto &I : enumerate(IndicesToVisit))
  235. EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
  236. }
  237. TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) {
  238. // * Don't visit the last item in one chunk, ensuring that visitation stops
  239. // at the record you specify, and the chunk is only partially visited.
  240. // In the below example, this is tested by visiting 0 and 1 but not 2,
  241. // all from the [0,3) chunk.
  242. TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
  243. std::vector<uint32_t> IndicesToVisit = {0, 1, 2};
  244. LazyRandomTypeCollection Types(GlobalState->TypeArray,
  245. GlobalState->TypeVector.size(),
  246. TestState->Offsets);
  247. for (uint32_t I : IndicesToVisit) {
  248. TypeIndex TI = TypeIndex::fromArrayIndex(I);
  249. CVType T = Types.getType(TI);
  250. EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
  251. Succeeded());
  252. }
  253. // [0, 8) should be visited.
  254. EXPECT_EQ(8u, Types.size());
  255. for (uint32_t I = 0; I < 8; ++I)
  256. EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
  257. // [0, 2]
  258. EXPECT_EQ(3u, TestState->Callbacks.count());
  259. for (auto I : enumerate(IndicesToVisit))
  260. EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
  261. }
  262. TEST_F(RandomAccessVisitorTest, InnerChunk) {
  263. // Test that when a request comes from a chunk in the middle of the partial
  264. // offsets array, that items from surrounding chunks are not visited or
  265. // added to the database.
  266. TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9});
  267. std::vector<uint32_t> IndicesToVisit = {5, 7};
  268. LazyRandomTypeCollection Types(GlobalState->TypeArray,
  269. GlobalState->TypeVector.size(),
  270. TestState->Offsets);
  271. for (uint32_t I : IndicesToVisit) {
  272. TypeIndex TI = TypeIndex::fromArrayIndex(I);
  273. CVType T = Types.getType(TI);
  274. EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
  275. Succeeded());
  276. }
  277. // [4, 9)
  278. EXPECT_EQ(5u, Types.size());
  279. for (uint32_t I = 4; I < 9; ++I)
  280. EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
  281. // 5, 7
  282. EXPECT_EQ(2u, TestState->Callbacks.count());
  283. for (auto &I : enumerate(IndicesToVisit))
  284. EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
  285. }
  286. TEST_F(RandomAccessVisitorTest, CrossChunkName) {
  287. AppendingTypeTableBuilder Builder(GlobalState->Allocator);
  288. // TypeIndex 0
  289. ClassRecord Class(TypeRecordKind::Class);
  290. Class.Name = "FooClass";
  291. Class.Options = ClassOptions::None;
  292. Class.MemberCount = 0;
  293. Class.Size = 4U;
  294. Class.DerivationList = TypeIndex::fromArrayIndex(0);
  295. Class.FieldList = TypeIndex::fromArrayIndex(0);
  296. Class.VTableShape = TypeIndex::fromArrayIndex(0);
  297. TypeIndex IndexZero = Builder.writeLeafType(Class);
  298. // TypeIndex 1 refers to type index 0.
  299. ModifierRecord Modifier(TypeRecordKind::Modifier);
  300. Modifier.ModifiedType = TypeIndex::fromArrayIndex(0);
  301. Modifier.Modifiers = ModifierOptions::Const;
  302. TypeIndex IndexOne = Builder.writeLeafType(Modifier);
  303. // set up a type stream that refers to the above two serialized records.
  304. std::vector<CVType> TypeArray = {
  305. {Builder.records()[0]},
  306. {Builder.records()[1]},
  307. };
  308. BinaryItemStream<CVType> ItemStream(llvm::support::little);
  309. ItemStream.setItems(TypeArray);
  310. VarStreamArray<CVType> TypeStream(ItemStream);
  311. // Figure out the byte offset of the second item.
  312. auto ItemOneIter = TypeStream.begin();
  313. ++ItemOneIter;
  314. // Set up a partial offsets buffer that contains the first and second items
  315. // in separate chunks.
  316. std::vector<TypeIndexOffset> TIO;
  317. TIO.push_back({IndexZero, ulittle32_t(0u)});
  318. TIO.push_back({IndexOne, ulittle32_t(ItemOneIter.offset())});
  319. ArrayRef<uint8_t> Buffer(reinterpret_cast<const uint8_t *>(TIO.data()),
  320. TIO.size() * sizeof(TypeIndexOffset));
  321. BinaryStreamReader Reader(Buffer, llvm::support::little);
  322. FixedStreamArray<TypeIndexOffset> PartialOffsets;
  323. ASSERT_THAT_ERROR(Reader.readArray(PartialOffsets, 2), Succeeded());
  324. LazyRandomTypeCollection Types(TypeStream, 2, PartialOffsets);
  325. StringRef Name = Types.getTypeName(IndexOne);
  326. EXPECT_EQ("const FooClass", Name);
  327. }