diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2017-05-16 19:46:52 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2017-05-16 19:46:52 +0000 | 
| commit | 6b3f41ed88e8e440e11a4fbf20b6600529f80049 (patch) | |
| tree | 928b056f24a634d628c80238dbbf10d41b1a71d5 /unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp | |
| parent | c46e6a5940c50058e00c0c5f9123fd82e338d29a (diff) | |
Notes
Diffstat (limited to 'unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp')
| -rw-r--r-- | unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp | 353 | 
1 files changed, 353 insertions, 0 deletions
diff --git a/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp b/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp new file mode 100644 index 000000000000..fedb5978da81 --- /dev/null +++ b/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp @@ -0,0 +1,353 @@ +//===- llvm/unittest/DebugInfo/CodeView/RandomAccessVisitorTest.cpp -------===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ErrorChecking.h" + +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/RandomAccessTypeVisitor.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" +#include "llvm/Support/BinaryItemStream.h" +#include "llvm/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace llvm { +namespace codeview { +inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) { +  if (R1.ElementType != R2.ElementType) +    return false; +  if (R1.IndexType != R2.IndexType) +    return false; +  if (R1.Name != R2.Name) +    return false; +  if (R1.Size != R2.Size) +    return false; +  return true; +} +inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) { +  return !(R1 == R2); +} + +inline bool operator==(const CVType &R1, const CVType &R2) { +  if (R1.Type != R2.Type) +    return false; +  if (R1.RecordData != R2.RecordData) +    return false; +  return true; +} +inline bool operator!=(const CVType &R1, const CVType &R2) { +  return !(R1 == R2); +} +} +} + +namespace llvm { +template <> struct BinaryItemTraits<CVType> { +  static size_t length(const CVType &Item) { return Item.length(); } +  static ArrayRef<uint8_t> bytes(const CVType &Item) { return Item.data(); } +}; +} + +namespace { + +class MockCallbacks : public TypeVisitorCallbacks { +public: +  virtual Error visitTypeBegin(CVType &CVR, TypeIndex Index) { +    Indices.push_back(Index); +    return Error::success(); +  } +  virtual Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) { +    VisitedRecords.push_back(AR); +    RawRecords.push_back(CVR); +    return Error::success(); +  } + +  uint32_t count() const { +    assert(Indices.size() == RawRecords.size()); +    assert(Indices.size() == VisitedRecords.size()); +    return Indices.size(); +  } +  std::vector<TypeIndex> Indices; +  std::vector<CVType> RawRecords; +  std::vector<ArrayRecord> VisitedRecords; +}; + +class RandomAccessVisitorTest : public testing::Test { +public: +  RandomAccessVisitorTest() {} + +  static void SetUpTestCase() { +    GlobalState = llvm::make_unique<GlobalTestState>(); + +    TypeTableBuilder Builder(GlobalState->Allocator); + +    uint32_t Offset = 0; +    for (int I = 0; I < 11; ++I) { +      ArrayRecord AR(TypeRecordKind::Array); +      AR.ElementType = TypeIndex::Int32(); +      AR.IndexType = TypeIndex::UInt32(); +      AR.Size = I; +      std::string Name; +      raw_string_ostream Stream(Name); +      Stream << "Array [" << I << "]"; +      AR.Name = GlobalState->Strings.save(Stream.str()); +      GlobalState->Records.push_back(AR); +      GlobalState->Indices.push_back(Builder.writeKnownType(AR)); + +      CVType Type(TypeLeafKind::LF_ARRAY, Builder.records().back()); +      GlobalState->TypeVector.push_back(Type); + +      GlobalState->AllOffsets.push_back( +          {GlobalState->Indices.back(), ulittle32_t(Offset)}); +      Offset += Type.length(); +    } + +    GlobalState->ItemStream.setItems(GlobalState->TypeVector); +    GlobalState->TypeArray = VarStreamArray<CVType>(GlobalState->ItemStream); +  } + +  static void TearDownTestCase() { GlobalState.reset(); } + +  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(const RandomAccessTypeVisitor &Visitor, +                              uint32_t Index) { +    TypeIndex TI = TypeIndex::fromArrayIndex(Index); +    if (!Visitor.database().contains(TI)) +      return false; +    if (GlobalState->TypeVector[Index] != Visitor.database().getTypeRecord(TI)) +      return false; +    return true; +  } + +  bool ValidateVisitedRecord(uint32_t VisitationOrder, +                             uint32_t GlobalArrayIndex) { +    TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex); +    if (TI != TestState->Callbacks.Indices[VisitationOrder]) +      return false; + +    if (GlobalState->TypeVector[TI.toArrayIndex()] != +        TestState->Callbacks.RawRecords[VisitationOrder]) +      return false; + +    if (GlobalState->Records[TI.toArrayIndex()] != +        TestState->Callbacks.VisitedRecords[VisitationOrder]) +      return false; + +    return true; +  } + +  struct GlobalTestState { +    GlobalTestState() : Strings(Allocator), ItemStream(llvm::support::little) {} + +    BumpPtrAllocator Allocator; +    StringSaver Strings; + +    std::vector<ArrayRecord> Records; +    std::vector<TypeIndex> Indices; +    std::vector<TypeIndexOffset> AllOffsets; +    std::vector<CVType> TypeVector; +    BinaryItemStream<CVType> ItemStream; +    VarStreamArray<CVType> TypeArray; + +    MutableBinaryByteStream Stream; +  }; + +  struct PerTestState { +    FixedStreamArray<TypeIndexOffset> Offsets; + +    TypeVisitorCallbackPipeline Pipeline; +    TypeDeserializer Deserializer; +    MockCallbacks Callbacks; +  }; + +  FixedStreamArray<TypeIndexOffset> +  createPartialOffsets(MutableBinaryByteStream &Storage, +                       std::initializer_list<uint32_t> Indices) { + +    uint32_t Count = Indices.size(); +    uint32_t Size = Count * sizeof(TypeIndexOffset); +    uint8_t *Buffer = GlobalState->Allocator.Allocate<uint8_t>(Size); +    MutableArrayRef<uint8_t> Bytes(Buffer, Size); +    Storage = MutableBinaryByteStream(Bytes, support::little); +    BinaryStreamWriter Writer(Storage); +    for (const auto I : Indices) +      consumeError(Writer.writeObject(GlobalState->AllOffsets[I])); + +    BinaryStreamReader Reader(Storage); +    FixedStreamArray<TypeIndexOffset> Result; +    consumeError(Reader.readArray(Result, Count)); +    return Result; +  } + +  static std::unique_ptr<GlobalTestState> GlobalState; +  std::unique_ptr<PerTestState> TestState; +}; + +std::unique_ptr<RandomAccessVisitorTest::GlobalTestState> +    RandomAccessVisitorTest::GlobalState; +} + +TEST_F(RandomAccessVisitorTest, MultipleVisits) { +  TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8}); +  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); +    EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); +  } + +  // [0,8) should be present +  EXPECT_EQ(8u, Visitor.database().size()); +  for (uint32_t I = 0; I < 8; ++I) +    EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); + +  // 5, 5, 5 +  EXPECT_EQ(3u, TestState->Callbacks.count()); +  for (auto I : enumerate(IndicesToVisit)) +    EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); +} + +TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) { +  // Visit multiple items from the same "chunk" in reverse order.  In this +  // example, it's 7 then 4 then 2.  At the end, all records from 0 to 7 should +  // be known by the database, but only 2, 4, and 7 should have been visited. +  TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8}); + +  std::vector<uint32_t> IndicesToVisit = {7, 4, 2}; + +  RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, +                                  GlobalState->TypeVector.size(), +                                  TestState->Offsets); + +  for (uint32_t I : IndicesToVisit) { +    TypeIndex TI = TypeIndex::fromArrayIndex(I); +    EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); +  } + +  // [0, 7] +  EXPECT_EQ(8u, Visitor.database().size()); +  for (uint32_t I = 0; I < 8; ++I) +    EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); + +  // 2, 4, 7 +  EXPECT_EQ(3u, TestState->Callbacks.count()); +  for (auto I : enumerate(IndicesToVisit)) +    EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); +} + +TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) { +  // * Visit multiple items from the same chunk in ascending order, ensuring +  //   that intermediate items are not visited.  In the below example, it's +  //   5 -> 6 -> 7 which come from the [4,8) chunk. +  TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8}); + +  std::vector<uint32_t> IndicesToVisit = {2, 4, 7}; + +  RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, +                                  GlobalState->TypeVector.size(), +                                  TestState->Offsets); + +  for (uint32_t I : IndicesToVisit) { +    TypeIndex TI = TypeIndex::fromArrayIndex(I); +    EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); +  } + +  // [0, 7] +  EXPECT_EQ(8u, Visitor.database().size()); +  for (uint32_t I = 0; I < 8; ++I) +    EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); + +  // 2, 4, 7 +  EXPECT_EQ(3u, TestState->Callbacks.count()); +  for (auto &I : enumerate(IndicesToVisit)) +    EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); +} + +TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) { +  // * Don't visit the last item in one chunk, ensuring that visitation stops +  //   at the record you specify, and the chunk is only partially visited. +  //   In the below example, this is tested by visiting 0 and 1 but not 2, +  //   all from the [0,3) chunk. +  TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8}); + +  std::vector<uint32_t> IndicesToVisit = {0, 1, 2}; + +  RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, +                                  GlobalState->TypeVector.size(), +                                  TestState->Offsets); + +  for (uint32_t I : IndicesToVisit) { +    TypeIndex TI = TypeIndex::fromArrayIndex(I); +    EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); +  } + +  // [0, 8) should be visited. +  EXPECT_EQ(8u, Visitor.database().size()); +  for (uint32_t I = 0; I < 8; ++I) +    EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); + +  // [0, 2] +  EXPECT_EQ(3u, TestState->Callbacks.count()); +  for (auto I : enumerate(IndicesToVisit)) +    EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); +} + +TEST_F(RandomAccessVisitorTest, InnerChunk) { +  // Test that when a request comes from a chunk in the middle of the partial +  // offsets array, that items from surrounding chunks are not visited or +  // added to the database. +  TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9}); + +  std::vector<uint32_t> IndicesToVisit = {5, 7}; + +  RandomAccessTypeVisitor Visitor(GlobalState->TypeArray, +                                  GlobalState->TypeVector.size(), +                                  TestState->Offsets); + +  for (uint32_t I : IndicesToVisit) { +    TypeIndex TI = TypeIndex::fromArrayIndex(I); +    EXPECT_NO_ERROR(Visitor.visitTypeIndex(TI, TestState->Pipeline)); +  } + +  // [4, 9) +  EXPECT_EQ(5u, Visitor.database().size()); +  for (uint32_t I = 4; I < 9; ++I) +    EXPECT_TRUE(ValidateDatabaseRecord(Visitor, I)); + +  // 5, 7 +  EXPECT_EQ(2u, TestState->Callbacks.count()); +  for (auto &I : enumerate(IndicesToVisit)) +    EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value())); +}  | 
