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/test/tools/llvm-dwarfdump/X86/section_sizes.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-dwarfdump/X86/section_sizes.test @@ -0,0 +1,42 @@ +## 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 + +# CHECK: {{.*}}: file format ELF64-x86-64 +# CHECK-NEXT: ---------------------------------------------------- +# CHECK-NEXT: SECTION SIZE +# CHECK-NEXT: ------------ ----------------------- +# CHECK-NEXT: .debug_foo 8 (1.12%) +# CHECK-NEXT: .debug_info 17 (2.39%) +# CHECK-NEXT: .debug_line 19 (2.67%) +# CHECK-NEXT: .debug_loc 1 (0.14%) +# CHECK-NEXT: .debug_str 13 (1.83%) +# CHECK-EMPTY: +# CHECK-NEXT: all debug sections: 58 (8.15%) +# CHECK-NEXT: ---------------------------------------------------- +# CHECK-NEXT: TOTAL FILE SIZE: 712 +# CHECK-NEXT: ---------------------------------------------------- + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .debug_info + Type: SHT_PROGBITS + Content: 00636C616E672076657273696F6E20332E + - Name: .debug_line + Type: SHT_PROGBITS + Content: 00636C616E672076657273696F6E20332E3922 + - Name: .debug_loc + Type: SHT_PROGBITS + Content: 02 + - Name: .debug_str + Type: SHT_PROGBITS + Content: 00636C616E6720766572736962 + - Name: .debug_foo + Type: SHT_PROGBITS + Content: 00636C616E672072 Index: llvm/tools/llvm-dwarfdump/CMakeLists.txt =================================================================== --- llvm/tools/llvm-dwarfdump/CMakeLists.txt +++ llvm/tools/llvm-dwarfdump/CMakeLists.txt @@ -9,6 +9,7 @@ add_llvm_tool(llvm-dwarfdump Statistics.cpp + SectionSizes.cpp llvm-dwarfdump.cpp ) Index: llvm/tools/llvm-dwarfdump/SectionSizes.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-dwarfdump/SectionSizes.cpp @@ -0,0 +1,110 @@ +//===-- 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/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/WithColor.h" + +#include + +#define DEBUG_TYPE "dwarfdump" +using namespace llvm; +using namespace object; + +using SectionSizeMap = std::map; + +/// 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; +}; + +static void reportWarning(Error Err, Twine &Filename) { + assert(Err); + + // Flush the standard output. + outs().flush(); + handleAllErrors( + createFileError(Filename, std::move(Err)), [&](const ErrorInfoBase &EI) { + WithColor::warning(errs(), "llvm-dwarfdump") << EI.message() << "\n"; + }); +} + +static void prettyPrintSectionSizes(const ObjectFile &Obj, + const SectionSizes &Sizes, + raw_ostream &OS) { + OS << "----------------------------------------------------" + << "\n"; + OS << " SECTION \t\t\t SIZE\n"; + OS << "------------ \t\t -----------------------\n"; + + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + OS << " " << DebugSec.first << " \t\t " << DebugSec.second << " (" + << format("%0.2f", DebugSec.second / (double)Sizes.TotalObjectSize * 100) + << "%)\n"; + } + OS << "\n"; + OS << " all debug sections: \t " << Sizes.TotalDebugSectionsSize << " (" + << format("%0.2f", Sizes.TotalDebugSectionsSize / + (double)Sizes.TotalObjectSize * 100) + << "%)\n"; + OS << "----------------------------------------------------" + << "\n"; + OS << " TOTAL FILE SIZE: " << Sizes.TotalObjectSize << "\n"; + OS << "----------------------------------------------------" + << "\n"; +} + +static bool isDebugSection(StringRef SectionName) { + return SectionName.startswith(".debug") || + SectionName.startswith(".zdebug") || SectionName == ".gdb_index"; +} + +static void calculateSizes(const ObjectFile &Obj, SectionSizes &Sizes, + 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 { + reportWarning(NameOrErr.takeError(), Filename); + } + + LLVM_DEBUG(dbgs() << SectionName.str() << ": " << Section.getSize()); + + if (!isDebugSection(SectionName)) + continue; + + Sizes.TotalDebugSectionsSize += Section.getSize(); + Sizes.DebugSectionSizes[SectionName] += Section.getSize(); + } +} + +bool collectSecsSizesForObjectFile(ObjectFile &Obj, Twine Filename, + raw_ostream &OS) { + SectionSizes Sizes; + + // Get the section sizes. + calculateSizes(Obj, Sizes, Filename); + + OS << "----------------------------------------------------\n"; + OS << Filename.str() << ": file format " << Obj.getFileFormatName() << "\n"; + + prettyPrintSectionSizes(Obj, Sizes, OS); + + return true; +} 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."), @@ -280,6 +284,8 @@ using HandlerFn = std::function; +using HandlerSecSizesFn = + std::function; /// Print only DIEs that have a certain name. static bool filterByName(const StringSet<> &Names, DWARFDie Die, @@ -413,6 +419,9 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, raw_ostream &OS); +bool collectSecsSizesForObjectFile(ObjectFile &Obj, Twine Filename, + raw_ostream &OS); + static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, raw_ostream &OS) { logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(), @@ -463,10 +472,11 @@ } static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, - HandlerFn HandleObj, raw_ostream &OS); + raw_ostream &OS, HandlerFn HandleObj, + HandlerSecSizesFn HandleObj2 = nullptr); -static bool handleArchive(StringRef Filename, Archive &Arch, - HandlerFn HandleObj, raw_ostream &OS) { +static bool handleArchive(StringRef Filename, Archive &Arch, raw_ostream &OS, + HandlerFn HandleObj, HandlerSecSizesFn HandleObj2) { bool Result = true; Error Err = Error::success(); for (auto Child : Arch.children(Err)) { @@ -475,7 +485,9 @@ auto NameOrErr = Child.getName(); error(Filename, errorToErrorCode(NameOrErr.takeError())); std::string Name = (Filename + "(" + NameOrErr.get() + ")").str(); - Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS); + Result &= HandleObj ? handleBuffer(Name, BuffOrErr.get(), OS, HandleObj) + : handleBuffer(Name, BuffOrErr.get(), OS, nullptr, + HandleObj2); } error(Filename, errorToErrorCode(std::move(Err))); @@ -483,7 +495,8 @@ } static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, - HandlerFn HandleObj, raw_ostream &OS) { + raw_ostream &OS, HandlerFn HandleObj, + HandlerSecSizesFn HandleObj2) { Expected> BinOrErr = object::createBinary(Buffer); error(Filename, errorToErrorCode(BinOrErr.takeError())); @@ -491,7 +504,8 @@ if (auto *Obj = dyn_cast(BinOrErr->get())) { if (filterArch(*Obj)) { std::unique_ptr DICtx = DWARFContext::create(*Obj); - Result = HandleObj(*Obj, *DICtx, Filename, OS); + Result = HandleObj ? HandleObj(*Obj, *DICtx, Filename, OS) + : HandleObj2(*Obj, Filename, OS); } } else if (auto *Fat = dyn_cast(BinOrErr->get())) @@ -502,30 +516,32 @@ auto &Obj = **MachOOrErr; if (filterArch(Obj)) { std::unique_ptr DICtx = DWARFContext::create(Obj); - Result &= HandleObj(Obj, *DICtx, ObjName, OS); + Result &= HandleObj ? HandleObj(Obj, *DICtx, ObjName, OS) + : HandleObj2(Obj, ObjName, OS); } continue; } else consumeError(MachOOrErr.takeError()); if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { error(ObjName, errorToErrorCode(ArchiveOrErr.takeError())); - Result &= handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS); + Result &= handleArchive(ObjName, *ArchiveOrErr.get(), OS, HandleObj, + HandleObj2); continue; } else consumeError(ArchiveOrErr.takeError()); } else if (auto *Arch = dyn_cast(BinOrErr->get())) - Result = handleArchive(Filename, *Arch, HandleObj, OS); + Result = handleArchive(Filename, *Arch, OS, HandleObj, HandleObj2); return Result; } -static bool handleFile(StringRef Filename, HandlerFn HandleObj, - raw_ostream &OS) { +static bool handleFile(StringRef Filename, raw_ostream &OS, HandlerFn HandleObj, + HandlerSecSizesFn HandleObj2 = nullptr) { ErrorOr> BuffOrErr = - MemoryBuffer::getFileOrSTDIN(Filename); + MemoryBuffer::getFileOrSTDIN(Filename); error(Filename, BuffOrErr.getError()); std::unique_ptr Buffer = std::move(BuffOrErr.get()); - return handleBuffer(Filename, *Buffer, HandleObj, OS); + return handleBuffer(Filename, *Buffer, OS, HandleObj, HandleObj2); } /// If the input path is a .dSYM bundle (as created by the dsymutil tool), @@ -632,15 +648,20 @@ 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 handleFile(Object, OutputFile.os(), verifyObjectFile); })) return 1; - } else if (Statistics) + } else if (Statistics) { for (auto Object : Objects) - handleFile(Object, collectStatsForObjectFile, OutputFile.os()); - else + handleFile(Object, OutputFile.os(), collectStatsForObjectFile); + } else if (ShowSectionSizes) { for (auto Object : Objects) - handleFile(Object, dumpObjectFile, OutputFile.os()); + handleFile(Object, OutputFile.os(), nullptr, + collectSecsSizesForObjectFile); + } else { + for (auto Object : Objects) + handleFile(Object, OutputFile.os(), dumpObjectFile); + } return EXIT_SUCCESS; }