summaryrefslogtreecommitdiff
path: root/tools/llvm-dwarfdump
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-12-18 20:10:56 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-12-18 20:10:56 +0000
commit044eb2f6afba375a914ac9d8024f8f5142bb912e (patch)
tree1475247dc9f9fe5be155ebd4c9069c75aadf8c20 /tools/llvm-dwarfdump
parenteb70dddbd77e120e5d490bd8fbe7ff3f8fa81c6b (diff)
Notes
Diffstat (limited to 'tools/llvm-dwarfdump')
-rw-r--r--tools/llvm-dwarfdump/CMakeLists.txt8
-rw-r--r--tools/llvm-dwarfdump/Statistics.cpp239
-rw-r--r--tools/llvm-dwarfdump/fuzzer/CMakeLists.txt6
-rw-r--r--tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp13
-rw-r--r--tools/llvm-dwarfdump/llvm-dwarfdump.cpp573
5 files changed, 720 insertions, 119 deletions
diff --git a/tools/llvm-dwarfdump/CMakeLists.txt b/tools/llvm-dwarfdump/CMakeLists.txt
index 9a2e53f5a4bb..77620e0faaf8 100644
--- a/tools/llvm-dwarfdump/CMakeLists.txt
+++ b/tools/llvm-dwarfdump/CMakeLists.txt
@@ -1,13 +1,15 @@
set(LLVM_LINK_COMPONENTS
DebugInfoDWARF
+ AllTargetsDescs
+ AllTargetsInfos
+ MC
Object
Support
)
add_llvm_tool(llvm-dwarfdump
+ Statistics.cpp
llvm-dwarfdump.cpp
)
-if(LLVM_USE_SANITIZE_COVERAGE)
- add_subdirectory(fuzzer)
-endif()
+add_subdirectory(fuzzer)
diff --git a/tools/llvm-dwarfdump/Statistics.cpp b/tools/llvm-dwarfdump/Statistics.cpp
new file mode 100644
index 000000000000..9a7454a52624
--- /dev/null
+++ b/tools/llvm-dwarfdump/Statistics.cpp
@@ -0,0 +1,239 @@
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/DebugInfo/DIContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
+#include "llvm/Object/ObjectFile.h"
+
+#define DEBUG_TYPE "dwarfdump"
+using namespace llvm;
+using namespace object;
+
+/// 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 variables with location across all inlined instances.
+ unsigned TotalVarWithLoc = 0;
+ /// Number of constants with location across all inlined instances.
+ unsigned ConstantMembers = 0;
+ /// List of all Variables in this function.
+ SmallDenseSet<uint32_t, 4> VarsInFunction;
+ /// Compile units also cover a PC range, but have this flag set to false.
+ bool IsFunction = false;
+};
+
+/// Holds accumulated global statistics about local variables.
+struct GlobalStats {
+ /// Total number of PC range bytes covered by DW_AT_locations.
+ unsigned ScopeBytesCovered = 0;
+ /// Total number of PC range bytes in each variable's enclosing scope,
+ /// starting from the first definition of the variable.
+ unsigned ScopeBytesFromFirstDefinition = 0;
+};
+
+/// Extract the low pc from a Die.
+static uint64_t getLowPC(DWARFDie Die) {
+ if (Die.getAddressRanges().size())
+ return Die.getAddressRanges()[0].LowPC;
+ return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0);
+}
+
+/// Collect debug info quality metrics for one DIE.
+static void collectStatsForDie(DWARFDie Die, std::string Prefix,
+ uint64_t ScopeLowPC, uint64_t BytesInScope,
+ StringMap<PerFunctionStats> &FnStatMap,
+ GlobalStats &GlobalStats) {
+ bool HasLoc = false;
+ uint64_t BytesCovered = 0;
+ uint64_t OffsetToFirstDefinition = 0;
+ if (Die.find(dwarf::DW_AT_const_value)) {
+ // This catches constant members *and* variables.
+ HasLoc = true;
+ BytesCovered = BytesInScope;
+ } else if (Die.getTag() == dwarf::DW_TAG_variable ||
+ Die.getTag() == dwarf::DW_TAG_formal_parameter) {
+ // Handle variables and function arguments.
+ auto FormValue = Die.find(dwarf::DW_AT_location);
+ HasLoc = FormValue.hasValue();
+ if (HasLoc) {
+ // Get PC coverage.
+ if (auto DebugLocOffset = FormValue->getAsSectionOffset()) {
+ auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc();
+ if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) {
+ for (auto Entry : List->Entries)
+ BytesCovered += Entry.End - Entry.Begin;
+ if (List->Entries.size()) {
+ uint64_t FirstDef = List->Entries[0].Begin;
+ uint64_t UnitOfs = getLowPC(Die.getDwarfUnit()->getUnitDIE());
+ // Ranges sometimes start before the lexical scope.
+ if (UnitOfs + FirstDef >= ScopeLowPC)
+ OffsetToFirstDefinition = UnitOfs + FirstDef - ScopeLowPC;
+ // Or even after it. Count that as a failure.
+ if (OffsetToFirstDefinition > BytesInScope)
+ OffsetToFirstDefinition = 0;
+ }
+ }
+ assert(BytesInScope);
+ } else {
+ // Assume the entire range is covered by a single location.
+ BytesCovered = BytesInScope;
+ }
+ }
+ } else {
+ // Not a variable or constant member.
+ return;
+ }
+
+ // Collect PC range coverage data.
+ auto &FnStats = FnStatMap[Prefix];
+ if (DWARFDie D =
+ Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
+ Die = D;
+ // This is a unique ID for the variable inside the current object file.
+ unsigned CanonicalDieOffset = Die.getOffset();
+ FnStats.VarsInFunction.insert(CanonicalDieOffset);
+ if (BytesInScope) {
+ FnStats.TotalVarWithLoc += (unsigned)HasLoc;
+ // Adjust for the fact the variables often start their lifetime in the
+ // middle of the scope.
+ BytesInScope -= OffsetToFirstDefinition;
+ // Turns out we have a lot of ranges that extend past the lexical scope.
+ GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered);
+ GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope;
+ assert(GlobalStats.ScopeBytesCovered <=
+ GlobalStats.ScopeBytesFromFirstDefinition);
+ } else {
+ FnStats.ConstantMembers++;
+ }
+}
+
+/// Recursively collect debug info quality metrics.
+static void collectStatsRecursive(DWARFDie Die, std::string Prefix,
+ uint64_t ScopeLowPC, uint64_t BytesInScope,
+ StringMap<PerFunctionStats> &FnStatMap,
+ GlobalStats &GlobalStats) {
+ // Handle any kind of lexical scope.
+ if (Die.getTag() == dwarf::DW_TAG_subprogram ||
+ Die.getTag() == dwarf::DW_TAG_inlined_subroutine ||
+ Die.getTag() == dwarf::DW_TAG_lexical_block) {
+ // Ignore forward declarations.
+ if (Die.find(dwarf::DW_AT_declaration))
+ return;
+
+ // Count the function.
+ if (Die.getTag() != dwarf::DW_TAG_lexical_block) {
+ StringRef Name = Die.getName(DINameKind::LinkageName);
+ if (Name.empty())
+ Name = Die.getName(DINameKind::ShortName);
+ Prefix = 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];
+ FnStats.NumFnInlined++;
+ FnStats.IsFunction = true;
+ }
+
+ // PC Ranges.
+ auto Ranges = Die.getAddressRanges();
+ uint64_t BytesInThisScope = 0;
+ for (auto Range : Ranges)
+ BytesInThisScope += Range.HighPC - Range.LowPC;
+ ScopeLowPC = getLowPC(Die);
+
+ if (BytesInThisScope)
+ BytesInScope = BytesInThisScope;
+ } else {
+ // Not a scope, visit the Die itself. It could be a variable.
+ collectStatsForDie(Die, Prefix, ScopeLowPC, BytesInScope, FnStatMap,
+ GlobalStats);
+ }
+
+ // Traverse children.
+ DWARFDie Child = Die.getFirstChild();
+ while (Child) {
+ collectStatsRecursive(Child, Prefix, ScopeLowPC, BytesInScope, FnStatMap,
+ GlobalStats);
+ Child = Child.getSibling();
+ }
+}
+
+/// Print machine-readable output.
+/// The machine-readable format is single-line JSON output.
+/// \{
+static void printDatum(raw_ostream &OS, const char *Key, StringRef Value) {
+ OS << ",\"" << Key << "\":\"" << Value << '"';
+ DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
+}
+static void printDatum(raw_ostream &OS, const char *Key, uint64_t Value) {
+ OS << ",\"" << Key << "\":" << Value;
+ DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
+}
+/// \}
+
+/// Collect debug info quality metrics for an entire DIContext.
+///
+/// Do the impossible and reduce the quality of the debug info down to a few
+/// numbers. The idea is to condense the data into numbers that can be tracked
+/// over time to identify trends in newer compiler versions and gauge the effect
+/// 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) {
+ StringRef FormatName = Obj.getFileFormatName();
+ GlobalStats GlobalStats;
+ StringMap<PerFunctionStats> Statistics;
+ for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
+ if (DWARFDie CUDie = CU->getUnitDIE(false))
+ collectStatsRecursive(CUDie, "/", 0, 0, Statistics, GlobalStats);
+
+ /// 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 = 1;
+ unsigned VarTotal = 0;
+ unsigned VarUnique = 0;
+ unsigned VarWithLoc = 0;
+ unsigned NumFunctions = 0;
+ unsigned NumInlinedFunctions = 0;
+ for (auto &Entry : Statistics) {
+ PerFunctionStats &Stats = Entry.getValue();
+ unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined;
+ unsigned Constants = Stats.ConstantMembers;
+ VarWithLoc += Stats.TotalVarWithLoc + Constants;
+ VarTotal += TotalVars + Constants;
+ VarUnique += Stats.VarsInFunction.size();
+ DEBUG(for (auto V : Stats.VarsInFunction)
+ llvm::dbgs() << Entry.getKey() << ": " << V << "\n");
+ NumFunctions += Stats.IsFunction;
+ NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
+ }
+
+ // Print summary.
+ OS.SetBufferSize(1024);
+ OS << "{\"version\":\"" << Version << '"';
+ 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, "inlined functions", NumInlinedFunctions);
+ printDatum(OS, "unique source variables", VarUnique);
+ printDatum(OS, "source variables", VarTotal);
+ printDatum(OS, "variables with location", VarWithLoc);
+ printDatum(OS, "scope bytes total",
+ GlobalStats.ScopeBytesFromFirstDefinition);
+ printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
+ OS << "}\n";
+ DEBUG(
+ llvm::dbgs() << "Total Availability: "
+ << (int)std::round((VarWithLoc * 100.0) / VarTotal) << "%\n";
+ llvm::dbgs() << "PC Ranges covered: "
+ << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
+ GlobalStats.ScopeBytesFromFirstDefinition)
+ << "%\n");
+ return true;
+}
diff --git a/tools/llvm-dwarfdump/fuzzer/CMakeLists.txt b/tools/llvm-dwarfdump/fuzzer/CMakeLists.txt
index 1de35a3de478..318c4f7bfce4 100644
--- a/tools/llvm-dwarfdump/fuzzer/CMakeLists.txt
+++ b/tools/llvm-dwarfdump/fuzzer/CMakeLists.txt
@@ -4,11 +4,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
-add_llvm_executable(llvm-dwarfdump-fuzzer
+add_llvm_fuzzer(llvm-dwarfdump-fuzzer
EXCLUDE_FROM_ALL
llvm-dwarfdump-fuzzer.cpp
)
-
-target_link_libraries(llvm-dwarfdump-fuzzer
- LLVMFuzzer
- )
diff --git a/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp b/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp
index 32e173f9d1fc..53c74df40280 100644
--- a/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp
+++ b/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp
@@ -20,7 +20,7 @@
using namespace llvm;
using namespace object;
-extern "C" void LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
+extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
std::unique_ptr<MemoryBuffer> Buff = MemoryBuffer::getMemBuffer(
StringRef((const char *)data, size), "", false);
@@ -28,9 +28,14 @@ extern "C" void LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
ObjectFile::createObjectFile(Buff->getMemBufferRef());
if (auto E = ObjOrErr.takeError()) {
consumeError(std::move(E));
- return;
+ return 0;
}
ObjectFile &Obj = *ObjOrErr.get();
- std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
- DICtx->dump(nulls(), DIDT_All);
+ std::unique_ptr<DIContext> DICtx = DWARFContext::create(Obj);
+
+
+ DIDumpOptions opts;
+ opts.DumpType = DIDT_All;
+ DICtx->dump(nulls(), opts);
+ return 0;
}
diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index ec5e554d4f5f..e4e34efff842 100644
--- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -12,12 +12,13 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/Archive.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
-#include "llvm/Object/RelocVisitor.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
@@ -25,120 +26,379 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Regex.h"
#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
-#include <algorithm>
-#include <cstring>
-#include <string>
-#include <system_error>
using namespace llvm;
using namespace object;
-static cl::list<std::string>
-InputFilenames(cl::Positional, cl::desc("<input object files or .dSYM bundles>"),
- cl::ZeroOrMore);
-
-static cl::opt<DIDumpType> DumpType(
- "debug-dump", cl::init(DIDT_All), cl::desc("Dump of debug sections:"),
- cl::values(
- clEnumValN(DIDT_All, "all", "Dump all debug sections"),
- clEnumValN(DIDT_Abbrev, "abbrev", ".debug_abbrev"),
- clEnumValN(DIDT_AbbrevDwo, "abbrev.dwo", ".debug_abbrev.dwo"),
- clEnumValN(DIDT_AppleNames, "apple_names", ".apple_names"),
- clEnumValN(DIDT_AppleTypes, "apple_types", ".apple_types"),
- clEnumValN(DIDT_AppleNamespaces, "apple_namespaces",
- ".apple_namespaces"),
- clEnumValN(DIDT_AppleObjC, "apple_objc", ".apple_objc"),
- clEnumValN(DIDT_Aranges, "aranges", ".debug_aranges"),
- clEnumValN(DIDT_Info, "info", ".debug_info"),
- clEnumValN(DIDT_InfoDwo, "info.dwo", ".debug_info.dwo"),
- clEnumValN(DIDT_Types, "types", ".debug_types"),
- clEnumValN(DIDT_TypesDwo, "types.dwo", ".debug_types.dwo"),
- clEnumValN(DIDT_Line, "line", ".debug_line"),
- clEnumValN(DIDT_LineDwo, "line.dwo", ".debug_line.dwo"),
- clEnumValN(DIDT_Loc, "loc", ".debug_loc"),
- clEnumValN(DIDT_LocDwo, "loc.dwo", ".debug_loc.dwo"),
- clEnumValN(DIDT_Frames, "frames", ".debug_frame"),
- clEnumValN(DIDT_Macro, "macro", ".debug_macinfo"),
- clEnumValN(DIDT_Ranges, "ranges", ".debug_ranges"),
- clEnumValN(DIDT_Pubnames, "pubnames", ".debug_pubnames"),
- clEnumValN(DIDT_Pubtypes, "pubtypes", ".debug_pubtypes"),
- clEnumValN(DIDT_GnuPubnames, "gnu_pubnames", ".debug_gnu_pubnames"),
- clEnumValN(DIDT_GnuPubtypes, "gnu_pubtypes", ".debug_gnu_pubtypes"),
- clEnumValN(DIDT_Str, "str", ".debug_str"),
- clEnumValN(DIDT_StrOffsets, "str_offsets", ".debug_str_offsets"),
- clEnumValN(DIDT_StrDwo, "str.dwo", ".debug_str.dwo"),
- clEnumValN(DIDT_StrOffsetsDwo, "str_offsets.dwo",
- ".debug_str_offsets.dwo"),
- clEnumValN(DIDT_CUIndex, "cu_index", ".debug_cu_index"),
- clEnumValN(DIDT_GdbIndex, "gdb_index", ".gdb_index"),
- clEnumValN(DIDT_TUIndex, "tu_index", ".debug_tu_index")));
+/// Parser for options that take an optional offest argument.
+/// @{
+struct OffsetOption {
+ uint64_t Val = 0;
+ bool HasValue = false;
+ bool IsRequested = false;
+};
-static cl::opt<bool>
- SummarizeTypes("summarize-types",
- cl::desc("Abbreviate the description of type unit entries"));
+namespace llvm {
+namespace cl {
+template <>
+class parser<OffsetOption> final : public basic_parser<OffsetOption> {
+public:
+ parser(Option &O) : basic_parser(O) {}
+
+ /// Return true on error.
+ bool parse(Option &O, StringRef ArgName, StringRef Arg, OffsetOption &Val) {
+ if (Arg == "") {
+ Val.Val = 0;
+ Val.HasValue = false;
+ Val.IsRequested = true;
+ return false;
+ }
+ if (Arg.getAsInteger(0, Val.Val))
+ return O.error("'" + Arg + "' value invalid for integer argument!");
+ Val.HasValue = true;
+ Val.IsRequested = true;
+ return false;
+ }
+
+ enum ValueExpected getValueExpectedFlagDefault() const {
+ return ValueOptional;
+ }
+
+ void printOptionInfo(const Option &O, size_t GlobalWidth) const {
+ outs() << " -" << O.ArgStr;
+ Option::printHelpStr(O.HelpStr, GlobalWidth, getOptionWidth(O));
+ }
+
+ void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,
+ size_t GlobalWidth) const {
+ printOptionName(O, GlobalWidth);
+ outs() << "[=offset]";
+ }
+
+ // An out-of-line virtual method to provide a 'home' for this class.
+ void anchor() override {};
+};
+} // cl
+} // llvm
-static cl::opt<bool> Verify("verify", cl::desc("Verify the DWARF debug info"));
+/// @}
+/// Command line options.
+/// @{
-static cl::opt<bool> Quiet("quiet",
- cl::desc("Use with -verify to not emit to STDOUT."));
+namespace {
+using namespace cl;
-static cl::opt<bool> Brief("brief", cl::desc("Print fewer low-level details"));
+OptionCategory DwarfDumpCategory("Specific Options");
+static opt<bool> Help("h", desc("Alias for -help"), Hidden,
+ cat(DwarfDumpCategory));
+static list<std::string>
+ InputFilenames(Positional, desc("<input object files or .dSYM bundles>"),
+ ZeroOrMore, cat(DwarfDumpCategory));
-static void error(StringRef Filename, std::error_code EC) {
+cl::OptionCategory SectionCategory("Section-specific Dump Options",
+ "These control which sections are dumped. "
+ "Where applicable these parameters take an "
+ "optional =<offset> argument to dump only "
+ "the entry at the specified offset.");
+
+static opt<bool> DumpAll("all", desc("Dump all debug info sections"),
+ cat(SectionCategory));
+static alias DumpAllAlias("a", desc("Alias for -all"), aliasopt(DumpAll));
+
+// Options for dumping specific sections.
+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));
+#include "llvm/BinaryFormat/Dwarf.def"
+#undef HANDLE_DWARF_SECTION
+
+static alias DumpDebugFrameAlias("eh-frame", desc("Alias for -debug-frame"),
+ NotHidden, cat(SectionCategory),
+ aliasopt(DumpDebugFrame));
+static list<std::string>
+ ArchFilters("arch",
+ desc("Dump debug information for the specified CPU "
+ "architecture only. Architectures may be specified by "
+ "name or by number. This option can be specified "
+ "multiple times, once for each desired architecture."),
+ cat(DwarfDumpCategory));
+static opt<bool>
+ Diff("diff",
+ desc("Emit diff-friendly output by omitting offsets and addresses."),
+ cat(DwarfDumpCategory));
+static list<std::string>
+ Find("find",
+ desc("Search for the exact match for <name> in the accelerator tables "
+ "and print the matching debug information entries. When no "
+ "accelerator tables are available, the slower but more complete "
+ "-name option can be used instead."),
+ value_desc("name"), cat(DwarfDumpCategory));
+static alias FindAlias("f", desc("Alias for -find."), aliasopt(Find));
+static opt<bool>
+ IgnoreCase("ignore-case",
+ desc("Ignore case distinctions in when searching by name."),
+ value_desc("i"), cat(DwarfDumpCategory));
+static alias IgnoreCaseAlias("i", desc("Alias for -ignore-case."),
+ aliasopt(IgnoreCase));
+static list<std::string> Name(
+ "name",
+ desc("Find and print all debug info entries whose name (DW_AT_name "
+ "attribute) matches the exact text in <pattern>. When used with the "
+ "the -regex option <pattern> is interpreted as a regular expression."),
+ value_desc("pattern"), cat(DwarfDumpCategory));
+static alias NameAlias("n", desc("Alias for -name"), aliasopt(Name));
+static opt<unsigned>
+ Lookup("lookup",
+ desc("Lookup <address> in the debug information and print out any"
+ "available file, function, block and line table details."),
+ value_desc("address"), cat(DwarfDumpCategory));
+static opt<std::string>
+ OutputFilename("out-file", cl::init(""),
+ cl::desc("Redirect output to the specified file."),
+ cl::value_desc("filename"));
+static alias OutputFilenameAlias("o", desc("Alias for -out-file."),
+ aliasopt(OutputFilename),
+ cat(DwarfDumpCategory));
+static opt<bool>
+ UseRegex("regex",
+ desc("Treat any <pattern> strings as regular expressions when "
+ "searching instead of just as an exact string match."),
+ cat(DwarfDumpCategory));
+static alias RegexAlias("x", desc("Alias for -regex"), aliasopt(UseRegex));
+static opt<bool>
+ ShowChildren("show-children",
+ desc("Show a debug info entry's children when selectively "
+ "printing with the =<offset> option."),
+ cat(DwarfDumpCategory));
+static alias ShowChildrenAlias("c", desc("Alias for -show-children."),
+ aliasopt(ShowChildren));
+static opt<bool>
+ ShowParents("show-parents",
+ desc("Show a debug info entry's parents when selectively "
+ "printing with the =<offset> option."),
+ cat(DwarfDumpCategory));
+static alias ShowParentsAlias("p", desc("Alias for -show-parents."),
+ aliasopt(ShowParents));
+static opt<bool>
+ ShowForm("show-form",
+ desc("Show DWARF form types after the DWARF attribute types."),
+ cat(DwarfDumpCategory));
+static alias ShowFormAlias("F", desc("Alias for -show-form."),
+ aliasopt(ShowForm), cat(DwarfDumpCategory));
+static opt<unsigned> RecurseDepth(
+ "recurse-depth",
+ desc("Only recurse to a depth of N when displaying debug info entries."),
+ cat(DwarfDumpCategory), init(-1U), value_desc("N"));
+static alias RecurseDepthAlias("r", desc("Alias for -recurse-depth."),
+ aliasopt(RecurseDepth));
+
+static opt<bool>
+ SummarizeTypes("summarize-types",
+ desc("Abbreviate the description of type unit entries."),
+ cat(DwarfDumpCategory));
+static cl::opt<bool>
+ Statistics("statistics",
+ cl::desc("Emit JSON-formatted debug info quality metrics."),
+ 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."),
+ cat(DwarfDumpCategory));
+static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."),
+ cat(DwarfDumpCategory));
+static alias DumpUUIDAlias("u", desc("Alias for -uuid."), aliasopt(DumpUUID));
+static opt<bool> Verbose("verbose",
+ desc("Print more low-level encoding details."),
+ cat(DwarfDumpCategory));
+static alias VerboseAlias("v", desc("Alias for -verbose."), aliasopt(Verbose),
+ cat(DwarfDumpCategory));
+} // namespace
+/// @}
+//===----------------------------------------------------------------------===//
+
+static void error(StringRef Prefix, std::error_code EC) {
if (!EC)
return;
- errs() << Filename << ": " << EC.message() << "\n";
+ errs() << Prefix << ": " << EC.message() << "\n";
exit(1);
}
-static void DumpObjectFile(ObjectFile &Obj, Twine Filename) {
- std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
-
- outs() << Filename.str() << ":\tfile format " << Obj.getFileFormatName()
- << "\n\n";
-
-
- // Dump the complete DWARF structure.
+static DIDumpOptions getDumpOpts() {
DIDumpOptions DumpOpts;
DumpOpts.DumpType = DumpType;
+ DumpOpts.RecurseDepth = RecurseDepth;
+ DumpOpts.ShowAddresses = !Diff;
+ DumpOpts.ShowChildren = ShowChildren;
+ DumpOpts.ShowParents = ShowParents;
+ DumpOpts.ShowForm = ShowForm;
DumpOpts.SummarizeTypes = SummarizeTypes;
- DumpOpts.Brief = Brief;
- DICtx->dump(outs(), DumpOpts);
+ DumpOpts.Verbose = Verbose;
+ // In -verify mode, print DIEs without children in error messages.
+ if (Verify)
+ return DumpOpts.noImplicitRecursion();
+ return DumpOpts;
}
-static void DumpInput(StringRef Filename) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
- MemoryBuffer::getFileOrSTDIN(Filename);
- error(Filename, BuffOrErr.getError());
- std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get());
+static uint32_t getCPUType(MachOObjectFile &MachO) {
+ if (MachO.is64Bit())
+ return MachO.getHeader64().cputype;
+ else
+ return MachO.getHeader().cputype;
+}
- Expected<std::unique_ptr<Binary>> BinOrErr =
- object::createBinary(Buff->getMemBufferRef());
- if (!BinOrErr)
- error(Filename, errorToErrorCode(BinOrErr.takeError()));
+/// Return true if the object file has not been filtered by an --arch option.
+static bool filterArch(ObjectFile &Obj) {
+ if (ArchFilters.empty())
+ return true;
- if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get()))
- DumpObjectFile(*Obj, Filename);
- else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
- for (auto &ObjForArch : Fat->objects()) {
- auto MachOOrErr = ObjForArch.getAsObjectFile();
- error(Filename, errorToErrorCode(MachOOrErr.takeError()));
- DumpObjectFile(**MachOOrErr,
- Filename + " (" + ObjForArch.getArchFlagName() + ")");
+ if (auto *MachO = dyn_cast<MachOObjectFile>(&Obj)) {
+ std::string ObjArch =
+ Triple::getArchTypeName(MachO->getArchTriple().getArch());
+
+ for (auto Arch : ArchFilters) {
+ // Match name.
+ if (Arch == ObjArch)
+ return true;
+
+ // Match architecture number.
+ unsigned Value;
+ if (!StringRef(Arch).getAsInteger(0, Value))
+ if (Value == getCPUType(*MachO))
+ return true;
+ }
+ }
+ return false;
+}
+
+using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, Twine,
+ raw_ostream &)>;
+
+/// Print only DIEs that have a certain name.
+static void filterByName(const StringSet<> &Names,
+ DWARFContext::cu_iterator_range CUs, raw_ostream &OS) {
+ for (const auto &CU : CUs)
+ for (const auto &Entry : CU->dies()) {
+ DWARFDie Die = {CU.get(), &Entry};
+ if (const char *NamePtr = Die.getName(DINameKind::ShortName)) {
+ std::string Name =
+ (IgnoreCase && !UseRegex) ? StringRef(NamePtr).lower() : NamePtr;
+ // Match regular expression.
+ if (UseRegex)
+ for (auto Pattern : Names.keys()) {
+ Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags);
+ std::string Error;
+ if (!RE.isValid(Error)) {
+ errs() << "error in regular expression: " << Error << "\n";
+ exit(1);
+ }
+ if (RE.match(Name))
+ Die.dump(OS, 0, getDumpOpts());
+ }
+ // Match full text.
+ else if (Names.count(Name))
+ Die.dump(OS, 0, getDumpOpts());
+ }
}
+
}
-static bool VerifyObjectFile(ObjectFile &Obj, Twine Filename) {
- std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj));
-
+/// Handle the --lookup option and dump the DIEs and line info for the given
+/// address.
+static bool lookup(DWARFContext &DICtx, uint64_t Address, raw_ostream &OS) {
+ auto DIEsForAddr = DICtx.getDIEsForAddress(Lookup);
+
+ if (!DIEsForAddr)
+ return false;
+
+ DIDumpOptions DumpOpts = getDumpOpts();
+ DumpOpts.RecurseDepth = 0;
+ DIEsForAddr.CompileUnit->dump(OS, DumpOpts);
+ if (DIEsForAddr.FunctionDIE) {
+ DIEsForAddr.FunctionDIE.dump(OS, 2, DumpOpts);
+ if (DIEsForAddr.BlockDIE)
+ DIEsForAddr.BlockDIE.dump(OS, 4, DumpOpts);
+ }
+
+ if (DILineInfo LineInfo = DICtx.getLineInfoForAddress(Lookup))
+ LineInfo.dump(OS);
+
+ 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) {
+ logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(),
+ Filename.str() + ": ");
+ // The UUID dump already contains all the same information.
+ if (!(DumpType & DIDT_UUID) || DumpType == DIDT_All)
+ OS << Filename << ":\tfile format " << Obj.getFileFormatName() << '\n';
+
+ // Handle the --lookup option.
+ if (Lookup)
+ return lookup(DICtx, Lookup, OS);
+
+ // Handle the --name option.
+ if (!Name.empty()) {
+ StringSet<> Names;
+ for (auto name : Name)
+ Names.insert((IgnoreCase && !UseRegex) ? StringRef(name).lower() : name);
+
+ filterByName(Names, DICtx.compile_units(), OS);
+ filterByName(Names, DICtx.dwo_compile_units(), OS);
+ return true;
+ }
+
+ // Handle the --find option and lower it to --debug-info=<offset>.
+ if (!Find.empty()) {
+ DumpOffsets[DIDT_ID_DebugInfo] = [&]() -> llvm::Optional<uint64_t> {
+ for (auto Name : Find) {
+ auto find = [&](const DWARFAcceleratorTable &Accel)
+ -> llvm::Optional<uint64_t> {
+ for (auto Entry : Accel.equal_range(Name))
+ for (auto Atom : Entry)
+ if (auto Offset = Atom.getAsSectionOffset())
+ return Offset;
+ return None;
+ };
+ if (auto Offset = find(DICtx.getAppleNames()))
+ return DumpOffsets[DIDT_ID_DebugInfo] = *Offset;
+ if (auto Offset = find(DICtx.getAppleTypes()))
+ return DumpOffsets[DIDT_ID_DebugInfo] = *Offset;
+ if (auto Offset = find(DICtx.getAppleNamespaces()))
+ return DumpOffsets[DIDT_ID_DebugInfo] = *Offset;
+ }
+ return None;
+ }();
+ // Early exit if --find was specified but the current file doesn't have it.
+ if (!DumpOffsets[DIDT_ID_DebugInfo])
+ return true;
+ }
+
+ // Dump the complete DWARF structure.
+ DICtx.dump(OS, getDumpOpts(), DumpOffsets);
+ return true;
+}
+
+static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
+ Twine Filename, raw_ostream &OS) {
// Verify the DWARF and exit with non-zero exit status if verification
// fails.
- raw_ostream &stream = Quiet ? nulls() : outs();
+ raw_ostream &stream = Quiet ? nulls() : OS;
stream << "Verifying " << Filename.str() << ":\tfile format "
<< Obj.getFileFormatName() << "\n";
- bool Result = DICtx->verify(stream, DumpType);
+ bool Result = DICtx.verify(stream, getDumpOpts());
if (Result)
stream << "No errors.\n";
else
@@ -146,30 +406,72 @@ static bool VerifyObjectFile(ObjectFile &Obj, Twine Filename) {
return Result;
}
-static bool VerifyInput(StringRef Filename) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
- MemoryBuffer::getFileOrSTDIN(Filename);
- error(Filename, BuffOrErr.getError());
- std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get());
-
- Expected<std::unique_ptr<Binary>> BinOrErr =
- object::createBinary(Buff->getMemBufferRef());
- if (!BinOrErr)
- error(Filename, errorToErrorCode(BinOrErr.takeError()));
-
+static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
+ HandlerFn HandleObj, raw_ostream &OS);
+
+static bool handleArchive(StringRef Filename, Archive &Arch,
+ HandlerFn HandleObj, raw_ostream &OS) {
+ bool Result = true;
+ Error Err = Error::success();
+ for (auto Child : Arch.children(Err)) {
+ auto BuffOrErr = Child.getMemoryBufferRef();
+ error(Filename, errorToErrorCode(BuffOrErr.takeError()));
+ auto NameOrErr = Child.getName();
+ error(Filename, errorToErrorCode(NameOrErr.takeError()));
+ std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();
+ Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS);
+ }
+ error(Filename, errorToErrorCode(std::move(Err)));
+
+ return Result;
+}
+
+static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
+ HandlerFn HandleObj, raw_ostream &OS) {
+ Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer);
+ error(Filename, errorToErrorCode(BinOrErr.takeError()));
+
bool Result = true;
- if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get()))
- Result = VerifyObjectFile(*Obj, Filename);
+ 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);
+ }
+ }
else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
for (auto &ObjForArch : Fat->objects()) {
- auto MachOOrErr = ObjForArch.getAsObjectFile();
- error(Filename, errorToErrorCode(MachOOrErr.takeError()));
- if (!VerifyObjectFile(**MachOOrErr, Filename + " (" + ObjForArch.getArchFlagName() + ")"))
- Result = false;
+ std::string ObjName =
+ (Filename + "(" + ObjForArch.getArchFlagName() + ")").str();
+ 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);
+ }
+ continue;
+ } else
+ consumeError(MachOOrErr.takeError());
+ if (auto ArchiveOrErr = ObjForArch.getAsArchive()) {
+ error(ObjName, errorToErrorCode(ArchiveOrErr.takeError()));
+ Result &= handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS);
+ continue;
+ } else
+ consumeError(ArchiveOrErr.takeError());
}
+ else if (auto *Arch = dyn_cast<Archive>(BinOrErr->get()))
+ Result = handleArchive(Filename, *Arch, HandleObj, OS);
return Result;
}
+static bool handleFile(StringRef Filename, HandlerFn HandleObj,
+ raw_ostream &OS) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
+ MemoryBuffer::getFileOrSTDIN(Filename);
+ error(Filename, BuffOrErr.getError());
+ std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
+ return handleBuffer(Filename, *Buffer, HandleObj, OS);
+}
+
/// If the input path is a .dSYM bundle (as created by the dsymutil tool),
/// replace it with individual entries for each of the object files inside the
/// bundle otherwise return the input path.
@@ -209,7 +511,59 @@ int main(int argc, char **argv) {
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
- cl::ParseCommandLineOptions(argc, argv, "llvm dwarf dumper\n");
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+
+ HideUnrelatedOptions({&DwarfDumpCategory, &SectionCategory});
+ cl::ParseCommandLineOptions(
+ argc, argv,
+ "pretty-print DWARF debug information in object files"
+ " and debug info archives.\n");
+
+ if (Help) {
+ PrintHelpMessage(/*Hidden =*/false, /*Categorized =*/true);
+ return 0;
+ }
+
+ std::unique_ptr<ToolOutputFile> OutputFile;
+ if (!OutputFilename.empty()) {
+ std::error_code EC;
+ OutputFile = llvm::make_unique<ToolOutputFile>(OutputFilename, EC,
+ sys::fs::F_None);
+ error("Unable to open output file" + OutputFilename, EC);
+ // Don't remove output file if we exit with an error.
+ OutputFile->keep();
+ }
+
+ raw_ostream &OS = OutputFile ? OutputFile->os() : outs();
+ bool OffsetRequested = false;
+
+ // 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) \
+ if (Dump##ENUM_NAME.IsRequested) { \
+ DumpType |= DIDT_##ENUM_NAME; \
+ if (Dump##ENUM_NAME.HasValue) { \
+ DumpOffsets[DIDT_ID_##ENUM_NAME] = Dump##ENUM_NAME.Val; \
+ OffsetRequested = true; \
+ } \
+ }
+#include "llvm/BinaryFormat/Dwarf.def"
+#undef HANDLE_DWARF_SECTION
+ if (DumpUUID)
+ DumpType |= DIDT_UUID;
+ if (DumpAll)
+ DumpType = DIDT_All;
+ if (DumpType == DIDT_Null) {
+ if (Verbose)
+ DumpType = DIDT_All;
+ else
+ DumpType = DIDT_DebugInfo;
+ }
+
+ // Unless dumping a specific DIE, default to --show-children.
+ if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && Find.empty())
+ ShowChildren = true;
// Defaults to a.out if no filenames specified.
if (InputFilenames.size() == 0)
@@ -224,11 +578,16 @@ int main(int argc, char **argv) {
if (Verify) {
// If we encountered errors during verify, exit with a non-zero exit status.
- if (!std::all_of(Objects.begin(), Objects.end(), VerifyInput))
+ if (!std::all_of(Objects.begin(), Objects.end(), [&](std::string Object) {
+ return handleFile(Object, verifyObjectFile, OS);
+ }))
exit(1);
- } else {
- std::for_each(Objects.begin(), Objects.end(), DumpInput);
- }
+ } else if (Statistics)
+ for (auto Object : Objects)
+ handleFile(Object, collectStatsForObjectFile, OS);
+ else
+ for (auto Object : Objects)
+ handleFile(Object, dumpObjectFile, OS);
return EXIT_SUCCESS;
}