diff --git a/llvm/include/llvm/DebugInfo/GSYM/DwarfTransformer.h b/llvm/include/llvm/DebugInfo/GSYM/DwarfTransformer.h index fd2433a677b8..3abf3bacabdd 100644 --- a/llvm/include/llvm/DebugInfo/GSYM/DwarfTransformer.h +++ b/llvm/include/llvm/DebugInfo/GSYM/DwarfTransformer.h @@ -22,6 +22,7 @@ namespace gsym { struct CUInfo; struct FunctionInfo; class GsymCreator; +class OutputAggregator; /// A class that transforms the DWARF in a DWARFContext into GSYM information /// by populating the GsymCreator object that it is constructed with. This @@ -52,9 +53,9 @@ public: /// /// \returns An error indicating any fatal issues that happen when parsing /// the DWARF, or Error::success() if all goes well. - llvm::Error convert(uint32_t NumThreads, raw_ostream *OS); + llvm::Error convert(uint32_t NumThreads, OutputAggregator &OS); - llvm::Error verify(StringRef GsymPath, raw_ostream &OS); + llvm::Error verify(StringRef GsymPath, OutputAggregator &OS); private: @@ -79,7 +80,7 @@ private: /// information. /// /// \param Die The DWARF debug info entry to parse. - void handleDie(raw_ostream *Strm, CUInfo &CUI, DWARFDie Die); + void handleDie(OutputAggregator &Strm, CUInfo &CUI, DWARFDie Die); DWARFContext &DICtx; GsymCreator &Gsym; diff --git a/llvm/include/llvm/DebugInfo/GSYM/GsymCreator.h b/llvm/include/llvm/DebugInfo/GSYM/GsymCreator.h index bb52f3f2cd56..855a275725c4 100644 --- a/llvm/include/llvm/DebugInfo/GSYM/GsymCreator.h +++ b/llvm/include/llvm/DebugInfo/GSYM/GsymCreator.h @@ -28,6 +28,7 @@ namespace llvm { namespace gsym { class FileWriter; +class OutputAggregator; /// GsymCreator is used to emit GSYM data to a stand alone file or section /// within a file. @@ -360,7 +361,7 @@ public: /// function infos, and function infos that were merged or removed. /// \returns An error object that indicates success or failure of the /// finalize. - llvm::Error finalize(llvm::raw_ostream &OS); + llvm::Error finalize(OutputAggregator &OS); /// Set the UUID value. /// diff --git a/llvm/include/llvm/DebugInfo/GSYM/ObjectFileTransformer.h b/llvm/include/llvm/DebugInfo/GSYM/ObjectFileTransformer.h index 2018e0962ca7..fe44e82e0dd2 100644 --- a/llvm/include/llvm/DebugInfo/GSYM/ObjectFileTransformer.h +++ b/llvm/include/llvm/DebugInfo/GSYM/ObjectFileTransformer.h @@ -13,8 +13,6 @@ namespace llvm { -class raw_ostream; - namespace object { class ObjectFile; } @@ -22,6 +20,7 @@ class ObjectFile; namespace gsym { class GsymCreator; +class OutputAggregator; class ObjectFileTransformer { public: @@ -40,8 +39,8 @@ public: /// /// \returns An error indicating any fatal issues that happen when parsing /// the DWARF, or Error::success() if all goes well. - static llvm::Error convert(const object::ObjectFile &Obj, raw_ostream *Log, - GsymCreator &Gsym); + static llvm::Error convert(const object::ObjectFile &Obj, + OutputAggregator &Output, GsymCreator &Gsym); }; } // namespace gsym diff --git a/llvm/include/llvm/DebugInfo/GSYM/OutputAggregator.h b/llvm/include/llvm/DebugInfo/GSYM/OutputAggregator.h new file mode 100644 index 000000000000..8deea3bff1ed --- /dev/null +++ b/llvm/include/llvm/DebugInfo/GSYM/OutputAggregator.h @@ -0,0 +1,75 @@ +//===- DwarfTransformer.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_GSYM_OUTPUTAGGREGATOR_H +#define LLVM_DEBUGINFO_GSYM_OUTPUTAGGREGATOR_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/GSYM/ExtractRanges.h" + +#include +#include + +namespace llvm { + +class raw_ostream; + +namespace gsym { + +class OutputAggregator { +protected: + // A std::map is preferable over an llvm::StringMap for presenting results + // in a predictable order. + std::map Aggregation; + raw_ostream *Out; + +public: + OutputAggregator(raw_ostream *out) : Out(out) {} + + size_t GetNumCategories() const { return Aggregation.size(); } + + void Report(StringRef s, std::function detailCallback) { + Aggregation[std::string(s)]++; + if (GetOS()) + detailCallback(*Out); + } + + void EnumerateResults( + std::function handleCounts) const { + for (auto &&[name, count] : Aggregation) + handleCounts(name, count); + } + + raw_ostream *GetOS() const { return Out; } + + // You can just use the stream, and if it's null, nothing happens. + // Don't do a lot of stuff like this, but it's convenient for silly stuff. + // It doesn't work with things that have custom insertion operators, though. + template OutputAggregator &operator<<(T &&value) { + if (Out != nullptr) + *Out << value; + return *this; + } + + // For multi-threaded usage, we can collect stuff in another aggregator, + // then merge it in here + void Merge(const OutputAggregator &other) { + for (auto &&[name, count] : other.Aggregation) { + auto it = Aggregation.find(name); + if (it == Aggregation.end()) + Aggregation.emplace(name, count); + else + it->second += count; + } + } +}; + +} // namespace gsym +} // namespace llvm + +#endif // LLVM_DEBUGINFO_GSYM_OUTPUTAGGREGATOR_H diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp index 6268c7845a14..3a28cd412de9 100644 --- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp +++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp @@ -21,6 +21,8 @@ #include "llvm/DebugInfo/GSYM/GsymCreator.h" #include "llvm/DebugInfo/GSYM/GsymReader.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" +#include "llvm/DebugInfo/GSYM/OutputAggregator.h" + #include using namespace llvm; @@ -215,9 +217,9 @@ ConvertDWARFRanges(const DWARFAddressRangesVector &DwarfRanges) { return Ranges; } -static void parseInlineInfo(GsymCreator &Gsym, raw_ostream *Log, CUInfo &CUI, - DWARFDie Die, uint32_t Depth, FunctionInfo &FI, - InlineInfo &Parent, +static void parseInlineInfo(GsymCreator &Gsym, OutputAggregator &Out, + CUInfo &CUI, DWARFDie Die, uint32_t Depth, + FunctionInfo &FI, InlineInfo &Parent, const AddressRanges &AllParentRanges, bool &WarnIfEmpty) { if (!hasInlineInfo(Die, Depth)) @@ -250,14 +252,18 @@ static void parseInlineInfo(GsymCreator &Gsym, raw_ostream *Log, CUInfo &CUI, // 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"; - } + } else + Out.Report("Function DIE has uncontained address range", + [&](raw_ostream &OS) { + OS << "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"; + }); } } } @@ -281,26 +287,30 @@ static void parseInlineInfo(GsymCreator &Gsym, raw_ostream *Log, CUInfo &CUI, 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, + parseInlineInfo(Gsym, Out, 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"; - } + } else + Out.Report( + "Inlined function die has invlaid file index in DW_AT_call_file", + [&](raw_ostream &OS) { + OS << "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, Log, CUI, ChildDie, Depth + 1, FI, Parent, + parseInlineInfo(Gsym, Out, CUI, ChildDie, Depth + 1, FI, Parent, AllParentRanges, WarnIfEmpty); } } -static void convertFunctionLineTable(raw_ostream *Log, CUInfo &CUI, +static void convertFunctionLineTable(OutputAggregator &Out, CUInfo &CUI, DWARFDie Die, GsymCreator &Gsym, FunctionInfo &FI) { std::vector RowVector; @@ -319,15 +329,15 @@ static void convertFunctionLineTable(raw_ostream *Log, CUInfo &CUI, if (FilePath.empty()) { // If we had a DW_AT_decl_file, but got no file then we need to emit a // warning. - if (Log) { + 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); - *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"; - } + 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 " + << "line entry from the DW_AT_decl_file/DW_AT_decl_line " + << "attributes.\n"; + }); return; } if (auto Line = @@ -347,14 +357,15 @@ static void convertFunctionLineTable(raw_ostream *Log, CUInfo &CUI, std::optional 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"; - } + Out.Report( + "Invalid file index in DWARF line table", [&](raw_ostream &OS) { + OS << "error: function DIE at " << HEX32(Die.getOffset()) << " has " + << "a line entry with invalid DWARF file index, this entry will " + << "be removed:\n"; + Row.dumpTableHeader(OS, /*Indent=*/0); + Row.dump(OS); + OS << "\n"; + }); continue; } const uint32_t FileIdx = OptFileIdx.value(); @@ -367,12 +378,15 @@ 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()) { - 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()); - } + Out.Report("Start address lies between valid Row table entries", + [&](raw_ostream &OS) { + OS << "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(OS, 0, DIDumpOptions::getForSingleDIE()); + }); RowAddress = FI.Range.start(); } else { continue; @@ -388,20 +402,21 @@ static void convertFunctionLineTable(raw_ostream *Log, CUInfo &CUI, // 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 (Log && !Gsym.isQuiet()) { - *Log << "warning: duplicate line table detected for DIE:\n"; - Die.dump(*Log, 0, DIDumpOptions::getForSingleDIE()); - } - } else { - 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()); - } - } + if (FirstLE && *FirstLE == LE) + // if (Log && !Gsym.isQuiet()) { TODO <-- This looks weird + Out.Report("Duplicate line table detected", [&](raw_ostream &OS) { + OS << "warning: duplicate line table detected for DIE:\n"; + Die.dump(OS, 0, DIDumpOptions::getForSingleDIE()); + }); + else + Out.Report("Non-monotonically increasing addresses", + [&](raw_ostream &OS) { + OS << "error: line table has addresses that do not " + << "monotonically increase:\n"; + for (uint32_t RowIndex2 : RowVector) + CUI.LineTable->Rows[RowIndex2].dump(OS); + Die.dump(OS, 0, DIDumpOptions::getForSingleDIE()); + }); break; } @@ -429,7 +444,8 @@ 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(OutputAggregator &Out, CUInfo &CUI, + DWARFDie Die) { switch (Die.getTag()) { case dwarf::DW_TAG_subprogram: { Expected RangesOrError = Die.getAddressRanges(); @@ -442,11 +458,11 @@ void DwarfTransformer::handleDie(raw_ostream *OS, CUInfo &CUI, DWARFDie Die) { break; auto NameIndex = getQualifiedNameIndex(Die, CUI.Language, Gsym); if (!NameIndex) { - if (OS) { - *OS << "error: function at " << HEX64(Die.getOffset()) - << " has no name\n "; - Die.dump(*OS, 0, DIDumpOptions::getForSingleDIE()); - } + Out.Report("Function has no name", [&](raw_ostream &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 @@ -472,8 +488,8 @@ void DwarfTransformer::handleDie(raw_ostream *OS, CUInfo &CUI, DWARFDie Die) { // Many linkers can't remove DWARF and might set the LowPC to zero. Since // high PC can be an offset from the low PC in more recent DWARF versions - // we need to watch for a zero'ed low pc which we do using - // ValidTextRanges below. + // we need to watch for a zero'ed low pc which we do using ValidTextRanges + // below. if (!Gsym.IsValidTextAddress(Range.LowPC)) { // We expect zero and -1 to be invalid addresses in DWARF depending // on the linker of the DWARF. This indicates a function was stripped @@ -482,13 +498,15 @@ void DwarfTransformer::handleDie(raw_ostream *OS, CUInfo &CUI, DWARFDie Die) { if (Range.LowPC != 0) { if (!Gsym.isQuiet()) { // Unexpected invalid address, emit a warning - 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()); - } + Out.Report("Address range starts outside executable section", + [&](raw_ostream &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; @@ -498,14 +516,14 @@ void DwarfTransformer::handleDie(raw_ostream *OS, CUInfo &CUI, DWARFDie Die) { FI.Range = {Range.LowPC, Range.HighPC}; FI.Name = *NameIndex; if (CUI.LineTable) - convertFunctionLineTable(OS, CUI, Die, Gsym, FI); + convertFunctionLineTable(Out, CUI, Die, Gsym, FI); if (hasInlineInfo(Die, 0)) { FI.Inline = InlineInfo(); FI.Inline->Name = *NameIndex; FI.Inline->Ranges.insert(FI.Range); bool WarnIfEmpty = true; - parseInlineInfo(Gsym, OS, CUI, Die, 0, FI, *FI.Inline, + parseInlineInfo(Gsym, Out, 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 @@ -517,11 +535,14 @@ void DwarfTransformer::handleDie(raw_ostream *OS, CUInfo &CUI, DWARFDie Die) { // 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()); - } + if (WarnIfEmpty && !Gsym.isQuiet()) + Out.Report("DIE contains inline functions with no valid ranges", + [&](raw_ostream &OS) { + 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; } } @@ -532,33 +553,28 @@ void DwarfTransformer::handleDie(raw_ostream *OS, CUInfo &CUI, DWARFDie Die) { break; } for (DWARFDie ChildDie : Die.children()) - handleDie(OS, CUI, ChildDie); + handleDie(Out, CUI, ChildDie); } -Error DwarfTransformer::convert(uint32_t NumThreads, raw_ostream *OS) { +Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) { size_t NumBefore = Gsym.getNumFunctionInfos(); - std::once_flag flag; auto getDie = [&](DWARFUnit &DwarfUnit) -> DWARFDie { DWARFDie ReturnDie = DwarfUnit.getUnitDIE(false); if (DwarfUnit.getDWOId()) { DWARFUnit *DWOCU = DwarfUnit.getNonSkeletonUnitDIE(false).getDwarfUnit(); - if (!DWOCU->isDWOUnit()) { - if (OS) { - std::string DWOName = dwarf::toString( - DwarfUnit.getUnitDIE().find( - {dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), - ""); - *OS << "warning: Unable to retrieve DWO .debug_info section for " - << DWOName << "\n"; - } else { - std::call_once(flag, []() { - outs() - << "warning: Unable to retrieve DWO .debug_info section for " - "some " - "object files. (Remove the --quiet flag for full output)\n"; - }); - } - } else { + if (!DWOCU->isDWOUnit()) + Out.Report( + "warning: Unable to retrieve DWO .debug_info section for some " + "object files. (Remove the --quiet flag for full output)", + [&](raw_ostream &OS) { + std::string DWOName = dwarf::toString( + DwarfUnit.getUnitDIE().find( + {dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), + ""); + OS << "warning: Unable to retrieve DWO .debug_info section for " + << DWOName << "\n"; + }); + else { ReturnDie = DWOCU->getUnitDIE(false); } } @@ -570,7 +586,7 @@ Error DwarfTransformer::convert(uint32_t NumThreads, raw_ostream *OS) { for (const auto &CU : DICtx.compile_units()) { DWARFDie Die = getDie(*CU); CUInfo CUI(DICtx, dyn_cast(CU.get())); - handleDie(OS, CUI, Die); + handleDie(Out, CUI, Die); } } else { // LLVM Dwarf parser is not thread-safe and we need to parse all DWARF up @@ -596,29 +612,31 @@ Error DwarfTransformer::convert(uint32_t NumThreads, raw_ostream *OS) { DWARFDie Die = getDie(*CU); if (Die) { CUInfo CUI(DICtx, dyn_cast(CU.get())); - pool.async([this, CUI, &LogMutex, OS, Die]() mutable { - std::string ThreadLogStorage; - raw_string_ostream ThreadOS(ThreadLogStorage); - handleDie(OS ? &ThreadOS: nullptr, CUI, Die); - ThreadOS.flush(); - if (OS && !ThreadLogStorage.empty()) { - // Print ThreadLogStorage lines into an actual stream under a lock - std::lock_guard guard(LogMutex); - *OS << ThreadLogStorage; + pool.async([this, CUI, &LogMutex, Out, Die]() mutable { + std::string storage; + raw_string_ostream StrStream(storage); + OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr); + handleDie(ThreadOut, CUI, Die); + // Print ThreadLogStorage lines into an actual stream under a lock + std::lock_guard guard(LogMutex); + if (Out.GetOS()) { + StrStream.flush(); + Out << storage; } + Out.Merge(ThreadOut); }); } } pool.wait(); } size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore; - if (OS) - *OS << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n"; + Out << "Loaded " << FunctionsAddedCount << " functions from DWARF.\n"; return Error::success(); } -llvm::Error DwarfTransformer::verify(StringRef GsymPath, raw_ostream &Log) { - Log << "Verifying GSYM file \"" << GsymPath << "\":\n"; +llvm::Error DwarfTransformer::verify(StringRef GsymPath, + OutputAggregator &Out) { + Out << "Verifying GSYM file \"" << GsymPath << "\":\n"; auto Gsym = GsymReader::openFile(GsymPath); if (!Gsym) @@ -637,9 +655,9 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath, raw_ostream &Log) { auto FI = Gsym->getFunctionInfo(*FuncAddr); if (!FI) - return createStringError(std::errc::invalid_argument, - "failed to extract function info for address 0x%" - PRIu64, *FuncAddr); + return createStringError( + std::errc::invalid_argument, + "failed to extract function info for address 0x%" PRIu64, *FuncAddr); for (auto Addr = *FuncAddr; Addr < *FuncAddr + FI->size(); ++Addr) { const object::SectionedAddress SectAddr{ @@ -664,24 +682,27 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath, raw_ostream &Log) { } if (NumDwarfInlineInfos > 0 && NumDwarfInlineInfos != LR->Locations.size()) { - Log << "error: address " << HEX64(Addr) << " has " - << NumDwarfInlineInfos << " DWARF inline frames and GSYM has " - << LR->Locations.size() << "\n"; - Log << " " << NumDwarfInlineInfos << " DWARF frames:\n"; - for (size_t Idx = 0; Idx < NumDwarfInlineInfos; ++Idx) { - const auto &dii = DwarfInlineInfos.getFrame(Idx); - Log << " [" << Idx << "]: " << dii.FunctionName << " @ " - << dii.FileName << ':' << dii.Line << '\n'; + if (Out.GetOS()) { + raw_ostream &Log = *Out.GetOS(); + Log << "error: address " << HEX64(Addr) << " has " + << NumDwarfInlineInfos << " DWARF inline frames and GSYM has " + << LR->Locations.size() << "\n"; + Log << " " << NumDwarfInlineInfos << " DWARF frames:\n"; + for (size_t Idx = 0; Idx < NumDwarfInlineInfos; ++Idx) { + const auto &dii = DwarfInlineInfos.getFrame(Idx); + Log << " [" << Idx << "]: " << dii.FunctionName << " @ " + << dii.FileName << ':' << dii.Line << '\n'; + } + Log << " " << LR->Locations.size() << " GSYM frames:\n"; + for (size_t Idx = 0, count = LR->Locations.size(); Idx < count; + ++Idx) { + const auto &gii = LR->Locations[Idx]; + Log << " [" << Idx << "]: " << gii.Name << " @ " << gii.Dir + << '/' << gii.Base << ':' << gii.Line << '\n'; + } + DwarfInlineInfos = DICtx.getInliningInfoForAddress(SectAddr, DLIS); + Gsym->dump(Log, *FI); } - Log << " " << LR->Locations.size() << " GSYM frames:\n"; - for (size_t Idx = 0, count = LR->Locations.size(); - Idx < count; ++Idx) { - const auto &gii = LR->Locations[Idx]; - Log << " [" << Idx << "]: " << gii.Name << " @ " << gii.Dir - << '/' << gii.Base << ':' << gii.Line << '\n'; - } - DwarfInlineInfos = DICtx.getInliningInfoForAddress(SectAddr, DLIS); - Gsym->dump(Log, *FI); continue; } @@ -693,17 +714,18 @@ llvm::Error DwarfTransformer::verify(StringRef GsymPath, raw_ostream &Log) { gsymFilename = LR->getSourceFile(Idx); // Verify function name if (dii.FunctionName.find(gii.Name.str()) != 0) - Log << "error: address " << HEX64(Addr) << " DWARF function \"" + Out << "error: address " << HEX64(Addr) << " DWARF function \"" << dii.FunctionName.c_str() << "\" doesn't match GSYM function \"" << gii.Name << "\"\n"; + // Verify source file path if (dii.FileName != gsymFilename) - Log << "error: address " << HEX64(Addr) << " DWARF path \"" + Out << "error: address " << HEX64(Addr) << " DWARF path \"" << dii.FileName.c_str() << "\" doesn't match GSYM path \"" << gsymFilename.c_str() << "\"\n"; // Verify source file line if (dii.Line != gii.Line) - Log << "error: address " << HEX64(Addr) << " DWARF line " + Out << "error: address " << HEX64(Addr) << " DWARF line " << dii.Line << " != GSYM line " << gii.Line << "\n"; } } diff --git a/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp b/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp index 74138755090a..deedaeea2fe2 100644 --- a/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp +++ b/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp @@ -9,6 +9,7 @@ #include "llvm/DebugInfo/GSYM/FileWriter.h" #include "llvm/DebugInfo/GSYM/Header.h" #include "llvm/DebugInfo/GSYM/LineTable.h" +#include "llvm/DebugInfo/GSYM/OutputAggregator.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/raw_ostream.h" @@ -188,7 +189,7 @@ llvm::Error GsymCreator::encode(FileWriter &O) const { return ErrorSuccess(); } -llvm::Error GsymCreator::finalize(llvm::raw_ostream &OS) { +llvm::Error GsymCreator::finalize(OutputAggregator &Out) { std::lock_guard Guard(Mutex); if (Finalized) return createStringError(std::errc::invalid_argument, "already finalized"); @@ -247,26 +248,29 @@ llvm::Error GsymCreator::finalize(llvm::raw_ostream &OS) { // address ranges that have debug info are last in // the sort. if (!(Prev == Curr)) { - if (Prev.hasRichInfo() && Curr.hasRichInfo()) { - if (!Quiet) { - OS << "warning: same address range contains " - "different debug " - << "info. Removing:\n" - << Prev << "\nIn favor of this one:\n" - << Curr << "\n"; - } - } + if (Prev.hasRichInfo() && Curr.hasRichInfo()) + Out.Report( + "Duplicate address ranges with different debug info.", + [&](raw_ostream &OS) { + OS << "warning: same address range contains " + "different debug " + << "info. Removing:\n" + << Prev << "\nIn favor of this one:\n" + << Curr << "\n"; + }); + // We want to swap the current entry with the previous since // later entries with the same range always have more debug info // or different debug info. std::swap(Prev, Curr); } } else { - if (!Quiet) { // print warnings about overlaps + Out.Report("Overlapping function ranges", [&](raw_ostream &OS) { + // print warnings about overlaps OS << "warning: function ranges overlap:\n" << Prev << "\n" << Curr << "\n"; - } + }); FinalizedFuncs.emplace_back(std::move(Curr)); } } else { @@ -293,8 +297,8 @@ llvm::Error GsymCreator::finalize(llvm::raw_ostream &OS) { Funcs.back().Range = {Funcs.back().Range.start(), Range->end()}; } } - OS << "Pruned " << NumBefore - Funcs.size() << " functions, ended with " - << Funcs.size() << " total\n"; + Out << "Pruned " << NumBefore - Funcs.size() << " functions, ended with " + << Funcs.size() << " total\n"; } return Error::success(); } @@ -494,8 +498,9 @@ llvm::Error GsymCreator::saveSegments(StringRef Path, GsymCreator *GC = ExpectedGC->get(); if (GC == NULL) break; // We had not more functions to encode. - raw_null_ostream ErrorStrm; - llvm::Error Err = GC->finalize(ErrorStrm); + // Don't collect any messages at all + OutputAggregator Out(nullptr); + llvm::Error Err = GC->finalize(Out); if (Err) return Err; std::string SegmentedGsymPath; diff --git a/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp b/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp index a60b2d386076..2500e172f52d 100644 --- a/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp +++ b/llvm/lib/DebugInfo/GSYM/ObjectFileTransformer.cpp @@ -14,8 +14,9 @@ #include "llvm/Support/DataExtractor.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h" #include "llvm/DebugInfo/GSYM/GsymCreator.h" +#include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h" +#include "llvm/DebugInfo/GSYM/OutputAggregator.h" using namespace llvm; using namespace gsym; @@ -68,7 +69,7 @@ static std::vector getUUID(const object::ObjectFile &Obj) { } llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj, - raw_ostream *Log, + OutputAggregator &Out, GsymCreator &Gsym) { using namespace llvm::object; @@ -99,8 +100,8 @@ llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj, const uint64_t size = IsELF ? ELFSymbolRef(Sym).getSize() : 0; Expected Name = Sym.getName(); if (!Name) { - if (Log) - logAllUnhandledErrors(Name.takeError(), *Log, + if (Out.GetOS()) + logAllUnhandledErrors(Name.takeError(), *Out.GetOS(), "ObjectFileTransformer: "); else consumeError(Name.takeError()); @@ -114,8 +115,8 @@ llvm::Error ObjectFileTransformer::convert(const object::ObjectFile &Obj, FunctionInfo(*AddrOrErr, size, Gsym.insertString(*Name, NoCopy))); } size_t FunctionsAddedCount = Gsym.getNumFunctionInfos() - NumBefore; - if (Log) - *Log << "Loaded " << FunctionsAddedCount - << " functions from symbol table.\n"; + if (Out.GetOS()) + *Out.GetOS() << "Loaded " << FunctionsAddedCount + << " functions from symbol table.\n"; return Error::success(); } diff --git a/llvm/test/tools/llvm-gsymutil/X86/elf-dwo.yaml b/llvm/test/tools/llvm-gsymutil/X86/elf-dwo.yaml index 109f2fe73e69..b430e8573b31 100644 --- a/llvm/test/tools/llvm-gsymutil/X86/elf-dwo.yaml +++ b/llvm/test/tools/llvm-gsymutil/X86/elf-dwo.yaml @@ -15,8 +15,8 @@ ## WARNING-QUIET: Input file: {{.*\.yaml\.tmp}} ## WARNING-QUIET: Output file (x86_64): {{.*\.yaml\.tmp\.gsym}} -## WARNING-QUIET: warning: Unable to retrieve DWO .debug_info section for some object files. (Remove the --quiet flag for full output) ## WARNING-QUIET: Pruned 0 functions, ended with 10 total +## WARNING-QUIET: warning: Unable to retrieve DWO .debug_info section for some object files. (Remove the --quiet flag for full output) diff --git a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp index e5ae726546d4..2de9c76fd68c 100644 --- a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp +++ b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp @@ -43,6 +43,7 @@ #include "llvm/DebugInfo/GSYM/InlineInfo.h" #include "llvm/DebugInfo/GSYM/LookupResult.h" #include "llvm/DebugInfo/GSYM/ObjectFileTransformer.h" +#include "llvm/DebugInfo/GSYM/OutputAggregator.h" #include using namespace llvm; @@ -300,16 +301,10 @@ static std::optional getImageBaseAddress(object::ObjectFile &Obj) { return std::nullopt; } -static llvm::Error handleObjectFile(ObjectFile &Obj, - const std::string &OutFile) { +static llvm::Error handleObjectFile(ObjectFile &Obj, const std::string &OutFile, + OutputAggregator &Out) { auto ThreadCount = NumThreads > 0 ? NumThreads : std::thread::hardware_concurrency(); - auto &OS = outs(); - // Make a stream refernce that will become a /dev/null log stream if - // Quiet is true, or normal output if Quiet is false. This can stop the - // errors and warnings from being displayed and producing too much output - // when they aren't desired. - raw_ostream *LogOS = Quiet ? nullptr : &outs(); GsymCreator Gsym(Quiet); @@ -354,17 +349,17 @@ static llvm::Error handleObjectFile(ObjectFile &Obj, Gsym.SetValidTextRanges(TextRanges); // Convert all DWARF to GSYM. - if (auto Err = DT.convert(ThreadCount, LogOS)) + if (auto Err = DT.convert(ThreadCount, Out)) return Err; // Get the UUID and convert symbol table to GSYM. - if (auto Err = ObjectFileTransformer::convert(Obj, LogOS, Gsym)) + if (auto Err = ObjectFileTransformer::convert(Obj, Out, Gsym)) return Err; // Finalize the GSYM to make it ready to save to disk. This will remove // duplicate FunctionInfo entries where we might have found an entry from // debug info and also a symbol table entry from the object file. - if (auto Err = Gsym.finalize(OS)) + if (auto Err = Gsym.finalize(Out)) return Err; // Save the GSYM file to disk. @@ -381,7 +376,7 @@ static llvm::Error handleObjectFile(ObjectFile &Obj, // Verify the DWARF if requested. This will ensure all the info in the DWARF // can be looked up in the GSYM and that all lookups get matching data. if (Verify) { - if (auto Err = DT.verify(OutFile, OS)) + if (auto Err = DT.verify(OutFile, Out)) return Err; } @@ -389,7 +384,8 @@ static llvm::Error handleObjectFile(ObjectFile &Obj, } static llvm::Error handleBuffer(StringRef Filename, MemoryBufferRef Buffer, - const std::string &OutFile) { + const std::string &OutFile, + OutputAggregator &Out) { Expected> BinOrErr = object::createBinary(Buffer); error(Filename, errorToErrorCode(BinOrErr.takeError())); @@ -397,7 +393,7 @@ static llvm::Error handleBuffer(StringRef Filename, MemoryBufferRef Buffer, Triple ObjTriple(Obj->makeTriple()); auto ArchName = ObjTriple.getArchName(); outs() << "Output file (" << ArchName << "): " << OutFile << "\n"; - if (auto Err = handleObjectFile(*Obj, OutFile)) + if (auto Err = handleObjectFile(*Obj, OutFile, Out)) return Err; } else if (auto *Fat = dyn_cast(BinOrErr->get())) { // Iterate over all contained architectures and filter out any that were @@ -431,7 +427,7 @@ static llvm::Error handleBuffer(StringRef Filename, MemoryBufferRef Buffer, ArchOutFile.append(ArchName.str()); } outs() << "Output file (" << ArchName << "): " << ArchOutFile << "\n"; - if (auto Err = handleObjectFile(*Obj, ArchOutFile)) + if (auto Err = handleObjectFile(*Obj, ArchOutFile, Out)) return Err; } } @@ -439,15 +435,16 @@ static llvm::Error handleBuffer(StringRef Filename, MemoryBufferRef Buffer, } static llvm::Error handleFileConversionToGSYM(StringRef Filename, - const std::string &OutFile) { + const std::string &OutFile, + OutputAggregator &Out) { ErrorOr> BuffOrErr = MemoryBuffer::getFileOrSTDIN(Filename); error(Filename, BuffOrErr.getError()); std::unique_ptr Buffer = std::move(BuffOrErr.get()); - return handleBuffer(Filename, *Buffer, OutFile); + return handleBuffer(Filename, *Buffer, OutFile, Out); } -static llvm::Error convertFileToGSYM(raw_ostream &OS) { +static llvm::Error convertFileToGSYM(OutputAggregator &Out) { // Expand any .dSYM bundles to the individual object files contained therein. std::vector Objects; std::string OutFile = OutputFilename; @@ -456,7 +453,7 @@ static llvm::Error convertFileToGSYM(raw_ostream &OS) { OutFile += ".gsym"; } - OS << "Input file: " << ConvertFilename << "\n"; + Out << "Input file: " << ConvertFilename << "\n"; if (auto DsymObjectsOrErr = MachOObjectFile::findDsymObjectMembers(ConvertFilename)) { @@ -469,7 +466,7 @@ static llvm::Error convertFileToGSYM(raw_ostream &OS) { } for (StringRef Object : Objects) - if (Error Err = handleFileConversionToGSYM(Object, OutFile)) + if (Error Err = handleFileConversionToGSYM(Object, OutFile, Out)) return Err; return Error::success(); } @@ -507,6 +504,7 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) { raw_ostream &OS = outs(); + OutputAggregator Aggregation(&OS); if (!ConvertFilename.empty()) { // Convert DWARF to GSYM if (!InputFilenames.empty()) { @@ -515,8 +513,12 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) { return 1; } // Call error() if we have an error and it will exit with a status of 1 - if (auto Err = convertFileToGSYM(OS)) + if (auto Err = convertFileToGSYM(Aggregation)) error("DWARF conversion failed: ", std::move(Err)); + // Report the errors from aggregator: + Aggregation.EnumerateResults([&](StringRef category, unsigned count) { + OS << category << " occurred " << count << " time(s)\n"; + }); return 0; } diff --git a/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp b/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp index f7ee7693ac2e..621b6feac8d2 100644 --- a/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp +++ b/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp @@ -18,6 +18,7 @@ #include "llvm/DebugInfo/GSYM/GsymReader.h" #include "llvm/DebugInfo/GSYM/Header.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" +#include "llvm/DebugInfo/GSYM/OutputAggregator.h" #include "llvm/DebugInfo/GSYM/StringTable.h" #include "llvm/ObjectYAML/DWARFEmitter.h" #include "llvm/Support/DataExtractor.h" @@ -971,9 +972,10 @@ TEST(GSYMTest, TestGsymCreatorEncodeErrors) { "GsymCreator wasn't finalized prior to encoding"); std::string finalizeIssues; raw_string_ostream OS(finalizeIssues); - llvm::Error finalizeErr = GC.finalize(OS); + OutputAggregator Agg(&OS); + llvm::Error finalizeErr = GC.finalize(Agg); ASSERT_FALSE(bool(finalizeErr)); - finalizeErr = GC.finalize(OS); + finalizeErr = GC.finalize(Agg); ASSERT_TRUE(bool(finalizeErr)); checkError("already finalized", std::move(finalizeErr)); // Verify we get an error trying to encode a GsymCreator with a UUID that is @@ -1043,7 +1045,8 @@ TEST(GSYMTest, TestGsymCreator1ByteAddrOffsets) { const uint32_t Func2Name = GC.insertString("bar"); GC.addFunctionInfo(FunctionInfo(BaseAddr+0x00, 0x10, Func1Name)); GC.addFunctionInfo(FunctionInfo(BaseAddr+0x20, 0x10, Func2Name)); - Error Err = GC.finalize(llvm::nulls()); + OutputAggregator Null(nullptr); + Error Err = GC.finalize(Null); ASSERT_FALSE(Err); TestEncodeDecode(GC, llvm::endianness::little, GSYM_VERSION, AddrOffSize, BaseAddr, @@ -1065,7 +1068,8 @@ TEST(GSYMTest, TestGsymCreator2ByteAddrOffsets) { const uint32_t Func2Name = GC.insertString("bar"); GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name)); GC.addFunctionInfo(FunctionInfo(BaseAddr+0x200, 0x100, Func2Name)); - Error Err = GC.finalize(llvm::nulls()); + OutputAggregator Null(nullptr); + Error Err = GC.finalize(Null); ASSERT_FALSE(Err); TestEncodeDecode(GC, llvm::endianness::little, GSYM_VERSION, AddrOffSize, BaseAddr, @@ -1087,7 +1091,8 @@ TEST(GSYMTest, TestGsymCreator4ByteAddrOffsets) { const uint32_t Func2Name = GC.insertString("bar"); GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name)); GC.addFunctionInfo(FunctionInfo(BaseAddr+0x20000, 0x100, Func2Name)); - Error Err = GC.finalize(llvm::nulls()); + OutputAggregator Null(nullptr); + Error Err = GC.finalize(Null); ASSERT_FALSE(Err); TestEncodeDecode(GC, llvm::endianness::little, GSYM_VERSION, AddrOffSize, BaseAddr, @@ -1109,7 +1114,8 @@ TEST(GSYMTest, TestGsymCreator8ByteAddrOffsets) { const uint32_t Func2Name = GC.insertString("bar"); GC.addFunctionInfo(FunctionInfo(BaseAddr+0x000, 0x100, Func1Name)); GC.addFunctionInfo(FunctionInfo(BaseAddr+0x100000000, 0x100, Func2Name)); - Error Err = GC.finalize(llvm::nulls()); + OutputAggregator Null(nullptr); + Error Err = GC.finalize(Null); ASSERT_FALSE(Err); TestEncodeDecode(GC, llvm::endianness::little, GSYM_VERSION, AddrOffSize, BaseAddr, @@ -1148,7 +1154,8 @@ TEST(GSYMTest, TestGsymReader) { const auto ByteOrder = llvm::endianness::native; GC.addFunctionInfo(FunctionInfo(Func1Addr, FuncSize, Func1Name)); GC.addFunctionInfo(FunctionInfo(Func2Addr, FuncSize, Func2Name)); - Error FinalizeErr = GC.finalize(llvm::nulls()); + OutputAggregator Null(nullptr); + Error FinalizeErr = GC.finalize(Null); ASSERT_FALSE(FinalizeErr); SmallString<512> Str; raw_svector_ostream OutStrm(Str); @@ -1213,7 +1220,8 @@ TEST(GSYMTest, TestGsymLookups) { Inline3.Ranges.insert(AddressRange(0x1016, 0x1018)); FI.Inline->Children.emplace_back(Inline3); GC.addFunctionInfo(std::move(FI)); - Error FinalizeErr = GC.finalize(llvm::nulls()); + OutputAggregator Null(nullptr); + Error FinalizeErr = GC.finalize(Null); ASSERT_FALSE(FinalizeErr); SmallString<512> Str; raw_svector_ostream OutStrm(Str); @@ -1329,11 +1337,12 @@ TEST(GSYMTest, TestDWARFFunctionWithAddresses) { DWARFContext::create(*ErrOrSections, 8); ASSERT_TRUE(DwarfContext.get() != nullptr); auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); GsymCreator GC; DwarfTransformer DT(*DwarfContext, GC); const uint32_t ThreadCount = 1; - ASSERT_THAT_ERROR(DT.convert(ThreadCount, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -1406,11 +1415,12 @@ TEST(GSYMTest, TestDWARFFunctionWithAddressAndOffset) { DWARFContext::create(*ErrOrSections, 8); ASSERT_TRUE(DwarfContext.get() != nullptr); auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); GsymCreator GC; DwarfTransformer DT(*DwarfContext, GC); const uint32_t ThreadCount = 1; - ASSERT_THAT_ERROR(DT.convert(ThreadCount, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -1513,11 +1523,12 @@ TEST(GSYMTest, TestDWARFStructMethodNoMangled) { DWARFContext::create(*ErrOrSections, 8); ASSERT_TRUE(DwarfContext.get() != nullptr); auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); GsymCreator GC; DwarfTransformer DT(*DwarfContext, GC); const uint32_t ThreadCount = 1; - ASSERT_THAT_ERROR(DT.convert(ThreadCount, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -1613,6 +1624,7 @@ TEST(GSYMTest, TestDWARFTextRanges) { DWARFContext::create(*ErrOrSections, 8); ASSERT_TRUE(DwarfContext.get() != nullptr); auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); GsymCreator GC; DwarfTransformer DT(*DwarfContext, GC); // Only allow addresses between [0x1000 - 0x2000) to be linked into the @@ -1621,8 +1633,8 @@ TEST(GSYMTest, TestDWARFTextRanges) { TextRanges.insert(AddressRange(0x1000, 0x2000)); GC.SetValidTextRanges(TextRanges); const uint32_t ThreadCount = 1; - ASSERT_THAT_ERROR(DT.convert(ThreadCount, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -1650,8 +1662,8 @@ TEST(GSYMTest, TestEmptySymbolEndAddressOfTextRanges) { TextRanges.insert(AddressRange(0x1000, 0x2000)); GC.SetValidTextRanges(TextRanges); GC.addFunctionInfo(FunctionInfo(0x1500, 0, GC.insertString("symbol"))); - auto &OS = llvm::nulls(); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + OutputAggregator Null(nullptr); + ASSERT_THAT_ERROR(GC.finalize(Null), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -1816,11 +1828,12 @@ TEST(GSYMTest, TestDWARFInlineInfo) { DWARFContext::create(*ErrOrSections, 8); ASSERT_TRUE(DwarfContext.get() != nullptr); auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); GsymCreator GC; DwarfTransformer DT(*DwarfContext, GC); const uint32_t ThreadCount = 1; - ASSERT_THAT_ERROR(DT.convert(ThreadCount, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -2076,11 +2089,12 @@ TEST(GSYMTest, TestDWARFNoLines) { DWARFContext::create(*ErrOrSections, 8); ASSERT_TRUE(DwarfContext.get() != nullptr); auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); GsymCreator GC; DwarfTransformer DT(*DwarfContext, GC); const uint32_t ThreadCount = 1; - ASSERT_THAT_ERROR(DT.convert(ThreadCount, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -2255,11 +2269,12 @@ TEST(GSYMTest, TestDWARFDeadStripAddr4) { DWARFContext::create(*ErrOrSections, 4); ASSERT_TRUE(DwarfContext.get() != nullptr); auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); GsymCreator GC; DwarfTransformer DT(*DwarfContext, GC); const uint32_t ThreadCount = 1; - ASSERT_THAT_ERROR(DT.convert(ThreadCount, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -2395,11 +2410,12 @@ TEST(GSYMTest, TestDWARFDeadStripAddr8) { DWARFContext::create(*ErrOrSections, 8); ASSERT_TRUE(DwarfContext.get() != nullptr); auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); GsymCreator GC; DwarfTransformer DT(*DwarfContext, GC); const uint32_t ThreadCount = 1; - ASSERT_THAT_ERROR(DT.convert(ThreadCount, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -2430,7 +2446,8 @@ TEST(GSYMTest, TestGsymCreatorMultipleSymbolsWithNoSize) { const uint32_t Func2Name = GC.insertString("bar"); GC.addFunctionInfo(FunctionInfo(BaseAddr, 0, Func1Name)); GC.addFunctionInfo(FunctionInfo(BaseAddr, 0, Func2Name)); - Error Err = GC.finalize(llvm::nulls()); + OutputAggregator Null(nullptr); + Error Err = GC.finalize(Null); ASSERT_FALSE(Err); TestEncodeDecode(GC, llvm::endianness::little, GSYM_VERSION, AddrOffSize, BaseAddr, @@ -2485,7 +2502,8 @@ static void AddFunctionInfo(GsymCreator &GC, const char *FuncName, // Finalize a GsymCreator, encode it and decode it and return the error or // GsymReader that was successfully decoded. static Expected FinalizeEncodeAndDecode(GsymCreator &GC) { - Error FinalizeErr = GC.finalize(llvm::nulls()); + OutputAggregator Null(nullptr); + Error FinalizeErr = GC.finalize(Null); if (FinalizeErr) return std::move(FinalizeErr); SmallString<1024> Str; @@ -3033,11 +3051,12 @@ TEST(GSYMTest, TestDWARFInlineRangeScopes) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -3260,11 +3279,12 @@ TEST(GSYMTest, TestDWARFEmptyInline) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -3496,11 +3516,12 @@ TEST(GSYMTest, TestFinalizeForLineTables) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); SmallString<512> Str; raw_svector_ostream OutStrm(Str); const auto ByteOrder = llvm::endianness::native; @@ -3775,11 +3796,12 @@ TEST(GSYMTest, TestRangeWarnings) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); OS.flush(); SmallString<512> Str; raw_svector_ostream OutStrm(Str); @@ -3977,11 +3999,12 @@ TEST(GSYMTest, TestEmptyRangeWarnings) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); OS.flush(); SmallString<512> Str; raw_svector_ostream OutStrm(Str); @@ -4129,11 +4152,12 @@ TEST(GSYMTest, TestEmptyLinkageName) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); OS.flush(); SmallString<512> Str; raw_svector_ostream OutStrm(Str); @@ -4290,11 +4314,12 @@ TEST(GSYMTest, TestLineTablesWithEmptyRanges) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); OS.flush(); SmallString<512> Str; raw_svector_ostream OutStrm(Str); @@ -4610,11 +4635,12 @@ TEST(GSYMTest, TestHandlingOfInvalidFileIndexes) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); OS.flush(); SmallString<512> Str; raw_svector_ostream OutStrm(Str); @@ -4825,11 +4851,12 @@ TEST(GSYMTest, TestLookupsOfOverlappingAndUnequalRanges) { 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, &OS), Succeeded()); - ASSERT_THAT_ERROR(GC.finalize(OS), Succeeded()); + ASSERT_THAT_ERROR(DT.convert(ThreadCount, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); OS.flush(); SmallString<512> Str; raw_svector_ostream OutStrm(Str);