You've already forked linux-packaging-mono
Imported Upstream version 5.18.0.167
Former-commit-id: 289509151e0fee68a1b591a20c9f109c3c789d3a
This commit is contained in:
parent
e19d552987
commit
b084638f15
@ -1,4 +0,0 @@
|
||||
add_subdirectory(CodeView)
|
||||
add_subdirectory(DWARF)
|
||||
add_subdirectory(MSF)
|
||||
add_subdirectory(PDB)
|
@ -1,15 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
DebugInfoCodeView
|
||||
)
|
||||
|
||||
set(DebugInfoCodeViewSources
|
||||
RandomAccessVisitorTest.cpp
|
||||
TypeHashingTest.cpp
|
||||
TypeIndexDiscoveryTest.cpp
|
||||
)
|
||||
|
||||
add_llvm_unittest(DebugInfoCodeViewTests
|
||||
${DebugInfoCodeViewSources}
|
||||
)
|
||||
|
||||
target_link_libraries(DebugInfoCodeViewTests PRIVATE LLVMTestingSupport)
|
@ -1,402 +0,0 @@
|
||||
//===- 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 "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecordMapping.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 "llvm/Testing/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>();
|
||||
|
||||
AppendingTypeTableBuilder 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.writeLeafType(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>();
|
||||
}
|
||||
|
||||
void TearDown() override { TestState.reset(); }
|
||||
|
||||
protected:
|
||||
bool ValidateDatabaseRecord(LazyRandomTypeCollection &Types, uint32_t Index) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(Index);
|
||||
if (!Types.contains(TI))
|
||||
return false;
|
||||
if (GlobalState->TypeVector[Index] != Types.getType(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;
|
||||
|
||||
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});
|
||||
LazyRandomTypeCollection Types(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);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
|
||||
Succeeded());
|
||||
}
|
||||
|
||||
// [0,8) should be present
|
||||
EXPECT_EQ(8u, Types.size());
|
||||
for (uint32_t I = 0; I < 8; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, 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};
|
||||
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
|
||||
Succeeded());
|
||||
}
|
||||
|
||||
// [0, 7]
|
||||
EXPECT_EQ(8u, Types.size());
|
||||
for (uint32_t I = 0; I < 8; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, 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};
|
||||
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
|
||||
Succeeded());
|
||||
}
|
||||
|
||||
// [0, 7]
|
||||
EXPECT_EQ(8u, Types.size());
|
||||
for (uint32_t I = 0; I < 8; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, 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};
|
||||
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
|
||||
Succeeded());
|
||||
}
|
||||
|
||||
// [0, 8) should be visited.
|
||||
EXPECT_EQ(8u, Types.size());
|
||||
for (uint32_t I = 0; I < 8; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, 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};
|
||||
|
||||
LazyRandomTypeCollection Types(GlobalState->TypeArray,
|
||||
GlobalState->TypeVector.size(),
|
||||
TestState->Offsets);
|
||||
|
||||
for (uint32_t I : IndicesToVisit) {
|
||||
TypeIndex TI = TypeIndex::fromArrayIndex(I);
|
||||
CVType T = Types.getType(TI);
|
||||
EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
|
||||
Succeeded());
|
||||
}
|
||||
|
||||
// [4, 9)
|
||||
EXPECT_EQ(5u, Types.size());
|
||||
for (uint32_t I = 4; I < 9; ++I)
|
||||
EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
|
||||
|
||||
// 5, 7
|
||||
EXPECT_EQ(2u, TestState->Callbacks.count());
|
||||
for (auto &I : enumerate(IndicesToVisit))
|
||||
EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
|
||||
}
|
||||
|
||||
TEST_F(RandomAccessVisitorTest, CrossChunkName) {
|
||||
AppendingTypeTableBuilder Builder(GlobalState->Allocator);
|
||||
|
||||
// TypeIndex 0
|
||||
ClassRecord Class(TypeRecordKind::Class);
|
||||
Class.Name = "FooClass";
|
||||
Class.Options = ClassOptions::None;
|
||||
Class.MemberCount = 0;
|
||||
Class.Size = 4U;
|
||||
Class.DerivationList = TypeIndex::fromArrayIndex(0);
|
||||
Class.FieldList = TypeIndex::fromArrayIndex(0);
|
||||
Class.VTableShape = TypeIndex::fromArrayIndex(0);
|
||||
TypeIndex IndexZero = Builder.writeLeafType(Class);
|
||||
|
||||
// TypeIndex 1 refers to type index 0.
|
||||
ModifierRecord Modifier(TypeRecordKind::Modifier);
|
||||
Modifier.ModifiedType = TypeIndex::fromArrayIndex(0);
|
||||
Modifier.Modifiers = ModifierOptions::Const;
|
||||
TypeIndex IndexOne = Builder.writeLeafType(Modifier);
|
||||
|
||||
// set up a type stream that refers to the above two serialized records.
|
||||
std::vector<CVType> TypeArray;
|
||||
TypeArray.push_back(
|
||||
CVType(static_cast<TypeLeafKind>(Class.Kind), Builder.records()[0]));
|
||||
TypeArray.push_back(
|
||||
CVType(static_cast<TypeLeafKind>(Modifier.Kind), Builder.records()[1]));
|
||||
BinaryItemStream<CVType> ItemStream(llvm::support::little);
|
||||
ItemStream.setItems(TypeArray);
|
||||
VarStreamArray<CVType> TypeStream(ItemStream);
|
||||
|
||||
// Figure out the byte offset of the second item.
|
||||
auto ItemOneIter = TypeStream.begin();
|
||||
++ItemOneIter;
|
||||
|
||||
// Set up a partial offsets buffer that contains the first and second items
|
||||
// in separate chunks.
|
||||
std::vector<TypeIndexOffset> TIO;
|
||||
TIO.push_back({IndexZero, ulittle32_t(0u)});
|
||||
TIO.push_back({IndexOne, ulittle32_t(ItemOneIter.offset())});
|
||||
ArrayRef<uint8_t> Buffer(reinterpret_cast<const uint8_t *>(TIO.data()),
|
||||
TIO.size() * sizeof(TypeIndexOffset));
|
||||
|
||||
BinaryStreamReader Reader(Buffer, llvm::support::little);
|
||||
FixedStreamArray<TypeIndexOffset> PartialOffsets;
|
||||
ASSERT_THAT_ERROR(Reader.readArray(PartialOffsets, 2), Succeeded());
|
||||
|
||||
LazyRandomTypeCollection Types(TypeStream, 2, PartialOffsets);
|
||||
|
||||
StringRef Name = Types.getTypeName(IndexOne);
|
||||
EXPECT_EQ("const FooClass", Name);
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
//===- llvm/unittest/DebugInfo/CodeView/TypeHashingTest.cpp ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeHashing.h"
|
||||
#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
static TypeIndex createPointerRecord(AppendingTypeTableBuilder &Builder,
|
||||
TypeIndex TI) {
|
||||
PointerRecord PR(TypeRecordKind::Pointer);
|
||||
PR.setAttrs(PointerKind::Near32, PointerMode::Pointer, PointerOptions::None,
|
||||
4);
|
||||
PR.ReferentType = TI;
|
||||
return Builder.writeLeafType(PR);
|
||||
}
|
||||
|
||||
static TypeIndex createArgListRecord(AppendingTypeTableBuilder &Builder,
|
||||
TypeIndex Q, TypeIndex R) {
|
||||
ArgListRecord AR(TypeRecordKind::ArgList);
|
||||
AR.ArgIndices.push_back(Q);
|
||||
AR.ArgIndices.push_back(R);
|
||||
return Builder.writeLeafType(AR);
|
||||
}
|
||||
|
||||
static TypeIndex createProcedureRecord(AppendingTypeTableBuilder &Builder,
|
||||
uint32_t ParamCount, TypeIndex Return,
|
||||
TypeIndex ArgList) {
|
||||
ProcedureRecord PR(TypeRecordKind::Procedure);
|
||||
PR.ArgumentList = ArgList;
|
||||
PR.CallConv = CallingConvention::NearC;
|
||||
PR.Options = FunctionOptions::None;
|
||||
PR.ParameterCount = ParamCount;
|
||||
PR.ReturnType = Return;
|
||||
return Builder.writeLeafType(PR);
|
||||
}
|
||||
|
||||
static ArrayRef<uint8_t> hash_of(ArrayRef<GloballyHashedType> Hashes,
|
||||
TypeIndex TI) {
|
||||
return Hashes[TI.toArrayIndex()].Hash;
|
||||
}
|
||||
|
||||
static void verifyHashUniqueness(ArrayRef<GloballyHashedType> Hashes) {
|
||||
assert(!Hashes.empty());
|
||||
|
||||
for (size_t I = 0; I < Hashes.size() - 1; ++I) {
|
||||
for (size_t J = I + 1; J < Hashes.size(); ++J) {
|
||||
EXPECT_NE(Hashes[I].Hash, Hashes[J].Hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TypeHashingTest, ContentHash) {
|
||||
SimpleTypeSerializer Serializer;
|
||||
|
||||
TypeIndex CharStar(SimpleTypeKind::SignedCharacter,
|
||||
SimpleTypeMode::NearPointer32);
|
||||
|
||||
BumpPtrAllocator Alloc;
|
||||
AppendingTypeTableBuilder Ordering1(Alloc);
|
||||
AppendingTypeTableBuilder Ordering2(Alloc);
|
||||
|
||||
TypeIndex CharP(SimpleTypeKind::SignedCharacter, SimpleTypeMode::NearPointer);
|
||||
TypeIndex IntP(SimpleTypeKind::Int32, SimpleTypeMode::NearPointer);
|
||||
TypeIndex DoubleP(SimpleTypeKind::Float64, SimpleTypeMode::NearPointer);
|
||||
|
||||
// We're going to the same type sequence with two different orderings, and
|
||||
// then confirm all records are hashed the same.
|
||||
|
||||
TypeIndex CharPP[2];
|
||||
TypeIndex IntPP[2];
|
||||
TypeIndex IntPPP[2];
|
||||
TypeIndex DoublePP[2];
|
||||
TypeIndex Args[2];
|
||||
TypeIndex Proc[2];
|
||||
|
||||
// Ordering 1
|
||||
// ----------------------------------------
|
||||
// LF_POINTER 0x1000 {char**}
|
||||
// Referent = char*
|
||||
// LF_POINTER 0x1001 {int**}
|
||||
// Referent = int*
|
||||
// LF_POINTER 0x1002 {int***}
|
||||
// Referent = 0x1001
|
||||
// LF_ARGLIST 0x1003 {(char**, int***)}
|
||||
// Arg[0] = 0x1000
|
||||
// Arg[1] = 0x1002
|
||||
// LF_PROCEDURE 0x1004 {int** func(char**, int***)}
|
||||
// ArgList = 0x1003
|
||||
// ReturnType = 0x1001
|
||||
std::vector<GloballyHashedType> Ordering1Hashes;
|
||||
CharPP[0] = createPointerRecord(Ordering1, CharP);
|
||||
IntPP[0] = createPointerRecord(Ordering1, IntP);
|
||||
IntPPP[0] = createPointerRecord(Ordering1, IntPP[0]);
|
||||
Args[0] = createArgListRecord(Ordering1, CharPP[0], IntPPP[0]);
|
||||
Proc[0] = createProcedureRecord(Ordering1, 2, IntPP[0], Args[0]);
|
||||
|
||||
ASSERT_EQ(0x1000U, CharPP[0].getIndex());
|
||||
ASSERT_EQ(0x1001U, IntPP[0].getIndex());
|
||||
ASSERT_EQ(0x1002U, IntPPP[0].getIndex());
|
||||
ASSERT_EQ(0x1003U, Args[0].getIndex());
|
||||
ASSERT_EQ(0x1004U, Proc[0].getIndex());
|
||||
|
||||
auto Hashes1 = GloballyHashedType::hashTypes(Ordering1.records());
|
||||
|
||||
// Ordering 2
|
||||
// ----------------------------------------
|
||||
// LF_POINTER 0x1000 {int**}
|
||||
// Referent = int*
|
||||
// LF_POINTER 0x1001 {int***}
|
||||
// Referent = 0x1000
|
||||
// LF_POINTER 0x1002 {char**}
|
||||
// Referent = char*
|
||||
// LF_POINTER 0x1003 {double**}
|
||||
// Referent = double*
|
||||
// LF_ARGLIST 0x1004 {(char**, int***)}
|
||||
// Arg[0] = 0x1002
|
||||
// Arg[1] = 0x1001
|
||||
// LF_PROCEDURE 0x1005 {int** func(char**, int***)}
|
||||
// ArgList = 0x1004
|
||||
// ReturnType = 0x1000
|
||||
IntPP[1] = createPointerRecord(Ordering2, IntP);
|
||||
IntPPP[1] = createPointerRecord(Ordering2, IntPP[1]);
|
||||
CharPP[1] = createPointerRecord(Ordering2, CharP);
|
||||
DoublePP[1] = createPointerRecord(Ordering2, DoubleP);
|
||||
Args[1] = createArgListRecord(Ordering2, CharPP[1], IntPPP[1]);
|
||||
Proc[1] = createProcedureRecord(Ordering2, 2, IntPP[1], Args[1]);
|
||||
auto Hashes2 = GloballyHashedType::hashTypes(Ordering2.records());
|
||||
|
||||
ASSERT_EQ(0x1000U, IntPP[1].getIndex());
|
||||
ASSERT_EQ(0x1001U, IntPPP[1].getIndex());
|
||||
ASSERT_EQ(0x1002U, CharPP[1].getIndex());
|
||||
ASSERT_EQ(0x1003U, DoublePP[1].getIndex());
|
||||
ASSERT_EQ(0x1004U, Args[1].getIndex());
|
||||
ASSERT_EQ(0x1005U, Proc[1].getIndex());
|
||||
|
||||
// Sanity check to make sure all same-ordering hashes are different
|
||||
// from each other.
|
||||
verifyHashUniqueness(Hashes1);
|
||||
verifyHashUniqueness(Hashes2);
|
||||
|
||||
EXPECT_EQ(hash_of(Hashes1, IntPP[0]), hash_of(Hashes2, IntPP[1]));
|
||||
EXPECT_EQ(hash_of(Hashes1, IntPPP[0]), hash_of(Hashes2, IntPPP[1]));
|
||||
EXPECT_EQ(hash_of(Hashes1, CharPP[0]), hash_of(Hashes2, CharPP[1]));
|
||||
EXPECT_EQ(hash_of(Hashes1, Args[0]), hash_of(Hashes2, Args[1]));
|
||||
EXPECT_EQ(hash_of(Hashes1, Proc[0]), hash_of(Hashes2, Proc[1]));
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,21 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
AsmPrinter
|
||||
DebugInfoDWARF
|
||||
MC
|
||||
Object
|
||||
ObjectYAML
|
||||
Support
|
||||
)
|
||||
|
||||
set(DebugInfoSources
|
||||
DwarfGenerator.cpp
|
||||
DWARFDebugInfoTest.cpp
|
||||
DWARFFormValueTest.cpp
|
||||
)
|
||||
|
||||
add_llvm_unittest(DebugInfoDWARFTests
|
||||
${DebugInfoSources}
|
||||
)
|
||||
|
||||
target_link_libraries(DebugInfoDWARFTests PRIVATE LLVMTestingSupport)
|
@ -1 +0,0 @@
|
||||
cb7bf82d86f603bf5f6b0e88a0b0cf586ba4a1c6
|
@ -1,177 +0,0 @@
|
||||
//===- llvm/unittest/DebugInfo/DWARFFormValueTest.cpp ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/LEB128.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <climits>
|
||||
using namespace llvm;
|
||||
using namespace dwarf;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(DWARFFormValue, FixedFormSizes) {
|
||||
Optional<uint8_t> RefSize;
|
||||
Optional<uint8_t> AddrSize;
|
||||
|
||||
// Test 32 bit DWARF version 2 with 4 byte addresses.
|
||||
DWARFFormParams Params_2_4_32 = {2, 4, DWARF32};
|
||||
RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_2_4_32);
|
||||
AddrSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_2_4_32);
|
||||
EXPECT_TRUE(RefSize.hasValue());
|
||||
EXPECT_TRUE(AddrSize.hasValue());
|
||||
EXPECT_EQ(*RefSize, *AddrSize);
|
||||
|
||||
// Test 32 bit DWARF version 2 with 8 byte addresses.
|
||||
DWARFFormParams Params_2_8_32 = {2, 8, DWARF32};
|
||||
RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_2_8_32);
|
||||
AddrSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_2_8_32);
|
||||
EXPECT_TRUE(RefSize.hasValue());
|
||||
EXPECT_TRUE(AddrSize.hasValue());
|
||||
EXPECT_EQ(*RefSize, *AddrSize);
|
||||
|
||||
// DW_FORM_ref_addr is 4 bytes in DWARF 32 in DWARF version 3 and beyond.
|
||||
DWARFFormParams Params_3_4_32 = {3, 4, DWARF32};
|
||||
RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_3_4_32);
|
||||
EXPECT_TRUE(RefSize.hasValue());
|
||||
EXPECT_EQ(*RefSize, 4);
|
||||
|
||||
DWARFFormParams Params_4_4_32 = {4, 4, DWARF32};
|
||||
RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_4_4_32);
|
||||
EXPECT_TRUE(RefSize.hasValue());
|
||||
EXPECT_EQ(*RefSize, 4);
|
||||
|
||||
DWARFFormParams Params_5_4_32 = {5, 4, DWARF32};
|
||||
RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_5_4_32);
|
||||
EXPECT_TRUE(RefSize.hasValue());
|
||||
EXPECT_EQ(*RefSize, 4);
|
||||
|
||||
// DW_FORM_ref_addr is 8 bytes in DWARF 64 in DWARF version 3 and beyond.
|
||||
DWARFFormParams Params_3_8_64 = {3, 8, DWARF64};
|
||||
RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_3_8_64);
|
||||
EXPECT_TRUE(RefSize.hasValue());
|
||||
EXPECT_EQ(*RefSize, 8);
|
||||
|
||||
DWARFFormParams Params_4_8_64 = {4, 8, DWARF64};
|
||||
RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_4_8_64);
|
||||
EXPECT_TRUE(RefSize.hasValue());
|
||||
EXPECT_EQ(*RefSize, 8);
|
||||
|
||||
DWARFFormParams Params_5_8_64 = {5, 8, DWARF64};
|
||||
RefSize = DWARFFormValue::getFixedByteSize(DW_FORM_ref_addr, Params_5_8_64);
|
||||
EXPECT_TRUE(RefSize.hasValue());
|
||||
EXPECT_EQ(*RefSize, 8);
|
||||
}
|
||||
|
||||
bool isFormClass(dwarf::Form Form, DWARFFormValue::FormClass FC) {
|
||||
return DWARFFormValue(Form).isFormClass(FC);
|
||||
}
|
||||
|
||||
TEST(DWARFFormValue, FormClass) {
|
||||
EXPECT_TRUE(isFormClass(DW_FORM_addr, DWARFFormValue::FC_Address));
|
||||
EXPECT_FALSE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_Address));
|
||||
EXPECT_TRUE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_Constant));
|
||||
EXPECT_TRUE(isFormClass(DW_FORM_data8, DWARFFormValue::FC_SectionOffset));
|
||||
EXPECT_TRUE(
|
||||
isFormClass(DW_FORM_sec_offset, DWARFFormValue::FC_SectionOffset));
|
||||
EXPECT_TRUE(isFormClass(DW_FORM_GNU_str_index, DWARFFormValue::FC_String));
|
||||
EXPECT_TRUE(isFormClass(DW_FORM_GNU_addr_index, DWARFFormValue::FC_Address));
|
||||
EXPECT_FALSE(isFormClass(DW_FORM_ref_addr, DWARFFormValue::FC_Address));
|
||||
EXPECT_TRUE(isFormClass(DW_FORM_ref_addr, DWARFFormValue::FC_Reference));
|
||||
EXPECT_TRUE(isFormClass(DW_FORM_ref_sig8, DWARFFormValue::FC_Reference));
|
||||
}
|
||||
|
||||
template<typename RawTypeT>
|
||||
DWARFFormValue createDataXFormValue(dwarf::Form Form, RawTypeT Value) {
|
||||
char Raw[sizeof(RawTypeT)];
|
||||
memcpy(Raw, &Value, sizeof(RawTypeT));
|
||||
uint32_t Offset = 0;
|
||||
DWARFFormValue Result(Form);
|
||||
DWARFDataExtractor Data(StringRef(Raw, sizeof(RawTypeT)),
|
||||
sys::IsLittleEndianHost, sizeof(void *));
|
||||
Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32});
|
||||
return Result;
|
||||
}
|
||||
|
||||
DWARFFormValue createULEBFormValue(uint64_t Value) {
|
||||
SmallString<10> RawData;
|
||||
raw_svector_ostream OS(RawData);
|
||||
encodeULEB128(Value, OS);
|
||||
uint32_t Offset = 0;
|
||||
DWARFFormValue Result(DW_FORM_udata);
|
||||
DWARFDataExtractor Data(OS.str(), sys::IsLittleEndianHost, sizeof(void *));
|
||||
Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32});
|
||||
return Result;
|
||||
}
|
||||
|
||||
DWARFFormValue createSLEBFormValue(int64_t Value) {
|
||||
SmallString<10> RawData;
|
||||
raw_svector_ostream OS(RawData);
|
||||
encodeSLEB128(Value, OS);
|
||||
uint32_t Offset = 0;
|
||||
DWARFFormValue Result(DW_FORM_sdata);
|
||||
DWARFDataExtractor Data(OS.str(), sys::IsLittleEndianHost, sizeof(void *));
|
||||
Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32});
|
||||
return Result;
|
||||
}
|
||||
|
||||
TEST(DWARFFormValue, SignedConstantForms) {
|
||||
// Check that we correctly sign extend fixed size forms.
|
||||
auto Sign1 = createDataXFormValue<uint8_t>(DW_FORM_data1, -123);
|
||||
auto Sign2 = createDataXFormValue<uint16_t>(DW_FORM_data2, -12345);
|
||||
auto Sign4 = createDataXFormValue<uint32_t>(DW_FORM_data4, -123456789);
|
||||
auto Sign8 = createDataXFormValue<uint64_t>(DW_FORM_data8, -1);
|
||||
EXPECT_EQ(Sign1.getAsSignedConstant().getValue(), -123);
|
||||
EXPECT_EQ(Sign2.getAsSignedConstant().getValue(), -12345);
|
||||
EXPECT_EQ(Sign4.getAsSignedConstant().getValue(), -123456789);
|
||||
EXPECT_EQ(Sign8.getAsSignedConstant().getValue(), -1);
|
||||
|
||||
// Check that we can handle big positive values, but that we return
|
||||
// an error just over the limit.
|
||||
auto UMax = createULEBFormValue(LLONG_MAX);
|
||||
auto TooBig = createULEBFormValue(uint64_t(LLONG_MAX) + 1);
|
||||
EXPECT_EQ(UMax.getAsSignedConstant().getValue(), LLONG_MAX);
|
||||
EXPECT_EQ(TooBig.getAsSignedConstant().hasValue(), false);
|
||||
|
||||
// Sanity check some other forms.
|
||||
auto Data1 = createDataXFormValue<uint8_t>(DW_FORM_data1, 120);
|
||||
auto Data2 = createDataXFormValue<uint16_t>(DW_FORM_data2, 32000);
|
||||
auto Data4 = createDataXFormValue<uint32_t>(DW_FORM_data4, 2000000000);
|
||||
auto Data8 = createDataXFormValue<uint64_t>(DW_FORM_data8, 0x1234567812345678LL);
|
||||
auto LEBMin = createSLEBFormValue(LLONG_MIN);
|
||||
auto LEBMax = createSLEBFormValue(LLONG_MAX);
|
||||
auto LEB1 = createSLEBFormValue(-42);
|
||||
auto LEB2 = createSLEBFormValue(42);
|
||||
EXPECT_EQ(Data1.getAsSignedConstant().getValue(), 120);
|
||||
EXPECT_EQ(Data2.getAsSignedConstant().getValue(), 32000);
|
||||
EXPECT_EQ(Data4.getAsSignedConstant().getValue(), 2000000000);
|
||||
EXPECT_EQ(Data8.getAsSignedConstant().getValue(), 0x1234567812345678LL);
|
||||
EXPECT_EQ(LEBMin.getAsSignedConstant().getValue(), LLONG_MIN);
|
||||
EXPECT_EQ(LEBMax.getAsSignedConstant().getValue(), LLONG_MAX);
|
||||
EXPECT_EQ(LEB1.getAsSignedConstant().getValue(), -42);
|
||||
EXPECT_EQ(LEB2.getAsSignedConstant().getValue(), 42);
|
||||
|
||||
// Data16 is a little tricky.
|
||||
char Cksum[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
DWARFFormValue Data16(DW_FORM_data16);
|
||||
DWARFDataExtractor DE16(StringRef(Cksum, 16), sys::IsLittleEndianHost,
|
||||
sizeof(void *));
|
||||
uint32_t Offset = 0;
|
||||
Data16.extractValue(DE16, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32});
|
||||
SmallString<32> Str;
|
||||
raw_svector_ostream Res(Str);
|
||||
Data16.dump(Res, DIDumpOptions());
|
||||
EXPECT_EQ(memcmp(Str.data(), "000102030405060708090a0b0c0d0e0f", 32), 0);
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
@ -1,269 +0,0 @@
|
||||
//===--- unittests/DebugInfo/DWARF/DwarfGenerator.cpp -----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DwarfGenerator.h"
|
||||
#include "../lib/CodeGen/AsmPrinter/DwarfStringPool.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/CodeGen/DIE.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
|
||||
#include "llvm/MC/MCAsmBackend.h"
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
#include "llvm/MC/MCCodeEmitter.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCDwarf.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCObjectFileInfo.h"
|
||||
#include "llvm/MC/MCRegisterInfo.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/MCTargetOptionsCommandFlags.def"
|
||||
#include "llvm/PassAnalysisSupport.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace dwarf;
|
||||
|
||||
namespace {} // end anonymous namespace
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// dwarfgen::DIE implementation.
|
||||
//===----------------------------------------------------------------------===//
|
||||
unsigned dwarfgen::DIE::computeSizeAndOffsets(unsigned Offset) {
|
||||
auto &DG = CU->getGenerator();
|
||||
return Die->computeOffsetsAndAbbrevs(DG.getAsmPrinter(), DG.getAbbrevSet(),
|
||||
Offset);
|
||||
}
|
||||
|
||||
void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, uint64_t U) {
|
||||
auto &DG = CU->getGenerator();
|
||||
Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
|
||||
DIEInteger(U));
|
||||
}
|
||||
|
||||
void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form,
|
||||
StringRef String) {
|
||||
auto &DG = CU->getGenerator();
|
||||
if (Form == DW_FORM_string) {
|
||||
Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
|
||||
new (DG.getAllocator())
|
||||
DIEInlineString(String, DG.getAllocator()));
|
||||
} else {
|
||||
Die->addValue(
|
||||
DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
|
||||
DIEString(DG.getStringPool().getEntry(*DG.getAsmPrinter(), String)));
|
||||
}
|
||||
}
|
||||
|
||||
void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form,
|
||||
dwarfgen::DIE &RefDie) {
|
||||
auto &DG = CU->getGenerator();
|
||||
Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
|
||||
DIEEntry(*RefDie.Die));
|
||||
}
|
||||
|
||||
void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, const void *P,
|
||||
size_t S) {
|
||||
auto &DG = CU->getGenerator();
|
||||
DIEBlock *Block = new (DG.getAllocator()) DIEBlock;
|
||||
for (size_t I = 0; I < S; ++I)
|
||||
Block->addValue(
|
||||
DG.getAllocator(), (dwarf::Attribute)0, dwarf::DW_FORM_data1,
|
||||
DIEInteger(
|
||||
(const_cast<uint8_t *>(static_cast<const uint8_t *>(P)))[I]));
|
||||
|
||||
Block->ComputeSize(DG.getAsmPrinter());
|
||||
Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
|
||||
Block);
|
||||
}
|
||||
|
||||
void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form) {
|
||||
auto &DG = CU->getGenerator();
|
||||
assert(Form == DW_FORM_flag_present);
|
||||
Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,
|
||||
DIEInteger(1));
|
||||
}
|
||||
|
||||
dwarfgen::DIE dwarfgen::DIE::addChild(dwarf::Tag Tag) {
|
||||
auto &DG = CU->getGenerator();
|
||||
return dwarfgen::DIE(CU,
|
||||
&Die->addChild(llvm::DIE::get(DG.getAllocator(), Tag)));
|
||||
}
|
||||
|
||||
dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() {
|
||||
return dwarfgen::DIE(this, &DU.getUnitDie());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// dwarfgen::Generator implementation.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
dwarfgen::Generator::Generator()
|
||||
: MAB(nullptr), MCE(nullptr), MS(nullptr), StringPool(nullptr),
|
||||
Abbreviations(Allocator) {}
|
||||
dwarfgen::Generator::~Generator() = default;
|
||||
|
||||
llvm::Expected<std::unique_ptr<dwarfgen::Generator>>
|
||||
dwarfgen::Generator::create(Triple TheTriple, uint16_t DwarfVersion) {
|
||||
std::unique_ptr<dwarfgen::Generator> GenUP(new dwarfgen::Generator());
|
||||
llvm::Error error = GenUP->init(TheTriple, DwarfVersion);
|
||||
if (error)
|
||||
return Expected<std::unique_ptr<dwarfgen::Generator>>(std::move(error));
|
||||
return Expected<std::unique_ptr<dwarfgen::Generator>>(std::move(GenUP));
|
||||
}
|
||||
|
||||
llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {
|
||||
Version = V;
|
||||
std::string ErrorStr;
|
||||
std::string TripleName;
|
||||
|
||||
// Get the target.
|
||||
const Target *TheTarget =
|
||||
TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr);
|
||||
if (!TheTarget)
|
||||
return make_error<StringError>(ErrorStr, inconvertibleErrorCode());
|
||||
|
||||
TripleName = TheTriple.getTriple();
|
||||
|
||||
// Create all the MC Objects.
|
||||
MRI.reset(TheTarget->createMCRegInfo(TripleName));
|
||||
if (!MRI)
|
||||
return make_error<StringError>(Twine("no register info for target ") +
|
||||
TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName));
|
||||
if (!MAI)
|
||||
return make_error<StringError>("no asm info for target " + TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
MOFI.reset(new MCObjectFileInfo);
|
||||
MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get()));
|
||||
MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, *MC);
|
||||
|
||||
MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));
|
||||
if (!MSTI)
|
||||
return make_error<StringError>("no subtarget info for target " + TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags();
|
||||
MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, MCOptions);
|
||||
if (!MAB)
|
||||
return make_error<StringError>("no asm backend for target " + TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
MII.reset(TheTarget->createMCInstrInfo());
|
||||
if (!MII)
|
||||
return make_error<StringError>("no instr info info for target " +
|
||||
TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC);
|
||||
if (!MCE)
|
||||
return make_error<StringError>("no code emitter for target " + TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
Stream = make_unique<raw_svector_ostream>(FileBytes);
|
||||
|
||||
MS = TheTarget->createMCObjectStreamer(
|
||||
TheTriple, *MC, std::unique_ptr<MCAsmBackend>(MAB), *Stream,
|
||||
std::unique_ptr<MCCodeEmitter>(MCE), *MSTI, MCOptions.MCRelaxAll,
|
||||
MCOptions.MCIncrementalLinkerCompatible,
|
||||
/*DWARFMustBeAtTheEnd*/ false);
|
||||
if (!MS)
|
||||
return make_error<StringError>("no object streamer for target " +
|
||||
TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
// Finally create the AsmPrinter we'll use to emit the DIEs.
|
||||
TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(),
|
||||
None));
|
||||
if (!TM)
|
||||
return make_error<StringError>("no target machine for target " + TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MCStreamer>(MS)));
|
||||
if (!Asm)
|
||||
return make_error<StringError>("no asm printer for target " + TripleName,
|
||||
inconvertibleErrorCode());
|
||||
|
||||
// Set the DWARF version correctly on all classes that we use.
|
||||
MC->setDwarfVersion(Version);
|
||||
Asm->setDwarfVersion(Version);
|
||||
|
||||
StringPool = llvm::make_unique<DwarfStringPool>(Allocator, *Asm, StringRef());
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
StringRef dwarfgen::Generator::generate() {
|
||||
// Offset from the first CU in the debug info section is 0 initially.
|
||||
unsigned SecOffset = 0;
|
||||
|
||||
// Iterate over each compile unit and set the size and offsets for each
|
||||
// DIE within each compile unit. All offsets are CU relative.
|
||||
for (auto &CU : CompileUnits) {
|
||||
// Set the absolute .debug_info offset for this compile unit.
|
||||
CU->setOffset(SecOffset);
|
||||
// The DIEs contain compile unit relative offsets.
|
||||
unsigned CUOffset = 11;
|
||||
CUOffset = CU->getUnitDIE().computeSizeAndOffsets(CUOffset);
|
||||
// Update our absolute .debug_info offset.
|
||||
SecOffset += CUOffset;
|
||||
CU->setLength(CUOffset - 4);
|
||||
}
|
||||
Abbreviations.Emit(Asm.get(), MOFI->getDwarfAbbrevSection());
|
||||
StringPool->emit(*Asm, MOFI->getDwarfStrSection());
|
||||
MS->SwitchSection(MOFI->getDwarfInfoSection());
|
||||
for (auto &CU : CompileUnits) {
|
||||
uint16_t Version = CU->getVersion();
|
||||
auto Length = CU->getLength();
|
||||
MC->setDwarfVersion(Version);
|
||||
assert(Length != -1U);
|
||||
Asm->EmitInt32(Length);
|
||||
Asm->EmitInt16(Version);
|
||||
if (Version <= 4) {
|
||||
Asm->EmitInt32(0);
|
||||
Asm->EmitInt8(CU->getAddressSize());
|
||||
} else {
|
||||
Asm->EmitInt8(dwarf::DW_UT_compile);
|
||||
Asm->EmitInt8(CU->getAddressSize());
|
||||
Asm->EmitInt32(0);
|
||||
}
|
||||
Asm->emitDwarfDIE(*CU->getUnitDIE().Die);
|
||||
}
|
||||
|
||||
MS->Finish();
|
||||
if (FileBytes.empty())
|
||||
return StringRef();
|
||||
return StringRef(FileBytes.data(), FileBytes.size());
|
||||
}
|
||||
|
||||
bool dwarfgen::Generator::saveFile(StringRef Path) {
|
||||
if (FileBytes.empty())
|
||||
return false;
|
||||
std::error_code EC;
|
||||
raw_fd_ostream Strm(Path, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
return false;
|
||||
Strm.write(FileBytes.data(), FileBytes.size());
|
||||
Strm.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
dwarfgen::CompileUnit &dwarfgen::Generator::addCompileUnit() {
|
||||
CompileUnits.push_back(std::unique_ptr<CompileUnit>(
|
||||
new CompileUnit(*this, Version, Asm->getPointerSize())));
|
||||
return *CompileUnits.back();
|
||||
}
|
@ -1,232 +0,0 @@
|
||||
//===--- unittests/DebugInfo/DWARF/DwarfGenerator.h -------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// A file that can generate DWARF debug info for unit tests.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H
|
||||
#define LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/CodeGen/DIE.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class AsmPrinter;
|
||||
class DIE;
|
||||
class DIEAbbrev;
|
||||
class DwarfStringPool;
|
||||
class MCAsmBackend;
|
||||
class MCAsmInfo;
|
||||
class MCCodeEmitter;
|
||||
class MCContext;
|
||||
struct MCDwarfLineTableParams;
|
||||
class MCInstrInfo;
|
||||
class MCObjectFileInfo;
|
||||
class MCRegisterInfo;
|
||||
class MCStreamer;
|
||||
class MCSubtargetInfo;
|
||||
class raw_fd_ostream;
|
||||
class TargetMachine;
|
||||
class Triple;
|
||||
|
||||
namespace dwarfgen {
|
||||
|
||||
class Generator;
|
||||
class CompileUnit;
|
||||
|
||||
/// A DWARF debug information entry class used to generate DWARF DIEs.
|
||||
///
|
||||
/// This class is used to quickly generate DWARF debug information by creating
|
||||
/// child DIEs or adding attributes to the current DIE. Instances of this class
|
||||
/// are created from the compile unit (dwarfgen::CompileUnit::getUnitDIE()) or
|
||||
/// by calling dwarfgen::DIE::addChild(...) and using the returned DIE object.
|
||||
class DIE {
|
||||
dwarfgen::CompileUnit *CU;
|
||||
llvm::DIE *Die;
|
||||
|
||||
protected:
|
||||
friend class Generator;
|
||||
friend class CompileUnit;
|
||||
|
||||
DIE(CompileUnit *U = nullptr, llvm::DIE *D = nullptr) : CU(U), Die(D) {}
|
||||
|
||||
/// Called with a compile/type unit relative offset prior to generating the
|
||||
/// DWARF debug info.
|
||||
///
|
||||
/// \param CUOffset the compile/type unit relative offset where the
|
||||
/// abbreviation code for this DIE will be encoded.
|
||||
unsigned computeSizeAndOffsets(unsigned CUOffset);
|
||||
|
||||
public:
|
||||
/// Add an attribute value that has no value.
|
||||
///
|
||||
/// \param Attr a dwarf::Attribute enumeration value or any uint16_t that
|
||||
/// represents a user defined DWARF attribute.
|
||||
/// \param Form the dwarf::Form to use when encoding the attribute. This is
|
||||
/// only used with the DW_FORM_flag_present form encoding.
|
||||
void addAttribute(uint16_t Attr, dwarf::Form Form);
|
||||
|
||||
/// Add an attribute value to be encoded as a DIEInteger
|
||||
///
|
||||
/// \param Attr a dwarf::Attribute enumeration value or any uint16_t that
|
||||
/// represents a user defined DWARF attribute.
|
||||
/// \param Form the dwarf::Form to use when encoding the attribute.
|
||||
/// \param U the unsigned integer to encode.
|
||||
void addAttribute(uint16_t Attr, dwarf::Form Form, uint64_t U);
|
||||
|
||||
/// Add an attribute value to be encoded as a DIEString or DIEInlinedString.
|
||||
///
|
||||
/// \param Attr a dwarf::Attribute enumeration value or any uint16_t that
|
||||
/// represents a user defined DWARF attribute.
|
||||
/// \param Form the dwarf::Form to use when encoding the attribute. The form
|
||||
/// must be one of DW_FORM_strp or DW_FORM_string.
|
||||
/// \param String the string to encode.
|
||||
void addAttribute(uint16_t Attr, dwarf::Form Form, StringRef String);
|
||||
|
||||
/// Add an attribute value to be encoded as a DIEEntry.
|
||||
///
|
||||
/// DIEEntry attributes refer to other llvm::DIE objects that have been
|
||||
/// created.
|
||||
///
|
||||
/// \param Attr a dwarf::Attribute enumeration value or any uint16_t that
|
||||
/// represents a user defined DWARF attribute.
|
||||
/// \param Form the dwarf::Form to use when encoding the attribute. The form
|
||||
/// must be one of DW_FORM_strp or DW_FORM_string.
|
||||
/// \param RefDie the DIE that this attriute refers to.
|
||||
void addAttribute(uint16_t Attr, dwarf::Form Form, dwarfgen::DIE &RefDie);
|
||||
|
||||
/// Add an attribute value to be encoded as a DIEBlock.
|
||||
///
|
||||
/// DIEBlock attributes refers to binary data that is stored as the
|
||||
/// attribute's value.
|
||||
///
|
||||
/// \param Attr a dwarf::Attribute enumeration value or any uint16_t that
|
||||
/// represents a user defined DWARF attribute.
|
||||
/// \param Form the dwarf::Form to use when encoding the attribute. The form
|
||||
/// must be one of DW_FORM_strp or DW_FORM_string.
|
||||
/// \param P a pointer to the data to store as the attribute value.
|
||||
/// \param S the size in bytes of the data pointed to by P .
|
||||
void addAttribute(uint16_t Attr, dwarf::Form Form, const void *P, size_t S);
|
||||
|
||||
/// Add a new child to this DIE object.
|
||||
///
|
||||
/// \param Tag the dwarf::Tag to assing to the llvm::DIE object.
|
||||
/// \returns the newly created DIE object that is now a child owned by this
|
||||
/// object.
|
||||
dwarfgen::DIE addChild(dwarf::Tag Tag);
|
||||
};
|
||||
|
||||
/// A DWARF compile unit used to generate DWARF compile/type units.
|
||||
///
|
||||
/// Instances of these classes are created by instances of the Generator
|
||||
/// class. All information required to generate a DWARF compile unit is
|
||||
/// contained inside this class.
|
||||
class CompileUnit {
|
||||
Generator &DG;
|
||||
BasicDIEUnit DU;
|
||||
|
||||
public:
|
||||
CompileUnit(Generator &D, uint16_t V, uint8_t A)
|
||||
: DG(D), DU(V, A, dwarf::DW_TAG_compile_unit) {}
|
||||
DIE getUnitDIE();
|
||||
Generator &getGenerator() { return DG; }
|
||||
uint64_t getOffset() const { return DU.getDebugSectionOffset(); }
|
||||
uint64_t getLength() const { return DU.getLength(); }
|
||||
uint16_t getVersion() const { return DU.getDwarfVersion(); }
|
||||
uint16_t getAddressSize() const { return DU.getAddressSize(); }
|
||||
void setOffset(uint64_t Offset) { DU.setDebugSectionOffset(Offset); }
|
||||
void setLength(uint64_t Length) { DU.setLength(Length); }
|
||||
};
|
||||
|
||||
/// A DWARF generator.
|
||||
///
|
||||
/// Generate DWARF for unit tests by creating any instance of this class and
|
||||
/// calling Generator::addCompileUnit(), and then getting the dwarfgen::DIE from
|
||||
/// the returned compile unit and adding attributes and children to each DIE.
|
||||
class Generator {
|
||||
std::unique_ptr<MCRegisterInfo> MRI;
|
||||
std::unique_ptr<MCAsmInfo> MAI;
|
||||
std::unique_ptr<MCObjectFileInfo> MOFI;
|
||||
std::unique_ptr<MCContext> MC;
|
||||
MCAsmBackend *MAB; // Owned by MCStreamer
|
||||
std::unique_ptr<MCInstrInfo> MII;
|
||||
std::unique_ptr<MCSubtargetInfo> MSTI;
|
||||
MCCodeEmitter *MCE; // Owned by MCStreamer
|
||||
MCStreamer *MS; // Owned by AsmPrinter
|
||||
std::unique_ptr<TargetMachine> TM;
|
||||
std::unique_ptr<AsmPrinter> Asm;
|
||||
BumpPtrAllocator Allocator;
|
||||
std::unique_ptr<DwarfStringPool> StringPool; // Entries owned by Allocator.
|
||||
std::vector<std::unique_ptr<CompileUnit>> CompileUnits;
|
||||
DIEAbbrevSet Abbreviations;
|
||||
|
||||
SmallString<4096> FileBytes;
|
||||
/// The stream we use to generate the DWARF into as an ELF file.
|
||||
std::unique_ptr<raw_svector_ostream> Stream;
|
||||
/// The DWARF version to generate.
|
||||
uint16_t Version;
|
||||
|
||||
/// Private constructor, call Generator::Create(...) to get a DWARF generator
|
||||
/// expected.
|
||||
Generator();
|
||||
|
||||
/// Create the streamer and setup the output buffer.
|
||||
llvm::Error init(Triple TheTriple, uint16_t DwarfVersion);
|
||||
|
||||
public:
|
||||
/// Create a DWARF generator or get an appropriate error.
|
||||
///
|
||||
/// \param TheTriple the triple to use when creating any required support
|
||||
/// classes needed to emit the DWARF.
|
||||
/// \param DwarfVersion the version of DWARF to emit.
|
||||
///
|
||||
/// \returns a llvm::Expected that either contains a unique_ptr to a Generator
|
||||
/// or a llvm::Error.
|
||||
static llvm::Expected<std::unique_ptr<Generator>>
|
||||
create(Triple TheTriple, uint16_t DwarfVersion);
|
||||
|
||||
~Generator();
|
||||
|
||||
/// Generate all DWARF sections and return a memory buffer that
|
||||
/// contains an ELF file that contains the DWARF.
|
||||
StringRef generate();
|
||||
|
||||
/// Add a compile unit to be generated.
|
||||
///
|
||||
/// \returns a dwarfgen::CompileUnit that can be used to retrieve the compile
|
||||
/// unit dwarfgen::DIE that can be used to add attributes and add child DIE
|
||||
/// objedts to.
|
||||
dwarfgen::CompileUnit &addCompileUnit();
|
||||
|
||||
BumpPtrAllocator &getAllocator() { return Allocator; }
|
||||
AsmPrinter *getAsmPrinter() const { return Asm.get(); }
|
||||
MCContext *getMCContext() const { return MC.get(); }
|
||||
DIEAbbrevSet &getAbbrevSet() { return Abbreviations; }
|
||||
DwarfStringPool &getStringPool() { return *StringPool; }
|
||||
|
||||
/// Save the generated DWARF file to disk.
|
||||
///
|
||||
/// \param Path the path to save the ELF file to.
|
||||
bool saveFile(StringRef Path);
|
||||
};
|
||||
|
||||
} // end namespace dwarfgen
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFGENERATOR_H
|
@ -1,15 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
DebugInfoMSF
|
||||
)
|
||||
|
||||
set(DebugInfoMSFSources
|
||||
MappedBlockStreamTest.cpp
|
||||
MSFBuilderTest.cpp
|
||||
MSFCommonTest.cpp
|
||||
)
|
||||
|
||||
add_llvm_unittest(DebugInfoMSFTests
|
||||
${DebugInfoMSFSources}
|
||||
)
|
||||
|
||||
target_link_libraries(DebugInfoMSFTests PRIVATE LLVMTestingSupport)
|
@ -1,397 +0,0 @@
|
||||
//===- MSFBuilderTest.cpp Tests manipulation of MSF stream metadata ------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFCommon.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::msf;
|
||||
using namespace testing;
|
||||
|
||||
namespace {
|
||||
class MSFBuilderTest : public testing::Test {
|
||||
protected:
|
||||
void initializeSimpleSuperBlock(msf::SuperBlock &SB) {
|
||||
initializeSuperBlock(SB);
|
||||
SB.NumBlocks = 1000;
|
||||
SB.NumDirectoryBytes = 8192;
|
||||
}
|
||||
|
||||
void initializeSuperBlock(msf::SuperBlock &SB) {
|
||||
::memset(&SB, 0, sizeof(SB));
|
||||
|
||||
::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic));
|
||||
SB.FreeBlockMapBlock = 1;
|
||||
SB.BlockMapAddr = 1;
|
||||
SB.BlockSize = 4096;
|
||||
SB.NumDirectoryBytes = 0;
|
||||
SB.NumBlocks = 2; // one for the Super Block, one for the directory
|
||||
}
|
||||
|
||||
BumpPtrAllocator Allocator;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_F(MSFBuilderTest, ValidateSuperBlockAccept) {
|
||||
// Test that a known good super block passes validation.
|
||||
SuperBlock SB;
|
||||
initializeSuperBlock(SB);
|
||||
|
||||
EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Succeeded());
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, ValidateSuperBlockReject) {
|
||||
// Test that various known problems cause a super block to be rejected.
|
||||
SuperBlock SB;
|
||||
initializeSimpleSuperBlock(SB);
|
||||
|
||||
// Mismatched magic
|
||||
SB.MagicBytes[0] = 8;
|
||||
EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Failed());
|
||||
initializeSimpleSuperBlock(SB);
|
||||
|
||||
// Block 0 is reserved for super block, can't be occupied by the block map
|
||||
SB.BlockMapAddr = 0;
|
||||
EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Failed());
|
||||
initializeSimpleSuperBlock(SB);
|
||||
|
||||
// Block sizes have to be powers of 2.
|
||||
SB.BlockSize = 3120;
|
||||
EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Failed());
|
||||
initializeSimpleSuperBlock(SB);
|
||||
|
||||
// The directory itself has a maximum size.
|
||||
SB.NumDirectoryBytes = SB.BlockSize * SB.BlockSize / 4;
|
||||
EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Succeeded());
|
||||
SB.NumDirectoryBytes = SB.NumDirectoryBytes + 4;
|
||||
EXPECT_THAT_ERROR(msf::validateSuperBlock(SB), Failed());
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestUsedBlocksMarkedAsUsed) {
|
||||
// Test that when assigning a stream to a known list of blocks, the blocks
|
||||
// are correctly marked as used after adding, but no other incorrect blocks
|
||||
// are accidentally marked as used.
|
||||
|
||||
std::vector<uint32_t> Blocks = {4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||
// Allocate some extra blocks at the end so we can verify that they're free
|
||||
// after the initialization.
|
||||
uint32_t NumBlocks = msf::getMinimumBlockCount() + Blocks.size() + 10;
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096, NumBlocks);
|
||||
ASSERT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(Blocks.size() * 4096, Blocks),
|
||||
Succeeded());
|
||||
|
||||
for (auto B : Blocks) {
|
||||
EXPECT_FALSE(Msf.isBlockFree(B));
|
||||
}
|
||||
|
||||
uint32_t FreeBlockStart = Blocks.back() + 1;
|
||||
for (uint32_t I = FreeBlockStart; I < NumBlocks; ++I) {
|
||||
EXPECT_TRUE(Msf.isBlockFree(I));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestAddStreamNoDirectoryBlockIncrease) {
|
||||
// Test that adding a new stream correctly updates the directory. This only
|
||||
// tests the case where the directory *DOES NOT* grow large enough that it
|
||||
// crosses a Block boundary.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
auto ExpectedL1 = Msf.build();
|
||||
EXPECT_THAT_EXPECTED(ExpectedL1, Succeeded());
|
||||
MSFLayout &L1 = *ExpectedL1;
|
||||
|
||||
auto OldDirBlocks = L1.DirectoryBlocks;
|
||||
EXPECT_EQ(1U, OldDirBlocks.size());
|
||||
|
||||
auto ExpectedMsf2 = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf2, Succeeded());
|
||||
auto &Msf2 = *ExpectedMsf2;
|
||||
|
||||
EXPECT_THAT_EXPECTED(Msf2.addStream(4000), Succeeded());
|
||||
EXPECT_EQ(1U, Msf2.getNumStreams());
|
||||
EXPECT_EQ(4000U, Msf2.getStreamSize(0));
|
||||
auto Blocks = Msf2.getStreamBlocks(0);
|
||||
EXPECT_EQ(1U, Blocks.size());
|
||||
|
||||
auto ExpectedL2 = Msf2.build();
|
||||
EXPECT_THAT_EXPECTED(ExpectedL2, Succeeded());
|
||||
MSFLayout &L2 = *ExpectedL2;
|
||||
auto NewDirBlocks = L2.DirectoryBlocks;
|
||||
EXPECT_EQ(1U, NewDirBlocks.size());
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestAddStreamWithDirectoryBlockIncrease) {
|
||||
// Test that adding a new stream correctly updates the directory. This only
|
||||
// tests the case where the directory *DOES* grow large enough that it
|
||||
// crosses a Block boundary. This is because the newly added stream occupies
|
||||
// so many Blocks that need to be indexed in the directory that the directory
|
||||
// crosses a Block boundary.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(4096 * 4096 / sizeof(uint32_t)),
|
||||
Succeeded());
|
||||
|
||||
auto ExpectedL1 = Msf.build();
|
||||
EXPECT_THAT_EXPECTED(ExpectedL1, Succeeded());
|
||||
MSFLayout &L1 = *ExpectedL1;
|
||||
auto DirBlocks = L1.DirectoryBlocks;
|
||||
EXPECT_EQ(2U, DirBlocks.size());
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestGrowStreamNoBlockIncrease) {
|
||||
// Test growing an existing stream by a value that does not affect the number
|
||||
// of blocks it occupies.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(1024), Succeeded());
|
||||
EXPECT_EQ(1024U, Msf.getStreamSize(0));
|
||||
auto OldStreamBlocks = Msf.getStreamBlocks(0);
|
||||
EXPECT_EQ(1U, OldStreamBlocks.size());
|
||||
|
||||
EXPECT_THAT_ERROR(Msf.setStreamSize(0, 2048), Succeeded());
|
||||
EXPECT_EQ(2048U, Msf.getStreamSize(0));
|
||||
auto NewStreamBlocks = Msf.getStreamBlocks(0);
|
||||
EXPECT_EQ(1U, NewStreamBlocks.size());
|
||||
|
||||
EXPECT_EQ(OldStreamBlocks, NewStreamBlocks);
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestGrowStreamWithBlockIncrease) {
|
||||
// Test that growing an existing stream to a value large enough that it causes
|
||||
// the need to allocate new Blocks to the stream correctly updates the
|
||||
// stream's
|
||||
// block list.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(2048), Succeeded());
|
||||
EXPECT_EQ(2048U, Msf.getStreamSize(0));
|
||||
std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);
|
||||
EXPECT_EQ(1U, OldStreamBlocks.size());
|
||||
|
||||
EXPECT_THAT_ERROR(Msf.setStreamSize(0, 6144), Succeeded());
|
||||
EXPECT_EQ(6144U, Msf.getStreamSize(0));
|
||||
std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);
|
||||
EXPECT_EQ(2U, NewStreamBlocks.size());
|
||||
|
||||
EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]);
|
||||
EXPECT_NE(NewStreamBlocks[0], NewStreamBlocks[1]);
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestShrinkStreamNoBlockDecrease) {
|
||||
// Test that shrinking an existing stream by a value that does not affect the
|
||||
// number of Blocks it occupies makes no changes to stream's block list.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(2048), Succeeded());
|
||||
EXPECT_EQ(2048U, Msf.getStreamSize(0));
|
||||
std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);
|
||||
EXPECT_EQ(1U, OldStreamBlocks.size());
|
||||
|
||||
EXPECT_THAT_ERROR(Msf.setStreamSize(0, 1024), Succeeded());
|
||||
EXPECT_EQ(1024U, Msf.getStreamSize(0));
|
||||
std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);
|
||||
EXPECT_EQ(1U, NewStreamBlocks.size());
|
||||
|
||||
EXPECT_EQ(OldStreamBlocks, NewStreamBlocks);
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestShrinkStreamWithBlockDecrease) {
|
||||
// Test that shrinking an existing stream to a value large enough that it
|
||||
// causes the need to deallocate new Blocks to the stream correctly updates
|
||||
// the stream's block list.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(6144), Succeeded());
|
||||
EXPECT_EQ(6144U, Msf.getStreamSize(0));
|
||||
std::vector<uint32_t> OldStreamBlocks = Msf.getStreamBlocks(0);
|
||||
EXPECT_EQ(2U, OldStreamBlocks.size());
|
||||
|
||||
EXPECT_THAT_ERROR(Msf.setStreamSize(0, 2048), Succeeded());
|
||||
EXPECT_EQ(2048U, Msf.getStreamSize(0));
|
||||
std::vector<uint32_t> NewStreamBlocks = Msf.getStreamBlocks(0);
|
||||
EXPECT_EQ(1U, NewStreamBlocks.size());
|
||||
|
||||
EXPECT_EQ(OldStreamBlocks[0], NewStreamBlocks[0]);
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestRejectReusedStreamBlock) {
|
||||
// Test that attempting to add a stream and assigning a block that is already
|
||||
// in use by another stream fails.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(6144), Succeeded());
|
||||
|
||||
std::vector<uint32_t> Blocks = {2, 3};
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(6144, Blocks), Failed());
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, TestBlockCountsWhenAddingStreams) {
|
||||
// Test that when adding multiple streams, the number of used and free Blocks
|
||||
// allocated to the MSF file are as expected.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
// one for the super block, one for the directory block map
|
||||
uint32_t NumUsedBlocks = Msf.getNumUsedBlocks();
|
||||
EXPECT_EQ(msf::getMinimumBlockCount(), NumUsedBlocks);
|
||||
EXPECT_EQ(0U, Msf.getNumFreeBlocks());
|
||||
|
||||
const uint32_t StreamSizes[] = {4000, 6193, 189723};
|
||||
for (int I = 0; I < 3; ++I) {
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(StreamSizes[I]), Succeeded());
|
||||
NumUsedBlocks += bytesToBlocks(StreamSizes[I], 4096);
|
||||
EXPECT_EQ(NumUsedBlocks, Msf.getNumUsedBlocks());
|
||||
EXPECT_EQ(0U, Msf.getNumFreeBlocks());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, BuildMsfLayout) {
|
||||
// Test that we can generate an MSFLayout structure from a valid layout
|
||||
// specification.
|
||||
auto ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
const uint32_t StreamSizes[] = {4000, 6193, 189723};
|
||||
uint32_t ExpectedNumBlocks = msf::getMinimumBlockCount();
|
||||
for (int I = 0; I < 3; ++I) {
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(StreamSizes[I]), Succeeded());
|
||||
ExpectedNumBlocks += bytesToBlocks(StreamSizes[I], 4096);
|
||||
}
|
||||
++ExpectedNumBlocks; // The directory itself should use 1 block
|
||||
|
||||
auto ExpectedLayout = Msf.build();
|
||||
EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());
|
||||
MSFLayout &L = *ExpectedLayout;
|
||||
EXPECT_EQ(4096U, L.SB->BlockSize);
|
||||
EXPECT_EQ(ExpectedNumBlocks, L.SB->NumBlocks);
|
||||
|
||||
EXPECT_EQ(1U, L.DirectoryBlocks.size());
|
||||
|
||||
EXPECT_EQ(3U, L.StreamMap.size());
|
||||
EXPECT_EQ(3U, L.StreamSizes.size());
|
||||
for (int I = 0; I < 3; ++I) {
|
||||
EXPECT_EQ(StreamSizes[I], L.StreamSizes[I]);
|
||||
uint32_t ExpectedNumBlocks = bytesToBlocks(StreamSizes[I], 4096);
|
||||
EXPECT_EQ(ExpectedNumBlocks, L.StreamMap[I].size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, UseDirectoryBlockHint) {
|
||||
Expected<MSFBuilder> ExpectedMsf = MSFBuilder::create(
|
||||
Allocator, 4096, msf::getMinimumBlockCount() + 1, false);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
uint32_t B = msf::getFirstUnreservedBlock();
|
||||
EXPECT_THAT_ERROR(Msf.setDirectoryBlocksHint({B + 1}), Succeeded());
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(2048, {B + 2}), Succeeded());
|
||||
|
||||
auto ExpectedLayout = Msf.build();
|
||||
EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());
|
||||
MSFLayout &L = *ExpectedLayout;
|
||||
EXPECT_EQ(msf::getMinimumBlockCount() + 2, L.SB->NumBlocks);
|
||||
EXPECT_EQ(1U, L.DirectoryBlocks.size());
|
||||
EXPECT_EQ(1U, L.StreamMap[0].size());
|
||||
|
||||
EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);
|
||||
EXPECT_EQ(B + 2, L.StreamMap[0].front());
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, DirectoryBlockHintInsufficient) {
|
||||
Expected<MSFBuilder> ExpectedMsf =
|
||||
MSFBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
uint32_t B = msf::getFirstUnreservedBlock();
|
||||
EXPECT_THAT_ERROR(Msf.setDirectoryBlocksHint({B + 1}), Succeeded());
|
||||
|
||||
uint32_t Size = 4096 * 4096 / 4;
|
||||
EXPECT_THAT_EXPECTED(Msf.addStream(Size), Succeeded());
|
||||
|
||||
auto ExpectedLayout = Msf.build();
|
||||
EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());
|
||||
MSFLayout &L = *ExpectedLayout;
|
||||
EXPECT_EQ(2U, L.DirectoryBlocks.size());
|
||||
EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, DirectoryBlockHintOverestimated) {
|
||||
Expected<MSFBuilder> ExpectedMsf =
|
||||
MSFBuilder::create(Allocator, 4096, msf::getMinimumBlockCount() + 2);
|
||||
EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
uint32_t B = msf::getFirstUnreservedBlock();
|
||||
EXPECT_THAT_ERROR(Msf.setDirectoryBlocksHint({B + 1, B + 2}), Succeeded());
|
||||
|
||||
ASSERT_THAT_EXPECTED(Msf.addStream(2048), Succeeded());
|
||||
|
||||
auto ExpectedLayout = Msf.build();
|
||||
ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());
|
||||
MSFLayout &L = *ExpectedLayout;
|
||||
EXPECT_EQ(1U, L.DirectoryBlocks.size());
|
||||
EXPECT_EQ(B + 1, L.DirectoryBlocks[0]);
|
||||
}
|
||||
|
||||
TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) {
|
||||
Expected<MSFBuilder> ExpectedMsf = MSFBuilder::create(Allocator, 4096);
|
||||
ASSERT_THAT_EXPECTED(ExpectedMsf, Succeeded());
|
||||
auto &Msf = *ExpectedMsf;
|
||||
|
||||
// A block is 4096 bytes, and every 4096 blocks we have 2 reserved FPM blocks.
|
||||
// By creating add a stream that spans 4096*4096*3 bytes, we ensure that we
|
||||
// cross over a couple of reserved FPM blocks, and that none of them are
|
||||
// allocated to the stream.
|
||||
constexpr uint32_t StreamSize = 4096 * 4096 * 3;
|
||||
Expected<uint32_t> SN = Msf.addStream(StreamSize);
|
||||
ASSERT_THAT_EXPECTED(SN, Succeeded());
|
||||
|
||||
auto ExpectedLayout = Msf.build();
|
||||
ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());
|
||||
MSFLayout &L = *ExpectedLayout;
|
||||
auto BlocksRef = L.StreamMap[*SN];
|
||||
std::vector<uint32_t> Blocks(BlocksRef.begin(), BlocksRef.end());
|
||||
EXPECT_EQ(StreamSize, L.StreamSizes[*SN]);
|
||||
|
||||
for (uint32_t I = 0; I <= 3; ++I) {
|
||||
// Pages from the regular FPM are allocated, while pages from the alt fpm
|
||||
// are free.
|
||||
EXPECT_FALSE(L.FreePageMap.test(1 + I * 4096));
|
||||
EXPECT_TRUE(L.FreePageMap.test(2 + I * 4096));
|
||||
}
|
||||
|
||||
for (uint32_t I = 1; I <= 3; ++I) {
|
||||
EXPECT_THAT(Blocks, Not(Contains(1 + I * 4096)));
|
||||
EXPECT_THAT(Blocks, Not(Contains(2 + I * 4096)));
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
//===- MSFBuilderTest.cpp Tests manipulation of MSF stream metadata ------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/MSF/MSFCommon.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::msf;
|
||||
|
||||
TEST(MSFCommonTest, BytesToBlocks) {
|
||||
EXPECT_EQ(0ULL, bytesToBlocks(0, 4096));
|
||||
EXPECT_EQ(1ULL, bytesToBlocks(12, 4096));
|
||||
EXPECT_EQ(1ULL, bytesToBlocks(4096, 4096));
|
||||
EXPECT_EQ(2ULL, bytesToBlocks(4097, 4096));
|
||||
EXPECT_EQ(2ULL, bytesToBlocks(600, 512));
|
||||
}
|
||||
|
||||
TEST(MSFCommonTest, FpmIntervals) {
|
||||
SuperBlock SB;
|
||||
SB.FreeBlockMapBlock = 1;
|
||||
SB.BlockSize = 4096;
|
||||
|
||||
MSFLayout L;
|
||||
L.SB = &SB;
|
||||
|
||||
SB.NumBlocks = 12;
|
||||
EXPECT_EQ(1u, getNumFpmIntervals(L, false));
|
||||
SB.NumBlocks = SB.BlockSize;
|
||||
EXPECT_EQ(1u, getNumFpmIntervals(L, false));
|
||||
SB.NumBlocks = SB.BlockSize + 1;
|
||||
EXPECT_EQ(1u, getNumFpmIntervals(L, false));
|
||||
SB.NumBlocks = SB.BlockSize * 8;
|
||||
EXPECT_EQ(1u, getNumFpmIntervals(L, false));
|
||||
SB.NumBlocks = SB.BlockSize * 8 + 1;
|
||||
EXPECT_EQ(2u, getNumFpmIntervals(L, false));
|
||||
|
||||
SB.NumBlocks = 12;
|
||||
EXPECT_EQ(1u, getNumFpmIntervals(L, true));
|
||||
SB.NumBlocks = SB.BlockSize;
|
||||
EXPECT_EQ(1u, getNumFpmIntervals(L, true));
|
||||
SB.NumBlocks = SB.BlockSize + 1;
|
||||
EXPECT_EQ(2u, getNumFpmIntervals(L, true));
|
||||
SB.NumBlocks = SB.BlockSize * 8;
|
||||
EXPECT_EQ(8u, getNumFpmIntervals(L, true));
|
||||
SB.NumBlocks = SB.BlockSize * 8 + 1;
|
||||
EXPECT_EQ(9u, getNumFpmIntervals(L, true));
|
||||
}
|
||||
|
||||
TEST(MSFCommonTest, FpmStreamLayout) {
|
||||
SuperBlock SB;
|
||||
MSFLayout L;
|
||||
L.SB = &SB;
|
||||
SB.FreeBlockMapBlock = 1;
|
||||
|
||||
// Each FPM block has 4096 bytes for a maximum of 4096*8 allocation states.
|
||||
SB.BlockSize = 4096;
|
||||
|
||||
// 1. When we're not including unused FPM data, the length of the FPM stream
|
||||
// should be only long enough to contain 1 bit for each block.
|
||||
|
||||
// 1a. When the PDB has <= 4096*8 blocks, there should only be one FPM block.
|
||||
SB.NumBlocks = 8000;
|
||||
MSFStreamLayout SL = getFpmStreamLayout(L, false, false);
|
||||
EXPECT_EQ(1000u, SL.Length);
|
||||
EXPECT_EQ(1u, SL.Blocks.size());
|
||||
EXPECT_EQ(SB.FreeBlockMapBlock, SL.Blocks.front());
|
||||
|
||||
SL = getFpmStreamLayout(L, false, true);
|
||||
EXPECT_EQ(1000u, SL.Length);
|
||||
EXPECT_EQ(1u, SL.Blocks.size());
|
||||
EXPECT_EQ(3u - SB.FreeBlockMapBlock, SL.Blocks.front());
|
||||
|
||||
// 1b. When the PDB has > 4096*8 blocks, there should be multiple FPM blocks.
|
||||
SB.NumBlocks = SB.BlockSize * 8 + 1;
|
||||
SL = getFpmStreamLayout(L, false, false);
|
||||
EXPECT_EQ(SB.BlockSize + 1, SL.Length);
|
||||
EXPECT_EQ(2u, SL.Blocks.size());
|
||||
EXPECT_EQ(SB.FreeBlockMapBlock, SL.Blocks[0]);
|
||||
EXPECT_EQ(SB.FreeBlockMapBlock + SB.BlockSize, SL.Blocks[1]);
|
||||
|
||||
SL = getFpmStreamLayout(L, false, true);
|
||||
EXPECT_EQ(SB.BlockSize + 1, SL.Length);
|
||||
EXPECT_EQ(2u, SL.Blocks.size());
|
||||
EXPECT_EQ(3u - SB.FreeBlockMapBlock, SL.Blocks[0]);
|
||||
EXPECT_EQ(3u - SB.FreeBlockMapBlock + SB.BlockSize, SL.Blocks[1]);
|
||||
|
||||
// 2. When we are including unused FPM data, there should be one FPM block
|
||||
// at every BlockSize interval in the file, even if entire FPM blocks are
|
||||
// unused.
|
||||
SB.NumBlocks = SB.BlockSize * 8 + 1;
|
||||
SL = getFpmStreamLayout(L, true, false);
|
||||
EXPECT_EQ(SB.BlockSize * 9, SL.Length);
|
||||
EXPECT_EQ(9u, SL.Blocks.size());
|
||||
for (int I = 0; I < 9; ++I)
|
||||
EXPECT_EQ(I * SB.BlockSize + SB.FreeBlockMapBlock, SL.Blocks[I]);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,17 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
DebugInfoCodeView
|
||||
DebugInfoMSF
|
||||
DebugInfoPDB
|
||||
)
|
||||
|
||||
set(DebugInfoPDBSources
|
||||
HashTableTest.cpp
|
||||
StringTableBuilderTest.cpp
|
||||
PDBApiTest.cpp
|
||||
)
|
||||
|
||||
add_llvm_unittest(DebugInfoPDBTests
|
||||
${DebugInfoPDBSources}
|
||||
)
|
||||
|
||||
target_link_libraries(DebugInfoPDBTests PRIVATE LLVMTestingSupport)
|
@ -1,168 +0,0 @@
|
||||
//===- llvm/unittest/DebugInfo/PDB/HashTableTest.cpp ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/PDB/Native/HashTable.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
#include "llvm/Support/BinaryStreamWriter.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::pdb;
|
||||
using namespace llvm::support;
|
||||
|
||||
namespace {
|
||||
class HashTableInternals : public HashTable {
|
||||
public:
|
||||
using HashTable::Buckets;
|
||||
using HashTable::Present;
|
||||
using HashTable::Deleted;
|
||||
};
|
||||
}
|
||||
|
||||
TEST(HashTableTest, TestSimple) {
|
||||
HashTable Table;
|
||||
EXPECT_EQ(0u, Table.size());
|
||||
EXPECT_GT(Table.capacity(), 0u);
|
||||
|
||||
Table.set(3, 7);
|
||||
EXPECT_EQ(1u, Table.size());
|
||||
ASSERT_NE(Table.end(), Table.find(3));
|
||||
EXPECT_EQ(7u, Table.get(3));
|
||||
}
|
||||
|
||||
TEST(HashTableTest, TestCollision) {
|
||||
HashTable Table;
|
||||
EXPECT_EQ(0u, Table.size());
|
||||
EXPECT_GT(Table.capacity(), 0u);
|
||||
|
||||
// We use knowledge of the hash table's implementation details to make sure
|
||||
// to add another value that is the equivalent to the first value modulo the
|
||||
// hash table's capacity.
|
||||
uint32_t N1 = Table.capacity() + 1;
|
||||
uint32_t N2 = 2 * N1;
|
||||
|
||||
Table.set(N1, 7);
|
||||
Table.set(N2, 12);
|
||||
EXPECT_EQ(2u, Table.size());
|
||||
ASSERT_NE(Table.end(), Table.find(N1));
|
||||
ASSERT_NE(Table.end(), Table.find(N2));
|
||||
|
||||
EXPECT_EQ(7u, Table.get(N1));
|
||||
EXPECT_EQ(12u, Table.get(N2));
|
||||
}
|
||||
|
||||
TEST(HashTableTest, TestRemove) {
|
||||
HashTable Table;
|
||||
EXPECT_EQ(0u, Table.size());
|
||||
EXPECT_GT(Table.capacity(), 0u);
|
||||
|
||||
Table.set(1, 2);
|
||||
Table.set(3, 4);
|
||||
EXPECT_EQ(2u, Table.size());
|
||||
ASSERT_NE(Table.end(), Table.find(1));
|
||||
ASSERT_NE(Table.end(), Table.find(3));
|
||||
|
||||
EXPECT_EQ(2u, Table.get(1));
|
||||
EXPECT_EQ(4u, Table.get(3));
|
||||
|
||||
Table.remove(1u);
|
||||
EXPECT_EQ(1u, Table.size());
|
||||
EXPECT_EQ(Table.end(), Table.find(1));
|
||||
ASSERT_NE(Table.end(), Table.find(3));
|
||||
EXPECT_EQ(4u, Table.get(3));
|
||||
}
|
||||
|
||||
TEST(HashTableTest, TestCollisionAfterMultipleProbes) {
|
||||
HashTable Table;
|
||||
EXPECT_EQ(0u, Table.size());
|
||||
EXPECT_GT(Table.capacity(), 0u);
|
||||
|
||||
// Probing looks for the first available slot. A slot may already be filled
|
||||
// as a result of an item with a *different* hash value already being there.
|
||||
// Test that when this happens, the probe still finds the value.
|
||||
uint32_t N1 = Table.capacity() + 1;
|
||||
uint32_t N2 = N1 + 1;
|
||||
uint32_t N3 = 2 * N1;
|
||||
|
||||
Table.set(N1, 7);
|
||||
Table.set(N2, 11);
|
||||
Table.set(N3, 13);
|
||||
EXPECT_EQ(3u, Table.size());
|
||||
ASSERT_NE(Table.end(), Table.find(N1));
|
||||
ASSERT_NE(Table.end(), Table.find(N2));
|
||||
ASSERT_NE(Table.end(), Table.find(N3));
|
||||
|
||||
EXPECT_EQ(7u, Table.get(N1));
|
||||
EXPECT_EQ(11u, Table.get(N2));
|
||||
EXPECT_EQ(13u, Table.get(N3));
|
||||
|
||||
// Remove the one that had been filled in the middle, then insert another one
|
||||
// with a collision. It should fill the newly emptied slot.
|
||||
Table.remove(N2);
|
||||
uint32_t N4 = N1 * 3;
|
||||
Table.set(N4, 17);
|
||||
EXPECT_EQ(3u, Table.size());
|
||||
ASSERT_NE(Table.end(), Table.find(N1));
|
||||
ASSERT_NE(Table.end(), Table.find(N3));
|
||||
ASSERT_NE(Table.end(), Table.find(N4));
|
||||
|
||||
EXPECT_EQ(7u, Table.get(N1));
|
||||
EXPECT_EQ(13u, Table.get(N3));
|
||||
EXPECT_EQ(17u, Table.get(N4));
|
||||
}
|
||||
|
||||
TEST(HashTableTest, Grow) {
|
||||
// So that we are independent of the load factor, `capacity` items, which is
|
||||
// guaranteed to trigger a grow. Then verify that the size is the same, the
|
||||
// capacity is larger, and all the original items are still in the table.
|
||||
|
||||
HashTable Table;
|
||||
uint32_t OldCapacity = Table.capacity();
|
||||
for (uint32_t I = 0; I < OldCapacity; ++I) {
|
||||
Table.set(OldCapacity + I * 2 + 1, I * 2 + 3);
|
||||
}
|
||||
EXPECT_EQ(OldCapacity, Table.size());
|
||||
EXPECT_GT(Table.capacity(), OldCapacity);
|
||||
for (uint32_t I = 0; I < OldCapacity; ++I) {
|
||||
ASSERT_NE(Table.end(), Table.find(OldCapacity + I * 2 + 1));
|
||||
EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(HashTableTest, Serialization) {
|
||||
HashTableInternals Table;
|
||||
uint32_t Cap = Table.capacity();
|
||||
for (uint32_t I = 0; I < Cap; ++I) {
|
||||
Table.set(Cap + I * 2 + 1, I * 2 + 3);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Buffer(Table.calculateSerializedLength());
|
||||
MutableBinaryByteStream Stream(Buffer, little);
|
||||
BinaryStreamWriter Writer(Stream);
|
||||
EXPECT_THAT_ERROR(Table.commit(Writer), Succeeded());
|
||||
// We should have written precisely the number of bytes we calculated earlier.
|
||||
EXPECT_EQ(Buffer.size(), Writer.getOffset());
|
||||
|
||||
HashTableInternals Table2;
|
||||
BinaryStreamReader Reader(Stream);
|
||||
EXPECT_THAT_ERROR(Table2.load(Reader), Succeeded());
|
||||
// We should have read precisely the number of bytes we calculated earlier.
|
||||
EXPECT_EQ(Buffer.size(), Reader.getOffset());
|
||||
|
||||
EXPECT_EQ(Table.size(), Table2.size());
|
||||
EXPECT_EQ(Table.capacity(), Table2.capacity());
|
||||
EXPECT_EQ(Table.Buckets, Table2.Buckets);
|
||||
EXPECT_EQ(Table.Present, Table2.Present);
|
||||
EXPECT_EQ(Table.Deleted, Table2.Deleted);
|
||||
}
|
432
external/llvm/unittests/DebugInfo/PDB/PDBApiTest.cpp
vendored
432
external/llvm/unittests/DebugInfo/PDB/PDBApiTest.cpp
vendored
@ -1,432 +0,0 @@
|
||||
//===- llvm/unittest/DebugInfo/PDB/PDBApiTest.cpp -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
|
||||
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/IPDBSession.h"
|
||||
#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
|
||||
#include "llvm/DebugInfo/PDB/IPDBTable.h"
|
||||
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolAnnotation.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolBlock.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolCompilandEnv.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolCustom.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolLabel.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeCustom.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeDimension.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeFriend.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeManaged.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBSymbolUsingNamespace.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBTypes.h"
|
||||
#include "gtest/gtest.h"
|
||||
using namespace llvm;
|
||||
using namespace llvm::pdb;
|
||||
|
||||
namespace {
|
||||
|
||||
#define MOCK_SYMBOL_ACCESSOR(Func) \
|
||||
decltype(std::declval<IPDBRawSymbol>().Func()) Func() const override { \
|
||||
typedef decltype(IPDBRawSymbol::Func()) ReturnType; \
|
||||
return ReturnType(); \
|
||||
}
|
||||
|
||||
class MockSession : public IPDBSession {
|
||||
uint64_t getLoadAddress() const override { return 0; }
|
||||
void setLoadAddress(uint64_t Address) override {}
|
||||
std::unique_ptr<PDBSymbolExe> getGlobalScope() override { return nullptr; }
|
||||
std::unique_ptr<PDBSymbol> getSymbolById(uint32_t SymbolId) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBSourceFile>
|
||||
getSourceFileById(uint32_t SymbolId) const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<PDBSymbol>
|
||||
findSymbolByAddress(uint64_t Address, PDB_SymType Type) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBEnumLineNumbers>
|
||||
findLineNumbers(const PDBSymbolCompiland &Compiland,
|
||||
const IPDBSourceFile &File) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBEnumLineNumbers>
|
||||
findLineNumbersByAddress(uint64_t Address, uint32_t Length) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBEnumSourceFiles>
|
||||
findSourceFiles(const PDBSymbolCompiland *Compiland, llvm::StringRef Pattern,
|
||||
PDB_NameSearchFlags Flags) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBSourceFile>
|
||||
findOneSourceFile(const PDBSymbolCompiland *Compiland,
|
||||
llvm::StringRef Pattern,
|
||||
PDB_NameSearchFlags Flags) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>>
|
||||
findCompilandsForSourceFile(llvm::StringRef Pattern,
|
||||
PDB_NameSearchFlags Flags) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<PDBSymbolCompiland>
|
||||
findOneCompilandForSourceFile(llvm::StringRef Pattern,
|
||||
PDB_NameSearchFlags Flags) const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<IPDBEnumSourceFiles> getAllSourceFiles() const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBEnumSourceFiles> getSourceFilesForCompiland(
|
||||
const PDBSymbolCompiland &Compiland) const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<IPDBEnumDataStreams> getDebugStreams() const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<IPDBEnumTables> getEnumTables() const override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class MockRawSymbol : public IPDBRawSymbol {
|
||||
public:
|
||||
MockRawSymbol(PDB_SymType SymType)
|
||||
: Type(SymType) {}
|
||||
|
||||
void dump(raw_ostream &OS, int Indent) const override {}
|
||||
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
findChildren(PDB_SymType Type) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
findChildren(PDB_SymType Type, StringRef Name,
|
||||
PDB_NameSearchFlags Flags) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
findChildrenByRVA(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags,
|
||||
uint32_t RVA) const override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<IPDBEnumSymbols>
|
||||
findInlineFramesByRVA(uint32_t RVA) const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void getDataBytes(llvm::SmallVector<uint8_t, 32> &bytes) const override {}
|
||||
void getFrontEndVersion(VersionInfo &Version) const override {}
|
||||
void getBackEndVersion(VersionInfo &Version) const override {}
|
||||
|
||||
PDB_SymType getSymTag() const override { return Type; }
|
||||
|
||||
std::string getUndecoratedNameEx(PDB_UndnameFlags Flags) const override {
|
||||
return {};
|
||||
}
|
||||
|
||||
MOCK_SYMBOL_ACCESSOR(getAccess)
|
||||
MOCK_SYMBOL_ACCESSOR(getAddressOffset)
|
||||
MOCK_SYMBOL_ACCESSOR(getAddressSection)
|
||||
MOCK_SYMBOL_ACCESSOR(getAge)
|
||||
MOCK_SYMBOL_ACCESSOR(getArrayIndexTypeId)
|
||||
MOCK_SYMBOL_ACCESSOR(getBaseDataOffset)
|
||||
MOCK_SYMBOL_ACCESSOR(getBaseDataSlot)
|
||||
MOCK_SYMBOL_ACCESSOR(getBaseSymbolId)
|
||||
MOCK_SYMBOL_ACCESSOR(getBuiltinType)
|
||||
MOCK_SYMBOL_ACCESSOR(getBitPosition)
|
||||
MOCK_SYMBOL_ACCESSOR(getCallingConvention)
|
||||
MOCK_SYMBOL_ACCESSOR(getClassParentId)
|
||||
MOCK_SYMBOL_ACCESSOR(getCompilerName)
|
||||
MOCK_SYMBOL_ACCESSOR(getCount)
|
||||
MOCK_SYMBOL_ACCESSOR(getCountLiveRanges)
|
||||
MOCK_SYMBOL_ACCESSOR(getLanguage)
|
||||
MOCK_SYMBOL_ACCESSOR(getLexicalParentId)
|
||||
MOCK_SYMBOL_ACCESSOR(getLibraryName)
|
||||
MOCK_SYMBOL_ACCESSOR(getLiveRangeStartAddressOffset)
|
||||
MOCK_SYMBOL_ACCESSOR(getLiveRangeStartAddressSection)
|
||||
MOCK_SYMBOL_ACCESSOR(getLiveRangeStartRelativeVirtualAddress)
|
||||
MOCK_SYMBOL_ACCESSOR(getLocalBasePointerRegisterId)
|
||||
MOCK_SYMBOL_ACCESSOR(getLowerBoundId)
|
||||
MOCK_SYMBOL_ACCESSOR(getMemorySpaceKind)
|
||||
MOCK_SYMBOL_ACCESSOR(getName)
|
||||
MOCK_SYMBOL_ACCESSOR(getNumberOfAcceleratorPointerTags)
|
||||
MOCK_SYMBOL_ACCESSOR(getNumberOfColumns)
|
||||
MOCK_SYMBOL_ACCESSOR(getNumberOfModifiers)
|
||||
MOCK_SYMBOL_ACCESSOR(getNumberOfRegisterIndices)
|
||||
MOCK_SYMBOL_ACCESSOR(getNumberOfRows)
|
||||
MOCK_SYMBOL_ACCESSOR(getObjectFileName)
|
||||
MOCK_SYMBOL_ACCESSOR(getOemId)
|
||||
MOCK_SYMBOL_ACCESSOR(getOemSymbolId)
|
||||
MOCK_SYMBOL_ACCESSOR(getOffsetInUdt)
|
||||
MOCK_SYMBOL_ACCESSOR(getPlatform)
|
||||
MOCK_SYMBOL_ACCESSOR(getRank)
|
||||
MOCK_SYMBOL_ACCESSOR(getRegisterId)
|
||||
MOCK_SYMBOL_ACCESSOR(getRegisterType)
|
||||
MOCK_SYMBOL_ACCESSOR(getRelativeVirtualAddress)
|
||||
MOCK_SYMBOL_ACCESSOR(getSamplerSlot)
|
||||
MOCK_SYMBOL_ACCESSOR(getSignature)
|
||||
MOCK_SYMBOL_ACCESSOR(getSizeInUdt)
|
||||
MOCK_SYMBOL_ACCESSOR(getSlot)
|
||||
MOCK_SYMBOL_ACCESSOR(getSourceFileName)
|
||||
MOCK_SYMBOL_ACCESSOR(getStride)
|
||||
MOCK_SYMBOL_ACCESSOR(getSubTypeId)
|
||||
MOCK_SYMBOL_ACCESSOR(getSymbolsFileName)
|
||||
MOCK_SYMBOL_ACCESSOR(getSymIndexId)
|
||||
MOCK_SYMBOL_ACCESSOR(getTargetOffset)
|
||||
MOCK_SYMBOL_ACCESSOR(getTargetRelativeVirtualAddress)
|
||||
MOCK_SYMBOL_ACCESSOR(getTargetVirtualAddress)
|
||||
MOCK_SYMBOL_ACCESSOR(getTargetSection)
|
||||
MOCK_SYMBOL_ACCESSOR(getTextureSlot)
|
||||
MOCK_SYMBOL_ACCESSOR(getTimeStamp)
|
||||
MOCK_SYMBOL_ACCESSOR(getToken)
|
||||
MOCK_SYMBOL_ACCESSOR(getTypeId)
|
||||
MOCK_SYMBOL_ACCESSOR(getUavSlot)
|
||||
MOCK_SYMBOL_ACCESSOR(getUndecoratedName)
|
||||
MOCK_SYMBOL_ACCESSOR(getUnmodifiedTypeId)
|
||||
MOCK_SYMBOL_ACCESSOR(getUpperBoundId)
|
||||
MOCK_SYMBOL_ACCESSOR(getVirtualBaseDispIndex)
|
||||
MOCK_SYMBOL_ACCESSOR(getVirtualBaseOffset)
|
||||
MOCK_SYMBOL_ACCESSOR(getVirtualTableShapeId)
|
||||
MOCK_SYMBOL_ACCESSOR(getDataKind)
|
||||
MOCK_SYMBOL_ACCESSOR(getGuid)
|
||||
MOCK_SYMBOL_ACCESSOR(getOffset)
|
||||
MOCK_SYMBOL_ACCESSOR(getThisAdjust)
|
||||
MOCK_SYMBOL_ACCESSOR(getVirtualBasePointerOffset)
|
||||
MOCK_SYMBOL_ACCESSOR(getLocationType)
|
||||
MOCK_SYMBOL_ACCESSOR(getMachineType)
|
||||
MOCK_SYMBOL_ACCESSOR(getThunkOrdinal)
|
||||
MOCK_SYMBOL_ACCESSOR(getLength)
|
||||
MOCK_SYMBOL_ACCESSOR(getVirtualBaseTableType)
|
||||
MOCK_SYMBOL_ACCESSOR(getLiveRangeLength)
|
||||
MOCK_SYMBOL_ACCESSOR(getVirtualAddress)
|
||||
MOCK_SYMBOL_ACCESSOR(getUdtKind)
|
||||
MOCK_SYMBOL_ACCESSOR(hasConstructor)
|
||||
MOCK_SYMBOL_ACCESSOR(hasCustomCallingConvention)
|
||||
MOCK_SYMBOL_ACCESSOR(hasFarReturn)
|
||||
MOCK_SYMBOL_ACCESSOR(isCode)
|
||||
MOCK_SYMBOL_ACCESSOR(isCompilerGenerated)
|
||||
MOCK_SYMBOL_ACCESSOR(isConstType)
|
||||
MOCK_SYMBOL_ACCESSOR(isEditAndContinueEnabled)
|
||||
MOCK_SYMBOL_ACCESSOR(isFunction)
|
||||
MOCK_SYMBOL_ACCESSOR(getAddressTaken)
|
||||
MOCK_SYMBOL_ACCESSOR(getNoStackOrdering)
|
||||
MOCK_SYMBOL_ACCESSOR(hasAlloca)
|
||||
MOCK_SYMBOL_ACCESSOR(hasAssignmentOperator)
|
||||
MOCK_SYMBOL_ACCESSOR(hasCTypes)
|
||||
MOCK_SYMBOL_ACCESSOR(hasCastOperator)
|
||||
MOCK_SYMBOL_ACCESSOR(hasDebugInfo)
|
||||
MOCK_SYMBOL_ACCESSOR(hasEH)
|
||||
MOCK_SYMBOL_ACCESSOR(hasEHa)
|
||||
MOCK_SYMBOL_ACCESSOR(hasFramePointer)
|
||||
MOCK_SYMBOL_ACCESSOR(hasInlAsm)
|
||||
MOCK_SYMBOL_ACCESSOR(hasInlineAttribute)
|
||||
MOCK_SYMBOL_ACCESSOR(hasInterruptReturn)
|
||||
MOCK_SYMBOL_ACCESSOR(hasLongJump)
|
||||
MOCK_SYMBOL_ACCESSOR(hasManagedCode)
|
||||
MOCK_SYMBOL_ACCESSOR(hasNestedTypes)
|
||||
MOCK_SYMBOL_ACCESSOR(hasNoInlineAttribute)
|
||||
MOCK_SYMBOL_ACCESSOR(hasNoReturnAttribute)
|
||||
MOCK_SYMBOL_ACCESSOR(hasOptimizedCodeDebugInfo)
|
||||
MOCK_SYMBOL_ACCESSOR(hasOverloadedOperator)
|
||||
MOCK_SYMBOL_ACCESSOR(hasSEH)
|
||||
MOCK_SYMBOL_ACCESSOR(hasSecurityChecks)
|
||||
MOCK_SYMBOL_ACCESSOR(hasSetJump)
|
||||
MOCK_SYMBOL_ACCESSOR(hasStrictGSCheck)
|
||||
MOCK_SYMBOL_ACCESSOR(isAcceleratorGroupSharedLocal)
|
||||
MOCK_SYMBOL_ACCESSOR(isAcceleratorPointerTagLiveRange)
|
||||
MOCK_SYMBOL_ACCESSOR(isAcceleratorStubFunction)
|
||||
MOCK_SYMBOL_ACCESSOR(isAggregated)
|
||||
MOCK_SYMBOL_ACCESSOR(isIntroVirtualFunction)
|
||||
MOCK_SYMBOL_ACCESSOR(isCVTCIL)
|
||||
MOCK_SYMBOL_ACCESSOR(isConstructorVirtualBase)
|
||||
MOCK_SYMBOL_ACCESSOR(isCxxReturnUdt)
|
||||
MOCK_SYMBOL_ACCESSOR(isDataAligned)
|
||||
MOCK_SYMBOL_ACCESSOR(isHLSLData)
|
||||
MOCK_SYMBOL_ACCESSOR(isHotpatchable)
|
||||
MOCK_SYMBOL_ACCESSOR(isIndirectVirtualBaseClass)
|
||||
MOCK_SYMBOL_ACCESSOR(isInterfaceUdt)
|
||||
MOCK_SYMBOL_ACCESSOR(isIntrinsic)
|
||||
MOCK_SYMBOL_ACCESSOR(isLTCG)
|
||||
MOCK_SYMBOL_ACCESSOR(isLocationControlFlowDependent)
|
||||
MOCK_SYMBOL_ACCESSOR(isMSILNetmodule)
|
||||
MOCK_SYMBOL_ACCESSOR(isMatrixRowMajor)
|
||||
MOCK_SYMBOL_ACCESSOR(isManagedCode)
|
||||
MOCK_SYMBOL_ACCESSOR(isMSILCode)
|
||||
MOCK_SYMBOL_ACCESSOR(isMultipleInheritance)
|
||||
MOCK_SYMBOL_ACCESSOR(isNaked)
|
||||
MOCK_SYMBOL_ACCESSOR(isNested)
|
||||
MOCK_SYMBOL_ACCESSOR(isOptimizedAway)
|
||||
MOCK_SYMBOL_ACCESSOR(isPacked)
|
||||
MOCK_SYMBOL_ACCESSOR(isPointerBasedOnSymbolValue)
|
||||
MOCK_SYMBOL_ACCESSOR(isPointerToDataMember)
|
||||
MOCK_SYMBOL_ACCESSOR(isPointerToMemberFunction)
|
||||
MOCK_SYMBOL_ACCESSOR(isPureVirtual)
|
||||
MOCK_SYMBOL_ACCESSOR(isRValueReference)
|
||||
MOCK_SYMBOL_ACCESSOR(isRefUdt)
|
||||
MOCK_SYMBOL_ACCESSOR(isReference)
|
||||
MOCK_SYMBOL_ACCESSOR(isRestrictedType)
|
||||
MOCK_SYMBOL_ACCESSOR(isReturnValue)
|
||||
MOCK_SYMBOL_ACCESSOR(isSafeBuffers)
|
||||
MOCK_SYMBOL_ACCESSOR(isScoped)
|
||||
MOCK_SYMBOL_ACCESSOR(isSdl)
|
||||
MOCK_SYMBOL_ACCESSOR(isSingleInheritance)
|
||||
MOCK_SYMBOL_ACCESSOR(isSplitted)
|
||||
MOCK_SYMBOL_ACCESSOR(isStatic)
|
||||
MOCK_SYMBOL_ACCESSOR(hasPrivateSymbols)
|
||||
MOCK_SYMBOL_ACCESSOR(isUnalignedType)
|
||||
MOCK_SYMBOL_ACCESSOR(isUnreached)
|
||||
MOCK_SYMBOL_ACCESSOR(isValueUdt)
|
||||
MOCK_SYMBOL_ACCESSOR(isVirtual)
|
||||
MOCK_SYMBOL_ACCESSOR(isVirtualBaseClass)
|
||||
MOCK_SYMBOL_ACCESSOR(isVirtualInheritance)
|
||||
MOCK_SYMBOL_ACCESSOR(isVolatileType)
|
||||
MOCK_SYMBOL_ACCESSOR(getValue)
|
||||
MOCK_SYMBOL_ACCESSOR(wasInlined)
|
||||
MOCK_SYMBOL_ACCESSOR(getUnused)
|
||||
|
||||
private:
|
||||
PDB_SymType Type;
|
||||
};
|
||||
|
||||
class PDBApiTest : public testing::Test {
|
||||
public:
|
||||
std::unordered_map<PDB_SymType, std::unique_ptr<PDBSymbol>> SymbolMap;
|
||||
|
||||
void SetUp() override {
|
||||
Session.reset(new MockSession());
|
||||
|
||||
InsertItemWithTag(PDB_SymType::None);
|
||||
InsertItemWithTag(PDB_SymType::Exe);
|
||||
InsertItemWithTag(PDB_SymType::Compiland);
|
||||
InsertItemWithTag(PDB_SymType::CompilandDetails);
|
||||
InsertItemWithTag(PDB_SymType::CompilandEnv);
|
||||
InsertItemWithTag(PDB_SymType::Function);
|
||||
InsertItemWithTag(PDB_SymType::Block);
|
||||
InsertItemWithTag(PDB_SymType::Data);
|
||||
InsertItemWithTag(PDB_SymType::Annotation);
|
||||
InsertItemWithTag(PDB_SymType::Label);
|
||||
InsertItemWithTag(PDB_SymType::PublicSymbol);
|
||||
InsertItemWithTag(PDB_SymType::UDT);
|
||||
InsertItemWithTag(PDB_SymType::Enum);
|
||||
InsertItemWithTag(PDB_SymType::FunctionSig);
|
||||
InsertItemWithTag(PDB_SymType::PointerType);
|
||||
InsertItemWithTag(PDB_SymType::ArrayType);
|
||||
InsertItemWithTag(PDB_SymType::BuiltinType);
|
||||
InsertItemWithTag(PDB_SymType::Typedef);
|
||||
InsertItemWithTag(PDB_SymType::BaseClass);
|
||||
InsertItemWithTag(PDB_SymType::Friend);
|
||||
InsertItemWithTag(PDB_SymType::FunctionArg);
|
||||
InsertItemWithTag(PDB_SymType::FuncDebugStart);
|
||||
InsertItemWithTag(PDB_SymType::FuncDebugEnd);
|
||||
InsertItemWithTag(PDB_SymType::UsingNamespace);
|
||||
InsertItemWithTag(PDB_SymType::VTableShape);
|
||||
InsertItemWithTag(PDB_SymType::VTable);
|
||||
InsertItemWithTag(PDB_SymType::Custom);
|
||||
InsertItemWithTag(PDB_SymType::Thunk);
|
||||
InsertItemWithTag(PDB_SymType::CustomType);
|
||||
InsertItemWithTag(PDB_SymType::ManagedType);
|
||||
InsertItemWithTag(PDB_SymType::Dimension);
|
||||
InsertItemWithTag(PDB_SymType::Max);
|
||||
}
|
||||
|
||||
template <class ExpectedType> void VerifyDyncast(PDB_SymType Tag) {
|
||||
for (auto item = SymbolMap.begin(); item != SymbolMap.end(); ++item) {
|
||||
EXPECT_EQ(item->first == Tag, llvm::isa<ExpectedType>(*item->second));
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyUnknownDyncasts() {
|
||||
for (auto item = SymbolMap.begin(); item != SymbolMap.end(); ++item) {
|
||||
bool should_match = false;
|
||||
if (item->first == PDB_SymType::None || item->first >= PDB_SymType::Max)
|
||||
should_match = true;
|
||||
|
||||
EXPECT_EQ(should_match, llvm::isa<PDBSymbolUnknown>(*item->second));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<IPDBSession> Session;
|
||||
|
||||
void InsertItemWithTag(PDB_SymType Tag) {
|
||||
auto RawSymbol = llvm::make_unique<MockRawSymbol>(Tag);
|
||||
auto Symbol = PDBSymbol::create(*Session, std::move(RawSymbol));
|
||||
SymbolMap.insert(std::make_pair(Tag, std::move(Symbol)));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PDBApiTest, Dyncast) {
|
||||
|
||||
// Most of the types have a one-to-one mapping between Tag and concrete type.
|
||||
VerifyDyncast<PDBSymbolExe>(PDB_SymType::Exe);
|
||||
VerifyDyncast<PDBSymbolCompiland>(PDB_SymType::Compiland);
|
||||
VerifyDyncast<PDBSymbolCompilandDetails>(PDB_SymType::CompilandDetails);
|
||||
VerifyDyncast<PDBSymbolCompilandEnv>(PDB_SymType::CompilandEnv);
|
||||
VerifyDyncast<PDBSymbolFunc>(PDB_SymType::Function);
|
||||
VerifyDyncast<PDBSymbolBlock>(PDB_SymType::Block);
|
||||
VerifyDyncast<PDBSymbolData>(PDB_SymType::Data);
|
||||
VerifyDyncast<PDBSymbolAnnotation>(PDB_SymType::Annotation);
|
||||
VerifyDyncast<PDBSymbolLabel>(PDB_SymType::Label);
|
||||
VerifyDyncast<PDBSymbolPublicSymbol>(PDB_SymType::PublicSymbol);
|
||||
VerifyDyncast<PDBSymbolTypeUDT>(PDB_SymType::UDT);
|
||||
VerifyDyncast<PDBSymbolTypeEnum>(PDB_SymType::Enum);
|
||||
VerifyDyncast<PDBSymbolTypeFunctionSig>(PDB_SymType::FunctionSig);
|
||||
VerifyDyncast<PDBSymbolTypePointer>(PDB_SymType::PointerType);
|
||||
VerifyDyncast<PDBSymbolTypeArray>(PDB_SymType::ArrayType);
|
||||
VerifyDyncast<PDBSymbolTypeBuiltin>(PDB_SymType::BuiltinType);
|
||||
VerifyDyncast<PDBSymbolTypeTypedef>(PDB_SymType::Typedef);
|
||||
VerifyDyncast<PDBSymbolTypeBaseClass>(PDB_SymType::BaseClass);
|
||||
VerifyDyncast<PDBSymbolTypeFriend>(PDB_SymType::Friend);
|
||||
VerifyDyncast<PDBSymbolTypeFunctionArg>(PDB_SymType::FunctionArg);
|
||||
VerifyDyncast<PDBSymbolFuncDebugStart>(PDB_SymType::FuncDebugStart);
|
||||
VerifyDyncast<PDBSymbolFuncDebugEnd>(PDB_SymType::FuncDebugEnd);
|
||||
VerifyDyncast<PDBSymbolUsingNamespace>(PDB_SymType::UsingNamespace);
|
||||
VerifyDyncast<PDBSymbolTypeVTableShape>(PDB_SymType::VTableShape);
|
||||
VerifyDyncast<PDBSymbolTypeVTable>(PDB_SymType::VTable);
|
||||
VerifyDyncast<PDBSymbolCustom>(PDB_SymType::Custom);
|
||||
VerifyDyncast<PDBSymbolThunk>(PDB_SymType::Thunk);
|
||||
VerifyDyncast<PDBSymbolTypeCustom>(PDB_SymType::CustomType);
|
||||
VerifyDyncast<PDBSymbolTypeManaged>(PDB_SymType::ManagedType);
|
||||
VerifyDyncast<PDBSymbolTypeDimension>(PDB_SymType::Dimension);
|
||||
|
||||
VerifyUnknownDyncasts();
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
@ -1,55 +0,0 @@
|
||||
//===- StringTableBuilderTest.cpp -----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
#include "llvm/Support/BinaryStreamWriter.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::pdb;
|
||||
using namespace llvm::support;
|
||||
|
||||
namespace {
|
||||
class StringTableBuilderTest : public ::testing::Test {};
|
||||
}
|
||||
|
||||
TEST_F(StringTableBuilderTest, Simple) {
|
||||
// Create /names table contents.
|
||||
PDBStringTableBuilder Builder;
|
||||
EXPECT_EQ(1U, Builder.insert("foo"));
|
||||
EXPECT_EQ(5U, Builder.insert("bar"));
|
||||
EXPECT_EQ(1U, Builder.insert("foo"));
|
||||
EXPECT_EQ(9U, Builder.insert("baz"));
|
||||
|
||||
std::vector<uint8_t> Buffer(Builder.calculateSerializedSize());
|
||||
MutableBinaryByteStream OutStream(Buffer, little);
|
||||
BinaryStreamWriter Writer(OutStream);
|
||||
EXPECT_THAT_ERROR(Builder.commit(Writer), Succeeded());
|
||||
|
||||
// Reads the contents back.
|
||||
BinaryByteStream InStream(Buffer, little);
|
||||
BinaryStreamReader Reader(InStream);
|
||||
PDBStringTable Table;
|
||||
EXPECT_THAT_ERROR(Table.reload(Reader), Succeeded());
|
||||
|
||||
EXPECT_EQ(3U, Table.getNameCount());
|
||||
EXPECT_EQ(1U, Table.getHashVersion());
|
||||
|
||||
EXPECT_THAT_EXPECTED(Table.getStringForID(1), HasValue("foo"));
|
||||
EXPECT_THAT_EXPECTED(Table.getStringForID(5), HasValue("bar"));
|
||||
EXPECT_THAT_EXPECTED(Table.getStringForID(9), HasValue("baz"));
|
||||
EXPECT_THAT_EXPECTED(Table.getIDForString("foo"), HasValue(1U));
|
||||
EXPECT_THAT_EXPECTED(Table.getIDForString("bar"), HasValue(5U));
|
||||
EXPECT_THAT_EXPECTED(Table.getIDForString("baz"), HasValue(9U));
|
||||
}
|
Reference in New Issue
Block a user