Index: llvm/docs/CommandGuide/llvm-dwarfdump.rst =================================================================== --- llvm/docs/CommandGuide/llvm-dwarfdump.rst +++ llvm/docs/CommandGuide/llvm-dwarfdump.rst @@ -105,6 +105,10 @@ When displaying debug info entries, only show children to a maximum depth of . +.. option:: --show-section-sizes + + Show the sizes of all debug sections. + .. option:: --statistics Collect debug info quality metrics and print the results Index: llvm/include/llvm/Object/ObjectFile.h =================================================================== --- llvm/include/llvm/Object/ObjectFile.h +++ llvm/include/llvm/Object/ObjectFile.h @@ -123,6 +123,9 @@ /// contains data (e.g. PROGBITS), but is not text. bool isBerkeleyData() const; + /// Whether this section is debug section. + bool isDebugSection(StringRef SectionName) const; + bool containsSymbol(SymbolRef S) const; relocation_iterator relocation_begin() const; @@ -272,6 +275,7 @@ virtual bool isSectionStripped(DataRefImpl Sec) const; virtual bool isBerkeleyText(DataRefImpl Sec) const; virtual bool isBerkeleyData(DataRefImpl Sec) const; + virtual bool isDebugSection(StringRef SectionName) const; virtual relocation_iterator section_rel_begin(DataRefImpl Sec) const = 0; virtual relocation_iterator section_rel_end(DataRefImpl Sec) const = 0; virtual Expected getRelocatedSection(DataRefImpl Sec) const; @@ -495,6 +499,10 @@ return OwningObject->isBerkeleyData(SectionPimpl); } +inline bool SectionRef::isDebugSection(StringRef SectionName) const { + return OwningObject->isDebugSection(SectionName); +} + inline relocation_iterator SectionRef::relocation_begin() const { return OwningObject->section_rel_begin(SectionPimpl); } Index: llvm/lib/Object/ObjectFile.cpp =================================================================== --- llvm/lib/Object/ObjectFile.cpp +++ llvm/lib/Object/ObjectFile.cpp @@ -91,6 +91,14 @@ return isSectionData(Sec); } +bool ObjectFile::isDebugSection(StringRef SectionName) const { + return SectionName.startswith(".debug") || + SectionName.startswith("__debug") || + SectionName.startswith(".zdebug") || + SectionName.startswith("__zdebug") || SectionName == ".gdb_index" || + SectionName == "__gdb_index"; +} + Expected ObjectFile::getRelocatedSection(DataRefImpl Sec) const { return section_iterator(SectionRef(Sec, this)); Index: llvm/test/tools/llvm-dwarfdump/X86/macho_archive_section_sizes.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-dwarfdump/X86/macho_archive_section_sizes.test @@ -0,0 +1,47 @@ +RUN: llvm-dwarfdump --show-section-sizes %S/../../dsymutil/Inputs/libfat-test.a \ +RUN: | FileCheck %s --match-full-lines --strict-whitespace + + CHECK:file: {{.*}}libfat-test.a(x86_64)(fat-test.o) +CHECK-NEXT:---------------------------------------------------- +CHECK-NEXT:SECTION SIZE +CHECK-NEXT:-------------- -- +CHECK-NEXT:__debug_info 60 (3.83%) +CHECK-NEXT:__debug_loc 0 (0.00%) +CHECK-NEXT:__debug_ranges 0 (0.00%) +CHECK-NEXT:__debug_abbrev 44 (2.81%) +CHECK-NEXT:__debug_line 43 (2.74%) +CHECK-NEXT:__debug_str 69 (4.40%) +CHECK-EMPTY: +CHECK-NEXT: Total Size: 216 (13.78%) +CHECK-NEXT: Total File Size: 1568 +CHECK-NEXT:---------------------------------------------------- + + CHECK:file: {{.*}}libfat-test.a(i386)(fat-test.o) +CHECK-NEXT:---------------------------------------------------- +CHECK-NEXT:SECTION SIZE +CHECK-NEXT:-------------- -- +CHECK-NEXT:__debug_info 56 (4.00%) +CHECK-NEXT:__debug_loc 0 (0.00%) +CHECK-NEXT:__debug_ranges 0 (0.00%) +CHECK-NEXT:__debug_abbrev 44 (3.14%) +CHECK-NEXT:__debug_line 43 (3.07%) +CHECK-NEXT:__debug_str 67 (4.79%) +CHECK-EMPTY: +CHECK-NEXT: Total Size: 210 (15.00%) +CHECK-NEXT: Total File Size: 1400 +CHECK-NEXT:---------------------------------------------------- + + CHECK:file: {{.*}}libfat-test.a(x86_64h)(fat-test.o) +CHECK-NEXT:---------------------------------------------------- +CHECK-NEXT:SECTION SIZE +CHECK-NEXT:-------------- -- +CHECK-NEXT:__debug_info 60 (3.83%) +CHECK-NEXT:__debug_loc 0 (0.00%) +CHECK-NEXT:__debug_ranges 0 (0.00%) +CHECK-NEXT:__debug_abbrev 44 (2.81%) +CHECK-NEXT:__debug_line 43 (2.74%) +CHECK-NEXT:__debug_str 70 (4.46%) +CHECK-EMPTY: +CHECK-NEXT: Total Size: 217 (13.84%) +CHECK-NEXT: Total File Size: 1568 +CHECK-NEXT:---------------------------------------------------- Index: llvm/test/tools/llvm-dwarfdump/X86/section_sizes.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-dwarfdump/X86/section_sizes.test @@ -0,0 +1,48 @@ +## Check how llvm-dwarfdump calculates section sizes with --show-section-sizes. + +# RUN: yaml2obj --docnum=1 %s -o %t +# RUN: llvm-dwarfdump --show-section-sizes %t | FileCheck %s --match-full-lines --strict-whitespace + +# CHECK:file: {{.*}} +# CHECK-NEXT:---------------------------------------------------- +# CHECK-NEXT:SECTION SIZE +# CHECK-NEXT:--------------- --- +# CHECK-NEXT:.debug_info 17 (1.74%) +# CHECK-NEXT:.debug_loc 1 (0.10%) +# CHECK-NEXT:.debug_type 26 (2.66%) +# CHECK-NEXT:.debug_foo 100 (10.25%) +# CHECK-NEXT:.debug_info.dwo 9 (0.92%) +# CHECK-NEXT:.debug_line 19 (1.95%) +# CHECK-EMPTY: +# CHECK-NEXT: Total Size: 172 (17.62%) +# CHECK-NEXT: Total File Size: 976 +# CHECK-NEXT:---------------------------------------------------- + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .debug_info + Type: SHT_PROGBITS + Size: 17 + - Name: .debug_line + Type: SHT_PROGBITS + Size: 19 + - Name: .debug_loc + Type: SHT_PROGBITS + Size: 1 + - Name: .debug_type + Type: SHT_PROGBITS + Size: 13 + - Name: .debug_type [1] + Type: SHT_PROGBITS + Size: 13 + - Name: .debug_foo + Type: SHT_PROGBITS + Size: 100 + - Name: .debug_info.dwo + Type: SHT_PROGBITS + Size: 9 Index: llvm/test/tools/llvm-dwarfdump/X86/statistics.ll =================================================================== --- llvm/test/tools/llvm-dwarfdump/X86/statistics.ll +++ llvm/test/tools/llvm-dwarfdump/X86/statistics.ll @@ -48,7 +48,11 @@ ; CHECK: "scope bytes covered": ; CHECK: "total function size":[[FUNCSIZE:[0-9]+]] ; CHECK: "total inlined function size":[[INLINESIZE:[0-9]+]] - +; CHECK: "size of __debug_info":380 +; CHECK: "size of __debug_loc":35 +; CHECK: "size of __debug_abbrev":303 +; CHECK: "size of __debug_line":117 +; CHECK: "size of __debug_str":204 ; ModuleID = '/tmp/quality.cpp' source_filename = "/tmp/quality.cpp" Index: llvm/tools/llvm-dwarfdump/CMakeLists.txt =================================================================== --- llvm/tools/llvm-dwarfdump/CMakeLists.txt +++ llvm/tools/llvm-dwarfdump/CMakeLists.txt @@ -8,6 +8,7 @@ ) add_llvm_tool(llvm-dwarfdump + SectionSizes.cpp Statistics.cpp llvm-dwarfdump.cpp ) Index: llvm/tools/llvm-dwarfdump/SectionSizes.h =================================================================== --- /dev/null +++ llvm/tools/llvm-dwarfdump/SectionSizes.h @@ -0,0 +1,48 @@ +//===- SectionSizes.h - Debug section sizes ---------------------*- 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_SECTION_SIZES_H +#define LLVM_TOOLS_SECTION_SIZES_H + +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace object; + +using SectionSizeMap = llvm::StringMap; + +/// Holds cumulative section sizes for an object file. +struct SectionSizes { + /// Map of .debug section names and their sizes across all such-named + /// sections. + SectionSizeMap DebugSectionSizes; + /// Total number of bytes of all sections. + uint64_t TotalObjectSize = 0; + /// Total number of bytes of all debug sections. + uint64_t TotalDebugSectionsSize = 0; +}; + +/// Get the maximum section name width of the found debug sections. +size_t getMaxSectionNameWidth(const SectionSizes &Sizes, + StringRef SectionNameColPrintName); + +/// Get the maximum section size bytes number width of the found debug sections. +size_t getMaxSectionSizeNumberWidth(const SectionSizes &Sizes); + +/// Pretty print the section sizes. +void prettyPrintSectionSizes(const ObjectFile &Obj, const SectionSizes &Sizes, + raw_ostream &OS); + +/// Calculate the section sizes. +void calculateSectionSizes(const ObjectFile &Obj, SectionSizes &Sizes, + const Twine &Filename); + +#endif // LLVM_TOOLS_SECTION_SIZES_H Index: llvm/tools/llvm-dwarfdump/SectionSizes.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-dwarfdump/SectionSizes.cpp @@ -0,0 +1,118 @@ +//===-- 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 "SectionSizes.h" + +#define DEBUG_TYPE "dwarfdump" + +size_t getMaxSectionNameWidth(const SectionSizes &Sizes, + StringRef SectionNameColPrintName) { + // The minimum column width should be the size of "SECTION" plus 2. + size_t MinSectionColWidth = SectionNameColPrintName.size() + 2; + + size_t MaxWidth = MinSectionColWidth; + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + StringRef SectionName = DebugSec.getKey(); + MaxWidth = std::max(MaxWidth, SectionName.size()); + } + return MaxWidth; +} + +size_t getMaxSectionSizeNumberWidth(const SectionSizes &Sizes) { + size_t MaxNumberWidth = 1; + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + size_t NumWidth = std::to_string(DebugSec.getValue()).size(); + MaxNumberWidth = std::max(MaxNumberWidth, NumWidth); + } + + return MaxNumberWidth; +} + +void prettyPrintSectionSizes(const ObjectFile &Obj, const SectionSizes &Sizes, + raw_ostream &OS) { + StringRef SectionNameColPrintName = "SECTION"; + StringRef SecttonSizePrintName = "SIZE"; + + size_t MaxNameWidth = getMaxSectionNameWidth(Sizes, SectionNameColPrintName); + size_t MaxNumberWidth = getMaxSectionSizeNumberWidth(Sizes); + + OS << "----------------------------------------------------" + << "\n"; + OS << SectionNameColPrintName; + size_t SectionColumnNameWith = SectionNameColPrintName.size(); + for (unsigned i = 0; i < (MaxNameWidth - SectionColumnNameWith) + 2; i++) + OS << " "; + OS << SecttonSizePrintName << "\n"; + for (unsigned i = 0; i < MaxNameWidth; i++) + OS << "-"; + OS << " "; + for (unsigned i = 0; i < MaxNumberWidth; i++) + OS << "-"; + OS << "\n"; + + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + OS << left_justify(DebugSec.getKey(), MaxNameWidth) << " "; + + auto NumBytes = std::to_string(DebugSec.getValue()); + OS << left_justify(NumBytes, MaxNumberWidth) << " (" + << format("%0.2f", DebugSec.getValue() / + static_cast(Sizes.TotalObjectSize) * 100) + << "%)\n"; + } + + OS << "\n"; + OS << " Total Size: " << Sizes.TotalDebugSectionsSize << " (" + << format("%0.2f", Sizes.TotalDebugSectionsSize / + static_cast(Sizes.TotalObjectSize) * 100) + << "%)\n"; + OS << " Total File Size: " << Sizes.TotalObjectSize << "\n"; + OS << "----------------------------------------------------" + << "\n"; +} + +void 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 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 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"; + + // TODO: If it's an archive, print the cumulative summary of all files from + // the archive. + + prettyPrintSectionSizes(Obj, Sizes, OS); + + return true; +} Index: llvm/tools/llvm-dwarfdump/Statistics.cpp =================================================================== --- llvm/tools/llvm-dwarfdump/Statistics.cpp +++ llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -15,6 +15,8 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Support/JSON.h" +#include "SectionSizes.h" + #define DEBUG_TYPE "dwarfdump" using namespace llvm; using namespace object; @@ -468,6 +470,7 @@ OS << ",\"" << Key << "\":" << Value; LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); } + static void printLocationStats(raw_ostream &OS, const char *Key, std::vector &LocationStats) { @@ -491,6 +494,12 @@ LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: " << LocationStats[NumOfCoverageCategories - 1]); } + +static void printSectionSizes(raw_ostream &OS, const SectionSizes &Sizes) { + for (const auto &DebugSec : Sizes.DebugSectionSizes) + OS << ",\"size of " << DebugSec.getKey() << "\":" << DebugSec.getValue(); +} + /// \} /// Collect debug info quality metrics for an entire DIContext. @@ -512,6 +521,10 @@ 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. @@ -602,6 +615,7 @@ printDatum(OS, "vars with binary location", VarWithLoc); printDatum(OS, "total variables procesed by location statistics", LocStats.NumVarParam); + printSectionSizes(OS, Sizes); printLocationStats(OS, "variables", LocStats.VarParamLocStats); printLocationStats(OS, "variables (excluding the debug entry values)", LocStats.VarParamNonEntryValLocStats); Index: llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp =================================================================== --- llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -208,6 +208,10 @@ Statistics("statistics", cl::desc("Emit JSON-formatted debug info quality metrics."), cat(DwarfDumpCategory)); +static cl::opt + ShowSectionSizes("show-section-sizes", + cl::desc("Show the sizes of all debug sections."), + cat(DwarfDumpCategory)); static opt Verify("verify", desc("Verify the DWARF debug info."), cat(DwarfDumpCategory)); static opt Quiet("quiet", desc("Use with -verify to not emit to STDOUT."), @@ -413,6 +417,9 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, const Twine &Filename, raw_ostream &OS); +bool collectObjectSectionSizes(ObjectFile &Obj, DWARFContext & /*DICtx*/, + const Twine &Filename, raw_ostream &OS); + static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, const Twine &Filename, raw_ostream &OS) { logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(), @@ -635,12 +642,16 @@ return handleFile(Object, verifyObjectFile, OutputFile.os()); })) return 1; - } else if (Statistics) + } else if (Statistics) { for (auto Object : Objects) handleFile(Object, collectStatsForObjectFile, OutputFile.os()); - else + } else if (ShowSectionSizes) { + for (auto Object : Objects) + handleFile(Object, collectObjectSectionSizes, OutputFile.os()); + } else { for (auto Object : Objects) handleFile(Object, dumpObjectFile, OutputFile.os()); + } return EXIT_SUCCESS; }