mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
llvm-dwarfdump: Add new variable, parameter and inlining statistics; also function source location statistics.
Add statistics for abstract origins, function, variable and parameter locations; break the 'variable' counts down into variables and parameters. Also update call site counting to check for DW_AT_call_{file,line} in addition to DW_TAG_call_site. Differential revision: https://reviews.llvm.org/D58849 llvm-svn: 355243
This commit is contained in:
parent
2827fda2e0
commit
f8c92d8822
1152
test/tools/llvm-dwarfdump/X86/Inputs/statistics-fib.s
Normal file
1152
test/tools/llvm-dwarfdump/X86/Inputs/statistics-fib.s
Normal file
File diff suppressed because it is too large
Load Diff
91
test/tools/llvm-dwarfdump/X86/statistics-v3.test
Normal file
91
test/tools/llvm-dwarfdump/X86/statistics-v3.test
Normal file
@ -0,0 +1,91 @@
|
||||
# Test of the llmv-dwarfdump --statistics newly added stats (version 3).
|
||||
#
|
||||
RUN: llvm-mc -triple x86_64-unknown-linux-gnu %S/Inputs/statistics-fib.s -filetype=obj -o %t-statistics-fib.o
|
||||
RUN: llvm-dwarfdump --statistics %t-statistics-fib.o | FileCheck %s
|
||||
|
||||
# Source program - A version of Fibonacci
|
||||
# Compilation options: -g -O3 -c
|
||||
#
|
||||
# int
|
||||
# real_fib (int x, int answers[11])
|
||||
# {
|
||||
# int result;
|
||||
#
|
||||
# if ((answers)[x] != -1)
|
||||
# return (answers)[x];
|
||||
#
|
||||
# result = real_fib(x-1, answers) + real_fib(x-2, answers);
|
||||
# (answers)[x] = result;
|
||||
#
|
||||
# return result;
|
||||
# }
|
||||
#
|
||||
# int
|
||||
# fib (int x)
|
||||
# {
|
||||
# int answers[11];
|
||||
# int i;
|
||||
#
|
||||
# if (x > 10)
|
||||
# return -1;
|
||||
#
|
||||
# for (i = 0; i < 11; i++)
|
||||
# answers[i] = -1;
|
||||
#
|
||||
# answers[0] = 0;
|
||||
# answers[1] = 1;
|
||||
# answers[2] = 1;
|
||||
#
|
||||
# return real_fib(x, answers);
|
||||
# }
|
||||
#
|
||||
# int main (int argc, char **argv)
|
||||
# {
|
||||
# int result;
|
||||
#
|
||||
# result = fib(3);
|
||||
# printf ("fibonacci(3) = %d\n", result);
|
||||
# result = fib(4);
|
||||
# printf ("fibonacci(4) = %d\n", result);
|
||||
# result = fib(5);
|
||||
# printf ("fibonacci(5) = %d\n", result);
|
||||
# result = fib(6);
|
||||
# printf ("fibonacci(6) = %d\n", result);
|
||||
# result = fib(7);
|
||||
# printf ("fibonacci(7) = %d\n", result);
|
||||
# result = fib(8);
|
||||
# printf ("fibonacci(8) = %d\n", result);
|
||||
# result = fib(9);
|
||||
# printf ("fibonacci(9) = %d\n", result);
|
||||
# result = fib(10);
|
||||
# printf ("fibonacci(10) = %d\n", result);
|
||||
#
|
||||
# return 0;
|
||||
# }
|
||||
#
|
||||
|
||||
CHECK: "version":3
|
||||
CHECK: "source functions":3
|
||||
CHECK: "source functions with location":3
|
||||
CHECK: "inlined functions":8
|
||||
CHECK: "inlined funcs with abstract origins":8
|
||||
CHECK: "unique source variables":9
|
||||
CHECK: "source variables":33
|
||||
|
||||
# Ideally the value below would be 33 but currently it's not.
|
||||
CHECK: "variables with location":24
|
||||
CHECK: "call site entries":8
|
||||
CHECK: "scope bytes total":2958
|
||||
CHECK: "scope bytes covered":1188
|
||||
CHECK: "total function size":636
|
||||
CHECK: "total inlined function size":388
|
||||
CHECK: "total formal params":13
|
||||
CHECK: "formal params with source location":13
|
||||
CHECK: "formal params with type":13
|
||||
CHECK: "formal params with binary location":13
|
||||
CHECK: "total vars":20
|
||||
CHECK: "vars with source location":20
|
||||
CHECK: "vars with type":20
|
||||
|
||||
# Ideally the value below would be 20, but currently it's not.
|
||||
CHECK: "vars with binary location":11
|
@ -1,6 +1,6 @@
|
||||
; RUN: llc -O0 %s -o - -filetype=obj \
|
||||
; RUN: | llvm-dwarfdump -statistics - | FileCheck %s
|
||||
; CHECK: "version":2
|
||||
; CHECK: "version":3
|
||||
|
||||
; int GlobalConst = 42;
|
||||
; int Global;
|
||||
|
@ -15,17 +15,38 @@ using namespace object;
|
||||
struct PerFunctionStats {
|
||||
/// Number of inlined instances of this function.
|
||||
unsigned NumFnInlined = 0;
|
||||
/// Number of variables with location across all inlined instances.
|
||||
/// Number of inlined instances that have abstract origins.
|
||||
unsigned NumAbstractOrigins = 0;
|
||||
/// Number of variables and parameters 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.
|
||||
/// 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.
|
||||
unsigned NumParams = 0;
|
||||
/// Number of function parameters with source location.
|
||||
unsigned NumParamSourceLocations = 0;
|
||||
/// Number of function parameters with type.
|
||||
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 wtih type.
|
||||
unsigned NumVarTypes = 0;
|
||||
/// Number of variables wtih DW_AT_location.
|
||||
unsigned NumVarLocations = 0;
|
||||
};
|
||||
|
||||
/// Holds accumulated global statistics about DIEs.
|
||||
@ -35,7 +56,8 @@ struct GlobalStats {
|
||||
/// Total number of PC range bytes in each variable's enclosing scope,
|
||||
/// starting from the first definition of the variable.
|
||||
unsigned ScopeBytesFromFirstDefinition = 0;
|
||||
/// Total number of call site entries (DW_TAG_call_site).
|
||||
/// Total number of call site entries (DW_TAG_call_site) or
|
||||
/// (DW_AT_call_file & DW_AT_call_line).
|
||||
unsigned CallSiteEntries = 0;
|
||||
/// Total byte size of concrete functions. This byte size includes
|
||||
/// inline functions contained in the concrete functions.
|
||||
@ -62,11 +84,13 @@ static uint64_t getLowPC(DWARFDie Die) {
|
||||
/// Collect debug info quality metrics for one DIE.
|
||||
static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
|
||||
std::string VarPrefix, uint64_t ScopeLowPC,
|
||||
uint64_t BytesInScope,
|
||||
uint32_t InlineDepth,
|
||||
uint64_t BytesInScope, uint32_t InlineDepth,
|
||||
StringMap<PerFunctionStats> &FnStatMap,
|
||||
GlobalStats &GlobalStats) {
|
||||
bool HasLoc = false;
|
||||
bool HasSrcLoc = false;
|
||||
bool HasType = false;
|
||||
bool IsArtificial = false;
|
||||
uint64_t BytesCovered = 0;
|
||||
uint64_t OffsetToFirstDefinition = 0;
|
||||
|
||||
@ -82,6 +106,16 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
|
||||
return;
|
||||
}
|
||||
|
||||
if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
|
||||
Die.findRecursively(dwarf::DW_AT_decl_line))
|
||||
HasSrcLoc = true;
|
||||
|
||||
if (Die.findRecursively(dwarf::DW_AT_type))
|
||||
HasType = true;
|
||||
|
||||
if (Die.find(dwarf::DW_AT_artificial))
|
||||
IsArtificial = true;
|
||||
|
||||
if (Die.find(dwarf::DW_AT_const_value)) {
|
||||
// This catches constant members *and* variables.
|
||||
HasLoc = true;
|
||||
@ -128,7 +162,7 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
|
||||
// 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);
|
||||
FnStats.VarsInFunction.insert(VarPrefix + VarName);
|
||||
if (BytesInScope) {
|
||||
FnStats.TotalVarWithLoc += (unsigned)HasLoc;
|
||||
// Adjust for the fact the variables often start their lifetime in the
|
||||
@ -144,13 +178,31 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
|
||||
} else {
|
||||
FnStats.TotalVarWithLoc += (unsigned)HasLoc;
|
||||
}
|
||||
if (!IsArtificial) {
|
||||
if (Die.getTag() == dwarf::DW_TAG_formal_parameter) {
|
||||
FnStats.NumParams++;
|
||||
if (HasType)
|
||||
FnStats.NumParamTypes++;
|
||||
if (HasSrcLoc)
|
||||
FnStats.NumParamSourceLocations++;
|
||||
if (HasLoc)
|
||||
FnStats.NumParamLocations++;
|
||||
} else if (Die.getTag() == dwarf::DW_TAG_variable) {
|
||||
FnStats.NumVars++;
|
||||
if (HasType)
|
||||
FnStats.NumVarTypes++;
|
||||
if (HasSrcLoc)
|
||||
FnStats.NumVarSourceLocations++;
|
||||
if (HasLoc)
|
||||
FnStats.NumVarLocations++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively collect debug info quality metrics.
|
||||
static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
|
||||
std::string VarPrefix, uint64_t ScopeLowPC,
|
||||
uint64_t BytesInScope,
|
||||
uint32_t InlineDepth,
|
||||
uint64_t BytesInScope, uint32_t InlineDepth,
|
||||
StringMap<PerFunctionStats> &FnStatMap,
|
||||
GlobalStats &GlobalStats) {
|
||||
// Handle any kind of lexical scope.
|
||||
@ -169,6 +221,10 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
|
||||
if (Die.find(dwarf::DW_AT_declaration))
|
||||
return;
|
||||
|
||||
// Check for call sites.
|
||||
if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
|
||||
GlobalStats.CallSiteEntries++;
|
||||
|
||||
// PC Ranges.
|
||||
auto RangesOrError = Die.getAddressRanges();
|
||||
if (!RangesOrError) {
|
||||
@ -193,11 +249,18 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
|
||||
return;
|
||||
// We've seen an (inlined) instance of this function.
|
||||
auto &FnStats = FnStatMap[Name];
|
||||
if (IsInlinedFunction)
|
||||
if (IsInlinedFunction) {
|
||||
FnStats.NumFnInlined++;
|
||||
if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
|
||||
FnStats.NumAbstractOrigins++;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if (BytesInThisScope) {
|
||||
@ -266,12 +329,22 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
|
||||
/// 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 = 2;
|
||||
unsigned VarTotal = 0;
|
||||
unsigned VarUnique = 0;
|
||||
unsigned VarWithLoc = 0;
|
||||
unsigned Version = 3;
|
||||
unsigned VarParamTotal = 0;
|
||||
unsigned VarParamUnique = 0;
|
||||
unsigned VarParamWithLoc = 0;
|
||||
unsigned NumFunctions = 0;
|
||||
unsigned NumInlinedFunctions = 0;
|
||||
unsigned NumFuncsWithSrcLoc = 0;
|
||||
unsigned NumAbstractOrigins = 0;
|
||||
unsigned ParamTotal = 0;
|
||||
unsigned ParamWithType = 0;
|
||||
unsigned ParamWithLoc = 0;
|
||||
unsigned ParamWithSrcLoc = 0;
|
||||
unsigned VarTotal = 0;
|
||||
unsigned VarWithType = 0;
|
||||
unsigned VarWithSrcLoc = 0;
|
||||
unsigned VarWithLoc = 0;
|
||||
for (auto &Entry : Statistics) {
|
||||
PerFunctionStats &Stats = Entry.getValue();
|
||||
unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined;
|
||||
@ -279,13 +352,24 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
|
||||
if (Stats.HasPCAddresses || !Stats.IsFunction)
|
||||
TotalVars += Stats.VarsInFunction.size();
|
||||
unsigned Constants = Stats.ConstantMembers;
|
||||
VarWithLoc += Stats.TotalVarWithLoc + Constants;
|
||||
VarTotal += TotalVars;
|
||||
VarUnique += Stats.VarsInFunction.size();
|
||||
LLVM_DEBUG(for (auto &V : Stats.VarsInFunction) llvm::dbgs()
|
||||
VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
|
||||
VarParamTotal += TotalVars;
|
||||
VarParamUnique += Stats.VarsInFunction.size();
|
||||
LLVM_DEBUG(for (auto &V
|
||||
: Stats.VarsInFunction) llvm::dbgs()
|
||||
<< Entry.getKey() << ": " << V.getKey() << "\n");
|
||||
NumFunctions += Stats.IsFunction;
|
||||
NumFuncsWithSrcLoc += Stats.HasSourceLocation;
|
||||
NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
|
||||
NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
|
||||
ParamTotal += Stats.NumParams;
|
||||
ParamWithType += Stats.NumParamTypes;
|
||||
ParamWithLoc += Stats.NumParamLocations;
|
||||
ParamWithSrcLoc += Stats.NumParamSourceLocations;
|
||||
VarTotal += Stats.NumVars;
|
||||
VarWithType += Stats.NumVarTypes;
|
||||
VarWithLoc += Stats.NumVarLocations;
|
||||
VarWithSrcLoc += Stats.NumVarSourceLocations;
|
||||
}
|
||||
|
||||
// Print summary.
|
||||
@ -296,20 +380,31 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
|
||||
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, "unique source variables", VarUnique);
|
||||
printDatum(OS, "source variables", VarTotal);
|
||||
printDatum(OS, "variables with location", VarWithLoc);
|
||||
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, "scope bytes total",
|
||||
GlobalStats.ScopeBytesFromFirstDefinition);
|
||||
printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
|
||||
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);
|
||||
OS << "}\n";
|
||||
LLVM_DEBUG(
|
||||
llvm::dbgs() << "Total Availability: "
|
||||
<< (int)std::round((VarWithLoc * 100.0) / VarTotal) << "%\n";
|
||||
<< (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal)
|
||||
<< "%\n";
|
||||
llvm::dbgs() << "PC Ranges covered: "
|
||||
<< (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
|
||||
GlobalStats.ScopeBytesFromFirstDefinition)
|
||||
|
Loading…
x
Reference in New Issue
Block a user