diff options
Diffstat (limited to 'unittests/DebugInfo')
| -rw-r--r-- | unittests/DebugInfo/CodeView/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp | 23 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp | 110 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp | 667 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp | 53 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DwarfGenerator.cpp | 341 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DwarfGenerator.h | 95 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DwarfUtils.cpp | 43 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DwarfUtils.h | 29 | ||||
| -rw-r--r-- | unittests/DebugInfo/MSF/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | unittests/DebugInfo/MSF/MSFBuilderTest.cpp | 21 | ||||
| -rw-r--r-- | unittests/DebugInfo/MSF/MSFCommonTest.cpp | 45 | ||||
| -rw-r--r-- | unittests/DebugInfo/PDB/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | unittests/DebugInfo/PDB/HashTableTest.cpp | 188 | ||||
| -rw-r--r-- | unittests/DebugInfo/PDB/PDBApiTest.cpp | 79 | ||||
| -rw-r--r-- | unittests/DebugInfo/PDB/StringTableBuilderTest.cpp | 48 | 
17 files changed, 1549 insertions, 219 deletions
diff --git a/unittests/DebugInfo/CodeView/CMakeLists.txt b/unittests/DebugInfo/CodeView/CMakeLists.txt index d06ccfaba72a..70a7b8af1447 100644 --- a/unittests/DebugInfo/CodeView/CMakeLists.txt +++ b/unittests/DebugInfo/CodeView/CMakeLists.txt @@ -2,14 +2,10 @@ set(LLVM_LINK_COMPONENTS    DebugInfoCodeView    ) -set(DebugInfoCodeViewSources +add_llvm_unittest(DebugInfoCodeViewTests    RandomAccessVisitorTest.cpp    TypeHashingTest.cpp    TypeIndexDiscoveryTest.cpp    ) -add_llvm_unittest(DebugInfoCodeViewTests -  ${DebugInfoCodeViewSources} -  ) -  target_link_libraries(DebugInfoCodeViewTests PRIVATE LLVMTestingSupport) diff --git a/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp b/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp index c51b9e723f04..60ca56b0d143 100644 --- a/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp +++ b/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp @@ -555,7 +555,7 @@ TEST_F(TypeIndexIteratorTest, DataSym) {  TEST_F(TypeIndexIteratorTest, RegisterSym) {    RegisterSym Reg(SymbolRecordKind::RegisterSym);    Reg.Index = TypeIndex::UInt32(); -  Reg.Register = RegisterId::EAX; +  Reg.Register = RegisterId::CVRegEAX;    Reg.Name = "Target";    writeSymbolRecords(Reg);    checkTypeReferences(0, Reg.Index); @@ -580,3 +580,24 @@ TEST_F(TypeIndexIteratorTest, CallerSym) {    checkTypeReferences(2, TypeIndex(7), TypeIndex(8), TypeIndex(9));  } +TEST_F(TypeIndexIteratorTest, Precomp) { +  PrecompRecord P(TypeRecordKind::Precomp); +  P.StartTypeIndex = TypeIndex::FirstNonSimpleIndex; +  P.TypesCount = 100; +  P.Signature = 0x12345678; +  P.PrecompFilePath = "C:/precomp.obj"; + +  EndPrecompRecord EP(TypeRecordKind::EndPrecomp); +  EP.Signature = P.Signature; + +  writeTypeRecords(P, EP); +  checkTypeReferences(0); +} + +// This is a test for getEncodedIntegerLength() +TEST_F(TypeIndexIteratorTest, VariableSizeIntegers) { +  BaseClassRecord BaseClass1(MemberAccess::Public, TypeIndex(47), (uint64_t)-1); +  BaseClassRecord BaseClass2(MemberAccess::Public, TypeIndex(48), 1); +  writeFieldList(BaseClass1, BaseClass2); +  checkTypeReferences(0, TypeIndex(47), TypeIndex(48)); +}
\ No newline at end of file diff --git a/unittests/DebugInfo/DWARF/CMakeLists.txt b/unittests/DebugInfo/DWARF/CMakeLists.txt index f490097a21a7..d97e573ea38f 100644 --- a/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -8,14 +8,12 @@ set(LLVM_LINK_COMPONENTS    Support    ) -set(DebugInfoSources +add_llvm_unittest(DebugInfoDWARFTests    DwarfGenerator.cpp +  DwarfUtils.cpp    DWARFDebugInfoTest.cpp +  DWARFDebugLineTest.cpp    DWARFFormValueTest.cpp    ) -add_llvm_unittest(DebugInfoDWARFTests -  ${DebugInfoSources} -  ) -  target_link_libraries(DebugInfoDWARFTests PRIVATE LLVMTestingSupport) diff --git a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index cb7bf82d86f6..442dea3c52f7 100644 --- a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/DebugInfo/DWARFFormValueTest.cpp ---------------------===// +//===- llvm/unittest/DebugInfo/DWARFDebugInfoTest.cpp ---------------------===//  //  //                     The LLVM Compiler Infrastructure  // @@ -8,6 +8,7 @@  //===----------------------------------------------------------------------===//  #include "DwarfGenerator.h" +#include "DwarfUtils.h"  #include "llvm/ADT/ArrayRef.h"  #include "llvm/ADT/Optional.h"  #include "llvm/ADT/SmallString.h" @@ -15,7 +16,6 @@  #include "llvm/ADT/Triple.h"  #include "llvm/BinaryFormat/Dwarf.h"  #include "llvm/CodeGen/AsmPrinter.h" -#include "llvm/Config/llvm-config.h"  #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"  #include "llvm/DebugInfo/DWARF/DWARFContext.h"  #include "llvm/DebugInfo/DWARF/DWARFDie.h" @@ -36,36 +36,10 @@  using namespace llvm;  using namespace dwarf; +using namespace utils;  namespace { -void initLLVMIfNeeded() { -  static bool gInitialized = false; -  if (!gInitialized) { -    gInitialized = true; -    InitializeAllTargets(); -    InitializeAllTargetMCs(); -    InitializeAllAsmPrinters(); -    InitializeAllAsmParsers(); -  } -} - -Triple getHostTripleForAddrSize(uint8_t AddrSize) { -  Triple PT(Triple::normalize(LLVM_HOST_TRIPLE)); - -  if (AddrSize == 8 && PT.isArch32Bit()) -    return PT.get64BitArchVariant(); -  if (AddrSize == 4 && PT.isArch64Bit()) -    return PT.get32BitArchVariant(); -  return PT; -} - -static bool isConfigurationSupported(Triple &T) { -  initLLVMIfNeeded(); -  std::string Err; -  return TargetRegistry::lookupTarget(T.getTriple(), Err); -} -  template <uint16_t Version, class AddrType, class RefAddrType>  void TestAllForms() {    Triple Triple = getHostTripleForAddrSize(sizeof(AddrType)); @@ -93,12 +67,21 @@ void TestAllForms() {    const uint32_t Dwarf32Values[] = {1, 2, 3, 4, 5, 6, 7, 8};    const char *StringValue = "Hello";    const char *StrpValue = "World"; +  const char *StrxValue = "Indexed"; +  const char *Strx1Value = "Indexed1"; +  const char *Strx2Value = "Indexed2"; +  const char *Strx3Value = "Indexed3"; +  const char *Strx4Value = "Indexed4";    auto ExpectedDG = dwarfgen::Generator::create(Triple, Version);    ASSERT_THAT_EXPECTED(ExpectedDG, Succeeded());    dwarfgen::Generator *DG = ExpectedDG.get().get();    dwarfgen::CompileUnit &CU = DG->addCompileUnit();    dwarfgen::DIE CUDie = CU.getUnitDIE(); + +  if (Version >= 5) +    CUDie.addStrOffsetsBaseAttribute(); +    uint16_t Attr = DW_AT_lo_user;    //---------------------------------------------------------------------- @@ -148,6 +131,19 @@ void TestAllForms() {    const auto Attr_DW_FORM_string = static_cast<dwarf::Attribute>(Attr++);    CUDie.addAttribute(Attr_DW_FORM_string, DW_FORM_string, StringValue); +  const auto Attr_DW_FORM_strx = static_cast<dwarf::Attribute>(Attr++); +  const auto Attr_DW_FORM_strx1 = static_cast<dwarf::Attribute>(Attr++); +  const auto Attr_DW_FORM_strx2 = static_cast<dwarf::Attribute>(Attr++); +  const auto Attr_DW_FORM_strx3 = static_cast<dwarf::Attribute>(Attr++); +  const auto Attr_DW_FORM_strx4 = static_cast<dwarf::Attribute>(Attr++); +  if (Version >= 5) { +    CUDie.addAttribute(Attr_DW_FORM_strx, DW_FORM_strx, StrxValue); +    CUDie.addAttribute(Attr_DW_FORM_strx1, DW_FORM_strx1, Strx1Value); +    CUDie.addAttribute(Attr_DW_FORM_strx2, DW_FORM_strx2, Strx2Value); +    CUDie.addAttribute(Attr_DW_FORM_strx3, DW_FORM_strx3, Strx3Value); +    CUDie.addAttribute(Attr_DW_FORM_strx4, DW_FORM_strx4, Strx4Value); +  } +    const auto Attr_DW_FORM_strp = static_cast<dwarf::Attribute>(Attr++);    CUDie.addAttribute(Attr_DW_FORM_strp, DW_FORM_strp, StrpValue); @@ -307,11 +303,33 @@ void TestAllForms() {    //----------------------------------------------------------------------    auto ExtractedStringValue = toString(DieDG.find(Attr_DW_FORM_string));    EXPECT_TRUE((bool)ExtractedStringValue); -  EXPECT_TRUE(strcmp(StringValue, *ExtractedStringValue) == 0); +  EXPECT_STREQ(StringValue, *ExtractedStringValue); + +  if (Version >= 5) { +    auto ExtractedStrxValue = toString(DieDG.find(Attr_DW_FORM_strx)); +    EXPECT_TRUE((bool)ExtractedStrxValue); +    EXPECT_STREQ(StrxValue, *ExtractedStrxValue); + +    auto ExtractedStrx1Value = toString(DieDG.find(Attr_DW_FORM_strx1)); +    EXPECT_TRUE((bool)ExtractedStrx1Value); +    EXPECT_STREQ(Strx1Value, *ExtractedStrx1Value); + +    auto ExtractedStrx2Value = toString(DieDG.find(Attr_DW_FORM_strx2)); +    EXPECT_TRUE((bool)ExtractedStrx2Value); +    EXPECT_STREQ(Strx2Value, *ExtractedStrx2Value); + +    auto ExtractedStrx3Value = toString(DieDG.find(Attr_DW_FORM_strx3)); +    EXPECT_TRUE((bool)ExtractedStrx3Value); +    EXPECT_STREQ(Strx3Value, *ExtractedStrx3Value); + +    auto ExtractedStrx4Value = toString(DieDG.find(Attr_DW_FORM_strx4)); +    EXPECT_TRUE((bool)ExtractedStrx4Value); +    EXPECT_STREQ(Strx4Value, *ExtractedStrx4Value); +  }    auto ExtractedStrpValue = toString(DieDG.find(Attr_DW_FORM_strp));    EXPECT_TRUE((bool)ExtractedStrpValue); -  EXPECT_TRUE(strcmp(StrpValue, *ExtractedStrpValue) == 0); +  EXPECT_STREQ(StrpValue, *ExtractedStrpValue);    //----------------------------------------------------------------------    // Test reference forms @@ -516,6 +534,11 @@ template <uint16_t Version, class AddrType> void TestChildren() {      EXPECT_TRUE(!NullDieDG.getSibling().isValid());      EXPECT_TRUE(!NullDieDG.getFirstChild().isValid());    } + +  // Verify the previous sibling of our subprogram is our integer base type. +  IntDieDG = NullDieDG.getPreviousSibling(); +  EXPECT_TRUE(IntDieDG.isValid()); +  EXPECT_EQ(IntDieDG.getTag(), DW_TAG_base_type);  }  TEST(DWARFDebugInfo, TestDWARF32Version2Addr4Children) { @@ -1098,6 +1121,27 @@ TEST(DWARFDebugInfo, TestRelations) {    // Make sure the parent of all the children of the B are the B.    EXPECT_EQ(C1.getParent(), C);    EXPECT_EQ(C2.getParent(), C); + +  // Make sure bidirectional iterator works as expected. +  auto Begin = A.begin(); +  auto End = A.end(); +  auto It = A.begin(); + +  EXPECT_EQ(It, Begin); +  EXPECT_EQ(*It, B); +  ++It; +  EXPECT_EQ(*It, C); +  ++It; +  EXPECT_EQ(*It, D); +  ++It; +  EXPECT_EQ(It, End); +  --It; +  EXPECT_EQ(*It, D); +  --It; +  EXPECT_EQ(*It, C); +  --It; +  EXPECT_EQ(*It, B); +  EXPECT_EQ(It, Begin);  }  TEST(DWARFDebugInfo, TestDWARFDie) { @@ -1191,7 +1235,7 @@ TEST(DWARFDebugInfo, TestEmptyChildren) {                           "    Attributes:\n"                           "debug_info:\n"                           "  - Length:\n" -                         "      TotalLength:          9\n" +                         "      TotalLength:          0\n"                           "    Version:         4\n"                           "    AbbrOffset:      0\n"                           "    AddrSize:        8\n" @@ -1201,7 +1245,7 @@ TEST(DWARFDebugInfo, TestEmptyChildren) {                           "      - AbbrCode:        0x00000000\n"                           "        Values:\n"; -  auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); +  auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata), true);    ASSERT_TRUE((bool)ErrOrSections);    std::unique_ptr<DWARFContext> DwarfContext =        DWARFContext::create(*ErrOrSections, 8); diff --git a/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp new file mode 100644 index 000000000000..99ed118e539b --- /dev/null +++ b/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp @@ -0,0 +1,667 @@ +//===- DWARFDebugLineTest.cpp ---------------------------------------------===// +// +//                     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 "DwarfUtils.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace dwarf; +using namespace dwarfgen; +using namespace object; +using namespace utils; +using namespace testing; + +namespace { +struct CommonFixture { +  CommonFixture() +      : LineData("", true, 0), Recoverable(Error::success()), +        RecordRecoverable(std::bind(&CommonFixture::recordRecoverable, this, +                                    std::placeholders::_1)), +        Unrecoverable(Error::success()), +        RecordUnrecoverable(std::bind(&CommonFixture::recordUnrecoverable, this, +                                      std::placeholders::_1)){}; + +  ~CommonFixture() { +    EXPECT_FALSE(Recoverable); +    EXPECT_FALSE(Unrecoverable); +  } + +  bool setupGenerator(uint16_t Version = 4) { +    Triple T = getHostTripleForAddrSize(8); +    if (!isConfigurationSupported(T)) +      return false; +    auto ExpectedGenerator = Generator::create(T, Version); +    if (ExpectedGenerator) +      Gen.reset(ExpectedGenerator->release()); +    return true; +  } + +  void generate() { +    Context = createContext(); +    assert(Context != nullptr && "test state is not valid"); +    const DWARFObject &Obj = Context->getDWARFObj(); +    LineData = DWARFDataExtractor(Obj, Obj.getLineSection(), +                                  sys::IsLittleEndianHost, 8); +  } + +  std::unique_ptr<DWARFContext> createContext() { +    if (!Gen) +      return nullptr; +    StringRef FileBytes = Gen->generate(); +    MemoryBufferRef FileBuffer(FileBytes, "dwarf"); +    auto Obj = object::ObjectFile::createObjectFile(FileBuffer); +    if (Obj) +      return DWARFContext::create(**Obj); +    return nullptr; +  } + +  DWARFDebugLine::SectionParser setupParser() { +    LineTable < = Gen->addLineTable(DWARF32); +    LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}}); +    LT.addStandardOpcode(DW_LNS_copy, {}); +    LT.addByte(0xaa); +    LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + +    LineTable <2 = Gen->addLineTable(DWARF64); +    LT2.addExtendedOpcode(9, DW_LNE_set_address, +                          {{0x11223344, LineTable::Quad}}); +    LT2.addStandardOpcode(DW_LNS_copy, {}); +    LT2.addByte(0xbb); +    LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + +    generate(); + +    return DWARFDebugLine::SectionParser(LineData, *Context, CUs, TUs); +  } + +  void recordRecoverable(Error Err) { +    Recoverable = joinErrors(std::move(Recoverable), std::move(Err)); +  } +  void recordUnrecoverable(Error Err) { +    Unrecoverable = joinErrors(std::move(Unrecoverable), std::move(Err)); +  } + +  void checkError(ArrayRef<StringRef> ExpectedMsgs, Error Err) { +    ASSERT_TRUE(Err.operator bool()); +    size_t WhichMsg = 0; +    Error Remaining = +        handleErrors(std::move(Err), [&](const ErrorInfoBase &Actual) { +          ASSERT_LT(WhichMsg, ExpectedMsgs.size()); +          // Use .str(), because googletest doesn't visualise a StringRef +          // properly. +          EXPECT_EQ(Actual.message(), ExpectedMsgs[WhichMsg++].str()); +        }); +    EXPECT_EQ(WhichMsg, ExpectedMsgs.size()); +    EXPECT_FALSE(Remaining); +  } + +  void checkError(StringRef ExpectedMsg, Error Err) { +    checkError(ArrayRef<StringRef>{ExpectedMsg}, std::move(Err)); +  } + +  void checkGetOrParseLineTableEmitsError(StringRef ExpectedMsg, +                                          uint64_t Offset = 0) { +    auto ExpectedLineTable = Line.getOrParseLineTable( +        LineData, Offset, *Context, nullptr, RecordRecoverable); +    EXPECT_FALSE(ExpectedLineTable); +    EXPECT_FALSE(Recoverable); + +    checkError(ExpectedMsg, ExpectedLineTable.takeError()); +  } + +  std::unique_ptr<Generator> Gen; +  std::unique_ptr<DWARFContext> Context; +  DWARFDataExtractor LineData; +  DWARFDebugLine Line; +  Error Recoverable; +  std::function<void(Error)> RecordRecoverable; +  Error Unrecoverable; +  std::function<void(Error)> RecordUnrecoverable; + +  SmallVector<std::unique_ptr<DWARFCompileUnit>, 2> CUs; +  std::deque<DWARFUnitSection<DWARFTypeUnit>> TUs; +}; + +// Fixtures must derive from "Test", but parameterised fixtures from +// "TestWithParam". It does not seem possible to inherit from both, so we share +// the common state in a separate class, inherited by the two fixture classes. +struct DebugLineBasicFixture : public Test, public CommonFixture {}; + +struct DebugLineParameterisedFixture +    : public TestWithParam<std::pair<uint16_t, DwarfFormat>>, +      public CommonFixture { +  void SetUp() { std::tie(Version, Format) = GetParam(); } + +  uint16_t Version; +  DwarfFormat Format; +}; + +void checkDefaultPrologue(uint16_t Version, DwarfFormat Format, +                          DWARFDebugLine::Prologue Prologue, +                          uint64_t BodyLength) { +  // Check version specific fields and values. +  uint64_t UnitLength; +  uint64_t PrologueLength; +  switch (Version) { +  case 4: +    PrologueLength = 36; +    UnitLength = PrologueLength + 2; +    EXPECT_EQ(Prologue.MaxOpsPerInst, 1u); +    break; +  case 2: +  case 3: +    PrologueLength = 35; +    UnitLength = PrologueLength + 2; +    break; +  case 5: +    PrologueLength = 39; +    UnitLength = PrologueLength + 4; +    EXPECT_EQ(Prologue.getAddressSize(), 8u); +    EXPECT_EQ(Prologue.SegSelectorSize, 0u); +    break; +  default: +    llvm_unreachable("unsupported DWARF version"); +  } +  UnitLength += BodyLength + (Format == DWARF32 ? 4 : 8); + +  EXPECT_EQ(Prologue.TotalLength, UnitLength); +  EXPECT_EQ(Prologue.PrologueLength, PrologueLength); +  EXPECT_EQ(Prologue.MinInstLength, 1u); +  EXPECT_EQ(Prologue.DefaultIsStmt, 1u); +  EXPECT_EQ(Prologue.LineBase, -5); +  EXPECT_EQ(Prologue.LineRange, 14u); +  EXPECT_EQ(Prologue.OpcodeBase, 13u); +  std::vector<uint8_t> ExpectedLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1}; +  EXPECT_EQ(Prologue.StandardOpcodeLengths, ExpectedLengths); +  ASSERT_EQ(Prologue.IncludeDirectories.size(), 1u); +  ASSERT_EQ(Prologue.IncludeDirectories[0].getForm(), DW_FORM_string); +  EXPECT_STREQ(*Prologue.IncludeDirectories[0].getAsCString(), "a dir"); +  ASSERT_EQ(Prologue.FileNames.size(), 1u); +  ASSERT_EQ(Prologue.FileNames[0].Name.getForm(), DW_FORM_string); +  EXPECT_STREQ(*Prologue.FileNames[0].Name.getAsCString(), "a file"); +} + +TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) { +  if (!setupGenerator()) +    return; +  generate(); + +  checkGetOrParseLineTableEmitsError( +      "offset 0x00000000 is not a valid debug line section offset", 0); +  // Repeat to show that an error is reported each time. +  checkGetOrParseLineTableEmitsError( +      "offset 0x00000000 is not a valid debug line section offset", 0); +  // Show that an error is reported for later offsets too. +  checkGetOrParseLineTableEmitsError( +      "offset 0x00000001 is not a valid debug line section offset", 1); +} + +TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(); +  LT.setCustomPrologue({{0, LineTable::Byte}}); + +  generate(); + +  checkGetOrParseLineTableEmitsError( +      "offset 0x00000001 is not a valid debug line section offset", 1); +} + +TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) { +  if (!setupGenerator(Version)) +    return; + +  SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " + +               (Format == DWARF64 ? "DWARF64" : "DWARF32")); + +  LineTable < = Gen->addLineTable(Format); +  LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}}); +  LT.addStandardOpcode(DW_LNS_copy, {}); +  LT.addByte(0xaa); +  LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + +  LineTable <2 = Gen->addLineTable(Format); +  LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}}); +  LT2.addStandardOpcode(DW_LNS_copy, {}); +  LT2.addByte(0xbb); +  LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {}); +  LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}}); +  LT2.addStandardOpcode(DW_LNS_copy, {}); +  LT2.addByte(0xcc); +  LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + +  generate(); + +  auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, +                                                    nullptr, RecordRecoverable); +  ASSERT_TRUE(ExpectedLineTable.operator bool()); +  EXPECT_FALSE(Recoverable); +  const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable; +  checkDefaultPrologue(Version, Format, Expected->Prologue, 16); +  EXPECT_EQ(Expected->Sequences.size(), 1u); + +  uint64_t SecondOffset = +      Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength; +  Recoverable = Error::success(); +  auto ExpectedLineTable2 = Line.getOrParseLineTable( +      LineData, SecondOffset, *Context, nullptr, RecordRecoverable); +  ASSERT_TRUE(ExpectedLineTable2.operator bool()); +  EXPECT_FALSE(Recoverable); +  const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2; +  checkDefaultPrologue(Version, Format, Expected2->Prologue, 32); +  EXPECT_EQ(Expected2->Sequences.size(), 2u); + +  EXPECT_NE(Expected, Expected2); + +  // Check that if the same offset is requested, the exact same pointer is +  // returned. +  Recoverable = Error::success(); +  auto ExpectedLineTable3 = Line.getOrParseLineTable( +      LineData, 0, *Context, nullptr, RecordRecoverable); +  ASSERT_TRUE(ExpectedLineTable3.operator bool()); +  EXPECT_FALSE(Recoverable); +  EXPECT_EQ(Expected, *ExpectedLineTable3); + +  Recoverable = Error::success(); +  auto ExpectedLineTable4 = Line.getOrParseLineTable( +      LineData, SecondOffset, *Context, nullptr, RecordRecoverable); +  ASSERT_TRUE(ExpectedLineTable4.operator bool()); +  EXPECT_FALSE(Recoverable); +  EXPECT_EQ(Expected2, *ExpectedLineTable4); + +  // TODO: Add tests that show that the body of the programs have been read +  // correctly. +} + +TEST_F(DebugLineBasicFixture, ErrorForReservedLength) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(); +  LT.setCustomPrologue({{0xffffff00, LineTable::Long}}); + +  generate(); + +  checkGetOrParseLineTableEmitsError( +      "parsing line table prologue at offset 0x00000000 unsupported reserved " +      "unit length found of value 0xffffff00"); +} + +TEST_F(DebugLineBasicFixture, ErrorForLowVersion) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(); +  LT.setCustomPrologue( +      {{LineTable::Half, LineTable::Long}, {1, LineTable::Half}}); + +  generate(); + +  checkGetOrParseLineTableEmitsError("parsing line table prologue at offset " +                                     "0x00000000 found unsupported version " +                                     "0x01"); +} + +TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) { +  if (!setupGenerator(5)) +    return; + +  LineTable < = Gen->addLineTable(); +  LT.setCustomPrologue({ +      {19, LineTable::Long}, // unit length +      {5, LineTable::Half},  // version +      {8, LineTable::Byte},  // addr size +      {0, LineTable::Byte},  // segment selector size +      {11, LineTable::Long}, // prologue length +      {1, LineTable::Byte},  // min instruction length +      {1, LineTable::Byte},  // max ops per instruction +      {1, LineTable::Byte},  // default is_stmt +      {0, LineTable::Byte},  // line base +      {14, LineTable::Byte}, // line range +      {2, LineTable::Byte},  // opcode base (small to reduce the amount of +                             // setup required). +      {0, LineTable::Byte},  // standard opcode lengths +      {0, LineTable::Byte},  // directory entry format count (should not be +                             // zero). +      {0, LineTable::ULEB},  // directories count +      {0, LineTable::Byte},  // file name entry format count +      {0, LineTable::ULEB}   // file name entry count +  }); + +  generate(); + +  checkGetOrParseLineTableEmitsError( +      "parsing line table prologue at 0x00000000 found an invalid directory or " +      "file table description at 0x00000014"); +} + +TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) { +  if (!setupGenerator(Version)) +    return; + +  SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " + +               (Format == DWARF64 ? "DWARF64" : "DWARF32")); + +  LineTable < = Gen->addLineTable(Format); +  DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue(); +  ++Prologue.PrologueLength; +  LT.setPrologue(Prologue); + +  generate(); + +  uint64_t ExpectedEnd = +      Prologue.TotalLength + 1 + Prologue.sizeofTotalLength(); +  checkGetOrParseLineTableEmitsError( +      (Twine("parsing line table prologue at 0x00000000 should have ended at " +             "0x000000") + +       Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" + +       Twine::utohexstr(ExpectedEnd - 1)) +          .str()); +} + +TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) { +  if (!setupGenerator(Version)) +    return; + +  SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " + +               (Format == DWARF64 ? "DWARF64" : "DWARF32")); + +  LineTable < = Gen->addLineTable(Format); +  DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue(); +  // FIXME: Ideally, we'd test for 1 less than expected, but the code does not +  // currently fail if missing only the terminator of a v2-4 file table. +  if (Version < 5) +    Prologue.PrologueLength -= 2; +  else +    Prologue.PrologueLength -= 1; +  LT.setPrologue(Prologue); + +  generate(); + +  uint64_t ExpectedEnd = +      Prologue.TotalLength - 1 + Prologue.sizeofTotalLength(); +  if (Version < 5) +    --ExpectedEnd; +  checkGetOrParseLineTableEmitsError( +      (Twine("parsing line table prologue at 0x00000000 should have ended at " +             "0x000000") + +       Twine::utohexstr(ExpectedEnd) + " but it ended at 0x000000" + +       Twine::utohexstr(ExpectedEnd + 1)) +          .str()); +} + +INSTANTIATE_TEST_CASE_P( +    LineTableTestParams, DebugLineParameterisedFixture, +    Values(std::make_pair( +               2, DWARF32), // Test lower-bound of v2-3 fields and DWARF32. +           std::make_pair(3, DWARF32), // Test upper-bound of v2-3 fields. +           std::make_pair(4, DWARF64), // Test v4 fields and DWARF64. +           std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)), ); + +TEST_F(DebugLineBasicFixture, ErrorForInvalidExtendedOpcodeLength) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(); +  // The Length should be 1 for an end sequence opcode. +  LT.addExtendedOpcode(2, DW_LNE_end_sequence, {}); + +  generate(); + +  checkGetOrParseLineTableEmitsError("unexpected line op length at offset " +                                     "0x00000030 expected 0x02 found 0x01"); +} + +TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(); +  // The line data extractor expects size 8 (Quad) addresses. +  LT.addExtendedOpcode(5, DW_LNE_set_address, {{0x11223344, LineTable::Long}}); +  LT.addStandardOpcode(DW_LNS_copy, {}); +  LT.addByte(0xaa); +  LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); + +  generate(); + +  checkGetOrParseLineTableEmitsError( +      "mismatching address size at offset 0x00000030 expected 0x08 found 0x04"); +} + +TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(); +  LT.addExtendedOpcode(9, DW_LNE_set_address, +                       {{0x1122334455667788, LineTable::Quad}}); +  LT.addStandardOpcode(DW_LNS_copy, {}); +  LT.addByte(0xaa); +  LT.addExtendedOpcode(1, DW_LNE_end_sequence, {}); +  LT.addExtendedOpcode(9, DW_LNE_set_address, +                       {{0x99aabbccddeeff00, LineTable::Quad}}); +  LT.addStandardOpcode(DW_LNS_copy, {}); +  LT.addByte(0xbb); +  LT.addByte(0xcc); + +  generate(); + +  auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context, +                                                    nullptr, RecordRecoverable); +  checkError("last sequence in debug line table is not terminated!", +             std::move(Recoverable)); +  ASSERT_TRUE(ExpectedLineTable.operator bool()); +  EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 6u); +  // The unterminated sequence is not added to the sequence list. +  EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u); +} + +TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) { +  if (!setupGenerator()) +    return; + +  DWARFDebugLine::SectionParser Parser = setupParser(); + +  EXPECT_EQ(Parser.getOffset(), 0u); +  ASSERT_FALSE(Parser.done()); + +  DWARFDebugLine::LineTable Parsed = +      Parser.parseNext(RecordRecoverable, RecordUnrecoverable); +  checkDefaultPrologue(4, DWARF32, Parsed.Prologue, 16); +  EXPECT_EQ(Parsed.Sequences.size(), 1u); +  EXPECT_EQ(Parser.getOffset(), 62u); +  ASSERT_FALSE(Parser.done()); + +  DWARFDebugLine::LineTable Parsed2 = +      Parser.parseNext(RecordRecoverable, RecordUnrecoverable); +  checkDefaultPrologue(4, DWARF64, Parsed2.Prologue, 16); +  EXPECT_EQ(Parsed2.Sequences.size(), 1u); +  EXPECT_EQ(Parser.getOffset(), 136u); +  EXPECT_TRUE(Parser.done()); + +  EXPECT_FALSE(Recoverable); +  EXPECT_FALSE(Unrecoverable); +} + +TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) { +  if (!setupGenerator()) +    return; + +  DWARFDebugLine::SectionParser Parser = setupParser(); + +  EXPECT_EQ(Parser.getOffset(), 0u); +  ASSERT_FALSE(Parser.done()); + +  Parser.skip(RecordUnrecoverable); +  EXPECT_EQ(Parser.getOffset(), 62u); +  ASSERT_FALSE(Parser.done()); + +  Parser.skip(RecordUnrecoverable); +  EXPECT_EQ(Parser.getOffset(), 136u); +  EXPECT_TRUE(Parser.done()); + +  EXPECT_FALSE(Unrecoverable); +} + +TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) { +  if (!setupGenerator()) +    return; + +  generate(); +  DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); + +  EXPECT_TRUE(Parser.done()); +} + +TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenParsing) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(); +  LT.setCustomPrologue({{0xffffff00, LineTable::Long}}); +  Gen->addLineTable(); +  generate(); + +  DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); +  Parser.parseNext(RecordRecoverable, RecordUnrecoverable); + +  EXPECT_EQ(Parser.getOffset(), 4u); +  EXPECT_TRUE(Parser.done()); +  EXPECT_FALSE(Recoverable); + +  checkError("parsing line table prologue at offset 0x00000000 unsupported " +             "reserved unit length found of value 0xffffff00", +             std::move(Unrecoverable)); +} + +TEST_F(DebugLineBasicFixture, ParserMovesToEndForBadLengthWhenSkipping) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(); +  LT.setCustomPrologue({{0xffffff00, LineTable::Long}}); +  Gen->addLineTable(); +  generate(); + +  DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); +  Parser.skip(RecordUnrecoverable); + +  EXPECT_EQ(Parser.getOffset(), 4u); +  EXPECT_TRUE(Parser.done()); + +  checkError("parsing line table prologue at offset 0x00000000 unsupported " +             "reserved unit length found of value 0xffffff00", +             std::move(Unrecoverable)); +} + +TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(DWARF32); +  LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}}); +  LineTable <2 = Gen->addLineTable(DWARF32); +  LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}}); +  generate(); + +  DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); +  Parser.parseNext(RecordRecoverable, RecordUnrecoverable); +  ASSERT_FALSE(Parser.done()); +  Parser.parseNext(RecordRecoverable, RecordUnrecoverable); + +  EXPECT_TRUE(Parser.done()); +  EXPECT_FALSE(Recoverable); + +  checkError({"parsing line table prologue at offset 0x00000000 found " +              "unsupported version 0x00", +              "parsing line table prologue at offset 0x00000006 found " +              "unsupported version 0x01"}, +             std::move(Unrecoverable)); +} + +TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(DWARF32); +  LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {}); +  LineTable <2 = Gen->addLineTable(DWARF32); +  LT2.addExtendedOpcode(9, DW_LNE_set_address, +                        {{0x1234567890abcdef, LineTable::Quad}}); +  LT2.addStandardOpcode(DW_LNS_copy, {}); +  LT2.addByte(0xbb); +  generate(); + +  DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); +  Parser.parseNext(RecordRecoverable, RecordUnrecoverable); +  EXPECT_FALSE(Recoverable); +  ASSERT_FALSE(Parser.done()); +  checkError( +      "unexpected line op length at offset 0x00000030 expected 0x42 found 0x01", +      std::move(Unrecoverable)); + +  // Reset the error state so that it does not confuse the next set of checks. +  Unrecoverable = Error::success(); +  Parser.parseNext(RecordRecoverable, RecordUnrecoverable); + +  EXPECT_TRUE(Parser.done()); +  checkError("last sequence in debug line table is not terminated!", +             std::move(Recoverable)); +  EXPECT_FALSE(Unrecoverable); +} + +TEST_F(DebugLineBasicFixture, +       ParserReportsPrologueErrorsInEachTableWhenSkipping) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(DWARF32); +  LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}}); +  LineTable <2 = Gen->addLineTable(DWARF32); +  LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}}); +  generate(); + +  DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); +  Parser.skip(RecordUnrecoverable); +  ASSERT_FALSE(Parser.done()); +  Parser.skip(RecordUnrecoverable); + +  EXPECT_TRUE(Parser.done()); + +  checkError({"parsing line table prologue at offset 0x00000000 found " +              "unsupported version 0x00", +              "parsing line table prologue at offset 0x00000006 found " +              "unsupported version 0x01"}, +             std::move(Unrecoverable)); +} + +TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) { +  if (!setupGenerator()) +    return; + +  LineTable < = Gen->addLineTable(DWARF32); +  LT.addExtendedOpcode(42, DW_LNE_end_sequence, {}); +  generate(); + +  DWARFDebugLine::SectionParser Parser(LineData, *Context, CUs, TUs); +  Parser.skip(RecordUnrecoverable); + +  EXPECT_TRUE(Parser.done()); +  EXPECT_FALSE(Unrecoverable); +} + +} // end anonymous namespace diff --git a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp index c552623a7866..582b58062c45 100644 --- a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp @@ -20,59 +20,6 @@ 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);  } diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp index 3aa52a0d5b8f..adc400c2fd0e 100644 --- a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp @@ -22,13 +22,15 @@  #include "llvm/MC/MCDwarf.h"  #include "llvm/MC/MCInstrInfo.h"  #include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h"  #include "llvm/MC/MCRegisterInfo.h"  #include "llvm/MC/MCStreamer.h"  #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.def" +#include "llvm/MC/MCTargetOptionsCommandFlags.inc"  #include "llvm/PassAnalysisSupport.h"  #include "llvm/Support/TargetRegistry.h"  #include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetLoweringObjectFile.h"  #include "llvm/Target/TargetMachine.h"  #include "llvm/Target/TargetOptions.h" @@ -52,17 +54,36 @@ void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, uint64_t U) {                  DIEInteger(U));  } +void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form, const MCExpr &Expr) { +  auto &DG = CU->getGenerator(); +  Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form, +                DIEExpr(&Expr)); +} +  void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form,                                   StringRef String) {    auto &DG = CU->getGenerator(); -  if (Form == DW_FORM_string) { +  switch (Form) { +  case DW_FORM_string:      Die->addValue(DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,                    new (DG.getAllocator())                        DIEInlineString(String, DG.getAllocator())); -  } else { +    break; + +  case DW_FORM_strp: +  case DW_FORM_GNU_str_index: +  case DW_FORM_strx: +  case DW_FORM_strx1: +  case DW_FORM_strx2: +  case DW_FORM_strx3: +  case DW_FORM_strx4:      Die->addValue(          DG.getAllocator(), static_cast<dwarf::Attribute>(A), Form,          DIEString(DG.getStringPool().getEntry(*DG.getAsmPrinter(), String))); +    break; + +  default: +    llvm_unreachable("Unhandled form!");    }  } @@ -95,6 +116,24 @@ void dwarfgen::DIE::addAttribute(uint16_t A, dwarf::Form Form) {                  DIEInteger(1));  } +void dwarfgen::DIE::addStrOffsetsBaseAttribute() { +  auto &DG = CU->getGenerator(); +  auto &MC = *DG.getMCContext(); +  AsmPrinter *Asm = DG.getAsmPrinter(); + +  const MCSymbol *SectionStart = +      Asm->getObjFileLowering().getDwarfStrOffSection()->getBeginSymbol(); + +  const MCExpr *Expr = +      MCSymbolRefExpr::create(DG.getStringOffsetsStartSym(), MC); + +  if (!Asm->MAI->doesDwarfUseRelocationsAcrossSections()) +    Expr = MCBinaryExpr::createSub( +        Expr, MCSymbolRefExpr::create(SectionStart, MC), MC); + +  addAttribute(dwarf::DW_AT_str_offsets_base, DW_FORM_sec_offset, *Expr); +} +  dwarfgen::DIE dwarfgen::DIE::addChild(dwarf::Tag Tag) {    auto &DG = CU->getGenerator();    return dwarfgen::DIE(CU, @@ -106,6 +145,229 @@ dwarfgen::DIE dwarfgen::CompileUnit::getUnitDIE() {  }  //===----------------------------------------------------------------------===// +/// dwarfgen::LineTable implementation. +//===----------------------------------------------------------------------===// +DWARFDebugLine::Prologue dwarfgen::LineTable::createBasicPrologue() const { +  DWARFDebugLine::Prologue P; +  switch (Version) { +  case 2: +  case 3: +    P.TotalLength = 41; +    P.PrologueLength = 35; +    break; +  case 4: +    P.TotalLength = 42; +    P.PrologueLength = 36; +    break; +  case 5: +    P.TotalLength = 47; +    P.PrologueLength = 39; +    P.FormParams.AddrSize = AddrSize; +    break; +  default: +    llvm_unreachable("unsupported version"); +  } +  if (Format == DWARF64) { +    P.TotalLength += 4; +    P.FormParams.Format = DWARF64; +  } +  P.FormParams.Version = Version; +  P.MinInstLength = 1; +  P.MaxOpsPerInst = 1; +  P.DefaultIsStmt = 1; +  P.LineBase = -5; +  P.LineRange = 14; +  P.OpcodeBase = 13; +  P.StandardOpcodeLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1}; +  P.IncludeDirectories.push_back(DWARFFormValue(DW_FORM_string)); +  P.IncludeDirectories.back().setPValue("a dir"); +  P.FileNames.push_back(DWARFDebugLine::FileNameEntry()); +  P.FileNames.back().Name.setPValue("a file"); +  P.FileNames.back().Name.setForm(DW_FORM_string); +  return P; +} + +void dwarfgen::LineTable::setPrologue(DWARFDebugLine::Prologue NewPrologue) { +  Prologue = NewPrologue; +  CustomPrologue.clear(); +} + +void dwarfgen::LineTable::setCustomPrologue( +    ArrayRef<ValueAndLength> NewPrologue) { +  Prologue.reset(); +  CustomPrologue = NewPrologue; +} + +void dwarfgen::LineTable::addByte(uint8_t Value) { +  Contents.push_back({Value, Byte}); +} + +void dwarfgen::LineTable::addStandardOpcode(uint8_t Opcode, +                                            ArrayRef<ValueAndLength> Operands) { +  Contents.push_back({Opcode, Byte}); +  Contents.insert(Contents.end(), Operands.begin(), Operands.end()); +} + +void dwarfgen::LineTable::addExtendedOpcode(uint64_t Length, uint8_t Opcode, +                                            ArrayRef<ValueAndLength> Operands) { +  Contents.push_back({0, Byte}); +  Contents.push_back({Length, ULEB}); +  Contents.push_back({Opcode, Byte}); +  Contents.insert(Contents.end(), Operands.begin(), Operands.end()); +} + +void dwarfgen::LineTable::generate(MCContext &MC, AsmPrinter &Asm) const { +  MC.setDwarfVersion(Version); + +  MCSymbol *EndSymbol = nullptr; +  if (!CustomPrologue.empty()) { +    writeData(CustomPrologue, Asm); +  } else if (!Prologue) { +    EndSymbol = writeDefaultPrologue(Asm); +  } else { +    writePrologue(Asm); +  } + +  writeData(Contents, Asm); +  if (EndSymbol != nullptr) +    Asm.OutStreamer->EmitLabel(EndSymbol); +} + +void dwarfgen::LineTable::writeData(ArrayRef<ValueAndLength> Data, +                                    AsmPrinter &Asm) const { +  for (auto Entry : Data) { +    switch (Entry.Length) { +    case Byte: +    case Half: +    case Long: +    case Quad: +      Asm.OutStreamer->EmitIntValue(Entry.Value, Entry.Length); +      continue; +    case ULEB: +      Asm.EmitULEB128(Entry.Value); +      continue; +    case SLEB: +      Asm.EmitSLEB128(Entry.Value); +      continue; +    } +    llvm_unreachable("unsupported ValueAndLength Length value"); +  } +} + +MCSymbol *dwarfgen::LineTable::writeDefaultPrologue(AsmPrinter &Asm) const { +  MCSymbol *UnitStart = Asm.createTempSymbol("line_unit_start"); +  MCSymbol *UnitEnd = Asm.createTempSymbol("line_unit_end"); +  if (Format == DwarfFormat::DWARF64) { +    Asm.emitInt32(0xffffffff); +    Asm.EmitLabelDifference(UnitEnd, UnitStart, 8); +  } else { +    Asm.EmitLabelDifference(UnitEnd, UnitStart, 4); +  } +  Asm.OutStreamer->EmitLabel(UnitStart); +  Asm.emitInt16(Version); +  if (Version == 5) { +    Asm.emitInt8(AddrSize); +    Asm.emitInt8(SegSize); +  } + +  MCSymbol *PrologueStart = Asm.createTempSymbol("line_prologue_start"); +  MCSymbol *PrologueEnd = Asm.createTempSymbol("line_prologue_end"); +  Asm.EmitLabelDifference(PrologueEnd, PrologueStart, +                          Format == DwarfFormat::DWARF64 ? 8 : 4); +  Asm.OutStreamer->EmitLabel(PrologueStart); + +  DWARFDebugLine::Prologue DefaultPrologue = createBasicPrologue(); +  writeProloguePayload(DefaultPrologue, Asm); +  Asm.OutStreamer->EmitLabel(PrologueEnd); +  return UnitEnd; +} + +void dwarfgen::LineTable::writePrologue(AsmPrinter &Asm) const { +  if (Format == DwarfFormat::DWARF64) { +    Asm.emitInt32(0xffffffff); +    Asm.emitInt64(Prologue->TotalLength); +  } else { +    Asm.emitInt32(Prologue->TotalLength); +  } +  Asm.emitInt16(Prologue->getVersion()); +  if (Version == 5) { +    Asm.emitInt8(Prologue->getAddressSize()); +    Asm.emitInt8(Prologue->SegSelectorSize); +  } +  if (Format == DwarfFormat::DWARF64) +    Asm.emitInt64(Prologue->PrologueLength); +  else +    Asm.emitInt32(Prologue->PrologueLength); + +  writeProloguePayload(*Prologue, Asm); +} + +static void writeCString(StringRef Str, AsmPrinter &Asm) { +  Asm.OutStreamer->EmitBytes(Str); +  Asm.emitInt8(0); +} + +static void writeV2IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue, +                                       AsmPrinter &Asm) { +  for (auto Include : Prologue.IncludeDirectories) { +    assert(Include.getAsCString() && "expected a string form for include dir"); +    writeCString(*Include.getAsCString(), Asm); +  } +  Asm.emitInt8(0); + +  for (auto File : Prologue.FileNames) { +    assert(File.Name.getAsCString() && "expected a string form for file name"); +    writeCString(*File.Name.getAsCString(), Asm); +    Asm.EmitULEB128(File.DirIdx); +    Asm.EmitULEB128(File.ModTime); +    Asm.EmitULEB128(File.Length); +  } +  Asm.emitInt8(0); +} + +static void writeV5IncludeAndFileTable(const DWARFDebugLine::Prologue &Prologue, +                                       AsmPrinter &Asm) { +  Asm.emitInt8(1); // directory_entry_format_count. +  // TODO: Add support for other content descriptions - we currently only +  // support a single DW_LNCT_path/DW_FORM_string. +  Asm.EmitULEB128(DW_LNCT_path); +  Asm.EmitULEB128(DW_FORM_string); +  Asm.EmitULEB128(Prologue.IncludeDirectories.size()); +  for (auto Include : Prologue.IncludeDirectories) { +    assert(Include.getAsCString() && "expected a string form for include dir"); +    writeCString(*Include.getAsCString(), Asm); +  } + +  Asm.emitInt8(1); // file_name_entry_format_count. +  Asm.EmitULEB128(DW_LNCT_path); +  Asm.EmitULEB128(DW_FORM_string); +  Asm.EmitULEB128(Prologue.FileNames.size()); +  for (auto File : Prologue.FileNames) { +    assert(File.Name.getAsCString() && "expected a string form for file name"); +    writeCString(*File.Name.getAsCString(), Asm); +  } +} + +void dwarfgen::LineTable::writeProloguePayload( +    const DWARFDebugLine::Prologue &Prologue, AsmPrinter &Asm) const { +  Asm.emitInt8(Prologue.MinInstLength); +  if (Version >= 4) +    Asm.emitInt8(Prologue.MaxOpsPerInst); +  Asm.emitInt8(Prologue.DefaultIsStmt); +  Asm.emitInt8(Prologue.LineBase); +  Asm.emitInt8(Prologue.LineRange); +  Asm.emitInt8(Prologue.OpcodeBase); +  for (auto Length : Prologue.StandardOpcodeLengths) { +    Asm.emitInt8(Length); +  } + +  if (Version < 5) +    writeV2IncludeAndFileTable(Prologue, Asm); +  else +    writeV5IncludeAndFileTable(Prologue, Asm); +} + +//===----------------------------------------------------------------------===//  /// dwarfgen::Generator implementation.  //===----------------------------------------------------------------------===// @@ -148,12 +410,13 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {      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(*MRI, TripleName, "", MCOptions); +  MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, MCOptions);    if (!MAB)      return make_error<StringError>("no asm backend for target " + TripleName,                                     inconvertibleErrorCode()); @@ -164,11 +427,16 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {                                         TripleName,                                     inconvertibleErrorCode()); -  MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); -  if (!MSTI) -    return make_error<StringError>("no subtarget info for target " + TripleName, +  TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(), +                                          None)); +  if (!TM) +    return make_error<StringError>("no target machine for target " + TripleName,                                     inconvertibleErrorCode()); +  TLOF = TM->getObjFileLowering(); +  MC.reset(new MCContext(MAI.get(), MRI.get(), TLOF)); +  TLOF->Initialize(*MC, *TM); +    MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC);    if (!MCE)      return make_error<StringError>("no code emitter for target " + TripleName, @@ -177,22 +445,17 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {    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, +      TheTriple, *MC, std::unique_ptr<MCAsmBackend>(MAB), +      MAB->createObjectWriter(*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()); +  // Finally create the AsmPrinter we'll use to emit the DIEs.    Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MCStreamer>(MS)));    if (!Asm)      return make_error<StringError>("no asm printer for target " + TripleName, @@ -203,6 +466,7 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) {    Asm->setDwarfVersion(Version);    StringPool = llvm::make_unique<DwarfStringPool>(Allocator, *Asm, StringRef()); +  StringOffsetsStartSym = Asm->createTempSymbol("str_offsets_base");    return Error::success();  } @@ -223,27 +487,36 @@ StringRef dwarfgen::Generator::generate() {      SecOffset += CUOffset;      CU->setLength(CUOffset - 4);    } -  Abbreviations.Emit(Asm.get(), MOFI->getDwarfAbbrevSection()); -  StringPool->emit(*Asm, MOFI->getDwarfStrSection()); -  MS->SwitchSection(MOFI->getDwarfInfoSection()); +  Abbreviations.Emit(Asm.get(), TLOF->getDwarfAbbrevSection()); + +  StringPool->emitStringOffsetsTableHeader(*Asm, TLOF->getDwarfStrOffSection(), +                                           StringOffsetsStartSym); +  StringPool->emit(*Asm, TLOF->getDwarfStrSection(), +                   TLOF->getDwarfStrOffSection()); + +  MS->SwitchSection(TLOF->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); +    Asm->emitInt32(Length); +    Asm->emitInt16(Version);      if (Version <= 4) { -      Asm->EmitInt32(0); -      Asm->EmitInt8(CU->getAddressSize()); +      Asm->emitInt32(0); +      Asm->emitInt8(CU->getAddressSize());      } else { -      Asm->EmitInt8(dwarf::DW_UT_compile); -      Asm->EmitInt8(CU->getAddressSize()); -      Asm->EmitInt32(0); +      Asm->emitInt8(dwarf::DW_UT_compile); +      Asm->emitInt8(CU->getAddressSize()); +      Asm->emitInt32(0);      }      Asm->emitDwarfDIE(*CU->getUnitDIE().Die);    } +  MS->SwitchSection(TLOF->getDwarfLineSection()); +  for (auto < : LineTables) +    LT->generate(*MC, *Asm); +    MS->Finish();    if (FileBytes.empty())      return StringRef(); @@ -263,7 +536,13 @@ bool dwarfgen::Generator::saveFile(StringRef Path) {  }  dwarfgen::CompileUnit &dwarfgen::Generator::addCompileUnit() { -  CompileUnits.push_back(std::unique_ptr<CompileUnit>( -      new CompileUnit(*this, Version, Asm->getPointerSize()))); +  CompileUnits.push_back( +      make_unique<CompileUnit>(*this, Version, Asm->getPointerSize()));    return *CompileUnits.back();  } + +dwarfgen::LineTable &dwarfgen::Generator::addLineTable(DwarfFormat Format) { +  LineTables.push_back( +      make_unique<LineTable>(Version, Format, Asm->getPointerSize())); +  return *LineTables.back(); +} diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.h b/unittests/DebugInfo/DWARF/DwarfGenerator.h index dd7e8709638d..40ecaa98d053 100644 --- a/unittests/DebugInfo/DWARF/DwarfGenerator.h +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.h @@ -36,11 +36,11 @@ class MCCodeEmitter;  class MCContext;  struct MCDwarfLineTableParams;  class MCInstrInfo; -class MCObjectFileInfo;  class MCRegisterInfo;  class MCStreamer;  class MCSubtargetInfo;  class raw_fd_ostream; +class TargetLoweringObjectFile;  class TargetMachine;  class Triple; @@ -89,6 +89,14 @@ public:    /// \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 DIEExpr +  /// +  /// \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 Expr the MC expression used to compute the value. +  void addAttribute(uint16_t Attr, dwarf::Form Form, const MCExpr &Expr); +    /// Add an attribute value to be encoded as a DIEString or DIEInlinedString.    ///    /// \param Attr a dwarf::Attribute enumeration value or any uint16_t that @@ -123,6 +131,9 @@ public:    /// \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 DW_AT_str_offsets_base attribute to this DIE. +  void addStrOffsetsBaseAttribute(); +    /// Add a new child to this DIE object.    ///    /// \param Tag the dwarf::Tag to assing to the llvm::DIE object. @@ -153,6 +164,72 @@ public:    void setLength(uint64_t Length) { DU.setLength(Length); }  }; +/// A DWARF line unit-like class used to generate DWARF line units. +/// +/// Instances of this class are created by instances of the Generator class. +class LineTable { +public: +  enum ValueLength { Byte = 1, Half = 2, Long = 4, Quad = 8, ULEB, SLEB }; + +  struct ValueAndLength { +    uint64_t Value; +    ValueLength Length; +  }; + +  LineTable(uint16_t Version, dwarf::DwarfFormat Format, uint8_t AddrSize, +            uint8_t SegSize = 0) +      : Version(Version), Format(Format), AddrSize(AddrSize), SegSize(SegSize) { +    assert(Version >= 2 && Version <= 5 && "unsupported version"); +  } + +  // Create a Prologue suitable to pass to setPrologue, with a single file and +  // include directory entry. +  DWARFDebugLine::Prologue createBasicPrologue() const; + +  // Set or replace the current prologue with the specified prologue. If no +  // prologue is set, a default one will be used when generating. +  void setPrologue(DWARFDebugLine::Prologue NewPrologue); +  // Used to write an arbitrary payload instead of the standard prologue. This +  // is useful if you wish to test handling of corrupt .debug_line sections. +  void setCustomPrologue(ArrayRef<ValueAndLength> NewPrologue); + +  // Add a byte to the program, with the given value. This can be used to +  // specify a special opcode, or to add arbitrary contents to the section. +  void addByte(uint8_t Value); +  // Add a standard opcode to the program. The opcode and operands do not have +  // to be valid. +  void addStandardOpcode(uint8_t Opcode, ArrayRef<ValueAndLength> Operands); +  // Add an extended opcode to the program with the specified length, opcode, +  // and operands. These values do not have to be valid. +  void addExtendedOpcode(uint64_t Length, uint8_t Opcode, +                         ArrayRef<ValueAndLength> Operands); + +  // Write the contents of the LineUnit to the current section in the generator. +  void generate(MCContext &MC, AsmPrinter &Asm) const; + +private: +  void writeData(ArrayRef<ValueAndLength> Data, AsmPrinter &Asm) const; +  MCSymbol *writeDefaultPrologue(AsmPrinter &Asm) const; +  void writePrologue(AsmPrinter &Asm) const; + +  void writeProloguePayload(const DWARFDebugLine::Prologue &Prologue, +                            AsmPrinter &Asm) const; + +  llvm::Optional<DWARFDebugLine::Prologue> Prologue; +  std::vector<ValueAndLength> CustomPrologue; +  std::vector<ValueAndLength> Contents; + +  // The Version field is used for determining how to write the Prologue, if a +  // non-custom prologue is used. The version value actually written, will be +  // that specified in the Prologue, if a custom prologue has been passed in. +  // Otherwise, it will be this value. +  uint16_t Version; + +  dwarf::DwarfFormat Format; +  uint8_t AddrSize; +  uint8_t SegSize; +}; +  /// A DWARF generator.  ///  /// Generate DWARF for unit tests by creating any instance of this class and @@ -161,7 +238,6 @@ public:  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; @@ -169,12 +245,16 @@ class Generator {    MCCodeEmitter *MCE; // Owned by MCStreamer    MCStreamer *MS;     // Owned by AsmPrinter    std::unique_ptr<TargetMachine> TM; +  TargetLoweringObjectFile *TLOF; // Owned by TargetMachine;    std::unique_ptr<AsmPrinter> Asm;    BumpPtrAllocator Allocator;    std::unique_ptr<DwarfStringPool> StringPool; // Entries owned by Allocator.    std::vector<std::unique_ptr<CompileUnit>> CompileUnits; +  std::vector<std::unique_ptr<LineTable>> LineTables;    DIEAbbrevSet Abbreviations; +  MCSymbol *StringOffsetsStartSym; +    SmallString<4096> FileBytes;    /// The stream we use to generate the DWARF into as an ELF file.    std::unique_ptr<raw_svector_ostream> Stream; @@ -210,14 +290,23 @@ public:    ///    /// \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. +  /// objects to.    dwarfgen::CompileUnit &addCompileUnit(); +  /// Add a line table unit to be generated. +  /// \param DwarfFormat the DWARF format to use (DWARF32 or DWARF64). +  /// +  /// \returns a dwarfgen::LineTable that can be used to customise the contents +  /// of the line table. +  LineTable & +  addLineTable(dwarf::DwarfFormat DwarfFormat = dwarf::DwarfFormat::DWARF32); +    BumpPtrAllocator &getAllocator() { return Allocator; }    AsmPrinter *getAsmPrinter() const { return Asm.get(); }    MCContext *getMCContext() const { return MC.get(); }    DIEAbbrevSet &getAbbrevSet() { return Abbreviations; }    DwarfStringPool &getStringPool() { return *StringPool; } +  MCSymbol *getStringOffsetsStartSym() const { return StringOffsetsStartSym; }    /// Save the generated DWARF file to disk.    /// diff --git a/unittests/DebugInfo/DWARF/DwarfUtils.cpp b/unittests/DebugInfo/DWARF/DwarfUtils.cpp new file mode 100644 index 000000000000..ef43c13db4eb --- /dev/null +++ b/unittests/DebugInfo/DWARF/DwarfUtils.cpp @@ -0,0 +1,43 @@ +//===--- unittests/DebugInfo/DWARF/DwarfUtils.cpp ---------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DwarfUtils.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" + +using namespace llvm; + +static void initLLVMIfNeeded() { +  static bool gInitialized = false; +  if (!gInitialized) { +    gInitialized = true; +    InitializeAllTargets(); +    InitializeAllTargetMCs(); +    InitializeAllAsmPrinters(); +    InitializeAllAsmParsers(); +  } +} + +Triple llvm::dwarf::utils::getHostTripleForAddrSize(uint8_t AddrSize) { +  Triple T(Triple::normalize(LLVM_HOST_TRIPLE)); + +  if (AddrSize == 8 && T.isArch32Bit()) +    return T.get64BitArchVariant(); +  if (AddrSize == 4 && T.isArch64Bit()) +    return T.get32BitArchVariant(); +  return T; +} + +bool llvm::dwarf::utils::isConfigurationSupported(Triple &T) { +  initLLVMIfNeeded(); +  std::string Err; +  return TargetRegistry::lookupTarget(T.getTriple(), Err); +} diff --git a/unittests/DebugInfo/DWARF/DwarfUtils.h b/unittests/DebugInfo/DWARF/DwarfUtils.h new file mode 100644 index 000000000000..3d8045679d54 --- /dev/null +++ b/unittests/DebugInfo/DWARF/DwarfUtils.h @@ -0,0 +1,29 @@ +//===--- unittests/DebugInfo/DWARF/DwarfUtils.h -----------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFUTILS_H +#define LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFUTILS_H + +#include <cstdint> + +namespace llvm { + +class Triple; + +namespace dwarf { +namespace utils { + +Triple getHostTripleForAddrSize(uint8_t AddrSize); +bool isConfigurationSupported(Triple &T); + +} // end namespace utils +} // end namespace dwarf +} // end namespace llvm + +#endif // LLVM_UNITTESTS_DEBUG_INFO_DWARF_DWARFUTILS_H diff --git a/unittests/DebugInfo/MSF/CMakeLists.txt b/unittests/DebugInfo/MSF/CMakeLists.txt index 20f3b2ab3dcd..0e48ab8f2f6b 100644 --- a/unittests/DebugInfo/MSF/CMakeLists.txt +++ b/unittests/DebugInfo/MSF/CMakeLists.txt @@ -2,14 +2,10 @@ set(LLVM_LINK_COMPONENTS    DebugInfoMSF    ) -set(DebugInfoMSFSources +add_llvm_unittest(DebugInfoMSFTests    MappedBlockStreamTest.cpp    MSFBuilderTest.cpp    MSFCommonTest.cpp    ) -add_llvm_unittest(DebugInfoMSFTests -  ${DebugInfoMSFSources} -  ) -  target_link_libraries(DebugInfoMSFTests PRIVATE LLVMTestingSupport) diff --git a/unittests/DebugInfo/MSF/MSFBuilderTest.cpp b/unittests/DebugInfo/MSF/MSFBuilderTest.cpp index a91ac8d443fe..16247951804c 100644 --- a/unittests/DebugInfo/MSF/MSFBuilderTest.cpp +++ b/unittests/DebugInfo/MSF/MSFBuilderTest.cpp @@ -112,7 +112,7 @@ TEST_F(MSFBuilderTest, TestAddStreamNoDirectoryBlockIncrease) {    EXPECT_THAT_EXPECTED(ExpectedMsf, Succeeded());    auto &Msf = *ExpectedMsf; -  auto ExpectedL1 = Msf.build(); +  auto ExpectedL1 = Msf.generateLayout();    EXPECT_THAT_EXPECTED(ExpectedL1, Succeeded());    MSFLayout &L1 = *ExpectedL1; @@ -129,7 +129,7 @@ TEST_F(MSFBuilderTest, TestAddStreamNoDirectoryBlockIncrease) {    auto Blocks = Msf2.getStreamBlocks(0);    EXPECT_EQ(1U, Blocks.size()); -  auto ExpectedL2 = Msf2.build(); +  auto ExpectedL2 = Msf2.generateLayout();    EXPECT_THAT_EXPECTED(ExpectedL2, Succeeded());    MSFLayout &L2 = *ExpectedL2;    auto NewDirBlocks = L2.DirectoryBlocks; @@ -149,7 +149,7 @@ TEST_F(MSFBuilderTest, TestAddStreamWithDirectoryBlockIncrease) {    EXPECT_THAT_EXPECTED(Msf.addStream(4096 * 4096 / sizeof(uint32_t)),                         Succeeded()); -  auto ExpectedL1 = Msf.build(); +  auto ExpectedL1 = Msf.generateLayout();    EXPECT_THAT_EXPECTED(ExpectedL1, Succeeded());    MSFLayout &L1 = *ExpectedL1;    auto DirBlocks = L1.DirectoryBlocks; @@ -289,7 +289,7 @@ TEST_F(MSFBuilderTest, BuildMsfLayout) {    }    ++ExpectedNumBlocks; // The directory itself should use 1 block -  auto ExpectedLayout = Msf.build(); +  auto ExpectedLayout = Msf.generateLayout();    EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());    MSFLayout &L = *ExpectedLayout;    EXPECT_EQ(4096U, L.SB->BlockSize); @@ -316,7 +316,7 @@ TEST_F(MSFBuilderTest, UseDirectoryBlockHint) {    EXPECT_THAT_ERROR(Msf.setDirectoryBlocksHint({B + 1}), Succeeded());    EXPECT_THAT_EXPECTED(Msf.addStream(2048, {B + 2}), Succeeded()); -  auto ExpectedLayout = Msf.build(); +  auto ExpectedLayout = Msf.generateLayout();    EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());    MSFLayout &L = *ExpectedLayout;    EXPECT_EQ(msf::getMinimumBlockCount() + 2, L.SB->NumBlocks); @@ -338,7 +338,7 @@ TEST_F(MSFBuilderTest, DirectoryBlockHintInsufficient) {    uint32_t Size = 4096 * 4096 / 4;    EXPECT_THAT_EXPECTED(Msf.addStream(Size), Succeeded()); -  auto ExpectedLayout = Msf.build(); +  auto ExpectedLayout = Msf.generateLayout();    EXPECT_THAT_EXPECTED(ExpectedLayout, Succeeded());    MSFLayout &L = *ExpectedLayout;    EXPECT_EQ(2U, L.DirectoryBlocks.size()); @@ -356,7 +356,7 @@ TEST_F(MSFBuilderTest, DirectoryBlockHintOverestimated) {    ASSERT_THAT_EXPECTED(Msf.addStream(2048), Succeeded()); -  auto ExpectedLayout = Msf.build(); +  auto ExpectedLayout = Msf.generateLayout();    ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());    MSFLayout &L = *ExpectedLayout;    EXPECT_EQ(1U, L.DirectoryBlocks.size()); @@ -376,7 +376,7 @@ TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) {    Expected<uint32_t> SN = Msf.addStream(StreamSize);    ASSERT_THAT_EXPECTED(SN, Succeeded()); -  auto ExpectedLayout = Msf.build(); +  auto ExpectedLayout = Msf.generateLayout();    ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded());    MSFLayout &L = *ExpectedLayout;    auto BlocksRef = L.StreamMap[*SN]; @@ -384,10 +384,9 @@ TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) {    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. +    // Pages from both FPMs are always allocated. +    EXPECT_FALSE(L.FreePageMap.test(2 + I * 4096));      EXPECT_FALSE(L.FreePageMap.test(1 + I * 4096)); -    EXPECT_TRUE(L.FreePageMap.test(2 + I * 4096));    }    for (uint32_t I = 1; I <= 3; ++I) { diff --git a/unittests/DebugInfo/MSF/MSFCommonTest.cpp b/unittests/DebugInfo/MSF/MSFCommonTest.cpp index 144f5b113fb5..ee9ac75d15ce 100644 --- a/unittests/DebugInfo/MSF/MSFCommonTest.cpp +++ b/unittests/DebugInfo/MSF/MSFCommonTest.cpp @@ -46,12 +46,47 @@ TEST(MSFCommonTest, FpmIntervals) {    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)); + +  // The FPM is going to look like this: +  // |  0  |   1   |   2   |  ...  |  4096  |  4097  |  4098  | ... | +  // | SB  |  FPM0 | FPM1  | Data  |  Data  |  FPM0  |  FPM1  | ... | +  // +  // So when there are up to 4097 blocks (last index 4096), the final blocks +  // are data blocks.  When there are 4098 blocks (last index 4097), there is +  // one terminating FPM block, and when there are 4099 blocks, there are two +  // terminating FPM blocks.  Make sure all these cases are handled. + +  // With 4096 or 4097 blocks, the last block is a data block so we only have +  // 1 interval. +  for (uint32_t I : {4096, 4097}) { +    // 1 FPM0 interval +    EXPECT_EQ(1U, getNumFpmIntervals(4096, I, true, 1)); +    EXPECT_EQ(1U, getNumFpmIntervals(4096, I, false, 1)); + +    // 1 FPM1 interval +    EXPECT_EQ(1U, getNumFpmIntervals(4096, I, true, 2)); +    EXPECT_EQ(1U, getNumFpmIntervals(4096, I, false, 2)); +  } + +  // With 4098 blocks, the last block belongs to FPM0 so we should have 2 FPM0 +  // intervals. +  EXPECT_EQ(2U, getNumFpmIntervals(4096, 4098, true, 1)); +  EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, false, 1)); + +  // And 1 FPM1 interval. +  EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, true, 2)); +  EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, false, 2)); + +  // With 4099 blocks, the last block belongs to FPM1 so we should have 2 +  // FPM0 intervals. +  EXPECT_EQ(2U, getNumFpmIntervals(4096, 4099, true, 1)); +  EXPECT_EQ(1U, getNumFpmIntervals(4096, 4099, false, 1)); + +  // And 2 FPM1 intervals. +  EXPECT_EQ(2U, getNumFpmIntervals(4096, 4099, true, 2)); +  EXPECT_EQ(1U, getNumFpmIntervals(4096, 4099, false, 2));  }  TEST(MSFCommonTest, FpmStreamLayout) { @@ -95,7 +130,7 @@ TEST(MSFCommonTest, FpmStreamLayout) {    // 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; +  SB.NumBlocks = SB.BlockSize * 8 + 3;    SL = getFpmStreamLayout(L, true, false);    EXPECT_EQ(SB.BlockSize * 9, SL.Length);    EXPECT_EQ(9u, SL.Blocks.size()); diff --git a/unittests/DebugInfo/PDB/CMakeLists.txt b/unittests/DebugInfo/PDB/CMakeLists.txt index b19ee2cf43a5..5410e5f895d4 100644 --- a/unittests/DebugInfo/PDB/CMakeLists.txt +++ b/unittests/DebugInfo/PDB/CMakeLists.txt @@ -4,14 +4,10 @@ set(LLVM_LINK_COMPONENTS    DebugInfoPDB    ) -set(DebugInfoPDBSources +add_llvm_unittest(DebugInfoPDBTests    HashTableTest.cpp    StringTableBuilderTest.cpp    PDBApiTest.cpp    ) -add_llvm_unittest(DebugInfoPDBTests -  ${DebugInfoPDBSources} -  ) -  target_link_libraries(DebugInfoPDBTests PRIVATE LLVMTestingSupport) diff --git a/unittests/DebugInfo/PDB/HashTableTest.cpp b/unittests/DebugInfo/PDB/HashTableTest.cpp index f1968e55e86f..301b215badf5 100644 --- a/unittests/DebugInfo/PDB/HashTableTest.cpp +++ b/unittests/DebugInfo/PDB/HashTableTest.cpp @@ -8,9 +8,14 @@  //===----------------------------------------------------------------------===//  #include "llvm/DebugInfo/PDB/Native/HashTable.h" + +#include "llvm/DebugInfo/PDB/Native/Hash.h" +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/Support/Allocator.h"  #include "llvm/Support/BinaryByteStream.h"  #include "llvm/Support/BinaryStreamReader.h"  #include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/Support/StringSaver.h"  #include "llvm/Testing/Support/Error.h"  #include "gtest/gtest.h" @@ -22,7 +27,8 @@ using namespace llvm::pdb;  using namespace llvm::support;  namespace { -class HashTableInternals : public HashTable { + +class HashTableInternals : public HashTable<uint32_t> {  public:    using HashTable::Buckets;    using HashTable::Present; @@ -31,18 +37,18 @@ public:  }  TEST(HashTableTest, TestSimple) { -  HashTable Table; +  HashTableInternals Table;    EXPECT_EQ(0u, Table.size());    EXPECT_GT(Table.capacity(), 0u); -  Table.set(3, 7); +  Table.set_as(3u, 7);    EXPECT_EQ(1u, Table.size()); -  ASSERT_NE(Table.end(), Table.find(3)); -  EXPECT_EQ(7u, Table.get(3)); +  ASSERT_NE(Table.end(), Table.find_as(3u)); +  EXPECT_EQ(7u, Table.get(3u));  }  TEST(HashTableTest, TestCollision) { -  HashTable Table; +  HashTableInternals Table;    EXPECT_EQ(0u, Table.size());    EXPECT_GT(Table.capacity(), 0u); @@ -52,39 +58,33 @@ TEST(HashTableTest, TestCollision) {    uint32_t N1 = Table.capacity() + 1;    uint32_t N2 = 2 * N1; -  Table.set(N1, 7); -  Table.set(N2, 12); +  Table.set_as(N1, 7); +  Table.set_as(N2, 12);    EXPECT_EQ(2u, Table.size()); -  ASSERT_NE(Table.end(), Table.find(N1)); -  ASSERT_NE(Table.end(), Table.find(N2)); +  ASSERT_NE(Table.end(), Table.find_as(N1)); +  ASSERT_NE(Table.end(), Table.find_as(N2));    EXPECT_EQ(7u, Table.get(N1));    EXPECT_EQ(12u, Table.get(N2));  }  TEST(HashTableTest, TestRemove) { -  HashTable Table; +  HashTableInternals Table;    EXPECT_EQ(0u, Table.size());    EXPECT_GT(Table.capacity(), 0u); -  Table.set(1, 2); -  Table.set(3, 4); +  Table.set_as(1u, 2); +  Table.set_as(3u, 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)); +  ASSERT_NE(Table.end(), Table.find_as(1u)); +  ASSERT_NE(Table.end(), Table.find_as(3u)); -  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)); +  EXPECT_EQ(2u, Table.get(1u)); +  EXPECT_EQ(4u, Table.get(3u));  }  TEST(HashTableTest, TestCollisionAfterMultipleProbes) { -  HashTable Table; +  HashTableInternals Table;    EXPECT_EQ(0u, Table.size());    EXPECT_GT(Table.capacity(), 0u); @@ -95,31 +95,17 @@ TEST(HashTableTest, TestCollisionAfterMultipleProbes) {    uint32_t N2 = N1 + 1;    uint32_t N3 = 2 * N1; -  Table.set(N1, 7); -  Table.set(N2, 11); -  Table.set(N3, 13); +  Table.set_as(N1, 7); +  Table.set_as(N2, 11); +  Table.set_as(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)); +  ASSERT_NE(Table.end(), Table.find_as(N1)); +  ASSERT_NE(Table.end(), Table.find_as(N2)); +  ASSERT_NE(Table.end(), Table.find_as(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) { @@ -127,15 +113,15 @@ TEST(HashTableTest, Grow) {    // 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; +  HashTableInternals Table;    uint32_t OldCapacity = Table.capacity();    for (uint32_t I = 0; I < OldCapacity; ++I) { -    Table.set(OldCapacity + I * 2 + 1, I * 2 + 3); +    Table.set_as(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)); +    ASSERT_NE(Table.end(), Table.find_as(OldCapacity + I * 2 + 1));      EXPECT_EQ(I * 2 + 3, Table.get(OldCapacity + I * 2 + 1));    }  } @@ -144,7 +130,7 @@ 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); +    Table.set_as(Cap + I * 2 + 1, I * 2 + 3);    }    std::vector<uint8_t> Buffer(Table.calculateSerializedLength()); @@ -166,3 +152,109 @@ TEST(HashTableTest, Serialization) {    EXPECT_EQ(Table.Present, Table2.Present);    EXPECT_EQ(Table.Deleted, Table2.Deleted);  } + +TEST(HashTableTest, NamedStreamMap) { +  std::vector<StringRef> Streams = {"One",  "Two", "Three", "Four", +                                    "Five", "Six", "Seven"}; +  StringMap<uint32_t> ExpectedIndices; +  for (uint32_t I = 0; I < Streams.size(); ++I) +    ExpectedIndices[Streams[I]] = I + 1; + +  // To verify the hash table actually works, we want to verify that insertion +  // order doesn't matter.  So try inserting in every possible order of 7 items. +  do { +    NamedStreamMap NSM; +    for (StringRef S : Streams) +      NSM.set(S, ExpectedIndices[S]); + +    EXPECT_EQ(Streams.size(), NSM.size()); + +    uint32_t N; +    EXPECT_TRUE(NSM.get("One", N)); +    EXPECT_EQ(1U, N); + +    EXPECT_TRUE(NSM.get("Two", N)); +    EXPECT_EQ(2U, N); + +    EXPECT_TRUE(NSM.get("Three", N)); +    EXPECT_EQ(3U, N); + +    EXPECT_TRUE(NSM.get("Four", N)); +    EXPECT_EQ(4U, N); + +    EXPECT_TRUE(NSM.get("Five", N)); +    EXPECT_EQ(5U, N); + +    EXPECT_TRUE(NSM.get("Six", N)); +    EXPECT_EQ(6U, N); + +    EXPECT_TRUE(NSM.get("Seven", N)); +    EXPECT_EQ(7U, N); +  } while (std::next_permutation(Streams.begin(), Streams.end())); +} + +namespace { +struct FooBar { +  uint32_t X; +  uint32_t Y; +}; + +} // namespace + +namespace llvm { +namespace pdb { +template <> struct PdbHashTraits<FooBar> { +  std::vector<char> Buffer; + +  PdbHashTraits() { Buffer.push_back(0); } + +  uint32_t hashLookupKey(StringRef S) const { +    return llvm::pdb::hashStringV1(S); +  } + +  StringRef storageKeyToLookupKey(uint32_t N) const { +    if (N >= Buffer.size()) +      return StringRef(); + +    return StringRef(Buffer.data() + N); +  } + +  uint32_t lookupKeyToStorageKey(StringRef S) { +    uint32_t N = Buffer.size(); +    Buffer.insert(Buffer.end(), S.begin(), S.end()); +    Buffer.push_back('\0'); +    return N; +  } +}; +} // namespace pdb +} // namespace llvm + +TEST(HashTableTest, NonTrivialValueType) { +  HashTable<FooBar> Table; +  uint32_t Cap = Table.capacity(); +  for (uint32_t I = 0; I < Cap; ++I) { +    FooBar F; +    F.X = I; +    F.Y = I + 1; +    Table.set_as(utostr(I), F); +  } + +  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()); + +  HashTable<FooBar> 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); +} diff --git a/unittests/DebugInfo/PDB/PDBApiTest.cpp b/unittests/DebugInfo/PDB/PDBApiTest.cpp index e998acf009ec..41b679825f17 100644 --- a/unittests/DebugInfo/PDB/PDBApiTest.cpp +++ b/unittests/DebugInfo/PDB/PDBApiTest.cpp @@ -11,7 +11,10 @@  #include "llvm/ADT/STLExtras.h"  #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h"  #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBSectionContrib.h"  #include "llvm/DebugInfo/PDB/IPDBSession.h"  #include "llvm/DebugInfo/PDB/IPDBSourceFile.h"  #include "llvm/DebugInfo/PDB/IPDBTable.h" @@ -63,7 +66,7 @@ namespace {  class MockSession : public IPDBSession {    uint64_t getLoadAddress() const override { return 0; } -  void setLoadAddress(uint64_t Address) override {} +  bool setLoadAddress(uint64_t Address) override { return false; }    std::unique_ptr<PDBSymbolExe> getGlobalScope() override { return nullptr; }    std::unique_ptr<PDBSymbol> getSymbolById(uint32_t SymbolId) const override {      return nullptr; @@ -72,11 +75,27 @@ class MockSession : public IPDBSession {    getSourceFileById(uint32_t SymbolId) const override {      return nullptr;    } - +  bool addressForVA(uint64_t VA, uint32_t &Section, +                    uint32_t &Offset) const override { +    return false; +  } +  bool addressForRVA(uint32_t RVA, uint32_t &Section, +                     uint32_t &Offset) const override { +    return false; +  }    std::unique_ptr<PDBSymbol>    findSymbolByAddress(uint64_t Address, PDB_SymType Type) const override {      return nullptr;    } +  std::unique_ptr<PDBSymbol> findSymbolByRVA(uint32_t RVA, +                                             PDB_SymType Type) const override { +    return nullptr; +  } +  std::unique_ptr<PDBSymbol> +  findSymbolBySectOffset(uint32_t Sect, uint32_t Offset, +                         PDB_SymType Type) const override { +    return nullptr; +  }    std::unique_ptr<IPDBEnumLineNumbers>    findLineNumbers(const PDBSymbolCompiland &Compiland,                    const IPDBSourceFile &File) const override { @@ -86,6 +105,15 @@ class MockSession : public IPDBSession {    findLineNumbersByAddress(uint64_t Address, uint32_t Length) const override {      return nullptr;    } +  std::unique_ptr<IPDBEnumLineNumbers> +  findLineNumbersByRVA(uint32_t RVA, uint32_t Length) const override { +    return nullptr; +  } +  std::unique_ptr<IPDBEnumLineNumbers> +  findLineNumbersBySectOffset(uint32_t Section, uint32_t Offset, +                              uint32_t Length) const override { +    return nullptr; +  }    std::unique_ptr<IPDBEnumSourceFiles>    findSourceFiles(const PDBSymbolCompiland *Compiland, llvm::StringRef Pattern,                    PDB_NameSearchFlags Flags) const override { @@ -123,6 +151,14 @@ class MockSession : public IPDBSession {    std::unique_ptr<IPDBEnumTables> getEnumTables() const override {      return nullptr;    } + +  std::unique_ptr<IPDBEnumInjectedSources> getInjectedSources() const override { +    return nullptr; +  } + +  std::unique_ptr<IPDBEnumSectionContribs> getSectionContribs() const override { +    return nullptr; +  }  };  class MockRawSymbol : public IPDBRawSymbol { @@ -142,14 +178,48 @@ public:      return nullptr;    }    std::unique_ptr<IPDBEnumSymbols> +  findChildrenByAddr(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags, +                     uint32_t Section, uint32_t Offset) const override { +    return nullptr; +  } +  std::unique_ptr<IPDBEnumSymbols> +  findChildrenByVA(PDB_SymType Type, StringRef Name, PDB_NameSearchFlags Flags, +                   uint64_t VA) 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> +  findInlineFramesByAddr(uint32_t Section, uint32_t Offset) const override { +    return nullptr; +  } +  std::unique_ptr<IPDBEnumSymbols>    findInlineFramesByRVA(uint32_t RVA) const override {      return nullptr;    } +  std::unique_ptr<IPDBEnumSymbols> +  findInlineFramesByVA(uint64_t VA) const override { +    return nullptr; +  } +  std::unique_ptr<IPDBEnumLineNumbers> findInlineeLines() const override { +    return nullptr; +  } +  std::unique_ptr<IPDBEnumLineNumbers> +  findInlineeLinesByAddr(uint32_t Section, uint32_t Offset, +                         uint32_t Length) const override { +    return nullptr; +  } +  std::unique_ptr<IPDBEnumLineNumbers> +  findInlineeLinesByRVA(uint32_t RVA, uint32_t Length) const override { +    return nullptr; +  } +  std::unique_ptr<IPDBEnumLineNumbers> +  findInlineeLinesByVA(uint64_t VA, uint32_t Length) const override { +    return nullptr; +  }    void getDataBytes(llvm::SmallVector<uint8_t, 32> &bytes) const override {}    void getFrontEndVersion(VersionInfo &Version) const override {} @@ -161,6 +231,10 @@ public:      return {};    } +  std::unique_ptr<IPDBLineNumber> getSrcLineOnTypeDefn() const override { +    return nullptr; +  } +    MOCK_SYMBOL_ACCESSOR(getAccess)    MOCK_SYMBOL_ACCESSOR(getAddressOffset)    MOCK_SYMBOL_ACCESSOR(getAddressSection) @@ -428,5 +502,4 @@ TEST_F(PDBApiTest, Dyncast) {    VerifyUnknownDyncasts();  } -  } // end anonymous namespace diff --git a/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp b/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp index 0efc2c6411b8..bf08ab20923b 100644 --- a/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp +++ b/unittests/DebugInfo/PDB/StringTableBuilderTest.cpp @@ -27,10 +27,29 @@ 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")); + +  // This test case is carefully constructed to ensure that at least one +  // string gets bucketed into slot 0, *and* to ensure that at least one +  // has a hash collision at the end of the bucket list so it has to +  // wrap around. +  uint32_t FooID = Builder.insert("foo"); +  uint32_t BarID = Builder.insert("bar"); +  uint32_t BazID = Builder.insert("baz"); +  uint32_t BuzzID = Builder.insert("buzz"); +  uint32_t BazzID = Builder.insert("bazz"); +  uint32_t BarrID = Builder.insert("barr"); + +  // Re-inserting the same item should return the same id. +  EXPECT_EQ(FooID, Builder.insert("foo")); +  EXPECT_EQ(BarID, Builder.insert("bar")); +  EXPECT_EQ(BazID, Builder.insert("baz")); +  EXPECT_EQ(BuzzID, Builder.insert("buzz")); +  EXPECT_EQ(BazzID, Builder.insert("bazz")); +  EXPECT_EQ(BarrID, Builder.insert("barr")); + +  // Each ID should be distinct. +  std::set<uint32_t> Distinct{FooID, BarID, BazID, BuzzID, BazzID, BarrID}; +  EXPECT_EQ(6U, Distinct.size());    std::vector<uint8_t> Buffer(Builder.calculateSerializedSize());    MutableBinaryByteStream OutStream(Buffer, little); @@ -43,13 +62,20 @@ TEST_F(StringTableBuilderTest, Simple) {    PDBStringTable Table;    EXPECT_THAT_ERROR(Table.reload(Reader), Succeeded()); -  EXPECT_EQ(3U, Table.getNameCount()); +  EXPECT_EQ(6U, 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)); +  EXPECT_THAT_EXPECTED(Table.getStringForID(FooID), HasValue("foo")); +  EXPECT_THAT_EXPECTED(Table.getStringForID(BarID), HasValue("bar")); +  EXPECT_THAT_EXPECTED(Table.getStringForID(BazID), HasValue("baz")); +  EXPECT_THAT_EXPECTED(Table.getStringForID(BuzzID), HasValue("buzz")); +  EXPECT_THAT_EXPECTED(Table.getStringForID(BazzID), HasValue("bazz")); +  EXPECT_THAT_EXPECTED(Table.getStringForID(BarrID), HasValue("barr")); + +  EXPECT_THAT_EXPECTED(Table.getIDForString("foo"), HasValue(FooID)); +  EXPECT_THAT_EXPECTED(Table.getIDForString("bar"), HasValue(BarID)); +  EXPECT_THAT_EXPECTED(Table.getIDForString("baz"), HasValue(BazID)); +  EXPECT_THAT_EXPECTED(Table.getIDForString("buzz"), HasValue(BuzzID)); +  EXPECT_THAT_EXPECTED(Table.getIDForString("bazz"), HasValue(BazzID)); +  EXPECT_THAT_EXPECTED(Table.getIDForString("barr"), HasValue(BarrID));  }  | 
