summaryrefslogtreecommitdiff
path: root/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp')
-rw-r--r--llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp247
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);