Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp
35294 views
//===- DwarfTransformer.cpp -----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include <thread>9#include <unordered_set>1011#include "llvm/DebugInfo/DIContext.h"12#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"13#include "llvm/DebugInfo/DWARF/DWARFContext.h"14#include "llvm/Support/Error.h"15#include "llvm/Support/ThreadPool.h"16#include "llvm/Support/raw_ostream.h"1718#include "llvm/DebugInfo/GSYM/DwarfTransformer.h"19#include "llvm/DebugInfo/GSYM/FunctionInfo.h"20#include "llvm/DebugInfo/GSYM/GsymCreator.h"21#include "llvm/DebugInfo/GSYM/GsymReader.h"22#include "llvm/DebugInfo/GSYM/InlineInfo.h"23#include "llvm/DebugInfo/GSYM/OutputAggregator.h"2425#include <optional>2627using namespace llvm;28using namespace gsym;2930struct llvm::gsym::CUInfo {31const DWARFDebugLine::LineTable *LineTable;32const char *CompDir;33std::vector<uint32_t> FileCache;34uint64_t Language = 0;35uint8_t AddrSize = 0;3637CUInfo(DWARFContext &DICtx, DWARFCompileUnit *CU) {38LineTable = DICtx.getLineTableForUnit(CU);39CompDir = CU->getCompilationDir();40FileCache.clear();41if (LineTable)42FileCache.assign(LineTable->Prologue.FileNames.size() + 1, UINT32_MAX);43DWARFDie Die = CU->getUnitDIE();44Language = dwarf::toUnsigned(Die.find(dwarf::DW_AT_language), 0);45AddrSize = CU->getAddressByteSize();46}4748/// Return true if Addr is the highest address for a given compile unit. The49/// highest address is encoded as -1, of all ones in the address. These high50/// addresses are used by some linkers to indicate that a function has been51/// dead stripped or didn't end up in the linked executable.52bool isHighestAddress(uint64_t Addr) const {53if (AddrSize == 4)54return Addr == UINT32_MAX;55else if (AddrSize == 8)56return Addr == UINT64_MAX;57return false;58}5960/// Convert a DWARF compile unit file index into a GSYM global file index.61///62/// Each compile unit in DWARF has its own file table in the line table63/// prologue. GSYM has a single large file table that applies to all files64/// from all of the info in a GSYM file. This function converts between the65/// two and caches and DWARF CU file index that has already been converted so66/// the first client that asks for a compile unit file index will end up67/// doing the conversion, and subsequent clients will get the cached GSYM68/// index.69std::optional<uint32_t> DWARFToGSYMFileIndex(GsymCreator &Gsym,70uint32_t DwarfFileIdx) {71if (!LineTable || DwarfFileIdx >= FileCache.size())72return std::nullopt;73uint32_t &GsymFileIdx = FileCache[DwarfFileIdx];74if (GsymFileIdx != UINT32_MAX)75return GsymFileIdx;76std::string File;77if (LineTable->getFileNameByIndex(78DwarfFileIdx, CompDir,79DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))80GsymFileIdx = Gsym.insertFile(File);81else82GsymFileIdx = 0;83return GsymFileIdx;84}85};868788static DWARFDie GetParentDeclContextDIE(DWARFDie &Die) {89if (DWARFDie SpecDie =90Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_specification)) {91if (DWARFDie SpecParent = GetParentDeclContextDIE(SpecDie))92return SpecParent;93}94if (DWARFDie AbstDie =95Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) {96if (DWARFDie AbstParent = GetParentDeclContextDIE(AbstDie))97return AbstParent;98}99100// We never want to follow parent for inlined subroutine - that would101// give us information about where the function is inlined, not what102// function is inlined103if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine)104return DWARFDie();105106DWARFDie ParentDie = Die.getParent();107if (!ParentDie)108return DWARFDie();109110switch (ParentDie.getTag()) {111case dwarf::DW_TAG_namespace:112case dwarf::DW_TAG_structure_type:113case dwarf::DW_TAG_union_type:114case dwarf::DW_TAG_class_type:115case dwarf::DW_TAG_subprogram:116return ParentDie; // Found parent decl context DIE117case dwarf::DW_TAG_lexical_block:118return GetParentDeclContextDIE(ParentDie);119default:120break;121}122123return DWARFDie();124}125126/// Get the GsymCreator string table offset for the qualified name for the127/// DIE passed in. This function will avoid making copies of any strings in128/// the GsymCreator when possible. We don't need to copy a string when the129/// string comes from our .debug_str section or is an inlined string in the130/// .debug_info. If we create a qualified name string in this function by131/// combining multiple strings in the DWARF string table or info, we will make132/// a copy of the string when we add it to the string table.133static std::optional<uint32_t>134getQualifiedNameIndex(DWARFDie &Die, uint64_t Language, GsymCreator &Gsym) {135// If the dwarf has mangled name, use mangled name136if (auto LinkageName = Die.getLinkageName()) {137// We have seen cases were linkage name is actually empty.138if (strlen(LinkageName) > 0)139return Gsym.insertString(LinkageName, /* Copy */ false);140}141142StringRef ShortName(Die.getName(DINameKind::ShortName));143if (ShortName.empty())144return std::nullopt;145146// For C++ and ObjC, prepend names of all parent declaration contexts147if (!(Language == dwarf::DW_LANG_C_plus_plus ||148Language == dwarf::DW_LANG_C_plus_plus_03 ||149Language == dwarf::DW_LANG_C_plus_plus_11 ||150Language == dwarf::DW_LANG_C_plus_plus_14 ||151Language == dwarf::DW_LANG_ObjC_plus_plus ||152// This should not be needed for C, but we see C++ code marked as C153// in some binaries. This should hurt, so let's do it for C as well154Language == dwarf::DW_LANG_C))155return Gsym.insertString(ShortName, /* Copy */ false);156157// Some GCC optimizations create functions with names ending with .isra.<num>158// or .part.<num> and those names are just DW_AT_name, not DW_AT_linkage_name159// If it looks like it could be the case, don't add any prefix160if (ShortName.starts_with("_Z") &&161(ShortName.contains(".isra.") || ShortName.contains(".part.")))162return Gsym.insertString(ShortName, /* Copy */ false);163164DWARFDie ParentDeclCtxDie = GetParentDeclContextDIE(Die);165if (ParentDeclCtxDie) {166std::string Name = ShortName.str();167while (ParentDeclCtxDie) {168StringRef ParentName(ParentDeclCtxDie.getName(DINameKind::ShortName));169if (!ParentName.empty()) {170// "lambda" names are wrapped in < >. Replace with { }171// to be consistent with demangled names and not to confuse with172// templates173if (ParentName.front() == '<' && ParentName.back() == '>')174Name = "{" + ParentName.substr(1, ParentName.size() - 2).str() + "}" +175"::" + Name;176else177Name = ParentName.str() + "::" + Name;178}179ParentDeclCtxDie = GetParentDeclContextDIE(ParentDeclCtxDie);180}181// Copy the name since we created a new name in a std::string.182return Gsym.insertString(Name, /* Copy */ true);183}184// Don't copy the name since it exists in the DWARF object file.185return Gsym.insertString(ShortName, /* Copy */ false);186}187188static bool hasInlineInfo(DWARFDie Die, uint32_t Depth) {189bool CheckChildren = true;190switch (Die.getTag()) {191case dwarf::DW_TAG_subprogram:192// Don't look into functions within functions.193CheckChildren = Depth == 0;194break;195case dwarf::DW_TAG_inlined_subroutine:196return true;197default:198break;199}200if (!CheckChildren)201return false;202for (DWARFDie ChildDie : Die.children()) {203if (hasInlineInfo(ChildDie, Depth + 1))204return true;205}206return false;207}208209static AddressRanges210ConvertDWARFRanges(const DWARFAddressRangesVector &DwarfRanges) {211AddressRanges Ranges;212for (const DWARFAddressRange &DwarfRange : DwarfRanges) {213if (DwarfRange.LowPC < DwarfRange.HighPC)214Ranges.insert({DwarfRange.LowPC, DwarfRange.HighPC});215}216return Ranges;217}218219static void parseInlineInfo(GsymCreator &Gsym, OutputAggregator &Out,220CUInfo &CUI, DWARFDie Die, uint32_t Depth,221FunctionInfo &FI, InlineInfo &Parent,222const AddressRanges &AllParentRanges,223bool &WarnIfEmpty) {224if (!hasInlineInfo(Die, Depth))225return;226227dwarf::Tag Tag = Die.getTag();228if (Tag == dwarf::DW_TAG_inlined_subroutine) {229// create new InlineInfo and append to parent.children230InlineInfo II;231AddressRanges AllInlineRanges;232Expected<DWARFAddressRangesVector> RangesOrError = Die.getAddressRanges();233if (RangesOrError) {234AllInlineRanges = ConvertDWARFRanges(RangesOrError.get());235uint32_t EmptyCount = 0;236for (const AddressRange &InlineRange : AllInlineRanges) {237// Check for empty inline range in case inline function was outlined238// or has not code239if (InlineRange.empty()) {240++EmptyCount;241} else {242if (Parent.Ranges.contains(InlineRange)) {243II.Ranges.insert(InlineRange);244} else {245// Only warn if the current inline range is not within any of all246// of the parent ranges. If we have a DW_TAG_subpgram with multiple247// ranges we will emit a FunctionInfo for each range of that248// function that only emits information within the current range,249// so we only want to emit an error if the DWARF has issues, not250// when a range currently just isn't in the range we are currently251// parsing for.252if (AllParentRanges.contains(InlineRange)) {253WarnIfEmpty = false;254} else255Out.Report("Function DIE has uncontained address range",256[&](raw_ostream &OS) {257OS << "error: inlined function DIE at "258<< HEX32(Die.getOffset()) << " has a range ["259<< HEX64(InlineRange.start()) << " - "260<< HEX64(InlineRange.end())261<< ") that isn't contained in "262<< "any parent address ranges, this inline range "263"will be "264"removed.\n";265});266}267}268}269// If we have all empty ranges for the inlines, then don't warn if we270// have an empty InlineInfo at the top level as all inline functions271// were elided.272if (EmptyCount == AllInlineRanges.size())273WarnIfEmpty = false;274}275if (II.Ranges.empty())276return;277278if (auto NameIndex = getQualifiedNameIndex(Die, CUI.Language, Gsym))279II.Name = *NameIndex;280const uint64_t DwarfFileIdx = dwarf::toUnsigned(281Die.findRecursively(dwarf::DW_AT_call_file), UINT32_MAX);282std::optional<uint32_t> OptGSymFileIdx =283CUI.DWARFToGSYMFileIndex(Gsym, DwarfFileIdx);284if (OptGSymFileIdx) {285II.CallFile = OptGSymFileIdx.value();286II.CallLine = dwarf::toUnsigned(Die.find(dwarf::DW_AT_call_line), 0);287// parse all children and append to parent288for (DWARFDie ChildDie : Die.children())289parseInlineInfo(Gsym, Out, CUI, ChildDie, Depth + 1, FI, II,290AllInlineRanges, WarnIfEmpty);291Parent.Children.emplace_back(std::move(II));292} else293Out.Report(294"Inlined function die has invlaid file index in DW_AT_call_file",295[&](raw_ostream &OS) {296OS << "error: inlined function DIE at " << HEX32(Die.getOffset())297<< " has an invalid file index " << DwarfFileIdx298<< " in its DW_AT_call_file attribute, this inline entry and "299"all "300<< "children will be removed.\n";301});302return;303}304if (Tag == dwarf::DW_TAG_subprogram || Tag == dwarf::DW_TAG_lexical_block) {305// skip this Die and just recurse down306for (DWARFDie ChildDie : Die.children())307parseInlineInfo(Gsym, Out, CUI, ChildDie, Depth + 1, FI, Parent,308AllParentRanges, WarnIfEmpty);309}310}311312static void convertFunctionLineTable(OutputAggregator &Out, CUInfo &CUI,313DWARFDie Die, GsymCreator &Gsym,314FunctionInfo &FI) {315std::vector<uint32_t> RowVector;316const uint64_t StartAddress = FI.startAddress();317const uint64_t EndAddress = FI.endAddress();318const uint64_t RangeSize = EndAddress - StartAddress;319const object::SectionedAddress SecAddress{320StartAddress, object::SectionedAddress::UndefSection};321322323if (!CUI.LineTable->lookupAddressRange(SecAddress, RangeSize, RowVector)) {324// If we have a DW_TAG_subprogram but no line entries, fall back to using325// the DW_AT_decl_file an d DW_AT_decl_line if we have both attributes.326std::string FilePath = Die.getDeclFile(327DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);328if (FilePath.empty()) {329// If we had a DW_AT_decl_file, but got no file then we need to emit a330// warning.331Out.Report("Invalid file index in DW_AT_decl_file", [&](raw_ostream &OS) {332const uint64_t DwarfFileIdx = dwarf::toUnsigned(333Die.findRecursively(dwarf::DW_AT_decl_file), UINT32_MAX);334OS << "error: function DIE at " << HEX32(Die.getOffset())335<< " has an invalid file index " << DwarfFileIdx336<< " in its DW_AT_decl_file attribute, unable to create a single "337<< "line entry from the DW_AT_decl_file/DW_AT_decl_line "338<< "attributes.\n";339});340return;341}342if (auto Line =343dwarf::toUnsigned(Die.findRecursively({dwarf::DW_AT_decl_line}))) {344LineEntry LE(StartAddress, Gsym.insertFile(FilePath), *Line);345FI.OptLineTable = LineTable();346FI.OptLineTable->push(LE);347}348return;349}350351FI.OptLineTable = LineTable();352DWARFDebugLine::Row PrevRow;353for (uint32_t RowIndex : RowVector) {354// Take file number and line/column from the row.355const DWARFDebugLine::Row &Row = CUI.LineTable->Rows[RowIndex];356std::optional<uint32_t> OptFileIdx =357CUI.DWARFToGSYMFileIndex(Gsym, Row.File);358if (!OptFileIdx) {359Out.Report(360"Invalid file index in DWARF line table", [&](raw_ostream &OS) {361OS << "error: function DIE at " << HEX32(Die.getOffset()) << " has "362<< "a line entry with invalid DWARF file index, this entry will "363<< "be removed:\n";364Row.dumpTableHeader(OS, /*Indent=*/0);365Row.dump(OS);366OS << "\n";367});368continue;369}370const uint32_t FileIdx = OptFileIdx.value();371uint64_t RowAddress = Row.Address.Address;372// Watch out for a RowAddress that is in the middle of a line table entry373// in the DWARF. If we pass an address in between two line table entries374// we will get a RowIndex for the previous valid line table row which won't375// be contained in our function. This is usually a bug in the DWARF due to376// linker problems or LTO or other DWARF re-linking so it is worth emitting377// an error, but not worth stopping the creation of the GSYM.378if (!FI.Range.contains(RowAddress)) {379if (RowAddress < FI.Range.start()) {380Out.Report("Start address lies between valid Row table entries",381[&](raw_ostream &OS) {382OS << "error: DIE has a start address whose LowPC is "383"between the "384"line table Row["385<< RowIndex << "] with address " << HEX64(RowAddress)386<< " and the next one.\n";387Die.dump(OS, 0, DIDumpOptions::getForSingleDIE());388});389RowAddress = FI.Range.start();390} else {391continue;392}393}394395LineEntry LE(RowAddress, FileIdx, Row.Line);396if (RowIndex != RowVector[0] && Row.Address < PrevRow.Address) {397// We have seen full duplicate line tables for functions in some398// DWARF files. Watch for those here by checking the last399// row was the function's end address (HighPC) and that the400// current line table entry's address is the same as the first401// line entry we already have in our "function_info.Lines". If402// so break out after printing a warning.403auto FirstLE = FI.OptLineTable->first();404if (FirstLE && *FirstLE == LE)405// if (Log && !Gsym.isQuiet()) { TODO <-- This looks weird406Out.Report("Duplicate line table detected", [&](raw_ostream &OS) {407OS << "warning: duplicate line table detected for DIE:\n";408Die.dump(OS, 0, DIDumpOptions::getForSingleDIE());409});410else411Out.Report("Non-monotonically increasing addresses",412[&](raw_ostream &OS) {413OS << "error: line table has addresses that do not "414<< "monotonically increase:\n";415for (uint32_t RowIndex2 : RowVector)416CUI.LineTable->Rows[RowIndex2].dump(OS);417Die.dump(OS, 0, DIDumpOptions::getForSingleDIE());418});419break;420}421422// Skip multiple line entries for the same file and line.423auto LastLE = FI.OptLineTable->last();424if (LastLE && LastLE->File == FileIdx && LastLE->Line == Row.Line)425continue;426// Only push a row if it isn't an end sequence. End sequence markers are427// included for the last address in a function or the last contiguous428// address in a sequence.429if (Row.EndSequence) {430// End sequence means that the next line entry could have a lower address431// that the previous entries. So we clear the previous row so we don't432// trigger the line table error about address that do not monotonically433// increase.434PrevRow = DWARFDebugLine::Row();435} else {436FI.OptLineTable->push(LE);437PrevRow = Row;438}439}440// If not line table rows were added, clear the line table so we don't encode441// on in the GSYM file.442if (FI.OptLineTable->empty())443FI.OptLineTable = std::nullopt;444}445446void DwarfTransformer::handleDie(OutputAggregator &Out, CUInfo &CUI,447DWARFDie Die) {448switch (Die.getTag()) {449case dwarf::DW_TAG_subprogram: {450Expected<DWARFAddressRangesVector> RangesOrError = Die.getAddressRanges();451if (!RangesOrError) {452consumeError(RangesOrError.takeError());453break;454}455const DWARFAddressRangesVector &Ranges = RangesOrError.get();456if (Ranges.empty())457break;458auto NameIndex = getQualifiedNameIndex(Die, CUI.Language, Gsym);459if (!NameIndex) {460Out.Report("Function has no name", [&](raw_ostream &OS) {461OS << "error: function at " << HEX64(Die.getOffset())462<< " has no name\n ";463Die.dump(OS, 0, DIDumpOptions::getForSingleDIE());464});465break;466}467// All ranges for the subprogram DIE in case it has multiple. We need to468// pass this down into parseInlineInfo so we don't warn about inline469// ranges that are not in the current subrange of a function when they470// actually are in another subgrange. We do this because when a function471// has discontiguos ranges, we create multiple function entries with only472// the info for that range contained inside of it.473AddressRanges AllSubprogramRanges = ConvertDWARFRanges(Ranges);474475// Create a function_info for each range476for (const DWARFAddressRange &Range : Ranges) {477// The low PC must be less than the high PC. Many linkers don't remove478// DWARF for functions that don't get linked into the final executable.479// If both the high and low pc have relocations, linkers will often set480// the address values for both to the same value to indicate the function481// has been remove. Other linkers have been known to set the one or both482// PC values to a UINT32_MAX for 4 byte addresses and UINT64_MAX for 8483// byte addresses to indicate the function isn't valid. The check below484// tries to watch for these cases and abort if it runs into them.485if (Range.LowPC >= Range.HighPC || CUI.isHighestAddress(Range.LowPC))486break;487488// Many linkers can't remove DWARF and might set the LowPC to zero. Since489// high PC can be an offset from the low PC in more recent DWARF versions490// we need to watch for a zero'ed low pc which we do using ValidTextRanges491// below.492if (!Gsym.IsValidTextAddress(Range.LowPC)) {493// We expect zero and -1 to be invalid addresses in DWARF depending494// on the linker of the DWARF. This indicates a function was stripped495// and the debug info wasn't able to be stripped from the DWARF. If496// the LowPC isn't zero or -1, then we should emit an error.497if (Range.LowPC != 0) {498if (!Gsym.isQuiet()) {499// Unexpected invalid address, emit a warning500Out.Report("Address range starts outside executable section",501[&](raw_ostream &OS) {502OS << "warning: DIE has an address range whose "503"start address "504"is not in any executable sections ("505<< *Gsym.GetValidTextRanges()506<< ") and will not be processed:\n";507Die.dump(OS, 0, DIDumpOptions::getForSingleDIE());508});509}510}511break;512}513514FunctionInfo FI;515FI.Range = {Range.LowPC, Range.HighPC};516FI.Name = *NameIndex;517if (CUI.LineTable)518convertFunctionLineTable(Out, CUI, Die, Gsym, FI);519520if (hasInlineInfo(Die, 0)) {521FI.Inline = InlineInfo();522FI.Inline->Name = *NameIndex;523FI.Inline->Ranges.insert(FI.Range);524bool WarnIfEmpty = true;525parseInlineInfo(Gsym, Out, CUI, Die, 0, FI, *FI.Inline,526AllSubprogramRanges, WarnIfEmpty);527// Make sure we at least got some valid inline info other than just528// the top level function. If we didn't then remove the inline info529// from the function info. We have seen cases where LTO tries to modify530// the DWARF for functions and it messes up the address ranges for531// the inline functions so it is no longer valid.532//533// By checking if there are any valid children on the top level inline534// information object, we will know if we got anything valid from the535// debug info.536if (FI.Inline->Children.empty()) {537if (WarnIfEmpty && !Gsym.isQuiet())538Out.Report("DIE contains inline functions with no valid ranges",539[&](raw_ostream &OS) {540OS << "warning: DIE contains inline function "541"information that has no valid ranges, removing "542"inline information:\n";543Die.dump(OS, 0, DIDumpOptions::getForSingleDIE());544});545FI.Inline = std::nullopt;546}547}548Gsym.addFunctionInfo(std::move(FI));549}550} break;551default:552break;553}554for (DWARFDie ChildDie : Die.children())555handleDie(Out, CUI, ChildDie);556}557558Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {559size_t NumBefore = Gsym.getNumFunctionInfos();560auto getDie = [&](DWARFUnit &DwarfUnit) -> DWARFDie {561DWARFDie ReturnDie = DwarfUnit.getUnitDIE(false);562if (DwarfUnit.getDWOId()) {563DWARFUnit *DWOCU = DwarfUnit.getNonSkeletonUnitDIE(false).getDwarfUnit();564if (!DWOCU->isDWOUnit())565Out.Report(566"warning: Unable to retrieve DWO .debug_info section for some "567"object files. (Remove the --quiet flag for full output)",568[&](raw_ostream &OS) {569std::string DWOName = dwarf::toString(570DwarfUnit.getUnitDIE().find(571{dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}),572"");573OS << "warning: Unable to retrieve DWO .debug_info section for "574<< DWOName << "\n";575});576else {577ReturnDie = DWOCU->getUnitDIE(false);578}579}580return ReturnDie;581};582if (NumThreads == 1) {583// Parse all DWARF data from this thread, use the same string/file table584// for everything585for (const auto &CU : DICtx.compile_units()) {586DWARFDie Die = getDie(*CU);587CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));588handleDie(Out, CUI, Die);589}590} else {591// LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up592// front before we start accessing any DIEs since there might be593// cross compile unit references in the DWARF. If we don't do this we can594// end up crashing.595596// We need to call getAbbreviations sequentially first so that getUnitDIE()597// only works with its local data.598for (const auto &CU : DICtx.compile_units())599CU->getAbbreviations();600601// Now parse all DIEs in case we have cross compile unit references in a602// thread pool.603DefaultThreadPool pool(hardware_concurrency(NumThreads));604for (const auto &CU : DICtx.compile_units())605pool.async([&CU]() { CU->getUnitDIE(false /*CUDieOnly*/); });606pool.wait();607608// Now convert all DWARF to GSYM in a thread pool.609std::mutex LogMutex;610for (const auto &CU : DICtx.compile_units()) {611DWARFDie Die = getDie(*CU);612if (Die) {613CUInfo CUI(DICtx, dyn_cast<DWARFCompileUnit>(CU.get()));614pool.async([this, CUI, &LogMutex, &Out, Die]() mutable {615std::string storage;616raw_string_ostream StrStream(storage);617OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr);618handleDie(ThreadOut, CUI, Die);619// Print ThreadLogStorage lines into an actual stream under a lock620std::lock_guard<std::mutex> guard(LogMutex);621if (Out.GetOS()) {622StrStream.flush();623Out << storage;624}625Out.Merge(ThreadOut);626});627}628}629pool.wait();630}631size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore;632Out << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n";633return Error::success();634}635636llvm::Error DwarfTransformer::verify(StringRef GsymPath,637OutputAggregator &Out) {638Out << "Verifying GSYM file \"" << GsymPath << "\":\n";639640auto Gsym = GsymReader::openFile(GsymPath);641if (!Gsym)642return Gsym.takeError();643644auto NumAddrs = Gsym->getNumAddresses();645DILineInfoSpecifier DLIS(646DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,647DILineInfoSpecifier::FunctionNameKind::LinkageName);648std::string gsymFilename;649for (uint32_t I = 0; I < NumAddrs; ++I) {650auto FuncAddr = Gsym->getAddress(I);651if (!FuncAddr)652return createStringError(std::errc::invalid_argument,653"failed to extract address[%i]", I);654655auto FI = Gsym->getFunctionInfo(*FuncAddr);656if (!FI)657return createStringError(658std::errc::invalid_argument,659"failed to extract function info for address 0x%" PRIu64, *FuncAddr);660661for (auto Addr = *FuncAddr; Addr < *FuncAddr + FI->size(); ++Addr) {662const object::SectionedAddress SectAddr{663Addr, object::SectionedAddress::UndefSection};664auto LR = Gsym->lookup(Addr);665if (!LR)666return LR.takeError();667668auto DwarfInlineInfos =669DICtx.getInliningInfoForAddress(SectAddr, DLIS);670uint32_t NumDwarfInlineInfos = DwarfInlineInfos.getNumberOfFrames();671if (NumDwarfInlineInfos == 0) {672DwarfInlineInfos.addFrame(673DICtx.getLineInfoForAddress(SectAddr, DLIS));674}675676// Check for 1 entry that has no file and line info677if (NumDwarfInlineInfos == 1 &&678DwarfInlineInfos.getFrame(0).FileName == "<invalid>") {679DwarfInlineInfos = DIInliningInfo();680NumDwarfInlineInfos = 0;681}682if (NumDwarfInlineInfos > 0 &&683NumDwarfInlineInfos != LR->Locations.size()) {684if (Out.GetOS()) {685raw_ostream &Log = *Out.GetOS();686Log << "error: address " << HEX64(Addr) << " has "687<< NumDwarfInlineInfos << " DWARF inline frames and GSYM has "688<< LR->Locations.size() << "\n";689Log << " " << NumDwarfInlineInfos << " DWARF frames:\n";690for (size_t Idx = 0; Idx < NumDwarfInlineInfos; ++Idx) {691const auto &dii = DwarfInlineInfos.getFrame(Idx);692Log << " [" << Idx << "]: " << dii.FunctionName << " @ "693<< dii.FileName << ':' << dii.Line << '\n';694}695Log << " " << LR->Locations.size() << " GSYM frames:\n";696for (size_t Idx = 0, count = LR->Locations.size(); Idx < count;697++Idx) {698const auto &gii = LR->Locations[Idx];699Log << " [" << Idx << "]: " << gii.Name << " @ " << gii.Dir700<< '/' << gii.Base << ':' << gii.Line << '\n';701}702DwarfInlineInfos = DICtx.getInliningInfoForAddress(SectAddr, DLIS);703Gsym->dump(Log, *FI);704}705continue;706}707708for (size_t Idx = 0, count = LR->Locations.size(); Idx < count;709++Idx) {710const auto &gii = LR->Locations[Idx];711if (Idx < NumDwarfInlineInfos) {712const auto &dii = DwarfInlineInfos.getFrame(Idx);713gsymFilename = LR->getSourceFile(Idx);714// Verify function name715if (dii.FunctionName.find(gii.Name.str()) != 0)716Out << "error: address " << HEX64(Addr) << " DWARF function \""717<< dii.FunctionName.c_str()718<< "\" doesn't match GSYM function \"" << gii.Name << "\"\n";719720// Verify source file path721if (dii.FileName != gsymFilename)722Out << "error: address " << HEX64(Addr) << " DWARF path \""723<< dii.FileName.c_str() << "\" doesn't match GSYM path \""724<< gsymFilename.c_str() << "\"\n";725// Verify source file line726if (dii.Line != gii.Line)727Out << "error: address " << HEX64(Addr) << " DWARF line "728<< dii.Line << " != GSYM line " << gii.Line << "\n";729}730}731}732}733return Error::success();734}735736737