blob: c29ad783a9e61cd54605cba370ac9f2aa78c702a [file] [log] [blame]
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/JSON.h"
#define DEBUG_TYPE "dwarfdump"
using namespace llvm;
using namespace object;
/// This represents the number of categories of debug location coverage being
/// calculated. The first category is the number of variables with 0% location
/// coverage, but the last category is the number of variables with 100%
/// location coverage.
constexpr int NumOfCoverageCategories = 12;
/// 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 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 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 with type.
unsigned NumVarTypes = 0;
/// Number of variables with DW_AT_location.
unsigned NumVarLocations = 0;
};
/// Holds accumulated global statistics about DIEs.
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;
/// Total number of PC range bytes covered by DW_AT_locations with
/// the debug entry values (DW_OP_entry_value).
unsigned ScopeEntryValueBytesCovered = 0;
/// Total number of PC range bytes covered by DW_AT_locations of
/// formal parameters.
unsigned ParamScopeBytesCovered = 0;
/// Total number of PC range bytes in each variable's enclosing scope,
/// starting from the first definition of the variable (only for parameters).
unsigned ParamScopeBytesFromFirstDefinition = 0;
/// Total number of PC range bytes covered by DW_AT_locations with
/// the debug entry values (DW_OP_entry_value) (only for parameters).
unsigned ParamScopeEntryValueBytesCovered = 0;
/// Total number of PC range bytes covered by DW_AT_locations (only for local
/// variables).
unsigned VarScopeBytesCovered = 0;
/// Total number of PC range bytes in each variable's enclosing scope,
/// starting from the first definition of the variable (only for local
/// variables).
unsigned VarScopeBytesFromFirstDefinition = 0;
/// Total number of PC range bytes covered by DW_AT_locations with
/// the debug entry values (DW_OP_entry_value) (only for local variables).
unsigned VarScopeEntryValueBytesCovered = 0;
/// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
unsigned CallSiteEntries = 0;
/// Total number of call site DIEs (DW_TAG_call_site).
unsigned CallSiteDIEs = 0;
/// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
unsigned CallSiteParamDIEs = 0;
/// Total byte size of concrete functions. This byte size includes
/// inline functions contained in the concrete functions.
unsigned FunctionSize = 0;
/// Total byte size of inlined functions. This is the total number of bytes
/// for the top inline functions within concrete functions. This can help
/// tune the inline settings when compiling to match user expectations.
unsigned InlineFunctionSize = 0;
};
/// Holds accumulated debug location statistics about local variables and
/// formal parameters.
struct LocationStats {
/// Map the scope coverage decile to the number of variables in the decile.
/// The first element of the array (at the index zero) represents the number
/// of variables with the no debug location at all, but the last element
/// in the vector represents the number of fully covered variables within
/// its scope.
std::vector<unsigned> VarParamLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// Map non debug entry values coverage.
std::vector<unsigned> VarParamNonEntryValLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// The debug location statistics for formal parameters.
std::vector<unsigned> ParamLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// Map non debug entry values coverage for formal parameters.
std::vector<unsigned> ParamNonEntryValLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// The debug location statistics for local variables.
std::vector<unsigned> VarLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// Map non debug entry values coverage for local variables.
std::vector<unsigned> VarNonEntryValLocStats{
std::vector<unsigned>(NumOfCoverageCategories, 0)};
/// Total number of local variables and function parameters processed.
unsigned NumVarParam = 0;
/// Total number of formal parameters processed.
unsigned NumParam = 0;
/// Total number of local variables processed.
unsigned NumVar = 0;
};
/// Extract the low pc from a Die.
static uint64_t getLowPC(DWARFDie Die) {
auto RangesOrError = Die.getAddressRanges();
DWARFAddressRangesVector Ranges;
if (RangesOrError)
Ranges = RangesOrError.get();
else
llvm::consumeError(RangesOrError.takeError());
if (Ranges.size())
return Ranges[0].LowPC;
return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0);
}
/// Collect debug location statistics for one DIE.
static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope,
std::vector<unsigned> &VarParamLocStats,
std::vector<unsigned> &ParamLocStats,
std::vector<unsigned> &VarLocStats, bool IsParam,
bool IsLocalVar) {
auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned {
unsigned LocBucket = 100 * (double)BytesCovered / BytesInScope;
if (LocBucket == 0) {
// No debug location at all for the variable.
return 0;
} else if (LocBucket == 100 || BytesCovered > BytesInScope) {
// Fully covered variable within its scope.
return NumOfCoverageCategories - 1;
} else {
// Get covered range (e.g. 20%-29%).
LocBucket /= 10;
return LocBucket + 1;
}
};
unsigned CoverageBucket = getCoverageBucket();
VarParamLocStats[CoverageBucket]++;
if (IsParam)
ParamLocStats[CoverageBucket]++;
else if (IsLocalVar)
VarLocStats[CoverageBucket]++;
}
/// Collect debug info quality metrics for one DIE.
static void collectStatsForDie(DWARFDie Die, uint64_t UnitLowPC, std::string FnPrefix,
std::string VarPrefix, uint64_t ScopeLowPC,
uint64_t BytesInScope, uint32_t InlineDepth,
StringMap<PerFunctionStats> &FnStatMap,
GlobalStats &GlobalStats,
LocationStats &LocStats) {
bool HasLoc = false;
bool HasSrcLoc = false;
bool HasType = false;
bool IsArtificial = false;
uint64_t BytesCovered = 0;
uint64_t BytesEntryValuesCovered = 0;
uint64_t OffsetToFirstDefinition = 0;
auto &FnStats = FnStatMap[FnPrefix];
bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter;
bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable;
if (Die.getTag() == dwarf::DW_TAG_call_site ||
Die.getTag() == dwarf::DW_TAG_GNU_call_site) {
GlobalStats.CallSiteDIEs++;
return;
}
if (Die.getTag() == dwarf::DW_TAG_call_site_parameter ||
Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) {
GlobalStats.CallSiteParamDIEs++;
return;
}
if (!IsParam && !IsLocalVar && Die.getTag() != dwarf::DW_TAG_member) {
// Not a variable or constant member.
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;
auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
DWARFUnit *U = Die.getDwarfUnit();
DataExtractor Data(toStringRef(D),
Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize());
// Consider the expression containing the DW_OP_entry_value as
// an entry value.
return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) {
return Op.getCode() == dwarf::DW_OP_entry_value ||
Op.getCode() == dwarf::DW_OP_GNU_entry_value;
});
};
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_member) {
// Non-const member.
return;
}
// 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) {
uint64_t BytesEntryCovered = Entry.End - Entry.Begin;
BytesCovered += BytesEntryCovered;
if (IsEntryValue(Entry.Loc))
BytesEntryValuesCovered += BytesEntryCovered;
}
if (List->Entries.size()) {
uint64_t FirstDef = List->Entries[0].Begin;
uint64_t UnitOfs = UnitLowPC;
// 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;
}
}
}
// Calculate the debug location statistics.
if (BytesInScope) {
LocStats.NumVarParam++;
if (IsParam)
LocStats.NumParam++;
else if (IsLocalVar)
LocStats.NumVar++;
collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats,
LocStats.ParamLocStats, LocStats.VarLocStats, IsParam,
IsLocalVar);
// Non debug entry values coverage statistics.
collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope,
LocStats.VarParamNonEntryValLocStats,
LocStats.ParamNonEntryValLocStats,
LocStats.VarNonEntryValLocStats, IsParam, IsLocalVar);
}
// Collect PC range coverage data.
if (DWARFDie D =
Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
Die = D;
// 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);
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;
GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
if (IsParam) {
GlobalStats.ParamScopeBytesCovered +=
std::min(BytesInScope, BytesCovered);
GlobalStats.ParamScopeBytesFromFirstDefinition += BytesInScope;
GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
} else if (IsLocalVar) {
GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered);
GlobalStats.VarScopeBytesFromFirstDefinition += BytesInScope;
GlobalStats.VarScopeEntryValueBytesCovered += BytesEntryValuesCovered;
}
assert(GlobalStats.ScopeBytesCovered <=
GlobalStats.ScopeBytesFromFirstDefinition);
} else if (Die.getTag() == dwarf::DW_TAG_member) {
FnStats.ConstantMembers++;
} else {
FnStats.TotalVarWithLoc += (unsigned)HasLoc;
}
if (!IsArtificial) {
if (IsParam) {
FnStats.NumParams++;
if (HasType)
FnStats.NumParamTypes++;
if (HasSrcLoc)
FnStats.NumParamSourceLocations++;
if (HasLoc)
FnStats.NumParamLocations++;
} else if (IsLocalVar) {
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, uint64_t UnitLowPC, std::string FnPrefix,
std::string VarPrefix, uint64_t ScopeLowPC,
uint64_t BytesInScope, uint32_t InlineDepth,
StringMap<PerFunctionStats> &FnStatMap,
GlobalStats &GlobalStats,
LocationStats &LocStats) {
// Handle any kind of lexical scope.
const dwarf::Tag Tag = Die.getTag();
const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
if (IsFunction || IsInlinedFunction || IsBlock) {
// Reset VarPrefix when entering a new function.
if (Die.getTag() == dwarf::DW_TAG_subprogram ||
Die.getTag() == dwarf::DW_TAG_inlined_subroutine)
VarPrefix = "v";
// Ignore forward declarations.
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) {
llvm::consumeError(RangesOrError.takeError());
return;
}
auto Ranges = RangesOrError.get();
uint64_t BytesInThisScope = 0;
for (auto Range : Ranges)
BytesInThisScope += Range.HighPC - Range.LowPC;
ScopeLowPC = getLowPC(Die);
// Count the function.
if (!IsBlock) {
StringRef Name = Die.getName(DINameKind::LinkageName);
if (Name.empty())
Name = Die.getName(DINameKind::ShortName);
FnPrefix = 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];
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) {
BytesInScope = BytesInThisScope;
if (IsFunction)
GlobalStats.FunctionSize += BytesInThisScope;
else if (IsInlinedFunction && InlineDepth == 0)
GlobalStats.InlineFunctionSize += BytesInThisScope;
}
} else {
// Not a scope, visit the Die itself. It could be a variable.
collectStatsForDie(Die, UnitLowPC, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope,
InlineDepth, FnStatMap, GlobalStats, LocStats);
}
// Set InlineDepth correctly for child recursion
if (IsFunction)
InlineDepth = 0;
else if (IsInlinedFunction)
++InlineDepth;
// Traverse children.
unsigned LexicalBlockIndex = 0;
DWARFDie Child = Die.getFirstChild();
while (Child) {
std::string ChildVarPrefix = VarPrefix;
if (Child.getTag() == dwarf::DW_TAG_lexical_block)
ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
collectStatsRecursive(Child, UnitLowPC, FnPrefix, ChildVarPrefix, ScopeLowPC,
BytesInScope, InlineDepth, FnStatMap, GlobalStats,
LocStats);
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, json::Value Value) {
OS << ",\"" << Key << "\":" << Value;
LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
}
static void printLocationStats(raw_ostream &OS,
const char *Key,
std::vector<unsigned> &LocationStats) {
OS << ",\"" << Key << " with 0% of its scope covered\":"
<< LocationStats[0];
LLVM_DEBUG(llvm::dbgs() << Key << " with 0% of its scope covered: "
<< LocationStats[0] << '\n');
OS << ",\"" << Key << " with 1-9% of its scope covered\":"
<< LocationStats[1];
LLVM_DEBUG(llvm::dbgs() << Key << " with 1-9% of its scope covered: "
<< LocationStats[1] << '\n');
for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
OS << ",\"" << Key << " with " << (i - 1) * 10 << "-" << i * 10 - 1
<< "% of its scope covered\":" << LocationStats[i];
LLVM_DEBUG(llvm::dbgs()
<< Key << " with " << (i - 1) * 10 << "-" << i * 10 - 1
<< "% of its scope covered: " << LocationStats[i]);
}
OS << ",\"" << Key << " with 100% of its scope covered\":"
<< LocationStats[NumOfCoverageCategories - 1];
LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: "
<< LocationStats[NumOfCoverageCategories - 1]);
}
/// \}
/// 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;
LocationStats LocStats;
StringMap<PerFunctionStats> Statistics;
for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
collectStatsRecursive(CUDie, getLowPC(CUDie), "/", "g", 0, 0, 0,
Statistics, GlobalStats, LocStats);
/// 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 = 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;
// Count variables in concrete out-of-line functions and in global scope.
if (Stats.HasPCAddresses || !Stats.IsFunction)
TotalVars += Stats.VarsInFunction.size();
unsigned Constants = Stats.ConstantMembers;
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.
OS.SetBufferSize(1024);
OS << "{\"version\":" << Version;
LLVM_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, "source functions with location", NumFuncsWithSrcLoc);
printDatum(OS, "inlined functions", NumInlinedFunctions);
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, "call site DIEs", GlobalStats.CallSiteDIEs);
printDatum(OS, "call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
printDatum(OS, "scope bytes total",
GlobalStats.ScopeBytesFromFirstDefinition);
printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
printDatum(OS, "entry value scope bytes covered",
GlobalStats.ScopeEntryValueBytesCovered);
printDatum(OS, "formal params scope bytes total",
GlobalStats.ParamScopeBytesFromFirstDefinition);
printDatum(OS, "formal params scope bytes covered",
GlobalStats.ParamScopeBytesCovered);
printDatum(OS, "formal params entry value scope bytes covered",
GlobalStats.ParamScopeEntryValueBytesCovered);
printDatum(OS, "vars scope bytes total",
GlobalStats.VarScopeBytesFromFirstDefinition);
printDatum(OS, "vars scope bytes covered", GlobalStats.VarScopeBytesCovered);
printDatum(OS, "vars entry value scope bytes covered",
GlobalStats.VarScopeEntryValueBytesCovered);
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);
printDatum(OS, "total variables procesed by location statistics",
LocStats.NumVarParam);
printLocationStats(OS, "variables", LocStats.VarParamLocStats);
printLocationStats(OS, "variables (excluding the debug entry values)",
LocStats.VarParamNonEntryValLocStats);
printDatum(OS, "total params procesed by location statistics",
LocStats.NumParam);
printLocationStats(OS, "params", LocStats.ParamLocStats);
printLocationStats(OS, "params (excluding the debug entry values)",
LocStats.ParamNonEntryValLocStats);
printDatum(OS, "total vars procesed by location statistics", LocStats.NumVar);
printLocationStats(OS, "vars", LocStats.VarLocStats);
printLocationStats(OS, "vars (excluding the debug entry values)",
LocStats.VarNonEntryValLocStats);
OS << "}\n";
LLVM_DEBUG(
llvm::dbgs() << "Total Availability: "
<< (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal)
<< "%\n";
llvm::dbgs() << "PC Ranges covered: "
<< (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
GlobalStats.ScopeBytesFromFirstDefinition)
<< "%\n");
return true;
}