diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp index 7a0256f10ea60..9d7b767b65acb 100644 --- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp +++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp @@ -82,7 +82,6 @@ struct llvm::gsym::CUInfo { } }; - static DWARFDie GetParentDeclContextDIE(DWARFDie &Die) { if (DWARFDie SpecDie = Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_specification)) { @@ -170,7 +169,7 @@ getQualifiedNameIndex(DWARFDie &Die, uint64_t Language, GsymCreator &Gsym) { // templates if (ParentName.front() == '<' && ParentName.back() == '>') Name = "{" + ParentName.substr(1, ParentName.size() - 2).str() + "}" + - "::" + Name; + "::" + Name; else Name = ParentName.str() + "::" + Name; } @@ -338,9 +337,13 @@ static void convertFunctionLineTable(OutputAggregator &Out, CUInfo &CUI, if (FilePath.empty()) { // If we had a DW_AT_decl_file, but got no file then we need to emit a // warning. + const uint64_t DwarfFileIdx = dwarf::toUnsigned( + Die.findRecursively(dwarf::DW_AT_decl_file), UINT32_MAX); + // Check if there is no DW_AT_decl_line attribute, and don't report an + // error if it isn't there. + if (DwarfFileIdx == UINT32_MAX) + return; Out.Report("Invalid file index in DW_AT_decl_file", [&](raw_ostream &OS) { - const uint64_t DwarfFileIdx = dwarf::toUnsigned( - Die.findRecursively(dwarf::DW_AT_decl_file), UINT32_MAX); OS << "error: function DIE at " << HEX32(Die.getOffset()) << " has an invalid file index " << DwarfFileIdx << " in its DW_AT_decl_file attribute, unable to create a single " @@ -432,7 +435,7 @@ static void convertFunctionLineTable(OutputAggregator &Out, CUInfo &CUI, // Skip multiple line entries for the same file and line. auto LastLE = FI.OptLineTable->last(); if (LastLE && LastLE->File == FileIdx && LastLE->Line == Row.Line) - continue; + continue; // Only push a row if it isn't an end sequence. End sequence markers are // included for the last address in a function or the last contiguous // address in a sequence. @@ -718,8 +721,8 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath, for (uint32_t I = 0; I < NumAddrs; ++I) { auto FuncAddr = Gsym->getAddress(I); if (!FuncAddr) - return createStringError(std::errc::invalid_argument, - "failed to extract address[%i]", I); + return createStringError(std::errc::invalid_argument, + "failed to extract address[%i]", I); auto FI = Gsym->getFunctionInfo(*FuncAddr); if (!FI) @@ -734,8 +737,7 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath, if (!LR) return LR.takeError(); - auto DwarfInlineInfos = - DICtx.getInliningInfoForAddress(SectAddr, DLIS); + auto DwarfInlineInfos = DICtx.getInliningInfoForAddress(SectAddr, DLIS); uint32_t NumDwarfInlineInfos = DwarfInlineInfos.getNumberOfFrames(); if (NumDwarfInlineInfos == 0) { DwarfInlineInfos.addFrame( @@ -773,8 +775,7 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath, continue; } - for (size_t Idx = 0, count = LR->Locations.size(); Idx < count; - ++Idx) { + for (size_t Idx = 0, count = LR->Locations.size(); Idx < count; ++Idx) { const auto &gii = LR->Locations[Idx]; if (Idx < NumDwarfInlineInfos) { const auto &dii = DwarfInlineInfos.getFrame(Idx); diff --git a/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp b/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp index 33f53de2e77bc..8935714e00ff5 100644 --- a/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp +++ b/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp @@ -24,8 +24,8 @@ #include "llvm/Support/DataExtractor.h" #include "llvm/Testing/Support/Error.h" -#include "gtest/gtest.h" #include "gmock/gmock.h" +#include "gtest/gtest.h" #include using namespace llvm; @@ -99,7 +99,7 @@ TEST(GSYMTest, TestFunctionInfo) { const uint32_t FileIdx = 1; const uint32_t Line = 12; FI.OptLineTable = LineTable(); - FI.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line)); + FI.OptLineTable->push(LineEntry(StartAddr, FileIdx, Line)); EXPECT_TRUE(FI.hasRichInfo()); FI.clear(); EXPECT_FALSE(FI.isValid()); @@ -135,7 +135,7 @@ TEST(GSYMTest, TestFunctionInfo) { FunctionInfo FISymtab(StartAddr, Size, NameOffset); FunctionInfo FIWithLines(StartAddr, Size, NameOffset); FIWithLines.OptLineTable = LineTable(); - FIWithLines.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line)); + FIWithLines.OptLineTable->push(LineEntry(StartAddr, FileIdx, Line)); // Test that a FunctionInfo with just a name and size is less than one // that has name, size and any number of line table entries EXPECT_LT(FISymtab, FIWithLines); @@ -166,7 +166,7 @@ TEST(GSYMTest, TestFunctionInfo) { // Test if we have an entry with lines and one with more lines for the same // range, the ones with more lines is greater than the one with less. FunctionInfo FIWithMoreLines = FIWithLines; - FIWithMoreLines.OptLineTable->push(LineEntry(StartAddr,FileIdx,Line+5)); + FIWithMoreLines.OptLineTable->push(LineEntry(StartAddr, FileIdx, Line + 5)); EXPECT_LT(FIWithLines, FIWithMoreLines); // Test that if we have the same number of lines we compare the line entries @@ -198,24 +198,27 @@ TEST(GSYMTest, TestFunctionInfoDecodeErrors) { FileWriter FW(OutStrm, ByteOrder); const uint64_t BaseAddr = 0x100; TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000000: missing FunctionInfo Size"); + "0x00000000: missing FunctionInfo Size"); FW.writeU32(0x100); // Function size. TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000004: missing FunctionInfo Name"); + "0x00000004: missing FunctionInfo Name"); // Write out an invalid Name string table offset of zero. FW.writeU32(0); - TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, + TestFunctionInfoDecodeError( + ByteOrder, OutStrm.str(), BaseAddr, "0x00000004: invalid FunctionInfo Name value 0x00000000"); // Modify the Name to be 0x00000001, which is a valid value. FW.fixup32(0x00000001, 4); - TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, + TestFunctionInfoDecodeError( + ByteOrder, OutStrm.str(), BaseAddr, "0x00000008: missing FunctionInfo InfoType value"); auto FixupOffset = FW.tell(); FW.writeU32(1); // InfoType::LineTableInfo. - TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, + TestFunctionInfoDecodeError( + ByteOrder, OutStrm.str(), BaseAddr, "0x0000000c: missing FunctionInfo InfoType length"); FW.fixup32(7, FixupOffset); // Write an invalid InfoType enumeration value - FW.writeU32(0); // LineTableInfo InfoType data length. + FW.writeU32(0); // LineTableInfo InfoType data length. TestFunctionInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, "0x00000008: unsupported InfoType 7"); } @@ -278,25 +281,24 @@ static void TestFunctionInfoEncodeDecode(llvm::endianness ByteOrder, } static void AddLines(uint64_t FuncAddr, uint32_t FileIdx, FunctionInfo &FI) { - FI.OptLineTable = LineTable(); - LineEntry Line0(FuncAddr + 0x000, FileIdx, 10); - LineEntry Line1(FuncAddr + 0x010, FileIdx, 11); - LineEntry Line2(FuncAddr + 0x100, FileIdx, 1000); - FI.OptLineTable->push(Line0); - FI.OptLineTable->push(Line1); - FI.OptLineTable->push(Line2); + FI.OptLineTable = LineTable(); + LineEntry Line0(FuncAddr + 0x000, FileIdx, 10); + LineEntry Line1(FuncAddr + 0x010, FileIdx, 11); + LineEntry Line2(FuncAddr + 0x100, FileIdx, 1000); + FI.OptLineTable->push(Line0); + FI.OptLineTable->push(Line1); + FI.OptLineTable->push(Line2); } - static void AddInline(uint64_t FuncAddr, uint64_t FuncSize, FunctionInfo &FI) { - FI.Inline = InlineInfo(); - FI.Inline->Ranges.insert(AddressRange(FuncAddr, FuncAddr + FuncSize)); - InlineInfo Inline1; - Inline1.Ranges.insert(AddressRange(FuncAddr + 0x10, FuncAddr + 0x30)); - Inline1.Name = 1; - Inline1.CallFile = 1; - Inline1.CallLine = 11; - FI.Inline->Children.push_back(Inline1); + FI.Inline = InlineInfo(); + FI.Inline->Ranges.insert(AddressRange(FuncAddr, FuncAddr + FuncSize)); + InlineInfo Inline1; + Inline1.Ranges.insert(AddressRange(FuncAddr + 0x10, FuncAddr + 0x30)); + Inline1.Name = 1; + Inline1.CallFile = 1; + Inline1.CallLine = 11; + FI.Inline->Children.push_back(Inline1); } TEST(GSYMTest, TestFunctionInfoEncoding) { @@ -514,21 +516,25 @@ TEST(GSYMTest, TestInlineInfoDecodeErrors) { raw_svector_ostream OutStrm(Str); FileWriter FW(OutStrm, ByteOrder); const uint64_t BaseAddr = 0x100; - TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, + TestInlineInfoDecodeError( + ByteOrder, OutStrm.str(), BaseAddr, "0x00000000: missing InlineInfo address ranges data"); AddressRanges Ranges; - Ranges.insert({BaseAddr, BaseAddr+0x100}); + Ranges.insert({BaseAddr, BaseAddr + 0x100}); encodeRanges(Ranges, FW, BaseAddr); - TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, + TestInlineInfoDecodeError( + ByteOrder, OutStrm.str(), BaseAddr, "0x00000004: missing InlineInfo uint8_t indicating children"); FW.writeU8(0); TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000005: missing InlineInfo uint32_t for name"); + "0x00000005: missing InlineInfo uint32_t for name"); FW.writeU32(0); - TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, + TestInlineInfoDecodeError( + ByteOrder, OutStrm.str(), BaseAddr, "0x00000009: missing ULEB128 for InlineInfo call file"); FW.writeU8(0); - TestInlineInfoDecodeError(ByteOrder, OutStrm.str(), BaseAddr, + TestInlineInfoDecodeError( + ByteOrder, OutStrm.str(), BaseAddr, "0x0000000a: missing ULEB128 for InlineInfo call line"); } @@ -708,20 +714,20 @@ TEST(GSYMTest, TestLineTable) { const uint64_t StartAddr = 0x1000; const uint32_t FileIdx = 1; LineTable LT; - LineEntry Line0(StartAddr+0x000, FileIdx, 10); - LineEntry Line1(StartAddr+0x010, FileIdx, 11); - LineEntry Line2(StartAddr+0x100, FileIdx, 1000); + LineEntry Line0(StartAddr + 0x000, FileIdx, 10); + LineEntry Line1(StartAddr + 0x010, FileIdx, 11); + LineEntry Line2(StartAddr + 0x100, FileIdx, 1000); ASSERT_TRUE(LT.empty()); ASSERT_EQ(LT.size(), (size_t)0); LT.push(Line0); ASSERT_EQ(LT.size(), (size_t)1); LT.push(Line1); LT.push(Line2); - LT.push(LineEntry(StartAddr+0x120, FileIdx, 900)); - LT.push(LineEntry(StartAddr+0x120, FileIdx, 2000)); - LT.push(LineEntry(StartAddr+0x121, FileIdx, 2001)); - LT.push(LineEntry(StartAddr+0x122, FileIdx, 2002)); - LT.push(LineEntry(StartAddr+0x123, FileIdx, 2003)); + LT.push(LineEntry(StartAddr + 0x120, FileIdx, 900)); + LT.push(LineEntry(StartAddr + 0x120, FileIdx, 2000)); + LT.push(LineEntry(StartAddr + 0x121, FileIdx, 2001)); + LT.push(LineEntry(StartAddr + 0x122, FileIdx, 2002)); + LT.push(LineEntry(StartAddr + 0x123, FileIdx, 2003)); ASSERT_FALSE(LT.empty()); ASSERT_EQ(LT.size(), (size_t)8); // Test operator[]. @@ -783,30 +789,30 @@ TEST(GSYMTest, TestLineTableDecodeErrors) { FileWriter FW(OutStrm, ByteOrder); const uint64_t BaseAddr = 0x100; TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000000: missing LineTable MinDelta"); + "0x00000000: missing LineTable MinDelta"); FW.writeU8(1); // MinDelta (ULEB) TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000001: missing LineTable MaxDelta"); + "0x00000001: missing LineTable MaxDelta"); FW.writeU8(10); // MaxDelta (ULEB) TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000002: missing LineTable FirstLine"); + "0x00000002: missing LineTable FirstLine"); FW.writeU8(20); // FirstLine (ULEB) TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000003: EOF found before EndSequence"); + "0x00000003: EOF found before EndSequence"); // Test a SetFile with the argument missing from the stream FW.writeU8(1); // SetFile opcode (uint8_t) TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000004: EOF found before SetFile value"); + "0x00000004: EOF found before SetFile value"); FW.writeU8(5); // SetFile value as index (ULEB) // Test a AdvancePC with the argument missing from the stream FW.writeU8(2); // AdvancePC opcode (uint8_t) TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000006: EOF found before AdvancePC value"); + "0x00000006: EOF found before AdvancePC value"); FW.writeU8(20); // AdvancePC value as offset (ULEB) // Test a AdvancePC with the argument missing from the stream FW.writeU8(3); // AdvanceLine opcode (uint8_t) TestLineTableDecodeError(ByteOrder, OutStrm.str(), BaseAddr, - "0x00000008: EOF found before AdvanceLine value"); + "0x00000008: EOF found before AdvanceLine value"); FW.writeU8(20); // AdvanceLine value as offset (LLEB) } @@ -823,16 +829,17 @@ TEST(GSYMTest, TestLineTableEncodeErrors) { // Try to encode a line table where a line entry has an address that is less // than BaseAddr and verify we get an appropriate error. - LineEntry Line0(BaseAddr+0x000, FileIdx, 10); - LineEntry Line1(BaseAddr+0x010, FileIdx, 11); + LineEntry Line0(BaseAddr + 0x000, FileIdx, 10); + LineEntry Line1(BaseAddr + 0x010, FileIdx, 11); LT.push(Line0); LT.push(Line1); checkError("LineEntry has address 0x1000 which is less than the function " - "start address 0x1010", LT.encode(FW, BaseAddr+0x10)); + "start address 0x1010", + LT.encode(FW, BaseAddr + 0x10)); LT.clear(); - // Try to encode a line table where a line entries has an address that is less - // than BaseAddr and verify we get an appropriate error. + // Try to encode a line table where a line entries has an address that is + // less than BaseAddr and verify we get an appropriate error. LT.push(Line1); LT.push(Line0); checkError("LineEntry in LineTable not in ascending order", @@ -870,9 +877,9 @@ static void InitHeader(Header &H) { H.UUIDSize = 16; H.BaseAddress = 0x1000; H.NumAddresses = 1; - H.StrtabOffset= 0x2000; + H.StrtabOffset = 0x2000; H.StrtabSize = 0x1000; - for (size_t i=0; i bool { FI.OptLineTable = LineTable(); // Invalid line table. - return false; // Stop iterating + return false; // Stop iterating }); TestGsymCreatorEncodeError(llvm::endianness::little, GC, "attempted to encode invalid LineTable object"); @@ -997,7 +1004,7 @@ TEST(GSYMTest, TestGsymCreatorEncodeErrors) { GC.forEachFunctionInfo([](FunctionInfo &FI) -> bool { FI.OptLineTable = std::nullopt; FI.Inline = InlineInfo(); // Invalid InlineInfo. - return false; // Stop iterating + return false; // Stop iterating }); TestGsymCreatorEncodeError(llvm::endianness::little, GC, "attempted to encode invalid InlineInfo object"); @@ -1043,8 +1050,8 @@ TEST(GSYMTest, TestGsymCreator1ByteAddrOffsets) { constexpr uint8_t AddrOffSize = 1; const uint32_t Func1Name = GC.insertString("foo"); const uint32_t Func2Name = GC.insertString("bar"); - GC.addFunctionInfo(FunctionInfo(BaseAddr+0x00, 0x10, Func1Name)); - GC.addFunctionInfo(FunctionInfo(BaseAddr+0x20, 0x10, Func2Name)); + GC.addFunctionInfo(FunctionInfo(BaseAddr + 0x00, 0x10, Func1Name)); + GC.addFunctionInfo(FunctionInfo(BaseAddr + 0x20, 0x10, Func2Name)); OutputAggregator Null(nullptr); Error Err = GC.finalize(Null); ASSERT_FALSE(Err); @@ -1066,8 +1073,8 @@ TEST(GSYMTest, TestGsymCreator2ByteAddrOffsets) { constexpr uint8_t AddrOffSize = 2; const uint32_t Func1Name = GC.insertString("foo"); const uint32_t Func2Name = GC.insertString("bar"); - GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name)); - GC.addFunctionInfo(FunctionInfo(BaseAddr+0x200, 0x100, Func2Name)); + GC.addFunctionInfo(FunctionInfo(BaseAddr + 0x000, 0x100, Func1Name)); + GC.addFunctionInfo(FunctionInfo(BaseAddr + 0x200, 0x100, Func2Name)); OutputAggregator Null(nullptr); Error Err = GC.finalize(Null); ASSERT_FALSE(Err); @@ -1089,8 +1096,8 @@ TEST(GSYMTest, TestGsymCreator4ByteAddrOffsets) { constexpr uint8_t AddrOffSize = 4; const uint32_t Func1Name = GC.insertString("foo"); const uint32_t Func2Name = GC.insertString("bar"); - GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name)); - GC.addFunctionInfo(FunctionInfo(BaseAddr+0x20000, 0x100, Func2Name)); + GC.addFunctionInfo(FunctionInfo(BaseAddr + 0x000, 0x100, Func1Name)); + GC.addFunctionInfo(FunctionInfo(BaseAddr + 0x20000, 0x100, Func2Name)); OutputAggregator Null(nullptr); Error Err = GC.finalize(Null); ASSERT_FALSE(Err); @@ -1112,8 +1119,8 @@ TEST(GSYMTest, TestGsymCreator8ByteAddrOffsets) { constexpr uint8_t AddrOffSize = 8; const uint32_t Func1Name = GC.insertString("foo"); const uint32_t Func2Name = GC.insertString("bar"); - GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name)); - GC.addFunctionInfo(FunctionInfo(BaseAddr+0x100000000, 0x100, Func2Name)); + GC.addFunctionInfo(FunctionInfo(BaseAddr + 0x000, 0x100, Func1Name)); + GC.addFunctionInfo(FunctionInfo(BaseAddr + 0x100000000, 0x100, Func2Name)); OutputAggregator Null(nullptr); Error Err = GC.finalize(Null); ASSERT_FALSE(Err); @@ -1147,7 +1154,7 @@ TEST(GSYMTest, TestGsymReader) { GC.setUUID(UUID); constexpr uint64_t BaseAddr = 0x1000; constexpr uint64_t Func1Addr = BaseAddr; - constexpr uint64_t Func2Addr = BaseAddr+0x20; + constexpr uint64_t Func2Addr = BaseAddr + 0x20; constexpr uint64_t FuncSize = 0x10; const uint32_t Func1Name = GC.insertString("foo"); const uint32_t Func2Name = GC.insertString("bar"); @@ -1164,20 +1171,20 @@ TEST(GSYMTest, TestGsymReader) { ASSERT_FALSE((bool)Err); if (auto ExpectedGR = GsymReader::copyBuffer(OutStrm.str())) { const GsymReader &GR = ExpectedGR.get(); - VerifyFunctionInfoError(GR, Func1Addr-1, "address 0xfff is not in GSYM"); + VerifyFunctionInfoError(GR, Func1Addr - 1, "address 0xfff is not in GSYM"); FunctionInfo Func1(Func1Addr, FuncSize, Func1Name); VerifyFunctionInfo(GR, Func1Addr, Func1); - VerifyFunctionInfo(GR, Func1Addr+1, Func1); - VerifyFunctionInfo(GR, Func1Addr+FuncSize-1, Func1); - VerifyFunctionInfoError(GR, Func1Addr+FuncSize, + VerifyFunctionInfo(GR, Func1Addr + 1, Func1); + VerifyFunctionInfo(GR, Func1Addr + FuncSize - 1, Func1); + VerifyFunctionInfoError(GR, Func1Addr + FuncSize, "address 0x1010 is not in GSYM"); - VerifyFunctionInfoError(GR, Func2Addr-1, "address 0x101f is not in GSYM"); + VerifyFunctionInfoError(GR, Func2Addr - 1, "address 0x101f is not in GSYM"); FunctionInfo Func2(Func2Addr, FuncSize, Func2Name); VerifyFunctionInfo(GR, Func2Addr, Func2); - VerifyFunctionInfo(GR, Func2Addr+1, Func2); - VerifyFunctionInfo(GR, Func2Addr+FuncSize-1, Func2); - VerifyFunctionInfoError(GR, Func2Addr+FuncSize, + VerifyFunctionInfo(GR, Func2Addr + 1, Func2); + VerifyFunctionInfo(GR, Func2Addr + FuncSize - 1, Func2); + VerifyFunctionInfoError(GR, Func2Addr + FuncSize, "address 0x1030 is not in GSYM"); } } @@ -1234,53 +1241,57 @@ TEST(GSYMTest, TestGsymLookups) { // Verify inline info is correct when doing lookups. auto LR = GR->lookup(0x1000); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 5})); + EXPECT_THAT(LR->Locations, testing::ElementsAre( + SourceLocation{"main", "/tmp", "main.c", 5})); LR = GR->lookup(0x100F); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 5, 15})); + EXPECT_THAT(LR->Locations, testing::ElementsAre(SourceLocation{ + "main", "/tmp", "main.c", 5, 15})); LR = GR->lookup(0x1010); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 10}, - SourceLocation{"main", "/tmp", "main.c", 6, 16})); + EXPECT_THAT( + LR->Locations, + testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 10}, + SourceLocation{"main", "/tmp", "main.c", 6, 16})); LR = GR->lookup(0x1012); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline2", "/tmp", "foo.h", 20}, - SourceLocation{"inline1", "/tmp", "foo.h", 33, 2}, - SourceLocation{"main", "/tmp", "main.c", 6, 18})); + EXPECT_THAT( + LR->Locations, + testing::ElementsAre(SourceLocation{"inline2", "/tmp", "foo.h", 20}, + SourceLocation{"inline1", "/tmp", "foo.h", 33, 2}, + SourceLocation{"main", "/tmp", "main.c", 6, 18})); LR = GR->lookup(0x1014); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 11, 4}, - SourceLocation{"main", "/tmp", "main.c", 6, 20})); + EXPECT_THAT( + LR->Locations, + testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 11, 4}, + SourceLocation{"main", "/tmp", "main.c", 6, 20})); LR = GR->lookup(0x1016); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline3", "/tmp", "foo.h", 30}, - SourceLocation{"inline1", "/tmp", "foo.h", 35, 6}, - SourceLocation{"main", "/tmp", "main.c", 6, 22})); + EXPECT_THAT( + LR->Locations, + testing::ElementsAre(SourceLocation{"inline3", "/tmp", "foo.h", 30}, + SourceLocation{"inline1", "/tmp", "foo.h", 35, 6}, + SourceLocation{"main", "/tmp", "main.c", 6, 22})); LR = GR->lookup(0x1018); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 12, 8}, - SourceLocation{"main", "/tmp", "main.c", 6, 24})); + EXPECT_THAT( + LR->Locations, + testing::ElementsAre(SourceLocation{"inline1", "/tmp", "foo.h", 12, 8}, + SourceLocation{"main", "/tmp", "main.c", 6, 24})); LR = GR->lookup(0x1020); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 8, 32})); + EXPECT_THAT(LR->Locations, testing::ElementsAre(SourceLocation{ + "main", "/tmp", "main.c", 8, 32})); } - TEST(GSYMTest, TestDWARFFunctionWithAddresses) { // Create a single compile unit with a single function and make sure it gets // converted to DWARF correctly. The function's address range is in where @@ -1851,43 +1862,46 @@ TEST(GSYMTest, TestDWARFInlineInfo) { StringRef MethodName = GR->getString(ExpFI->Name); EXPECT_EQ(MethodName, "main"); - // Verify inline info is correct when doing lookups. + // Verify inline info is correct when doing lookups. auto LR = GR->lookup(0x1000); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 10})); - LR = GR->lookup(0x1100-1); + EXPECT_THAT(LR->Locations, testing::ElementsAre( + SourceLocation{"main", "/tmp", "main.c", 10})); + LR = GR->lookup(0x1100 - 1); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 10, 255})); + EXPECT_THAT(LR->Locations, testing::ElementsAre(SourceLocation{ + "main", "/tmp", "main.c", 10, 255})); LR = GR->lookup(0x1100); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 20}, - SourceLocation{"main", "/tmp", "main.c", 10, 256})); - LR = GR->lookup(0x1180-1); + EXPECT_THAT( + LR->Locations, + testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 20}, + SourceLocation{"main", "/tmp", "main.c", 10, 256})); + LR = GR->lookup(0x1180 - 1); ASSERT_THAT_EXPECTED(LR, Succeeded()); EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 20, 127}, - SourceLocation{"main", "/tmp", "main.c", 10, 383})); + testing::ElementsAre( + SourceLocation{"inline1", "/tmp", "inline.h", 20, 127}, + SourceLocation{"main", "/tmp", "main.c", 10, 383})); LR = GR->lookup(0x1180); ASSERT_THAT_EXPECTED(LR, Succeeded()); EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 21, 128}, - SourceLocation{"main", "/tmp", "main.c", 10, 384})); - LR = GR->lookup(0x1200-1); + testing::ElementsAre( + SourceLocation{"inline1", "/tmp", "inline.h", 21, 128}, + SourceLocation{"main", "/tmp", "main.c", 10, 384})); + LR = GR->lookup(0x1200 - 1); ASSERT_THAT_EXPECTED(LR, Succeeded()); EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"inline1", "/tmp", "inline.h", 21, 255}, - SourceLocation{"main", "/tmp", "main.c", 10, 511})); + testing::ElementsAre( + SourceLocation{"inline1", "/tmp", "inline.h", 21, 255}, + SourceLocation{"main", "/tmp", "main.c", 10, 511})); LR = GR->lookup(0x1200); ASSERT_THAT_EXPECTED(LR, Succeeded()); - EXPECT_THAT(LR->Locations, - testing::ElementsAre(SourceLocation{"main", "/tmp", "main.c", 11, 512})); + EXPECT_THAT(LR->Locations, testing::ElementsAre(SourceLocation{ + "main", "/tmp", "main.c", 11, 512})); } - TEST(GSYMTest, TestDWARFNoLines) { // Check that if a DW_TAG_subprogram doesn't have line table entries that // we fall back and use the DW_AT_decl_file and DW_AT_decl_line to at least @@ -2150,7 +2164,6 @@ TEST(GSYMTest, TestDWARFNoLines) { EXPECT_EQ(ExpFI->OptLineTable->first()->Line, 40u); } - TEST(GSYMTest, TestDWARFDeadStripAddr4) { // Check that various techniques that compilers use for dead code stripping // work for 4 byte addresses. Make sure we keep the good functions and @@ -2459,7 +2472,8 @@ TEST(GSYMTest, TestGsymCreatorMultipleSymbolsWithNoSize) { ArrayRef(UUID)); } -// Helper function to quickly create a FunctionInfo in a GsymCreator for testing. +// Helper function to quickly create a FunctionInfo in a GsymCreator for +// testing. static void AddFunctionInfo(GsymCreator &GC, const char *FuncName, uint64_t FuncAddr, const char *SourcePath, const char *HeaderPath) { @@ -2467,18 +2481,21 @@ static void AddFunctionInfo(GsymCreator &GC, const char *FuncName, FI.OptLineTable = LineTable(); const uint32_t SourceFileIdx = GC.insertFile(SourcePath); const uint32_t HeaderFileIdx = GC.insertFile(HeaderPath); - FI.OptLineTable->push(LineEntry(FuncAddr+0x00, SourceFileIdx, 5)); - FI.OptLineTable->push(LineEntry(FuncAddr+0x10, HeaderFileIdx, 10)); - FI.OptLineTable->push(LineEntry(FuncAddr+0x12, HeaderFileIdx, 20)); - FI.OptLineTable->push(LineEntry(FuncAddr+0x14, HeaderFileIdx, 11)); - FI.OptLineTable->push(LineEntry(FuncAddr+0x16, HeaderFileIdx, 30)); - FI.OptLineTable->push(LineEntry(FuncAddr+0x18, HeaderFileIdx, 12)); - FI.OptLineTable->push(LineEntry(FuncAddr+0x20, SourceFileIdx, 8)); + FI.OptLineTable->push(LineEntry(FuncAddr + 0x00, SourceFileIdx, 5)); + FI.OptLineTable->push(LineEntry(FuncAddr + 0x10, HeaderFileIdx, 10)); + FI.OptLineTable->push(LineEntry(FuncAddr + 0x12, HeaderFileIdx, 20)); + FI.OptLineTable->push(LineEntry(FuncAddr + 0x14, HeaderFileIdx, 11)); + FI.OptLineTable->push(LineEntry(FuncAddr + 0x16, HeaderFileIdx, 30)); + FI.OptLineTable->push(LineEntry(FuncAddr + 0x18, HeaderFileIdx, 12)); + FI.OptLineTable->push(LineEntry(FuncAddr + 0x20, SourceFileIdx, 8)); FI.Inline = InlineInfo(); - std::string InlineName1(FuncName); InlineName1.append("1"); - std::string InlineName2(FuncName); InlineName2.append("2"); - std::string InlineName3(FuncName); InlineName3.append("3"); + std::string InlineName1(FuncName); + InlineName1.append("1"); + std::string InlineName2(FuncName); + InlineName2.append("2"); + std::string InlineName3(FuncName); + InlineName3.append("3"); FI.Inline->Name = GC.insertString(InlineName1); FI.Inline->CallFile = SourceFileIdx; @@ -2530,7 +2547,7 @@ TEST(GSYMTest, TestGsymSegmenting) { AddFunctionInfo(GC, "baz", 0x4000, "/tmp/baz.c", "/tmp/baz.h"); Expected GR = FinalizeEncodeAndDecode(GC); ASSERT_THAT_EXPECTED(GR, Succeeded()); - //GR->dump(outs()); + // GR->dump(outs()); // Create segmented GSYM files where each file contains 1 function. We will // then test doing lookups on the "GR", or the full GSYM file and then test @@ -2552,7 +2569,8 @@ TEST(GSYMTest, TestGsymSegmenting) { GC.createSegment(57, FuncIdx); ASSERT_FALSE((bool)GCError); checkError("a segment size of 57 is to small to fit any function infos, " - "specify a larger value", GCError.takeError()); + "specify a larger value", + GCError.takeError()); // Make sure that the function index didn't get incremented when we didn't // encode any values into the segmented GsymCreator. ASSERT_EQ(FuncIdx, (size_t)0); @@ -2627,7 +2645,6 @@ TEST(GSYMTest, TestGsymSegmenting) { ASSERT_THAT_EXPECTED(GR2000->lookup(0x1000), Failed()); ASSERT_THAT_EXPECTED(GR2000->lookup(0x3000), Failed()); ASSERT_THAT_EXPECTED(GR2000->lookup(0x4000), Failed()); - } // Verify that all lookups match the range [0x3000-0x3030) when doing lookups @@ -2646,7 +2663,7 @@ TEST(GSYMTest, TestGsymSegmenting) { ASSERT_THAT_EXPECTED(GR3000->lookup(0x1000), Failed()); ASSERT_THAT_EXPECTED(GR3000->lookup(0x2000), Failed()); ASSERT_THAT_EXPECTED(GR3000->lookup(0x4000), Failed()); -} + } // Verify that all lookups match the range [0x4000-0x4030) when doing lookups // in the GsymReader that contains all functions and from the segmented @@ -2681,7 +2698,7 @@ TEST(GSYMTest, TestGsymSegmentingNoBase) { AddFunctionInfo(GC, "baz", 0x4000, "/tmp/baz.c", "/tmp/baz.h"); Expected GR = FinalizeEncodeAndDecode(GC); ASSERT_THAT_EXPECTED(GR, Succeeded()); - //GR->dump(outs()); + // GR->dump(outs()); // Create segmented GSYM files where each file contains 1 function. We will // then test doing lookups on the "GR", or the full GSYM file and then test @@ -2703,7 +2720,8 @@ TEST(GSYMTest, TestGsymSegmentingNoBase) { GC.createSegment(57, FuncIdx); ASSERT_FALSE((bool)GCError); checkError("a segment size of 57 is to small to fit any function infos, " - "specify a larger value", GCError.takeError()); + "specify a larger value", + GCError.takeError()); // Make sure that the function index didn't get incremented when we didn't // encode any values into the segmented GsymCreator. ASSERT_EQ(FuncIdx, (size_t)0); @@ -2778,7 +2796,6 @@ TEST(GSYMTest, TestGsymSegmentingNoBase) { ASSERT_THAT_EXPECTED(GR2000->lookup(0x1000), Failed()); ASSERT_THAT_EXPECTED(GR2000->lookup(0x3000), Failed()); ASSERT_THAT_EXPECTED(GR2000->lookup(0x4000), Failed()); - } // Verify that all lookups match the range [0x3000-0x3030) when doing lookups @@ -2797,7 +2814,7 @@ TEST(GSYMTest, TestGsymSegmentingNoBase) { ASSERT_THAT_EXPECTED(GR3000->lookup(0x1000), Failed()); ASSERT_THAT_EXPECTED(GR3000->lookup(0x2000), Failed()); ASSERT_THAT_EXPECTED(GR3000->lookup(0x4000), Failed()); -} + } // Verify that all lookups match the range [0x4000-0x4030) when doing lookups // in the GsymReader that contains all functions and from the segmented @@ -2819,7 +2836,6 @@ TEST(GSYMTest, TestGsymSegmentingNoBase) { } } - TEST(GSYMTest, TestDWARFInlineRangeScopes) { // Test cases where inlined functions address ranges are not contained in the // parent ranges and that we can successfully remove them and emit error @@ -3074,16 +3090,19 @@ TEST(GSYMTest, TestDWARFInlineRangeScopes) { StringRef FuncName = GR->getString(ExpFI->Name); EXPECT_EQ(FuncName, "foo"); std::vector ExpectedLogErrors = { - "error: inlined function DIE at 0x0000002a has a range [0x0000000000000fff " - "- 0x0000000000001001) that isn't contained in any parent address ranges, " - "this inline range will be removed.", - "error: inlined function DIE at 0x00000058 has a range [0x0000000000001000 " - "- 0x0000000000001100) that isn't contained in any parent address ranges, " - "this inline range will be removed." - }; + "error: inlined function DIE at 0x0000002a has a range " + "[0x0000000000000fff " + "- 0x0000000000001001) that isn't contained in any parent address " + "ranges, " + "this inline range will be removed.", + "error: inlined function DIE at 0x00000058 has a range " + "[0x0000000000001000 " + "- 0x0000000000001100) that isn't contained in any parent address " + "ranges, " + "this inline range will be removed."}; // Make sure all expected errors are in the error stream for the two invalid // inlined functions that we removed due to invalid range scoping. - for (const auto &Error: ExpectedLogErrors) { + for (const auto &Error : ExpectedLogErrors) { EXPECT_TRUE(OS.str().find(Error) != std::string::npos); } // The top level inline info is for the function "foo" itself. Verify that @@ -3096,7 +3115,6 @@ TEST(GSYMTest, TestDWARFInlineRangeScopes) { EXPECT_EQ(ExpFI->Inline->CallLine, 0u); EXPECT_EQ(ExpFI->Inline->Children.size(), 1u); - // The first inline function "valid1" contains two inline functions in the // DWARF, but one has an address range which isn't contained in any ranges // from "foo", so only 1 inline function be parsed. @@ -3107,7 +3125,6 @@ TEST(GSYMTest, TestDWARFInlineRangeScopes) { EXPECT_EQ(Inline1.CallLine, 11u); EXPECT_EQ(Inline1.Children.size(), 1u); - // The second inline function "valid2" contains two inline functions in the // DWARF, but one has an address range which isn't contained in any ranges // from "valid1", so only 1 inline function be parsed. @@ -3302,15 +3319,17 @@ TEST(GSYMTest, TestDWARFEmptyInline) { StringRef FuncName = GR->getString(ExpFI->Name); EXPECT_EQ(FuncName, "foo"); std::vector ExpectedLogErrors = { - "error: inlined function DIE at 0x0000002a has a range [0x0000000000001100" - " - 0x0000000000001200) that isn't contained in any parent address ranges," - " this inline range will be removed.", - "warning: DIE contains inline function information that has no valid " - "ranges, removing inline information:", + "error: inlined function DIE at 0x0000002a has a range " + "[0x0000000000001100" + " - 0x0000000000001200) that isn't contained in any parent address " + "ranges," + " this inline range will be removed.", + "warning: DIE contains inline function information that has no valid " + "ranges, removing inline information:", }; // Make sure all expected errors are in the error stream for the two invalid // inlined functions that we removed due to invalid range scoping. - for (const auto &Error: ExpectedLogErrors) { + for (const auto &Error : ExpectedLogErrors) { EXPECT_TRUE(OS.str().find(Error) != std::string::npos); } } @@ -3344,7 +3363,8 @@ TEST(GSYMTest, TestFinalizeForLineTables) { // DW_AT_high_pc (0x0000000000002050) // // 0x0000003f: NULL - // 0x00000040: Compile Unit: length = 0x0000003c, format = DWARF32, version = 0x0004, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x00000080) + // 0x00000040: Compile Unit: length = 0x0000003c, format = DWARF32, version = + // 0x0004, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x00000080) // // 0x0000004b: DW_TAG_compile_unit // DW_AT_name ("/tmp/main.cpp") @@ -3550,7 +3570,6 @@ TEST(GSYMTest, TestFinalizeForLineTables) { EXPECT_EQ(FuncName2, "bar"); } - TEST(GSYMTest, TestRangeWarnings) { // This example has a single compile unit that has a DW_TAG_subprogram that // has two discontiguous ranges. We will create two FunctionInfo objects for @@ -4026,7 +4045,6 @@ TEST(GSYMTest, TestEmptyRangeWarnings) { EXPECT_TRUE(errors.find("error:") == std::string::npos); } - TEST(GSYMTest, TestEmptyLinkageName) { // This example has a single compile unit that has a DW_TAG_subprogram that // has a function that has an empty linkage name and a valid normal name. @@ -4051,7 +4069,6 @@ TEST(GSYMTest, TestEmptyLinkageName) { // // 0x0000002e: NULL - StringRef yamldata = R"( debug_str: - '' @@ -4899,3 +4916,129 @@ TEST(GSYMTest, TestLookupsOfOverlappingAndUnequalRanges) { for (const auto &Line : ExpectedDumpLines) EXPECT_TRUE(DumpStr.find(Line) != std::string::npos); } + +TEST(GSYMTest, TestDWARFTransformNoErrorForMissingFileDecl) { + // Test that if llvm-gsymutil finds a line table for a compile unit and if + // there are no matching entries for a function in that compile unit, that + // it doesn't print out a error saying that a DIE has an invalid file index + // if there is no DW_AT_decl_file attribute. + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_name ("main.cpp") + // DW_AT_language (DW_LANG_C) + // DW_AT_stmt_list (0x00000000) + // + // 0x00000015: DW_TAG_subprogram + // DW_AT_name ("foo") + // DW_AT_low_pc (0x0000000000001000) + // DW_AT_high_pc (0x0000000000001050) + // + // 0x0000002a: NULL + // + // Line table that has entries, but none that match "foo": + // + // Address Line Column File ISA Discriminator OpIndex Flags + // ------------------ ------ ------ ------ --- ------------- ------- ----- + // 0x0000000000002000 10 0 1 0 0 0 is_stmt + // 0x0000000000002050 13 0 1 0 0 0 is_stmt + + StringRef yamldata = R"( + debug_str: + - '' + - main.cpp + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_language + Form: DW_FORM_udata + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_info: + - Length: 0x27 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x1 + - Value: 0x2 + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0xDEADBEEFDEADBEEF + CStr: foo + - Value: 0x1000 + - Value: 0x1050 + - AbbrCode: 0x0 + debug_line: + - Length: 58 + Version: 2 + PrologueLength: 31 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + Files: + - Name: main.cpp + DirIdx: 0 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 8192 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 80 + - Opcode: DW_LNS_advance_line + SData: 3 + Data: 0 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 + )"; + auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata); + ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded()); + std::unique_ptr DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + ASSERT_TRUE(DwarfContext.get() != nullptr); + std::string errors; + raw_string_ostream OS(errors); + OutputAggregator OSAgg(&OS); + GsymCreator GC; + DwarfTransformer DT(*DwarfContext, GC); + const uint32_t ThreadCount = 1; + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); + + // Make sure this warning is not in the binary + std::string error_str("error: function DIE at 0x00000015 has an invalid file " + "index 4294967295 in its DW_AT_decl_file attribute"); + EXPECT_TRUE(errors.find(error_str) == std::string::npos); +}