mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 04:02:41 +01:00
[dsymutil] Add option to print statistics about the .debug_info size.
This patch adds statistics about the contribution of each object file to the linked debug info. When --statistics is passed to dsymutil, it prints a table after linking as illustrated below. It lists the object file name, the size of the debug info in the object file in bytes, and the absolute size contribution to the linked dSYM and the percentage difference. The table is sorted by the output size, so the object files contributing the most to the link are listed first. .debug_info section size (in bytes) ------------------------------------------------------------------------------- Filename Object dSYM Change ------------------------------------------------------------------------------- basic2.macho.x86_64.o 210b 165b -24.00% basic3.macho.x86_64.o 177b 150b -16.51% basic1.macho.x86_64.o 125b 129b 3.15% ------------------------------------------------------------------------------- Total 512b 444b -14.23% ------------------------------------------------------------------------------- Differential revision: https://reviews.llvm.org/D79513
This commit is contained in:
parent
3a0e9df3e1
commit
5e66aeab68
@ -77,7 +77,7 @@ OPTIONS
|
||||
|
||||
Specifies an alternate ``path`` to place the dSYM bundle. The default dSYM
|
||||
bundle path is created by appending ``.dSYM`` to the executable name.
|
||||
|
||||
|
||||
.. option:: --papertrail
|
||||
|
||||
When running dsymutil as part of your build system, it can be desirable for
|
||||
@ -93,6 +93,14 @@ OPTIONS
|
||||
|
||||
Specify a directory to prepend the paths of the external remark files.
|
||||
|
||||
.. option:: --statistics
|
||||
|
||||
Print statistics about the contribution of each object file to the linked
|
||||
debug info. This prints a table after linking with the object file name, the
|
||||
size of the debug info in the object file (in bytes) and the size contributed
|
||||
(in bytes) to the linked dSYM. The table is sorted by the output size listing
|
||||
the obj ect files with the largest contribution first.
|
||||
|
||||
.. option:: --symbol-map <bcsymbolmap>
|
||||
|
||||
Update the existing dSYMs inplace using symbol map specified.
|
||||
|
@ -260,6 +260,9 @@ public:
|
||||
/// Allows to generate log of linking process to the standard output.
|
||||
void setVerbosity(bool Verbose) { Options.Verbose = Verbose; }
|
||||
|
||||
/// Print statistics to standard output.
|
||||
void setStatistics(bool Statistics) { Options.Statistics = Statistics; }
|
||||
|
||||
/// Do not emit linked dwarf info.
|
||||
void setNoOutput(bool NoOut) { Options.NoOutput = NoOut; }
|
||||
|
||||
@ -556,9 +559,10 @@ private:
|
||||
/// Construct the output DIE tree by cloning the DIEs we
|
||||
/// chose to keep above. If there are no valid relocs, then there's
|
||||
/// nothing to clone/emit.
|
||||
void cloneAllCompileUnits(DWARFContext &DwarfContext, const DwarfFile &File,
|
||||
OffsetsStringPool &StringPool,
|
||||
bool IsLittleEndian);
|
||||
uint64_t cloneAllCompileUnits(DWARFContext &DwarfContext,
|
||||
const DwarfFile &File,
|
||||
OffsetsStringPool &StringPool,
|
||||
bool IsLittleEndian);
|
||||
|
||||
private:
|
||||
using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec;
|
||||
@ -757,6 +761,9 @@ private:
|
||||
/// Generate processing log to the standard output.
|
||||
bool Verbose = false;
|
||||
|
||||
/// Print statistics.
|
||||
bool Statistics = false;
|
||||
|
||||
/// Skip emitting output
|
||||
bool NoOutput = false;
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/LEB128.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/ThreadPool.h"
|
||||
@ -32,6 +33,21 @@
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// Hold the input and output of the debug info size in bytes.
|
||||
struct DebugInfoSize {
|
||||
uint64_t Input;
|
||||
uint64_t Output;
|
||||
};
|
||||
|
||||
/// Compute the total size of the debug info.
|
||||
static uint64_t getDebugInfoSize(DWARFContext &Dwarf) {
|
||||
uint64_t Size = 0;
|
||||
for (auto &Unit : Dwarf.compile_units()) {
|
||||
Size += Unit->getLength();
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our
|
||||
/// CompileUnit object instead.
|
||||
static CompileUnit *getUnitForOffset(const UnitListTy &Units, uint64_t Offset) {
|
||||
@ -2071,12 +2087,13 @@ Error DWARFLinker::loadClangModule(
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void DWARFLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext,
|
||||
const DwarfFile &File,
|
||||
OffsetsStringPool &StringPool,
|
||||
bool IsLittleEndian) {
|
||||
uint64_t DWARFLinker::DIECloner::cloneAllCompileUnits(
|
||||
DWARFContext &DwarfContext, const DwarfFile &File,
|
||||
OffsetsStringPool &StringPool, bool IsLittleEndian) {
|
||||
uint64_t OutputDebugInfoSize =
|
||||
Linker.Options.NoOutput ? 0 : Emitter->getDebugInfoSectionSize();
|
||||
const uint64_t StartOutputDebugInfoSize = OutputDebugInfoSize;
|
||||
|
||||
for (auto &CurrentUnit : CompileUnits) {
|
||||
auto InputDIE = CurrentUnit->getOrigUnit().getUnitDIE();
|
||||
CurrentUnit->setStartOffset(OutputDebugInfoSize);
|
||||
@ -2141,6 +2158,8 @@ void DWARFLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext,
|
||||
CurrentUnit->computeNextUnitOffset());
|
||||
}
|
||||
}
|
||||
|
||||
return OutputDebugInfoSize - StartOutputDebugInfoSize;
|
||||
}
|
||||
|
||||
void DWARFLinker::updateAccelKind(DWARFContext &Dwarf) {
|
||||
@ -2393,6 +2412,9 @@ bool DWARFLinker::link() {
|
||||
}
|
||||
};
|
||||
|
||||
// For each object file map how many bytes were emitted.
|
||||
StringMap<DebugInfoSize> SizeByObject;
|
||||
|
||||
// And then the remaining work in serial again.
|
||||
// Note, although this loop runs in serial, it can run in parallel with
|
||||
// the analyzeContextInfo loop so long as we process files with indices >=
|
||||
@ -2425,11 +2447,14 @@ bool DWARFLinker::link() {
|
||||
// need to reset the NextValidReloc index to the beginning.
|
||||
if (OptContext.File.Addresses->hasValidRelocs() ||
|
||||
LLVM_UNLIKELY(Options.Update)) {
|
||||
DIECloner(*this, TheDwarfEmitter, OptContext.File, DIEAlloc,
|
||||
OptContext.CompileUnits, Options.Update)
|
||||
.cloneAllCompileUnits(*OptContext.File.Dwarf, OptContext.File,
|
||||
OffsetsStringPool,
|
||||
OptContext.File.Dwarf->isLittleEndian());
|
||||
SizeByObject[OptContext.File.FileName].Input =
|
||||
getDebugInfoSize(*OptContext.File.Dwarf);
|
||||
SizeByObject[OptContext.File.FileName].Output =
|
||||
DIECloner(*this, TheDwarfEmitter, OptContext.File, DIEAlloc,
|
||||
OptContext.CompileUnits, Options.Update)
|
||||
.cloneAllCompileUnits(*OptContext.File.Dwarf, OptContext.File,
|
||||
OffsetsStringPool,
|
||||
OptContext.File.Dwarf->isLittleEndian());
|
||||
}
|
||||
if (!Options.NoOutput && !OptContext.CompileUnits.empty() &&
|
||||
LLVM_LIKELY(!Options.Update))
|
||||
@ -2505,6 +2530,53 @@ bool DWARFLinker::link() {
|
||||
Pool.wait();
|
||||
}
|
||||
|
||||
if (Options.Statistics) {
|
||||
// Create a vector sorted in descending order by output size.
|
||||
std::vector<std::pair<StringRef, DebugInfoSize>> Sorted;
|
||||
for (auto &E : SizeByObject)
|
||||
Sorted.emplace_back(E.first(), E.second);
|
||||
sort(Sorted.begin(), Sorted.end(), [](auto &LHS, auto &RHS) {
|
||||
return LHS.second.Output > RHS.second.Output;
|
||||
});
|
||||
|
||||
auto ComputePercentange = [](int64_t Input, int64_t Output) -> float {
|
||||
const float Difference = Output - Input;
|
||||
const float Sum = Input + Output;
|
||||
if (Sum == 0)
|
||||
return 0;
|
||||
return (Difference / (Sum / 2));
|
||||
};
|
||||
|
||||
int64_t InputTotal = 0;
|
||||
int64_t OutputTotal = 0;
|
||||
const char *FormatStr = "{0,-45} {1,10}b {2,10}b {3,8:P}\n";
|
||||
|
||||
// Print header.
|
||||
outs() << ".debug_info section size (in bytes)\n";
|
||||
outs() << "----------------------------------------------------------------"
|
||||
"---------------\n";
|
||||
outs() << "Filename Object "
|
||||
" dSYM Change\n";
|
||||
outs() << "----------------------------------------------------------------"
|
||||
"---------------\n";
|
||||
|
||||
// Print body.
|
||||
for (auto &E : Sorted) {
|
||||
InputTotal += E.second.Input;
|
||||
OutputTotal += E.second.Output;
|
||||
llvm::outs() << formatv(
|
||||
FormatStr, sys::path::filename(E.first).take_back(45), E.second.Input,
|
||||
E.second.Output, ComputePercentange(E.second.Input, E.second.Output));
|
||||
}
|
||||
// Print total and footer.
|
||||
outs() << "----------------------------------------------------------------"
|
||||
"---------------\n";
|
||||
llvm::outs() << formatv(FormatStr, "Total", InputTotal, OutputTotal,
|
||||
ComputePercentange(InputTotal, OutputTotal));
|
||||
outs() << "----------------------------------------------------------------"
|
||||
"---------------\n\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
21
test/tools/dsymutil/X86/statistics.test
Normal file
21
test/tools/dsymutil/X86/statistics.test
Normal file
@ -0,0 +1,21 @@
|
||||
# RUN: dsymutil -statistics -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s
|
||||
#
|
||||
# CHECK: -------------------------------------------------------------------------------
|
||||
# CHECK-NEXT: Filename Object dSYM Change
|
||||
# CHECK-NEXT: -------------------------------------------------------------------------------
|
||||
# CHECK-DAG: basic2.macho.x86_64.o {{[0-9]+}}b {{[0-9]+}}b{{.*}}{{[0-9]+}}.{{[0-9]+}}%
|
||||
# CHECK-DAG: basic3.macho.x86_64.o {{[0-9]+}}b {{[0-9]+}}b{{.*}}{{[0-9]+}}.{{[0-9]+}}%
|
||||
# CHECK-DAG: basic1.macho.x86_64.o {{[0-9]+}}b {{[0-9]+}}b{{.*}}{{[0-9]+}}.{{[0-9]+}}%
|
||||
# CHECK: -------------------------------------------------------------------------------
|
||||
# CHECK-NEXT: Total {{[0-9]+}}b {{[0-9]+}}b{{.*}}{{[0-9]+}}.{{[0-9]+}}%
|
||||
# CHECK-NEXT: -------------------------------------------------------------------------------
|
||||
|
||||
---
|
||||
triple: 'x86_64-apple-darwin'
|
||||
objects:
|
||||
- filename: invalid.o
|
||||
timestamp: 1518197670
|
||||
symbols:
|
||||
- { sym: _main, objAddr: 0x0000000000000010, binAddr: 0x0000000100000FB0, size: 0x00000008 }
|
||||
- { sym: _g, objAddr: 0x0000000000000000, binAddr: 0x0000000100000FA0, size: 0x00000010 }
|
||||
...
|
@ -18,6 +18,7 @@ HELP: {{ -o <filename> }}
|
||||
HELP: -papertrail
|
||||
HELP: -remarks-output-format <format>
|
||||
HELP: -remarks-prepend-path <path>
|
||||
HELP: -statistics
|
||||
HELP: -symbol-map
|
||||
HELP: -symtab
|
||||
HELP: {{ -S }}
|
||||
|
@ -315,6 +315,7 @@ bool DwarfLinkerForBinary::link(const DebugMap &Map) {
|
||||
};
|
||||
|
||||
GeneralLinker.setVerbosity(Options.Verbose);
|
||||
GeneralLinker.setStatistics(Options.Statistics);
|
||||
GeneralLinker.setNoOutput(Options.NoOutput);
|
||||
GeneralLinker.setNoODR(Options.NoODR);
|
||||
GeneralLinker.setUpdate(Options.Update);
|
||||
|
@ -27,6 +27,9 @@ struct LinkOptions {
|
||||
/// Verbosity
|
||||
bool Verbose = false;
|
||||
|
||||
/// Statistics
|
||||
bool Statistics = false;
|
||||
|
||||
/// Skip emitting output
|
||||
bool NoOutput = false;
|
||||
|
||||
|
@ -24,6 +24,15 @@ def verbose: F<"verbose">,
|
||||
HelpText<"Enable verbose mode.">,
|
||||
Group<grp_general>;
|
||||
|
||||
def statistics: F<"statistics">,
|
||||
HelpText<"Print statistics about the contribution of each object file to "
|
||||
"the linked debug info. This prints a table after linking with the "
|
||||
"object file name, the size of the debug info in the object file "
|
||||
"(in bytes) and the size contributed (in bytes) to the linked dSYM. "
|
||||
"The table is sorted by the output size listing the object files "
|
||||
"with the largest contribution first.">,
|
||||
Group<grp_general>;
|
||||
|
||||
def verify: F<"verify">,
|
||||
HelpText<"Run the DWARF verifier on the linked DWARF debug info.">,
|
||||
Group<grp_general>;
|
||||
|
@ -220,6 +220,7 @@ static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
|
||||
Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);
|
||||
Options.LinkOpts.Update = Args.hasArg(OPT_update);
|
||||
Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);
|
||||
Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics);
|
||||
|
||||
if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) {
|
||||
Options.LinkOpts.TheAccelTableKind = *AccelKind;
|
||||
@ -548,7 +549,11 @@ int main(int argc, char **argv) {
|
||||
// Shared a single binary holder for all the link steps.
|
||||
BinaryHolder BinHolder(Options.LinkOpts.VFS);
|
||||
|
||||
ThreadPoolStrategy S = hardware_concurrency(Options.LinkOpts.Threads);
|
||||
// Statistics only require different architectures to be processed
|
||||
// sequentially, the link itself can still happen in parallel. Change the
|
||||
// thread pool strategy here instead of modifying LinkOpts.Threads.
|
||||
ThreadPoolStrategy S = hardware_concurrency(
|
||||
Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads);
|
||||
if (Options.LinkOpts.Threads == 0) {
|
||||
// If NumThreads is not specified, create one thread for each input, up to
|
||||
// the number of hardware threads.
|
||||
|
Loading…
Reference in New Issue
Block a user