| #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; |
| } |