diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
commit | cfca06d7963fa0909f90483b42a6d7d194d01e08 (patch) | |
tree | 209fb2a2d68f8f277793fc8df46c753d31bc853b /llvm/tools/llvm-dwarfdump | |
parent | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff) |
Notes
Diffstat (limited to 'llvm/tools/llvm-dwarfdump')
-rw-r--r-- | llvm/tools/llvm-dwarfdump/SectionSizes.cpp | 124 | ||||
-rw-r--r-- | llvm/tools/llvm-dwarfdump/Statistics.cpp | 407 | ||||
-rw-r--r-- | llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp | 136 | ||||
-rw-r--r-- | llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h | 43 |
4 files changed, 519 insertions, 191 deletions
diff --git a/llvm/tools/llvm-dwarfdump/SectionSizes.cpp b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp new file mode 100644 index 0000000000000..8c456d50baa7c --- /dev/null +++ b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp @@ -0,0 +1,124 @@ +//===-- SectionSizes.cpp - Debug section sizes ----------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm-dwarfdump.h" + +#define DEBUG_TYPE "dwarfdump" + +using namespace llvm; +using namespace llvm::dwarfdump; +using namespace llvm::object; + +static size_t getNameColumnWidth(const SectionSizes &Sizes, + const StringRef SectionNameTitle) { + // The minimum column width should be the size of "SECTION". + size_t Width = SectionNameTitle.size(); + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + StringRef SectionName = DebugSec.getKey(); + Width = std::max(Width, SectionName.size()); + } + return Width; +} + +static size_t getSizeColumnWidth(const SectionSizes &Sizes, + const StringRef SectionSizeTitle) { + // The minimum column width should be the size of the column title. + size_t Width = SectionSizeTitle.size(); + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + size_t NumWidth = std::to_string(DebugSec.getValue()).size(); + Width = std::max(Width, NumWidth); + } + return Width; +} + +static void prettyPrintSectionSizes(const ObjectFile &Obj, + const SectionSizes &Sizes, + raw_ostream &OS) { + const StringRef SectionNameTitle = "SECTION"; + const StringRef SectionSizeTitle = "SIZE (b)"; + + size_t NameColWidth = getNameColumnWidth(Sizes, SectionNameTitle); + size_t SizeColWidth = getSizeColumnWidth(Sizes, SectionSizeTitle); + + OS << "----------------------------------------------------" << '\n'; + OS << SectionNameTitle; + size_t SectionNameTitleWidth = SectionNameTitle.size(); + for (unsigned i = 0; i < (NameColWidth - SectionNameTitleWidth) + 2; i++) + OS << " "; + OS << SectionSizeTitle << '\n'; + for (unsigned i = 0; i < NameColWidth; i++) + OS << "-"; + OS << " "; + + for (unsigned i = 0; i < SizeColWidth; i++) + OS << "-"; + OS << '\n'; + + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + OS << left_justify(DebugSec.getKey(), NameColWidth) << " "; + + auto NumBytes = std::to_string(DebugSec.getValue()); + OS << right_justify(NumBytes, SizeColWidth) << " (" + << format("%0.2f", DebugSec.getValue() / + static_cast<double>(Sizes.TotalObjectSize) * 100) + << "%)\n"; + } + + OS << '\n'; + OS << " Total Size: " << Sizes.TotalDebugSectionsSize << " (" + << format("%0.2f", Sizes.TotalDebugSectionsSize / + static_cast<double>(Sizes.TotalObjectSize) * 100) + << "%)\n"; + OS << " Total File Size: " << Sizes.TotalObjectSize << '\n'; + OS << "----------------------------------------------------" << '\n'; +} + +void dwarfdump::calculateSectionSizes(const ObjectFile &Obj, + SectionSizes &Sizes, + const Twine &Filename) { + // Get total size. + Sizes.TotalObjectSize = Obj.getData().size(); + + for (const SectionRef &Section : Obj.sections()) { + StringRef SectionName; + if (Expected<StringRef> NameOrErr = Section.getName()) + SectionName = *NameOrErr; + else + WithColor::defaultWarningHandler( + createFileError(Filename, NameOrErr.takeError())); + + LLVM_DEBUG(dbgs() << SectionName.str() << ": " << Section.getSize() + << '\n'); + + if (!Section.isDebugSection(SectionName)) + continue; + + Sizes.TotalDebugSectionsSize += Section.getSize(); + Sizes.DebugSectionSizes[SectionName] += Section.getSize(); + } +} + +bool dwarfdump::collectObjectSectionSizes(ObjectFile &Obj, + DWARFContext & /*DICtx*/, + const Twine &Filename, + raw_ostream &OS) { + SectionSizes Sizes; + + // Get the section sizes. + calculateSectionSizes(Obj, Sizes, Filename); + + OS << "----------------------------------------------------\n"; + OS << "file: " << Filename.str() << '\n'; + + prettyPrintSectionSizes(Obj, Sizes, OS); + + // TODO: If the input file is an archive, print the cumulative summary of all + // files from the archive. + + return true; +} diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp index 5bef4d5148ca3..18b4c40c4d751 100644 --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -1,3 +1,12 @@ +//===-- Statistics.cpp - Debug Info quality metrics -----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm-dwarfdump.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" @@ -9,7 +18,8 @@ #define DEBUG_TYPE "dwarfdump" using namespace llvm; -using namespace object; +using namespace llvm::dwarfdump; +using namespace llvm::object; /// This represents the number of categories of debug location coverage being /// calculated. The first category is the number of variables with 0% location @@ -17,11 +27,14 @@ using namespace object; /// location coverage. constexpr int NumOfCoverageCategories = 12; +namespace { /// Holds statistics for one function (or other entity that has a PC range and /// contains variables, such as a compile unit). struct PerFunctionStats { /// Number of inlined instances of this function. unsigned NumFnInlined = 0; + /// Number of out-of-line instances of this function. + unsigned NumFnOutOfLine = 0; /// Number of inlined instances that have abstract origins. unsigned NumAbstractOrigins = 0; /// Number of variables and parameters with location across all inlined @@ -29,13 +42,12 @@ struct PerFunctionStats { unsigned TotalVarWithLoc = 0; /// Number of constants with location across all inlined instances. unsigned ConstantMembers = 0; + /// Number of arificial variables, parameters or members across all instances. + unsigned NumArtificial = 0; /// List of all Variables and parameters in this function. StringSet<> VarsInFunction; /// Compile units also cover a PC range, but have this flag set to false. bool IsFunction = false; - /// Verify function definition has PC addresses (for detecting when - /// a function has been inlined everywhere). - bool HasPCAddresses = false; /// Function has source location information. bool HasSourceLocation = false; /// Number of function parameters. @@ -46,14 +58,14 @@ struct PerFunctionStats { unsigned NumParamTypes = 0; /// Number of function parameters with a DW_AT_location. unsigned NumParamLocations = 0; - /// Number of variables. - unsigned NumVars = 0; - /// Number of variables with source location. - unsigned NumVarSourceLocations = 0; - /// Number of variables with type. - unsigned NumVarTypes = 0; - /// Number of variables with DW_AT_location. - unsigned NumVarLocations = 0; + /// Number of local variables. + unsigned NumLocalVars = 0; + /// Number of local variables with source location. + unsigned NumLocalVarSourceLocations = 0; + /// Number of local variables with type. + unsigned NumLocalVarTypes = 0; + /// Number of local variables with DW_AT_location. + unsigned NumLocalVarLocations = 0; }; /// Holds accumulated global statistics about DIEs. @@ -68,21 +80,19 @@ struct GlobalStats { /// Total number of PC range bytes covered by DW_AT_locations of /// formal parameters. unsigned ParamScopeBytesCovered = 0; - /// Total number of PC range bytes in each variable's enclosing scope - /// (only for parameters). + /// Total number of PC range bytes in each parameter's enclosing scope. unsigned ParamScopeBytes = 0; /// Total number of PC range bytes covered by DW_AT_locations with /// the debug entry values (DW_OP_entry_value) (only for parameters). unsigned ParamScopeEntryValueBytesCovered = 0; /// Total number of PC range bytes covered by DW_AT_locations (only for local /// variables). - unsigned VarScopeBytesCovered = 0; - /// Total number of PC range bytes in each variable's enclosing scope - /// (only for local variables). - unsigned VarScopeBytes = 0; + unsigned LocalVarScopeBytesCovered = 0; + /// Total number of PC range bytes in each local variable's enclosing scope. + unsigned LocalVarScopeBytes = 0; /// Total number of PC range bytes covered by DW_AT_locations with /// the debug entry values (DW_OP_entry_value) (only for local variables). - unsigned VarScopeEntryValueBytesCovered = 0; + unsigned LocalVarScopeEntryValueBytesCovered = 0; /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line). unsigned CallSiteEntries = 0; /// Total number of call site DIEs (DW_TAG_call_site). @@ -118,10 +128,10 @@ struct LocationStats { std::vector<unsigned> ParamNonEntryValLocStats{ std::vector<unsigned>(NumOfCoverageCategories, 0)}; /// The debug location statistics for local variables. - std::vector<unsigned> VarLocStats{ + std::vector<unsigned> LocalVarLocStats{ std::vector<unsigned>(NumOfCoverageCategories, 0)}; /// Map non debug entry values coverage for local variables. - std::vector<unsigned> VarNonEntryValLocStats{ + std::vector<unsigned> LocalVarNonEntryValLocStats{ std::vector<unsigned>(NumOfCoverageCategories, 0)}; /// Total number of local variables and function parameters processed. unsigned NumVarParam = 0; @@ -130,13 +140,14 @@ struct LocationStats { /// Total number of local variables processed. unsigned NumVar = 0; }; +} // namespace /// Collect debug location statistics for one DIE. static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope, std::vector<unsigned> &VarParamLocStats, std::vector<unsigned> &ParamLocStats, - std::vector<unsigned> &VarLocStats, bool IsParam, - bool IsLocalVar) { + std::vector<unsigned> &LocalVarLocStats, + bool IsParam, bool IsLocalVar) { auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned { // No debug location at all for the variable. if (BytesCovered == 0) @@ -155,7 +166,36 @@ static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope, if (IsParam) ParamLocStats[CoverageBucket]++; else if (IsLocalVar) - VarLocStats[CoverageBucket]++; + LocalVarLocStats[CoverageBucket]++; +} +/// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName +/// and DeclLine. The identifier aims to be unique for any unique entities, +/// but keeping the same among different instances of the same entity. +static std::string constructDieID(DWARFDie Die, + StringRef Prefix = StringRef()) { + std::string IDStr; + llvm::raw_string_ostream ID(IDStr); + ID << Prefix + << Die.getName(DINameKind::LinkageName); + + // Prefix + Name is enough for local variables and parameters. + if (!Prefix.empty() && !Prefix.equals("g")) + return ID.str(); + + auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file); + std::string File; + if (DeclFile) { + DWARFUnit *U = Die.getDwarfUnit(); + if (const auto *LT = U->getContext().getLineTableForUnit(U)) + if (LT->getFileNameByIndex( + dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) + File = std::string(sys::path::filename(File)); + } + ID << ":" << (File.empty() ? "/" : File); + ID << ":" + << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0); + return ID.str(); } /// Collect debug info quality metrics for one DIE. @@ -168,12 +208,13 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, bool HasLoc = false; bool HasSrcLoc = false; bool HasType = false; - bool IsArtificial = false; uint64_t BytesCovered = 0; uint64_t BytesEntryValuesCovered = 0; auto &FnStats = FnStatMap[FnPrefix]; bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter; bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable; + bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member && + Die.find(dwarf::DW_AT_const_value); if (Die.getTag() == dwarf::DW_TAG_call_site || Die.getTag() == dwarf::DW_TAG_GNU_call_site) { @@ -187,11 +228,15 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, return; } - if (!IsParam && !IsLocalVar && Die.getTag() != dwarf::DW_TAG_member) { + if (!IsParam && !IsLocalVar && !IsConstantMember) { // Not a variable or constant member. return; } + // Ignore declarations of global variables. + if (IsLocalVar && Die.find(dwarf::DW_AT_declaration)) + return; + if (Die.findRecursively(dwarf::DW_AT_decl_file) && Die.findRecursively(dwarf::DW_AT_decl_line)) HasSrcLoc = true; @@ -199,14 +244,12 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, if (Die.findRecursively(dwarf::DW_AT_type)) HasType = true; - if (Die.find(dwarf::DW_AT_artificial)) - IsArtificial = true; - auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { DWARFUnit *U = Die.getDwarfUnit(); DataExtractor Data(toStringRef(D), Die.getDwarfUnit()->getContext().isLittleEndian(), 0); - DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getAddressByteSize(), + U->getFormParams().Format); // Consider the expression containing the DW_OP_entry_value as // an entry value. return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { @@ -220,10 +263,6 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, HasLoc = true; BytesCovered = BytesInScope; } else { - if (Die.getTag() == dwarf::DW_TAG_member) { - // Non-const member. - return; - } // Handle variables and function arguments. Expected<std::vector<DWARFLocationExpression>> Loc = Die.getLocations(dwarf::DW_AT_location); @@ -257,25 +296,24 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, LocStats.NumVar++; collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats, - LocStats.ParamLocStats, LocStats.VarLocStats, IsParam, + LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam, IsLocalVar); // Non debug entry values coverage statistics. collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope, LocStats.VarParamNonEntryValLocStats, LocStats.ParamNonEntryValLocStats, - LocStats.VarNonEntryValLocStats, IsParam, IsLocalVar); + LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar); } // Collect PC range coverage data. if (DWARFDie D = Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) Die = D; - // By using the variable name + the path through the lexical block tree, the - // keys are consistent across duplicate abstract origins in different CUs. - std::string VarName = StringRef(Die.getName(DINameKind::ShortName)); - FnStats.VarsInFunction.insert(VarPrefix + VarName); + + std::string VarID = constructDieID(Die, VarPrefix); + FnStats.VarsInFunction.insert(VarID); + if (BytesInScope) { - FnStats.TotalVarWithLoc += (unsigned)HasLoc; // Turns out we have a lot of ranges that extend past the lexical scope. GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered); GlobalStats.ScopeBytes += BytesInScope; @@ -286,34 +324,43 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, GlobalStats.ParamScopeBytes += BytesInScope; GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; } else if (IsLocalVar) { - GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered); - GlobalStats.VarScopeBytes += BytesInScope; - GlobalStats.VarScopeEntryValueBytesCovered += BytesEntryValuesCovered; + GlobalStats.LocalVarScopeBytesCovered += + std::min(BytesInScope, BytesCovered); + GlobalStats.LocalVarScopeBytes += BytesInScope; + GlobalStats.LocalVarScopeEntryValueBytesCovered += + BytesEntryValuesCovered; } assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes); - } else if (Die.getTag() == dwarf::DW_TAG_member) { + } + + if (IsConstantMember) { FnStats.ConstantMembers++; - } else { - FnStats.TotalVarWithLoc += (unsigned)HasLoc; + return; } - if (!IsArtificial) { - if (IsParam) { - FnStats.NumParams++; - if (HasType) - FnStats.NumParamTypes++; - if (HasSrcLoc) - FnStats.NumParamSourceLocations++; - if (HasLoc) - FnStats.NumParamLocations++; - } else if (IsLocalVar) { - FnStats.NumVars++; - if (HasType) - FnStats.NumVarTypes++; - if (HasSrcLoc) - FnStats.NumVarSourceLocations++; - if (HasLoc) - FnStats.NumVarLocations++; - } + + FnStats.TotalVarWithLoc += (unsigned)HasLoc; + + if (Die.find(dwarf::DW_AT_artificial)) { + FnStats.NumArtificial++; + return; + } + + if (IsParam) { + FnStats.NumParams++; + if (HasType) + FnStats.NumParamTypes++; + if (HasSrcLoc) + FnStats.NumParamSourceLocations++; + if (HasLoc) + FnStats.NumParamLocations++; + } else if (IsLocalVar) { + FnStats.NumLocalVars++; + if (HasType) + FnStats.NumLocalVarTypes++; + if (HasSrcLoc) + FnStats.NumLocalVarSourceLocations++; + if (HasLoc) + FnStats.NumLocalVarLocations++; } } @@ -324,8 +371,12 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats, LocationStats &LocStats) { - // Handle any kind of lexical scope. const dwarf::Tag Tag = Die.getTag(); + // Skip function types. + if (Tag == dwarf::DW_TAG_subroutine_type) + return; + + // Handle any kind of lexical scope. const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; @@ -358,27 +409,25 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, // Count the function. if (!IsBlock) { - StringRef Name = Die.getName(DINameKind::LinkageName); - if (Name.empty()) - Name = Die.getName(DINameKind::ShortName); - FnPrefix = Name; // Skip over abstract origins. if (Die.find(dwarf::DW_AT_inline)) return; - // We've seen an (inlined) instance of this function. - auto &FnStats = FnStatMap[Name]; + std::string FnID = constructDieID(Die); + // We've seen an instance of this function. + auto &FnStats = FnStatMap[FnID]; + FnStats.IsFunction = true; if (IsInlinedFunction) { FnStats.NumFnInlined++; if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) FnStats.NumAbstractOrigins++; + } else { + FnStats.NumFnOutOfLine++; } - FnStats.IsFunction = true; - if (BytesInThisScope && !IsInlinedFunction) - FnStats.HasPCAddresses = true; - std::string FnName = StringRef(Die.getName(DINameKind::ShortName)); if (Die.findRecursively(dwarf::DW_AT_decl_file) && Die.findRecursively(dwarf::DW_AT_decl_line)) FnStats.HasSourceLocation = true; + // Update function prefix. + FnPrefix = FnID; } if (BytesInThisScope) { @@ -402,11 +451,14 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, // Traverse children. unsigned LexicalBlockIndex = 0; + unsigned FormalParameterIndex = 0; DWARFDie Child = Die.getFirstChild(); while (Child) { std::string ChildVarPrefix = VarPrefix; if (Child.getTag() == dwarf::DW_TAG_lexical_block) ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; + if (Child.getTag() == dwarf::DW_TAG_formal_parameter) + ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap, GlobalStats, LocStats); @@ -421,29 +473,44 @@ static void printDatum(raw_ostream &OS, const char *Key, json::Value Value) { OS << ",\"" << Key << "\":" << Value; LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); } -static void printLocationStats(raw_ostream &OS, - const char *Key, + +static void printLocationStats(raw_ostream &OS, const char *Key, std::vector<unsigned> &LocationStats) { - OS << ",\"" << Key << " with 0% of its scope covered\":" + OS << ",\"" << Key << " with 0% of parent scope covered by DW_AT_location\":" << LocationStats[0]; - LLVM_DEBUG(llvm::dbgs() << Key << " with 0% of its scope covered: " - << LocationStats[0] << '\n'); - OS << ",\"" << Key << " with (0%,10%) of its scope covered\":" + LLVM_DEBUG( + llvm::dbgs() << Key + << " with 0% of parent scope covered by DW_AT_location: \\" + << LocationStats[0] << '\n'); + OS << ",\"" << Key + << " with (0%,10%) of parent scope covered by DW_AT_location\":" << LocationStats[1]; - LLVM_DEBUG(llvm::dbgs() << Key << " with (0%,10%) of its scope covered: " - << LocationStats[1] << '\n'); + LLVM_DEBUG(llvm::dbgs() + << Key + << " with (0%,10%) of parent scope covered by DW_AT_location: " + << LocationStats[1] << '\n'); for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { OS << ",\"" << Key << " with [" << (i - 1) * 10 << "%," << i * 10 - << "%) of its scope covered\":" << LocationStats[i]; + << "%) of parent scope covered by DW_AT_location\":" << LocationStats[i]; LLVM_DEBUG(llvm::dbgs() << Key << " with [" << (i - 1) * 10 << "%," << i * 10 - << "%) of its scope covered: " << LocationStats[i]); + << "%) of parent scope covered by DW_AT_location: " + << LocationStats[i]); } - OS << ",\"" << Key << " with 100% of its scope covered\":" + OS << ",\"" << Key + << " with 100% of parent scope covered by DW_AT_location\":" << LocationStats[NumOfCoverageCategories - 1]; - LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: " - << LocationStats[NumOfCoverageCategories - 1]); + LLVM_DEBUG( + llvm::dbgs() << Key + << " with 100% of parent scope covered by DW_AT_location: " + << LocationStats[NumOfCoverageCategories - 1]); } + +static void printSectionSizes(raw_ostream &OS, const SectionSizes &Sizes) { + for (const auto &DebugSec : Sizes.DebugSectionSizes) + OS << ",\"#bytes in " << DebugSec.getKey() << "\":" << DebugSec.getValue(); +} + /// \} /// Collect debug info quality metrics for an entire DIContext. @@ -454,8 +521,9 @@ static void printLocationStats(raw_ostream &OS, /// of particular optimizations. The raw numbers themselves are not particularly /// useful, only the delta between compiling the same program with different /// compilers is. -bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, - Twine Filename, raw_ostream &OS) { +bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, + raw_ostream &OS) { StringRef FormatName = Obj.getFileFormatName(); GlobalStats GlobalStats; LocationStats LocStats; @@ -465,10 +533,14 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats); + /// Collect the sizes of debug sections. + SectionSizes Sizes; + calculateSectionSizes(Obj, Sizes, Filename); + /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the /// version. - unsigned Version = 4; + unsigned Version = 5; unsigned VarParamTotal = 0; unsigned VarParamUnique = 0; unsigned VarParamWithLoc = 0; @@ -480,16 +552,18 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, unsigned ParamWithType = 0; unsigned ParamWithLoc = 0; unsigned ParamWithSrcLoc = 0; - unsigned VarTotal = 0; - unsigned VarWithType = 0; - unsigned VarWithSrcLoc = 0; - unsigned VarWithLoc = 0; + unsigned LocalVarTotal = 0; + unsigned LocalVarWithType = 0; + unsigned LocalVarWithSrcLoc = 0; + unsigned LocalVarWithLoc = 0; for (auto &Entry : Statistics) { PerFunctionStats &Stats = Entry.getValue(); - unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined; - // Count variables in concrete out-of-line functions and in global scope. - if (Stats.HasPCAddresses || !Stats.IsFunction) - TotalVars += Stats.VarsInFunction.size(); + unsigned TotalVars = Stats.VarsInFunction.size() * + (Stats.NumFnInlined + Stats.NumFnOutOfLine); + // Count variables in global scope. + if (!Stats.IsFunction) + TotalVars = + Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial; unsigned Constants = Stats.ConstantMembers; VarParamWithLoc += Stats.TotalVarWithLoc + Constants; VarParamTotal += TotalVars; @@ -505,10 +579,10 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, ParamWithType += Stats.NumParamTypes; ParamWithLoc += Stats.NumParamLocations; ParamWithSrcLoc += Stats.NumParamSourceLocations; - VarTotal += Stats.NumVars; - VarWithType += Stats.NumVarTypes; - VarWithLoc += Stats.NumVarLocations; - VarWithSrcLoc += Stats.NumVarSourceLocations; + LocalVarTotal += Stats.NumLocalVars; + LocalVarWithType += Stats.NumLocalVarTypes; + LocalVarWithLoc += Stats.NumLocalVarLocations; + LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations; } // Print summary. @@ -516,56 +590,97 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, OS << "{\"version\":" << Version; LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; llvm::dbgs() << "---------------------------------\n"); + printDatum(OS, "file", Filename.str()); printDatum(OS, "format", FormatName); - printDatum(OS, "source functions", NumFunctions); - printDatum(OS, "source functions with location", NumFuncsWithSrcLoc); - printDatum(OS, "inlined functions", NumInlinedFunctions); - printDatum(OS, "inlined funcs with abstract origins", NumAbstractOrigins); - printDatum(OS, "unique source variables", VarParamUnique); - printDatum(OS, "source variables", VarParamTotal); - printDatum(OS, "variables with location", VarParamWithLoc); - printDatum(OS, "call site entries", GlobalStats.CallSiteEntries); - printDatum(OS, "call site DIEs", GlobalStats.CallSiteDIEs); - printDatum(OS, "call site parameter DIEs", GlobalStats.CallSiteParamDIEs); - printDatum(OS, "scope bytes total", GlobalStats.ScopeBytes); - printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); - printDatum(OS, "entry value scope bytes covered", + + printDatum(OS, "#functions", NumFunctions); + printDatum(OS, "#functions with location", NumFuncsWithSrcLoc); + printDatum(OS, "#inlined functions", NumInlinedFunctions); + printDatum(OS, "#inlined functions with abstract origins", + NumAbstractOrigins); + + // This includes local variables and formal parameters. + printDatum(OS, "#unique source variables", VarParamUnique); + printDatum(OS, "#source variables", VarParamTotal); + printDatum(OS, "#source variables with location", VarParamWithLoc); + + printDatum(OS, "#call site entries", GlobalStats.CallSiteEntries); + printDatum(OS, "#call site DIEs", GlobalStats.CallSiteDIEs); + printDatum(OS, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs); + + printDatum(OS, "sum_all_variables(#bytes in parent scope)", + GlobalStats.ScopeBytes); + printDatum(OS, + "sum_all_variables(#bytes in parent scope covered by " + "DW_AT_location)", + GlobalStats.ScopeBytesCovered); + printDatum(OS, + "sum_all_variables(#bytes in parent scope covered by " + "DW_OP_entry_value)", GlobalStats.ScopeEntryValueBytesCovered); - printDatum(OS, "formal params scope bytes total", + + printDatum(OS, "sum_all_params(#bytes in parent scope)", GlobalStats.ParamScopeBytes); - printDatum(OS, "formal params scope bytes covered", - GlobalStats.ParamScopeBytesCovered); - printDatum(OS, "formal params entry value scope bytes covered", + printDatum( + OS, + "sum_all_params(#bytes in parent scope covered by DW_AT_location)", + GlobalStats.ParamScopeBytesCovered); + printDatum(OS, + "sum_all_params(#bytes in parent scope covered by " + "DW_OP_entry_value)", GlobalStats.ParamScopeEntryValueBytesCovered); - printDatum(OS, "vars scope bytes total", GlobalStats.VarScopeBytes); - printDatum(OS, "vars scope bytes covered", GlobalStats.VarScopeBytesCovered); - printDatum(OS, "vars entry value scope bytes covered", - GlobalStats.VarScopeEntryValueBytesCovered); - printDatum(OS, "total function size", GlobalStats.FunctionSize); - printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize); - printDatum(OS, "total formal params", ParamTotal); - printDatum(OS, "formal params with source location", ParamWithSrcLoc); - printDatum(OS, "formal params with type", ParamWithType); - printDatum(OS, "formal params with binary location", ParamWithLoc); - printDatum(OS, "total vars", VarTotal); - printDatum(OS, "vars with source location", VarWithSrcLoc); - printDatum(OS, "vars with type", VarWithType); - printDatum(OS, "vars with binary location", VarWithLoc); - printDatum(OS, "total variables procesed by location statistics", + + printDatum(OS, "sum_all_local_vars(#bytes in parent scope)", + GlobalStats.LocalVarScopeBytes); + printDatum(OS, + "sum_all_local_vars(#bytes in parent scope covered by " + "DW_AT_location)", + GlobalStats.LocalVarScopeBytesCovered); + printDatum(OS, + "sum_all_local_vars(#bytes in parent scope covered by " + "DW_OP_entry_value)", + GlobalStats.LocalVarScopeEntryValueBytesCovered); + + printDatum(OS, "#bytes witin functions", GlobalStats.FunctionSize); + printDatum(OS, "#bytes witin inlined functions", + GlobalStats.InlineFunctionSize); + + // Print the summary for formal parameters. + printDatum(OS, "#params", ParamTotal); + printDatum(OS, "#params with source location", ParamWithSrcLoc); + printDatum(OS, "#params with type", ParamWithType); + printDatum(OS, "#params with binary location", ParamWithLoc); + + // Print the summary for local variables. + printDatum(OS, "#local vars", LocalVarTotal); + printDatum(OS, "#local vars with source location", LocalVarWithSrcLoc); + printDatum(OS, "#local vars with type", LocalVarWithType); + printDatum(OS, "#local vars with binary location", LocalVarWithLoc); + + // Print the debug section sizes. + printSectionSizes(OS, Sizes); + + // Print the location statistics for variables (includes local variables + // and formal parameters). + printDatum(OS, "#variables processed by location statistics", LocStats.NumVarParam); - printLocationStats(OS, "variables", LocStats.VarParamLocStats); - printLocationStats(OS, "variables (excluding the debug entry values)", + printLocationStats(OS, "#variables", LocStats.VarParamLocStats); + printLocationStats(OS, "#variables - entry values", LocStats.VarParamNonEntryValLocStats); - printDatum(OS, "total params procesed by location statistics", - LocStats.NumParam); - printLocationStats(OS, "params", LocStats.ParamLocStats); - printLocationStats(OS, "params (excluding the debug entry values)", + + // Print the location statistics for formal parameters. + printDatum(OS, "#params processed by location statistics", LocStats.NumParam); + printLocationStats(OS, "#params", LocStats.ParamLocStats); + printLocationStats(OS, "#params - entry values", LocStats.ParamNonEntryValLocStats); - printDatum(OS, "total vars procesed by location statistics", LocStats.NumVar); - printLocationStats(OS, "vars", LocStats.VarLocStats); - printLocationStats(OS, "vars (excluding the debug entry values)", - LocStats.VarNonEntryValLocStats); + + // Print the location statistics for local variables. + printDatum(OS, "#local vars processed by location statistics", + LocStats.NumVar); + printLocationStats(OS, "#local vars", LocStats.LocalVarLocStats); + printLocationStats(OS, "#local vars - entry values", + LocStats.LocalVarNonEntryValLocStats); OS << "}\n"; LLVM_DEBUG( llvm::dbgs() << "Total Availability: " diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index 374bdd482a8d6..d8fa4f9953dca 100644 --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm-dwarfdump.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" @@ -29,10 +30,13 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" +#include <cstdlib> using namespace llvm; -using namespace object; +using namespace llvm::dwarfdump; +using namespace llvm::object; +namespace { /// Parser for options that take an optional offest argument. /// @{ struct OffsetOption { @@ -40,6 +44,8 @@ struct OffsetOption { bool HasValue = false; bool IsRequested = false; }; +struct BoolOption : public OffsetOption {}; +} // namespace namespace llvm { namespace cl { @@ -57,7 +63,7 @@ public: return false; } if (Arg.getAsInteger(0, Val.Val)) - return O.error("'" + Arg + "' value invalid for integer argument!"); + return O.error("'" + Arg + "' value invalid for integer argument"); Val.HasValue = true; Val.IsRequested = true; return false; @@ -67,22 +73,42 @@ public: return ValueOptional; } - void printOptionInfo(const Option &O, size_t GlobalWidth) const { - outs() << " -" << O.ArgStr; - Option::printHelpStr(O.HelpStr, GlobalWidth, getOptionWidth(O)); - } + StringRef getValueName() const override { return StringRef("offset"); } void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, size_t GlobalWidth) const { printOptionName(O, GlobalWidth); outs() << "[=offset]"; } +}; + +template <> class parser<BoolOption> final : public basic_parser<BoolOption> { +public: + parser(Option &O) : basic_parser(O) {} + + /// Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) { + if (Arg != "") + return O.error("this is a flag and does not take a value"); + Val.Val = 0; + Val.HasValue = false; + Val.IsRequested = true; + return false; + } + + enum ValueExpected getValueExpectedFlagDefault() const { + return ValueOptional; + } - // An out-of-line virtual method to provide a 'home' for this class. - void anchor() override {}; + StringRef getValueName() const override { return StringRef(); } + + void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, + size_t GlobalWidth) const { + printOptionName(O, GlobalWidth); + } }; -} // cl -} // llvm +} // namespace cl +} // namespace llvm /// @} /// Command line options. @@ -110,10 +136,10 @@ static alias DumpAllAlias("a", desc("Alias for -all"), aliasopt(DumpAll)); static unsigned DumpType = DIDT_Null; static std::array<llvm::Optional<uint64_t>, (unsigned)DIDT_ID_Count> DumpOffsets; -#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \ - static opt<OffsetOption> Dump##ENUM_NAME( \ - CMDLINE_NAME, desc("Dump the " ELF_NAME " section"), \ - cat(SectionCategory)); +#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ + static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME, \ + desc("Dump the " ELF_NAME " section"), \ + cat(SectionCategory)); #include "llvm/BinaryFormat/Dwarf.def" #undef HANDLE_DWARF_SECTION @@ -208,6 +234,11 @@ static cl::opt<bool> Statistics("statistics", cl::desc("Emit JSON-formatted debug info quality metrics."), cat(DwarfDumpCategory)); +static cl::opt<bool> + ShowSectionSizes("show-section-sizes", + cl::desc("Show the sizes of all debug sections, " + "expressed in bytes."), + cat(DwarfDumpCategory)); static opt<bool> Verify("verify", desc("Verify the DWARF debug info."), cat(DwarfDumpCategory)); static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."), @@ -233,7 +264,7 @@ static void error(StringRef Prefix, std::error_code EC) { exit(1); } -static DIDumpOptions getDumpOpts() { +static DIDumpOptions getDumpOpts(DWARFContext &C) { DIDumpOptions DumpOpts; DumpOpts.DumpType = DumpType; DumpOpts.ChildRecurseDepth = ChildRecurseDepth; @@ -244,6 +275,7 @@ static DIDumpOptions getDumpOpts() { DumpOpts.ShowForm = ShowForm; DumpOpts.SummarizeTypes = SummarizeTypes; DumpOpts.Verbose = Verbose; + DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler(); // In -verify mode, print DIEs without children in error messages. if (Verify) return DumpOpts.noImplicitRecursion(); @@ -278,12 +310,13 @@ static bool filterArch(ObjectFile &Obj) { return false; } -using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, Twine, - raw_ostream &)>; +using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, + const Twine &, raw_ostream &)>; /// Print only DIEs that have a certain name. static bool filterByName(const StringSet<> &Names, DWARFDie Die, StringRef NameRef, raw_ostream &OS) { + DIDumpOptions DumpOpts = getDumpOpts(Die.getDwarfUnit()->getContext()); std::string Name = (IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str(); if (UseRegex) { @@ -296,13 +329,13 @@ static bool filterByName(const StringSet<> &Names, DWARFDie Die, exit(1); } if (RE.match(Name)) { - Die.dump(OS, 0, getDumpOpts()); + Die.dump(OS, 0, DumpOpts); return true; } } } else if (Names.count(Name)) { // Match full text. - Die.dump(OS, 0, getDumpOpts()); + Die.dump(OS, 0, DumpOpts); return true; } return false; @@ -375,8 +408,9 @@ static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx, llvm::sort(Dies); Dies.erase(std::unique(Dies.begin(), Dies.end()), Dies.end()); + DIDumpOptions DumpOpts = getDumpOpts(DICtx); for (DWARFDie Die : Dies) - Die.dump(OS, 0, getDumpOpts()); + Die.dump(OS, 0, DumpOpts); } /// Handle the --lookup option and dump the DIEs and line info for the given @@ -392,7 +426,7 @@ static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address, if (!DIEsForAddr) return false; - DIDumpOptions DumpOpts = getDumpOpts(); + DIDumpOptions DumpOpts = getDumpOpts(DICtx); DumpOpts.ChildRecurseDepth = 0; DIEsForAddr.CompileUnit->dump(OS, DumpOpts); if (DIEsForAddr.FunctionDIE) { @@ -410,11 +444,8 @@ static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address, return true; } -bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, - Twine Filename, raw_ostream &OS); - -static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, - raw_ostream &OS) { +static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, raw_ostream &OS) { logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(), Filename.str() + ": "); // The UUID dump already contains all the same information. @@ -443,18 +474,18 @@ static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, } // Dump the complete DWARF structure. - DICtx.dump(OS, getDumpOpts(), DumpOffsets); + DICtx.dump(OS, getDumpOpts(DICtx), DumpOffsets); return true; } static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx, - Twine Filename, raw_ostream &OS) { + const Twine &Filename, raw_ostream &OS) { // Verify the DWARF and exit with non-zero exit status if verification // fails. raw_ostream &stream = Quiet ? nulls() : OS; stream << "Verifying " << Filename.str() << ":\tfile format " << Obj.getFileFormatName() << "\n"; - bool Result = DICtx.verify(stream, getDumpOpts()); + bool Result = DICtx.verify(stream, getDumpOpts(DICtx)); if (Result) stream << "No errors.\n"; else @@ -488,10 +519,16 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, error(Filename, errorToErrorCode(BinOrErr.takeError())); bool Result = true; + auto RecoverableErrorHandler = [&](Error E) { + Result = false; + WithColor::defaultErrorHandler(std::move(E)); + }; if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) { if (filterArch(*Obj)) { - std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj); - Result = HandleObj(*Obj, *DICtx, Filename, OS); + std::unique_ptr<DWARFContext> DICtx = + DWARFContext::create(*Obj, nullptr, "", RecoverableErrorHandler); + if (!HandleObj(*Obj, *DICtx, Filename, OS)) + Result = false; } } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) @@ -501,15 +538,18 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, if (auto MachOOrErr = ObjForArch.getAsObjectFile()) { auto &Obj = **MachOOrErr; if (filterArch(Obj)) { - std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(Obj); - Result &= HandleObj(Obj, *DICtx, ObjName, OS); + std::unique_ptr<DWARFContext> DICtx = + DWARFContext::create(Obj, nullptr, "", RecoverableErrorHandler); + if (!HandleObj(Obj, *DICtx, ObjName, OS)) + Result = false; } continue; } else consumeError(MachOOrErr.takeError()); if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { error(ObjName, errorToErrorCode(ArchiveOrErr.takeError())); - Result &= handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS); + if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS)) + Result = false; continue; } else consumeError(ArchiveOrErr.takeError()); @@ -566,6 +606,10 @@ static std::vector<std::string> expandBundle(const std::string &InputPath) { int main(int argc, char **argv) { InitLLVM X(argc, argv); + // Flush outs() when printing to errs(). This avoids interleaving output + // between the two. + errs().tie(&outs()); + llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); @@ -593,7 +637,7 @@ int main(int argc, char **argv) { // Defaults to dumping all sections, unless brief mode is specified in which // case only the .debug_info section in dumped. -#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \ +#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ if (Dump##ENUM_NAME.IsRequested) { \ DumpType |= DIDT_##ENUM_NAME; \ if (Dump##ENUM_NAME.HasValue) { \ @@ -629,18 +673,20 @@ int main(int argc, char **argv) { Objects.insert(Objects.end(), Objs.begin(), Objs.end()); } + bool Success = true; if (Verify) { - // If we encountered errors during verify, exit with a non-zero exit status. - if (!all_of(Objects, [&](std::string Object) { - return handleFile(Object, verifyObjectFile, OutputFile.os()); - })) - return 1; - } else if (Statistics) for (auto Object : Objects) - handleFile(Object, collectStatsForObjectFile, OutputFile.os()); - else + Success &= handleFile(Object, verifyObjectFile, OutputFile.os()); + } else if (Statistics) { for (auto Object : Objects) - handleFile(Object, dumpObjectFile, OutputFile.os()); + Success &= handleFile(Object, collectStatsForObjectFile, OutputFile.os()); + } else if (ShowSectionSizes) { + for (auto Object : Objects) + Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os()); + } else { + for (auto Object : Objects) + Success &= handleFile(Object, dumpObjectFile, OutputFile.os()); + } - return EXIT_SUCCESS; + return Success ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h new file mode 100644 index 0000000000000..dc41298265d2a --- /dev/null +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h @@ -0,0 +1,43 @@ +//===-- llvm-dwarfdump - Debug info dumping utility -------------*- 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_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H +#define LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H + +#include "llvm/ADT/Twine.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace dwarfdump { + +/// Holds cumulative section sizes for an object file. +struct SectionSizes { + /// Map of .debug section names and their sizes across all such-named + /// sections. + StringMap<uint64_t> DebugSectionSizes; + /// Total number of bytes of all sections. + uint64_t TotalObjectSize = 0; + /// Total number of bytes of all debug sections. + uint64_t TotalDebugSectionsSize = 0; +}; + +/// Calculate the section sizes. +void calculateSectionSizes(const object::ObjectFile &Obj, SectionSizes &Sizes, + const Twine &Filename); + +bool collectStatsForObjectFile(object::ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, raw_ostream &OS); +bool collectObjectSectionSizes(object::ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, raw_ostream &OS); + +} // namespace dwarfdump +} // namespace llvm + +#endif |