summaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-dwarfdump
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-dwarfdump')
-rw-r--r--llvm/tools/llvm-dwarfdump/SectionSizes.cpp124
-rw-r--r--llvm/tools/llvm-dwarfdump/Statistics.cpp407
-rw-r--r--llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp136
-rw-r--r--llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h43
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