diff options
Diffstat (limited to 'llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp')
| -rw-r--r-- | llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp | 247 |
1 files changed, 179 insertions, 68 deletions
diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp index d266960ae302..0b225376349e 100644 --- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp +++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp @@ -65,10 +65,10 @@ struct llvm::gsym::CUInfo { /// the first client that asks for a compile unit file index will end up /// doing the conversion, and subsequent clients will get the cached GSYM /// index. - uint32_t DWARFToGSYMFileIndex(GsymCreator &Gsym, uint32_t DwarfFileIdx) { - if (!LineTable) - return 0; - assert(DwarfFileIdx < FileCache.size()); + std::optional<uint32_t> DWARFToGSYMFileIndex(GsymCreator &Gsym, + uint32_t DwarfFileIdx) { + if (!LineTable || DwarfFileIdx >= FileCache.size()) + return std::nullopt; uint32_t &GsymFileIdx = FileCache[DwarfFileIdx]; if (GsymFileIdx != UINT32_MAX) return GsymFileIdx; @@ -132,11 +132,11 @@ static DWARFDie GetParentDeclContextDIE(DWARFDie &Die) { static std::optional<uint32_t> getQualifiedNameIndex(DWARFDie &Die, uint64_t Language, GsymCreator &Gsym) { // If the dwarf has mangled name, use mangled name - if (auto LinkageName = - dwarf::toString(Die.findRecursively({dwarf::DW_AT_MIPS_linkage_name, - dwarf::DW_AT_linkage_name}), - nullptr)) - return Gsym.insertString(LinkageName, /* Copy */ false); + if (auto LinkageName = Die.getLinkageName()) { + // We have seen cases were linkage name is actually empty. + if (strlen(LinkageName) > 0) + return Gsym.insertString(LinkageName, /* Copy */ false); + } StringRef ShortName(Die.getName(DINameKind::ShortName)); if (ShortName.empty()) @@ -156,7 +156,7 @@ getQualifiedNameIndex(DWARFDie &Die, uint64_t Language, GsymCreator &Gsym) { // Some GCC optimizations create functions with names ending with .isra.<num> // or .part.<num> and those names are just DW_AT_name, not DW_AT_linkage_name // If it looks like it could be the case, don't add any prefix - if (ShortName.startswith("_Z") && + if (ShortName.starts_with("_Z") && (ShortName.contains(".isra.") || ShortName.contains(".part."))) return Gsym.insertString(ShortName, /* Copy */ false); @@ -205,9 +205,21 @@ static bool hasInlineInfo(DWARFDie Die, uint32_t Depth) { return false; } -static void parseInlineInfo(GsymCreator &Gsym, CUInfo &CUI, DWARFDie Die, - uint32_t Depth, FunctionInfo &FI, - InlineInfo &parent) { +static AddressRanges +ConvertDWARFRanges(const DWARFAddressRangesVector &DwarfRanges) { + AddressRanges Ranges; + for (const DWARFAddressRange &DwarfRange : DwarfRanges) { + if (DwarfRange.LowPC < DwarfRange.HighPC) + Ranges.insert({DwarfRange.LowPC, DwarfRange.HighPC}); + } + return Ranges; +} + +static void parseInlineInfo(GsymCreator &Gsym, raw_ostream *Log, CUInfo &CUI, + DWARFDie Die, uint32_t Depth, FunctionInfo &FI, + InlineInfo &Parent, + const AddressRanges &AllParentRanges, + bool &WarnIfEmpty) { if (!hasInlineInfo(Die, Depth)) return; @@ -215,39 +227,80 @@ static void parseInlineInfo(GsymCreator &Gsym, CUInfo &CUI, DWARFDie Die, if (Tag == dwarf::DW_TAG_inlined_subroutine) { // create new InlineInfo and append to parent.children InlineInfo II; - DWARFAddressRange FuncRange = - DWARFAddressRange(FI.startAddress(), FI.endAddress()); + AddressRanges AllInlineRanges; Expected<DWARFAddressRangesVector> RangesOrError = Die.getAddressRanges(); if (RangesOrError) { - for (const DWARFAddressRange &Range : RangesOrError.get()) { - // Check that the inlined function is within the range of the function - // info, it might not be in case of split functions - if (FuncRange.LowPC <= Range.LowPC && Range.HighPC <= FuncRange.HighPC) - II.Ranges.insert(AddressRange(Range.LowPC, Range.HighPC)); + AllInlineRanges = ConvertDWARFRanges(RangesOrError.get()); + uint32_t EmptyCount = 0; + for (const AddressRange &InlineRange : AllInlineRanges) { + // Check for empty inline range in case inline function was outlined + // or has not code + if (InlineRange.empty()) { + ++EmptyCount; + } else { + if (Parent.Ranges.contains(InlineRange)) { + II.Ranges.insert(InlineRange); + } else { + // Only warn if the current inline range is not within any of all + // of the parent ranges. If we have a DW_TAG_subpgram with multiple + // ranges we will emit a FunctionInfo for each range of that + // function that only emits information within the current range, + // so we only want to emit an error if the DWARF has issues, not + // when a range currently just isn't in the range we are currently + // parsing for. + if (AllParentRanges.contains(InlineRange)) { + WarnIfEmpty = false; + } else if (Log) { + *Log << "error: inlined function DIE at " + << HEX32(Die.getOffset()) << " has a range [" + << HEX64(InlineRange.start()) << " - " + << HEX64(InlineRange.end()) << ") that isn't contained in " + << "any parent address ranges, this inline range will be " + "removed.\n"; + } + } + } } + // If we have all empty ranges for the inlines, then don't warn if we + // have an empty InlineInfo at the top level as all inline functions + // were elided. + if (EmptyCount == AllInlineRanges.size()) + WarnIfEmpty = false; } if (II.Ranges.empty()) return; if (auto NameIndex = getQualifiedNameIndex(Die, CUI.Language, Gsym)) II.Name = *NameIndex; - II.CallFile = CUI.DWARFToGSYMFileIndex( - Gsym, dwarf::toUnsigned(Die.find(dwarf::DW_AT_call_file), 0)); - II.CallLine = dwarf::toUnsigned(Die.find(dwarf::DW_AT_call_line), 0); - // parse all children and append to parent - for (DWARFDie ChildDie : Die.children()) - parseInlineInfo(Gsym, CUI, ChildDie, Depth + 1, FI, II); - parent.Children.emplace_back(std::move(II)); + const uint64_t DwarfFileIdx = dwarf::toUnsigned( + Die.findRecursively(dwarf::DW_AT_call_file), UINT32_MAX); + std::optional<uint32_t> OptGSymFileIdx = + CUI.DWARFToGSYMFileIndex(Gsym, DwarfFileIdx); + if (OptGSymFileIdx) { + II.CallFile = OptGSymFileIdx.value(); + II.CallLine = dwarf::toUnsigned(Die.find(dwarf::DW_AT_call_line), 0); + // parse all children and append to parent + for (DWARFDie ChildDie : Die.children()) + parseInlineInfo(Gsym, Log, CUI, ChildDie, Depth + 1, FI, II, + AllInlineRanges, WarnIfEmpty); + Parent.Children.emplace_back(std::move(II)); + } else if (Log) { + *Log << "error: inlined function DIE at " << HEX32(Die.getOffset()) + << " has an invalid file index " << DwarfFileIdx + << " in its DW_AT_call_file attribute, this inline entry and all " + << "children will be removed.\n"; + } return; } if (Tag == dwarf::DW_TAG_subprogram || Tag == dwarf::DW_TAG_lexical_block) { // skip this Die and just recurse down for (DWARFDie ChildDie : Die.children()) - parseInlineInfo(Gsym, CUI, ChildDie, Depth + 1, FI, parent); + parseInlineInfo(Gsym, Log, CUI, ChildDie, Depth + 1, FI, Parent, + AllParentRanges, WarnIfEmpty); } } -static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, +static void convertFunctionLineTable(raw_ostream *Log, CUInfo &CUI, DWARFDie Die, GsymCreator &Gsym, FunctionInfo &FI) { std::vector<uint32_t> RowVector; @@ -263,8 +316,20 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, // the DW_AT_decl_file an d DW_AT_decl_line if we have both attributes. std::string FilePath = Die.getDeclFile( DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); - if (FilePath.empty()) + if (FilePath.empty()) { + // If we had a DW_AT_decl_file, but got no file then we need to emit a + // warning. + if (Log) { + const uint64_t DwarfFileIdx = dwarf::toUnsigned( + Die.findRecursively(dwarf::DW_AT_decl_file), UINT32_MAX); + *Log << "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 " + << "line entry from the DW_AT_decl_file/DW_AT_decl_line " + << "attributes.\n"; + } return; + } if (auto Line = dwarf::toUnsigned(Die.findRecursively({dwarf::DW_AT_decl_line}))) { LineEntry LE(StartAddress, Gsym.insertFile(FilePath), *Line); @@ -279,7 +344,20 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, for (uint32_t RowIndex : RowVector) { // Take file number and line/column from the row. const DWARFDebugLine::Row &Row = CUI.LineTable->Rows[RowIndex]; - const uint32_t FileIdx = CUI.DWARFToGSYMFileIndex(Gsym, Row.File); + std::optional<uint32_t> OptFileIdx = + CUI.DWARFToGSYMFileIndex(Gsym, Row.File); + if (!OptFileIdx) { + if (Log) { + *Log << "error: function DIE at " << HEX32(Die.getOffset()) << " has " + << "a line entry with invalid DWARF file index, this entry will " + << "be removed:\n"; + Row.dumpTableHeader(*Log, /*Indent=*/0); + Row.dump(*Log); + *Log << "\n"; + } + continue; + } + const uint32_t FileIdx = OptFileIdx.value(); uint64_t RowAddress = Row.Address.Address; // Watch out for a RowAddress that is in the middle of a line table entry // in the DWARF. If we pass an address in between two line table entries @@ -289,10 +367,12 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, // an error, but not worth stopping the creation of the GSYM. if (!FI.Range.contains(RowAddress)) { if (RowAddress < FI.Range.start()) { - Log << "error: DIE has a start address whose LowPC is between the " - "line table Row[" << RowIndex << "] with address " - << HEX64(RowAddress) << " and the next one.\n"; - Die.dump(Log, 0, DIDumpOptions::getForSingleDIE()); + if (Log) { + *Log << "error: DIE has a start address whose LowPC is between the " + "line table Row[" << RowIndex << "] with address " + << HEX64(RowAddress) << " and the next one.\n"; + Die.dump(*Log, 0, DIDumpOptions::getForSingleDIE()); + } RowAddress = FI.Range.start(); } else { continue; @@ -302,25 +382,25 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, LineEntry LE(RowAddress, FileIdx, Row.Line); if (RowIndex != RowVector[0] && Row.Address < PrevRow.Address) { // We have seen full duplicate line tables for functions in some - // DWARF files. Watch for those here by checking the the last + // DWARF files. Watch for those here by checking the last // row was the function's end address (HighPC) and that the // current line table entry's address is the same as the first // line entry we already have in our "function_info.Lines". If // so break out after printing a warning. auto FirstLE = FI.OptLineTable->first(); if (FirstLE && *FirstLE == LE) { - if (!Gsym.isQuiet()) { - Log << "warning: duplicate line table detected for DIE:\n"; - Die.dump(Log, 0, DIDumpOptions::getForSingleDIE()); + if (Log && !Gsym.isQuiet()) { + *Log << "warning: duplicate line table detected for DIE:\n"; + Die.dump(*Log, 0, DIDumpOptions::getForSingleDIE()); } } else { - // Print out (ignore if os == nulls as this is expensive) - Log << "error: line table has addresses that do not " - << "monotonically increase:\n"; - for (uint32_t RowIndex2 : RowVector) { - CUI.LineTable->Rows[RowIndex2].dump(Log); + if (Log) { + *Log << "error: line table has addresses that do not " + << "monotonically increase:\n"; + for (uint32_t RowIndex2 : RowVector) + CUI.LineTable->Rows[RowIndex2].dump(*Log); + Die.dump(*Log, 0, DIDumpOptions::getForSingleDIE()); } - Die.dump(Log, 0, DIDumpOptions::getForSingleDIE()); } break; } @@ -349,7 +429,7 @@ static void convertFunctionLineTable(raw_ostream &Log, CUInfo &CUI, FI.OptLineTable = std::nullopt; } -void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { +void DwarfTransformer::handleDie(raw_ostream *OS, CUInfo &CUI, DWARFDie Die) { switch (Die.getTag()) { case dwarf::DW_TAG_subprogram: { Expected<DWARFAddressRangesVector> RangesOrError = Die.getAddressRanges(); @@ -362,11 +442,20 @@ void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { break; auto NameIndex = getQualifiedNameIndex(Die, CUI.Language, Gsym); if (!NameIndex) { - OS << "error: function at " << HEX64(Die.getOffset()) - << " has no name\n "; - Die.dump(OS, 0, DIDumpOptions::getForSingleDIE()); + if (OS) { + *OS << "error: function at " << HEX64(Die.getOffset()) + << " has no name\n "; + Die.dump(*OS, 0, DIDumpOptions::getForSingleDIE()); + } break; } + // All ranges for the subprogram DIE in case it has multiple. We need to + // pass this down into parseInlineInfo so we don't warn about inline + // ranges that are not in the current subrange of a function when they + // actually are in another subgrange. We do this because when a function + // has discontiguos ranges, we create multiple function entries with only + // the info for that range contained inside of it. + AddressRanges AllSubprogramRanges = ConvertDWARFRanges(Ranges); // Create a function_info for each range for (const DWARFAddressRange &Range : Ranges) { @@ -393,11 +482,13 @@ void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { if (Range.LowPC != 0) { if (!Gsym.isQuiet()) { // Unexpected invalid address, emit a warning - OS << "warning: DIE has an address range whose start address is " - "not in any executable sections (" - << *Gsym.GetValidTextRanges() - << ") and will not be processed:\n"; - Die.dump(OS, 0, DIDumpOptions::getForSingleDIE()); + if (OS) { + *OS << "warning: DIE has an address range whose start address " + "is not in any executable sections (" + << *Gsym.GetValidTextRanges() + << ") and will not be processed:\n"; + Die.dump(*OS, 0, DIDumpOptions::getForSingleDIE()); + } } } break; @@ -406,14 +497,33 @@ void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { FunctionInfo FI; FI.Range = {Range.LowPC, Range.HighPC}; FI.Name = *NameIndex; - if (CUI.LineTable) { + if (CUI.LineTable) convertFunctionLineTable(OS, CUI, Die, Gsym, FI); - } + if (hasInlineInfo(Die, 0)) { FI.Inline = InlineInfo(); FI.Inline->Name = *NameIndex; FI.Inline->Ranges.insert(FI.Range); - parseInlineInfo(Gsym, CUI, Die, 0, FI, *FI.Inline); + bool WarnIfEmpty = true; + parseInlineInfo(Gsym, OS, CUI, Die, 0, FI, *FI.Inline, + AllSubprogramRanges, WarnIfEmpty); + // Make sure we at least got some valid inline info other than just + // the top level function. If we didn't then remove the inline info + // from the function info. We have seen cases where LTO tries to modify + // the DWARF for functions and it messes up the address ranges for + // the inline functions so it is no longer valid. + // + // By checking if there are any valid children on the top level inline + // information object, we will know if we got anything valid from the + // debug info. + if (FI.Inline->Children.empty()) { + if (WarnIfEmpty && OS && !Gsym.isQuiet()) { + *OS << "warning: DIE contains inline function information that has " + "no valid ranges, removing inline information:\n"; + Die.dump(*OS, 0, DIDumpOptions::getForSingleDIE()); + } + FI.Inline = std::nullopt; + } } Gsym.addFunctionInfo(std::move(FI)); } @@ -425,18 +535,18 @@ void DwarfTransformer::handleDie(raw_ostream &OS, CUInfo &CUI, DWARFDie Die) { handleDie(OS, CUI, ChildDie); } -Error DwarfTransformer::convert(uint32_t NumThreads) { +Error DwarfTransformer::convert(uint32_t NumThreads, raw_ostream *OS) { size_t NumBefore = Gsym.getNumFunctionInfos(); auto getDie = [&](DWARFUnit &DwarfUnit) -> DWARFDie { DWARFDie ReturnDie = DwarfUnit.getUnitDIE(false); - if (std::optional<uint64_t> DWOId = DwarfUnit.getDWOId()) { + if (DwarfUnit.getDWOId()) { DWARFUnit *DWOCU = DwarfUnit.getNonSkeletonUnitDIE(false).getDwarfUnit(); - if (!DWOCU->isDWOUnit()) { + if (OS && !DWOCU->isDWOUnit()) { std::string DWOName = dwarf::toString( DwarfUnit.getUnitDIE().find( {dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); - Log << "warning: Unable to retrieve DWO .debug_info section for " + *OS << "warning: Unable to retrieve DWO .debug_info section for " << DWOName << "\n"; } else { ReturnDie = DWOCU->getUnitDIE(false); @@ -450,7 +560,7 @@ Error DwarfTransformer::convert(uint32_t NumThreads) { for (const auto &CU : DICtx.compile_units()) { DWARFDie Die = getDie(*CU); CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get())); - handleDie(Log, CUI, Die); + handleDie(OS, CUI, Die); } } else { // LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up @@ -476,15 +586,15 @@ Error DwarfTransformer::convert(uint32_t NumThreads) { DWARFDie Die = getDie(*CU); if (Die) { CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get())); - pool.async([this, CUI, &LogMutex, Die]() mutable { + pool.async([this, CUI, &LogMutex, OS, Die]() mutable { std::string ThreadLogStorage; raw_string_ostream ThreadOS(ThreadLogStorage); - handleDie(ThreadOS, CUI, Die); + handleDie(OS ? &ThreadOS: nullptr, CUI, Die); ThreadOS.flush(); - if (!ThreadLogStorage.empty()) { + if (OS && !ThreadLogStorage.empty()) { // Print ThreadLogStorage lines into an actual stream under a lock std::lock_guard<std::mutex> guard(LogMutex); - Log << ThreadLogStorage; + *OS << ThreadLogStorage; } }); } @@ -492,11 +602,12 @@ Error DwarfTransformer::convert(uint32_t NumThreads) { pool.wait(); } size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore; - Log << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n"; + if (OS) + *OS << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n"; return Error::success(); } -llvm::Error DwarfTransformer::verify(StringRef GsymPath) { +llvm::Error DwarfTransformer::verify(StringRef GsymPath, raw_ostream &Log) { Log << "Verifying GSYM file \"" << GsymPath << "\":\n"; auto Gsym = GsymReader::openFile(GsymPath); |
