15ffd83dbSDimitry Andric //===-- Statistics.cpp - Debug Info quality metrics -----------------------===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric 95ffd83dbSDimitry Andric #include "llvm-dwarfdump.h" 100b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h" 110b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h" 120b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h" 130b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" 140b57cec5SDimitry Andric #include "llvm/Object/ObjectFile.h" 158bcb0991SDimitry Andric #include "llvm/Support/JSON.h" 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfdump" 180b57cec5SDimitry Andric using namespace llvm; 195ffd83dbSDimitry Andric using namespace llvm::dwarfdump; 205ffd83dbSDimitry Andric using namespace llvm::object; 210b57cec5SDimitry Andric 22fe6060f1SDimitry Andric namespace { 238bcb0991SDimitry Andric /// This represents the number of categories of debug location coverage being 248bcb0991SDimitry Andric /// calculated. The first category is the number of variables with 0% location 258bcb0991SDimitry Andric /// coverage, but the last category is the number of variables with 100% 268bcb0991SDimitry Andric /// location coverage. 278bcb0991SDimitry Andric constexpr int NumOfCoverageCategories = 12; 288bcb0991SDimitry Andric 29fe6060f1SDimitry Andric /// This is used for zero location coverage bucket. 30fe6060f1SDimitry Andric constexpr unsigned ZeroCoverageBucket = 0; 31fe6060f1SDimitry Andric 32*349cc55cSDimitry Andric /// The UINT64_MAX is used as an indication of the overflow. 33*349cc55cSDimitry Andric constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max(); 34*349cc55cSDimitry Andric 35fe6060f1SDimitry Andric /// This represents variables DIE offsets. 36fe6060f1SDimitry Andric using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>; 37fe6060f1SDimitry Andric /// This maps function DIE offset to its variables. 38fe6060f1SDimitry Andric using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>; 39fe6060f1SDimitry Andric /// This represents function DIE offsets containing an abstract_origin. 40fe6060f1SDimitry Andric using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>; 41fe6060f1SDimitry Andric 42*349cc55cSDimitry Andric /// This represents a data type for the stats and it helps us to 43*349cc55cSDimitry Andric /// detect an overflow. 44*349cc55cSDimitry Andric /// NOTE: This can be implemented as a template if there is an another type 45*349cc55cSDimitry Andric /// needing this. 46*349cc55cSDimitry Andric struct SaturatingUINT64 { 47*349cc55cSDimitry Andric /// Number that represents the stats. 48*349cc55cSDimitry Andric uint64_t Value; 49*349cc55cSDimitry Andric 50*349cc55cSDimitry Andric SaturatingUINT64(uint64_t Value_) : Value(Value_) {} 51*349cc55cSDimitry Andric 52*349cc55cSDimitry Andric void operator++(int) { return *this += 1; } 53*349cc55cSDimitry Andric void operator+=(uint64_t Value_) { 54*349cc55cSDimitry Andric if (Value != OverflowValue) { 55*349cc55cSDimitry Andric if (Value < OverflowValue - Value_) 56*349cc55cSDimitry Andric Value += Value_; 57*349cc55cSDimitry Andric else 58*349cc55cSDimitry Andric Value = OverflowValue; 59*349cc55cSDimitry Andric } 60*349cc55cSDimitry Andric } 61*349cc55cSDimitry Andric }; 62*349cc55cSDimitry Andric 630b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and 640b57cec5SDimitry Andric /// contains variables, such as a compile unit). 650b57cec5SDimitry Andric struct PerFunctionStats { 660b57cec5SDimitry Andric /// Number of inlined instances of this function. 67*349cc55cSDimitry Andric uint64_t NumFnInlined = 0; 685ffd83dbSDimitry Andric /// Number of out-of-line instances of this function. 69*349cc55cSDimitry Andric uint64_t NumFnOutOfLine = 0; 700b57cec5SDimitry Andric /// Number of inlined instances that have abstract origins. 71*349cc55cSDimitry Andric uint64_t NumAbstractOrigins = 0; 720b57cec5SDimitry Andric /// Number of variables and parameters with location across all inlined 730b57cec5SDimitry Andric /// instances. 74*349cc55cSDimitry Andric uint64_t TotalVarWithLoc = 0; 750b57cec5SDimitry Andric /// Number of constants with location across all inlined instances. 76*349cc55cSDimitry Andric uint64_t ConstantMembers = 0; 775ffd83dbSDimitry Andric /// Number of arificial variables, parameters or members across all instances. 78*349cc55cSDimitry Andric uint64_t NumArtificial = 0; 790b57cec5SDimitry Andric /// List of all Variables and parameters in this function. 800b57cec5SDimitry Andric StringSet<> VarsInFunction; 810b57cec5SDimitry Andric /// Compile units also cover a PC range, but have this flag set to false. 820b57cec5SDimitry Andric bool IsFunction = false; 830b57cec5SDimitry Andric /// Function has source location information. 840b57cec5SDimitry Andric bool HasSourceLocation = false; 850b57cec5SDimitry Andric /// Number of function parameters. 86*349cc55cSDimitry Andric uint64_t NumParams = 0; 870b57cec5SDimitry Andric /// Number of function parameters with source location. 88*349cc55cSDimitry Andric uint64_t NumParamSourceLocations = 0; 890b57cec5SDimitry Andric /// Number of function parameters with type. 90*349cc55cSDimitry Andric uint64_t NumParamTypes = 0; 910b57cec5SDimitry Andric /// Number of function parameters with a DW_AT_location. 92*349cc55cSDimitry Andric uint64_t NumParamLocations = 0; 935ffd83dbSDimitry Andric /// Number of local variables. 94*349cc55cSDimitry Andric uint64_t NumLocalVars = 0; 955ffd83dbSDimitry Andric /// Number of local variables with source location. 96*349cc55cSDimitry Andric uint64_t NumLocalVarSourceLocations = 0; 975ffd83dbSDimitry Andric /// Number of local variables with type. 98*349cc55cSDimitry Andric uint64_t NumLocalVarTypes = 0; 995ffd83dbSDimitry Andric /// Number of local variables with DW_AT_location. 100*349cc55cSDimitry Andric uint64_t NumLocalVarLocations = 0; 1010b57cec5SDimitry Andric }; 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs. 1040b57cec5SDimitry Andric struct GlobalStats { 1050b57cec5SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations. 106*349cc55cSDimitry Andric SaturatingUINT64 TotalBytesCovered = 0; 107e8d8bef9SDimitry Andric /// Total number of parent DIE PC range bytes covered by DW_AT_Locations. 108*349cc55cSDimitry Andric SaturatingUINT64 ScopeBytesCovered = 0; 109480093f4SDimitry Andric /// Total number of PC range bytes in each variable's enclosing scope. 110*349cc55cSDimitry Andric SaturatingUINT64 ScopeBytes = 0; 1118bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 1128bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value). 113*349cc55cSDimitry Andric SaturatingUINT64 ScopeEntryValueBytesCovered = 0; 1148bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations of 1158bcb0991SDimitry Andric /// formal parameters. 116*349cc55cSDimitry Andric SaturatingUINT64 ParamScopeBytesCovered = 0; 1175ffd83dbSDimitry Andric /// Total number of PC range bytes in each parameter's enclosing scope. 118*349cc55cSDimitry Andric SaturatingUINT64 ParamScopeBytes = 0; 1198bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 1208bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value) (only for parameters). 121*349cc55cSDimitry Andric SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0; 1228bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations (only for local 1238bcb0991SDimitry Andric /// variables). 124*349cc55cSDimitry Andric SaturatingUINT64 LocalVarScopeBytesCovered = 0; 1255ffd83dbSDimitry Andric /// Total number of PC range bytes in each local variable's enclosing scope. 126*349cc55cSDimitry Andric SaturatingUINT64 LocalVarScopeBytes = 0; 1278bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 1288bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value) (only for local variables). 129*349cc55cSDimitry Andric SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0; 1308bcb0991SDimitry Andric /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line). 131*349cc55cSDimitry Andric SaturatingUINT64 CallSiteEntries = 0; 1328bcb0991SDimitry Andric /// Total number of call site DIEs (DW_TAG_call_site). 133*349cc55cSDimitry Andric SaturatingUINT64 CallSiteDIEs = 0; 1348bcb0991SDimitry Andric /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter). 135*349cc55cSDimitry Andric SaturatingUINT64 CallSiteParamDIEs = 0; 1360b57cec5SDimitry Andric /// Total byte size of concrete functions. This byte size includes 1370b57cec5SDimitry Andric /// inline functions contained in the concrete functions. 138*349cc55cSDimitry Andric SaturatingUINT64 FunctionSize = 0; 1390b57cec5SDimitry Andric /// Total byte size of inlined functions. This is the total number of bytes 1400b57cec5SDimitry Andric /// for the top inline functions within concrete functions. This can help 1410b57cec5SDimitry Andric /// tune the inline settings when compiling to match user expectations. 142*349cc55cSDimitry Andric SaturatingUINT64 InlineFunctionSize = 0; 1438bcb0991SDimitry Andric }; 1448bcb0991SDimitry Andric 1458bcb0991SDimitry Andric /// Holds accumulated debug location statistics about local variables and 1468bcb0991SDimitry Andric /// formal parameters. 1478bcb0991SDimitry Andric struct LocationStats { 1488bcb0991SDimitry Andric /// Map the scope coverage decile to the number of variables in the decile. 1498bcb0991SDimitry Andric /// The first element of the array (at the index zero) represents the number 1508bcb0991SDimitry Andric /// of variables with the no debug location at all, but the last element 1518bcb0991SDimitry Andric /// in the vector represents the number of fully covered variables within 1528bcb0991SDimitry Andric /// its scope. 153*349cc55cSDimitry Andric std::vector<SaturatingUINT64> VarParamLocStats{ 154*349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1558bcb0991SDimitry Andric /// Map non debug entry values coverage. 156*349cc55cSDimitry Andric std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{ 157*349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1588bcb0991SDimitry Andric /// The debug location statistics for formal parameters. 159*349cc55cSDimitry Andric std::vector<SaturatingUINT64> ParamLocStats{ 160*349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1618bcb0991SDimitry Andric /// Map non debug entry values coverage for formal parameters. 162*349cc55cSDimitry Andric std::vector<SaturatingUINT64> ParamNonEntryValLocStats{ 163*349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1648bcb0991SDimitry Andric /// The debug location statistics for local variables. 165*349cc55cSDimitry Andric std::vector<SaturatingUINT64> LocalVarLocStats{ 166*349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1678bcb0991SDimitry Andric /// Map non debug entry values coverage for local variables. 168*349cc55cSDimitry Andric std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{ 169*349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1708bcb0991SDimitry Andric /// Total number of local variables and function parameters processed. 171*349cc55cSDimitry Andric SaturatingUINT64 NumVarParam = 0; 1728bcb0991SDimitry Andric /// Total number of formal parameters processed. 173*349cc55cSDimitry Andric SaturatingUINT64 NumParam = 0; 1748bcb0991SDimitry Andric /// Total number of local variables processed. 175*349cc55cSDimitry Andric SaturatingUINT64 NumVar = 0; 1760b57cec5SDimitry Andric }; 1775ffd83dbSDimitry Andric } // namespace 1780b57cec5SDimitry Andric 1798bcb0991SDimitry Andric /// Collect debug location statistics for one DIE. 180e8d8bef9SDimitry Andric static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope, 181*349cc55cSDimitry Andric std::vector<SaturatingUINT64> &VarParamLocStats, 182*349cc55cSDimitry Andric std::vector<SaturatingUINT64> &ParamLocStats, 183*349cc55cSDimitry Andric std::vector<SaturatingUINT64> &LocalVarLocStats, 1845ffd83dbSDimitry Andric bool IsParam, bool IsLocalVar) { 185e8d8bef9SDimitry Andric auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned { 1868bcb0991SDimitry Andric // No debug location at all for the variable. 187e8d8bef9SDimitry Andric if (ScopeBytesCovered == 0) 1888bcb0991SDimitry Andric return 0; 1898bcb0991SDimitry Andric // Fully covered variable within its scope. 190e8d8bef9SDimitry Andric if (ScopeBytesCovered >= BytesInScope) 1918bcb0991SDimitry Andric return NumOfCoverageCategories - 1; 1928bcb0991SDimitry Andric // Get covered range (e.g. 20%-29%). 193e8d8bef9SDimitry Andric unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope; 1948bcb0991SDimitry Andric LocBucket /= 10; 1958bcb0991SDimitry Andric return LocBucket + 1; 1968bcb0991SDimitry Andric }; 1978bcb0991SDimitry Andric 1988bcb0991SDimitry Andric unsigned CoverageBucket = getCoverageBucket(); 199fe6060f1SDimitry Andric 200*349cc55cSDimitry Andric VarParamLocStats[CoverageBucket].Value++; 2018bcb0991SDimitry Andric if (IsParam) 202*349cc55cSDimitry Andric ParamLocStats[CoverageBucket].Value++; 2038bcb0991SDimitry Andric else if (IsLocalVar) 204*349cc55cSDimitry Andric LocalVarLocStats[CoverageBucket].Value++; 2055ffd83dbSDimitry Andric } 206fe6060f1SDimitry Andric 2075ffd83dbSDimitry Andric /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName 2085ffd83dbSDimitry Andric /// and DeclLine. The identifier aims to be unique for any unique entities, 2095ffd83dbSDimitry Andric /// but keeping the same among different instances of the same entity. 2105ffd83dbSDimitry Andric static std::string constructDieID(DWARFDie Die, 2115ffd83dbSDimitry Andric StringRef Prefix = StringRef()) { 2125ffd83dbSDimitry Andric std::string IDStr; 2135ffd83dbSDimitry Andric llvm::raw_string_ostream ID(IDStr); 2145ffd83dbSDimitry Andric ID << Prefix 2155ffd83dbSDimitry Andric << Die.getName(DINameKind::LinkageName); 2165ffd83dbSDimitry Andric 2175ffd83dbSDimitry Andric // Prefix + Name is enough for local variables and parameters. 2185ffd83dbSDimitry Andric if (!Prefix.empty() && !Prefix.equals("g")) 2195ffd83dbSDimitry Andric return ID.str(); 2205ffd83dbSDimitry Andric 2215ffd83dbSDimitry Andric auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file); 2225ffd83dbSDimitry Andric std::string File; 2235ffd83dbSDimitry Andric if (DeclFile) { 2245ffd83dbSDimitry Andric DWARFUnit *U = Die.getDwarfUnit(); 2255ffd83dbSDimitry Andric if (const auto *LT = U->getContext().getLineTableForUnit(U)) 2265ffd83dbSDimitry Andric if (LT->getFileNameByIndex( 2275ffd83dbSDimitry Andric dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(), 2285ffd83dbSDimitry Andric DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) 2295ffd83dbSDimitry Andric File = std::string(sys::path::filename(File)); 2305ffd83dbSDimitry Andric } 2315ffd83dbSDimitry Andric ID << ":" << (File.empty() ? "/" : File); 2325ffd83dbSDimitry Andric ID << ":" 2335ffd83dbSDimitry Andric << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0); 2345ffd83dbSDimitry Andric return ID.str(); 2358bcb0991SDimitry Andric } 2368bcb0991SDimitry Andric 237e8d8bef9SDimitry Andric /// Return the number of bytes in the overlap of ranges A and B. 238e8d8bef9SDimitry Andric static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) { 239e8d8bef9SDimitry Andric uint64_t Lower = std::max(A.LowPC, B.LowPC); 240e8d8bef9SDimitry Andric uint64_t Upper = std::min(A.HighPC, B.HighPC); 241e8d8bef9SDimitry Andric if (Lower >= Upper) 242e8d8bef9SDimitry Andric return 0; 243e8d8bef9SDimitry Andric return Upper - Lower; 244e8d8bef9SDimitry Andric } 245e8d8bef9SDimitry Andric 2460b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE. 247fe6060f1SDimitry Andric static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix, 248fe6060f1SDimitry Andric const std::string &VarPrefix, 249fe6060f1SDimitry Andric uint64_t BytesInScope, uint32_t InlineDepth, 2500b57cec5SDimitry Andric StringMap<PerFunctionStats> &FnStatMap, 2518bcb0991SDimitry Andric GlobalStats &GlobalStats, 252fe6060f1SDimitry Andric LocationStats &LocStats, 253fe6060f1SDimitry Andric AbstractOriginVarsTy *AbstractOriginVariables) { 254fe6060f1SDimitry Andric const dwarf::Tag Tag = Die.getTag(); 255fe6060f1SDimitry Andric // Skip CU node. 256fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_compile_unit) 257fe6060f1SDimitry Andric return; 258fe6060f1SDimitry Andric 2590b57cec5SDimitry Andric bool HasLoc = false; 2600b57cec5SDimitry Andric bool HasSrcLoc = false; 2610b57cec5SDimitry Andric bool HasType = false; 262e8d8bef9SDimitry Andric uint64_t TotalBytesCovered = 0; 263e8d8bef9SDimitry Andric uint64_t ScopeBytesCovered = 0; 2648bcb0991SDimitry Andric uint64_t BytesEntryValuesCovered = 0; 2658bcb0991SDimitry Andric auto &FnStats = FnStatMap[FnPrefix]; 266fe6060f1SDimitry Andric bool IsParam = Tag == dwarf::DW_TAG_formal_parameter; 267fe6060f1SDimitry Andric bool IsLocalVar = Tag == dwarf::DW_TAG_variable; 268fe6060f1SDimitry Andric bool IsConstantMember = Tag == dwarf::DW_TAG_member && 2695ffd83dbSDimitry Andric Die.find(dwarf::DW_AT_const_value); 2700b57cec5SDimitry Andric 271fe6060f1SDimitry Andric // For zero covered inlined variables the locstats will be 272fe6060f1SDimitry Andric // calculated later. 273fe6060f1SDimitry Andric bool DeferLocStats = false; 274fe6060f1SDimitry Andric 275fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) { 2768bcb0991SDimitry Andric GlobalStats.CallSiteDIEs++; 2770b57cec5SDimitry Andric return; 2780b57cec5SDimitry Andric } 2790b57cec5SDimitry Andric 280fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_call_site_parameter || 281fe6060f1SDimitry Andric Tag == dwarf::DW_TAG_GNU_call_site_parameter) { 2828bcb0991SDimitry Andric GlobalStats.CallSiteParamDIEs++; 2838bcb0991SDimitry Andric return; 2848bcb0991SDimitry Andric } 2858bcb0991SDimitry Andric 2865ffd83dbSDimitry Andric if (!IsParam && !IsLocalVar && !IsConstantMember) { 2870b57cec5SDimitry Andric // Not a variable or constant member. 2880b57cec5SDimitry Andric return; 2890b57cec5SDimitry Andric } 2900b57cec5SDimitry Andric 2915ffd83dbSDimitry Andric // Ignore declarations of global variables. 2925ffd83dbSDimitry Andric if (IsLocalVar && Die.find(dwarf::DW_AT_declaration)) 2935ffd83dbSDimitry Andric return; 2945ffd83dbSDimitry Andric 2950b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_decl_file) && 2960b57cec5SDimitry Andric Die.findRecursively(dwarf::DW_AT_decl_line)) 2970b57cec5SDimitry Andric HasSrcLoc = true; 2980b57cec5SDimitry Andric 2990b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_type)) 3000b57cec5SDimitry Andric HasType = true; 3010b57cec5SDimitry Andric 302fe6060f1SDimitry Andric if (Die.find(dwarf::DW_AT_abstract_origin)) { 303fe6060f1SDimitry Andric if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) { 304fe6060f1SDimitry Andric if (AbstractOriginVariables) { 305fe6060f1SDimitry Andric auto Offset = Die.find(dwarf::DW_AT_abstract_origin); 306fe6060f1SDimitry Andric // Do not track this variable any more, since it has location 307fe6060f1SDimitry Andric // coverage. 308fe6060f1SDimitry Andric llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue()); 309fe6060f1SDimitry Andric } 310fe6060f1SDimitry Andric } else { 311fe6060f1SDimitry Andric // The locstats will be handled at the end of 312fe6060f1SDimitry Andric // the collectStatsRecursive(). 313fe6060f1SDimitry Andric DeferLocStats = true; 314fe6060f1SDimitry Andric } 315fe6060f1SDimitry Andric } 316fe6060f1SDimitry Andric 3178bcb0991SDimitry Andric auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { 3188bcb0991SDimitry Andric DWARFUnit *U = Die.getDwarfUnit(); 3198bcb0991SDimitry Andric DataExtractor Data(toStringRef(D), 3208bcb0991SDimitry Andric Die.getDwarfUnit()->getContext().isLittleEndian(), 0); 3215ffd83dbSDimitry Andric DWARFExpression Expression(Data, U->getAddressByteSize(), 3225ffd83dbSDimitry Andric U->getFormParams().Format); 3238bcb0991SDimitry Andric // Consider the expression containing the DW_OP_entry_value as 3248bcb0991SDimitry Andric // an entry value. 325*349cc55cSDimitry Andric return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) { 3268bcb0991SDimitry Andric return Op.getCode() == dwarf::DW_OP_entry_value || 3278bcb0991SDimitry Andric Op.getCode() == dwarf::DW_OP_GNU_entry_value; 3288bcb0991SDimitry Andric }); 3298bcb0991SDimitry Andric }; 3308bcb0991SDimitry Andric 3310b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_const_value)) { 3320b57cec5SDimitry Andric // This catches constant members *and* variables. 3330b57cec5SDimitry Andric HasLoc = true; 334e8d8bef9SDimitry Andric ScopeBytesCovered = BytesInScope; 335e8d8bef9SDimitry Andric TotalBytesCovered = BytesInScope; 3360b57cec5SDimitry Andric } else { 3370b57cec5SDimitry Andric // Handle variables and function arguments. 338480093f4SDimitry Andric Expected<std::vector<DWARFLocationExpression>> Loc = 339480093f4SDimitry Andric Die.getLocations(dwarf::DW_AT_location); 340480093f4SDimitry Andric if (!Loc) { 341480093f4SDimitry Andric consumeError(Loc.takeError()); 3420b57cec5SDimitry Andric } else { 343480093f4SDimitry Andric HasLoc = true; 344480093f4SDimitry Andric // Get PC coverage. 345480093f4SDimitry Andric auto Default = find_if( 346480093f4SDimitry Andric *Loc, [](const DWARFLocationExpression &L) { return !L.Range; }); 347480093f4SDimitry Andric if (Default != Loc->end()) { 3480b57cec5SDimitry Andric // Assume the entire range is covered by a single location. 349e8d8bef9SDimitry Andric ScopeBytesCovered = BytesInScope; 350e8d8bef9SDimitry Andric TotalBytesCovered = BytesInScope; 351480093f4SDimitry Andric } else { 352e8d8bef9SDimitry Andric // Caller checks this Expected result already, it cannot fail. 353e8d8bef9SDimitry Andric auto ScopeRanges = cantFail(Die.getParent().getAddressRanges()); 354480093f4SDimitry Andric for (auto Entry : *Loc) { 355e8d8bef9SDimitry Andric TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC; 356e8d8bef9SDimitry Andric uint64_t ScopeBytesCoveredByEntry = 0; 357e8d8bef9SDimitry Andric // Calculate how many bytes of the parent scope this entry covers. 358e8d8bef9SDimitry Andric // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The 359e8d8bef9SDimitry Andric // address ranges defined by the bounded location descriptions of a 360e8d8bef9SDimitry Andric // location list may overlap". So in theory a variable can have 361e8d8bef9SDimitry Andric // multiple simultaneous locations, which would make this calculation 362e8d8bef9SDimitry Andric // misleading because we will count the overlapped areas 363e8d8bef9SDimitry Andric // twice. However, clang does not currently emit DWARF like this. 364e8d8bef9SDimitry Andric for (DWARFAddressRange R : ScopeRanges) { 365e8d8bef9SDimitry Andric ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R); 366e8d8bef9SDimitry Andric } 367e8d8bef9SDimitry Andric ScopeBytesCovered += ScopeBytesCoveredByEntry; 368480093f4SDimitry Andric if (IsEntryValue(Entry.Expr)) 369e8d8bef9SDimitry Andric BytesEntryValuesCovered += ScopeBytesCoveredByEntry; 370480093f4SDimitry Andric } 3710b57cec5SDimitry Andric } 3720b57cec5SDimitry Andric } 3730b57cec5SDimitry Andric } 3740b57cec5SDimitry Andric 3758bcb0991SDimitry Andric // Calculate the debug location statistics. 376fe6060f1SDimitry Andric if (BytesInScope && !DeferLocStats) { 377*349cc55cSDimitry Andric LocStats.NumVarParam.Value++; 3788bcb0991SDimitry Andric if (IsParam) 379*349cc55cSDimitry Andric LocStats.NumParam.Value++; 3808bcb0991SDimitry Andric else if (IsLocalVar) 381*349cc55cSDimitry Andric LocStats.NumVar.Value++; 3828bcb0991SDimitry Andric 383e8d8bef9SDimitry Andric collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats, 3845ffd83dbSDimitry Andric LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam, 3858bcb0991SDimitry Andric IsLocalVar); 3868bcb0991SDimitry Andric // Non debug entry values coverage statistics. 387e8d8bef9SDimitry Andric collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope, 3888bcb0991SDimitry Andric LocStats.VarParamNonEntryValLocStats, 3898bcb0991SDimitry Andric LocStats.ParamNonEntryValLocStats, 3905ffd83dbSDimitry Andric LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar); 3918bcb0991SDimitry Andric } 3928bcb0991SDimitry Andric 3930b57cec5SDimitry Andric // Collect PC range coverage data. 3940b57cec5SDimitry Andric if (DWARFDie D = 3950b57cec5SDimitry Andric Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) 3960b57cec5SDimitry Andric Die = D; 3975ffd83dbSDimitry Andric 3985ffd83dbSDimitry Andric std::string VarID = constructDieID(Die, VarPrefix); 3995ffd83dbSDimitry Andric FnStats.VarsInFunction.insert(VarID); 4005ffd83dbSDimitry Andric 401e8d8bef9SDimitry Andric GlobalStats.TotalBytesCovered += TotalBytesCovered; 4020b57cec5SDimitry Andric if (BytesInScope) { 403e8d8bef9SDimitry Andric GlobalStats.ScopeBytesCovered += ScopeBytesCovered; 404480093f4SDimitry Andric GlobalStats.ScopeBytes += BytesInScope; 4058bcb0991SDimitry Andric GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered; 4068bcb0991SDimitry Andric if (IsParam) { 407e8d8bef9SDimitry Andric GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered; 408480093f4SDimitry Andric GlobalStats.ParamScopeBytes += BytesInScope; 4098bcb0991SDimitry Andric GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; 4108bcb0991SDimitry Andric } else if (IsLocalVar) { 411e8d8bef9SDimitry Andric GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered; 4125ffd83dbSDimitry Andric GlobalStats.LocalVarScopeBytes += BytesInScope; 4135ffd83dbSDimitry Andric GlobalStats.LocalVarScopeEntryValueBytesCovered += 4145ffd83dbSDimitry Andric BytesEntryValuesCovered; 4158bcb0991SDimitry Andric } 416*349cc55cSDimitry Andric assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value); 4170b57cec5SDimitry Andric } 4185ffd83dbSDimitry Andric 4195ffd83dbSDimitry Andric if (IsConstantMember) { 4205ffd83dbSDimitry Andric FnStats.ConstantMembers++; 4215ffd83dbSDimitry Andric return; 4225ffd83dbSDimitry Andric } 4235ffd83dbSDimitry Andric 4245ffd83dbSDimitry Andric FnStats.TotalVarWithLoc += (unsigned)HasLoc; 4255ffd83dbSDimitry Andric 4265ffd83dbSDimitry Andric if (Die.find(dwarf::DW_AT_artificial)) { 4275ffd83dbSDimitry Andric FnStats.NumArtificial++; 4285ffd83dbSDimitry Andric return; 4295ffd83dbSDimitry Andric } 4305ffd83dbSDimitry Andric 4318bcb0991SDimitry Andric if (IsParam) { 4320b57cec5SDimitry Andric FnStats.NumParams++; 4330b57cec5SDimitry Andric if (HasType) 4340b57cec5SDimitry Andric FnStats.NumParamTypes++; 4350b57cec5SDimitry Andric if (HasSrcLoc) 4360b57cec5SDimitry Andric FnStats.NumParamSourceLocations++; 4370b57cec5SDimitry Andric if (HasLoc) 4380b57cec5SDimitry Andric FnStats.NumParamLocations++; 4398bcb0991SDimitry Andric } else if (IsLocalVar) { 4405ffd83dbSDimitry Andric FnStats.NumLocalVars++; 4410b57cec5SDimitry Andric if (HasType) 4425ffd83dbSDimitry Andric FnStats.NumLocalVarTypes++; 4430b57cec5SDimitry Andric if (HasSrcLoc) 4445ffd83dbSDimitry Andric FnStats.NumLocalVarSourceLocations++; 4450b57cec5SDimitry Andric if (HasLoc) 4465ffd83dbSDimitry Andric FnStats.NumLocalVarLocations++; 4470b57cec5SDimitry Andric } 4480b57cec5SDimitry Andric } 4490b57cec5SDimitry Andric 450fe6060f1SDimitry Andric /// Recursively collect variables from subprogram with DW_AT_inline attribute. 451fe6060f1SDimitry Andric static void collectAbstractOriginFnInfo( 452fe6060f1SDimitry Andric DWARFDie Die, uint64_t SPOffset, 453fe6060f1SDimitry Andric AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo) { 454fe6060f1SDimitry Andric DWARFDie Child = Die.getFirstChild(); 455fe6060f1SDimitry Andric while (Child) { 456fe6060f1SDimitry Andric const dwarf::Tag ChildTag = Child.getTag(); 457fe6060f1SDimitry Andric if (ChildTag == dwarf::DW_TAG_formal_parameter || 458fe6060f1SDimitry Andric ChildTag == dwarf::DW_TAG_variable) 459fe6060f1SDimitry Andric GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset()); 460fe6060f1SDimitry Andric else if (ChildTag == dwarf::DW_TAG_lexical_block) 461fe6060f1SDimitry Andric collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo); 462fe6060f1SDimitry Andric Child = Child.getSibling(); 463fe6060f1SDimitry Andric } 464fe6060f1SDimitry Andric } 465fe6060f1SDimitry Andric 4660b57cec5SDimitry Andric /// Recursively collect debug info quality metrics. 467fe6060f1SDimitry Andric static void collectStatsRecursive( 468fe6060f1SDimitry Andric DWARFDie Die, std::string FnPrefix, std::string VarPrefix, 469fe6060f1SDimitry Andric uint64_t BytesInScope, uint32_t InlineDepth, 470fe6060f1SDimitry Andric StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats, 471fe6060f1SDimitry Andric LocationStats &LocStats, 472fe6060f1SDimitry Andric AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, 473fe6060f1SDimitry Andric FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed, 474fe6060f1SDimitry Andric AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) { 475fe6060f1SDimitry Andric // Skip NULL nodes. 476fe6060f1SDimitry Andric if (Die.isNULL()) 477fe6060f1SDimitry Andric return; 478fe6060f1SDimitry Andric 4790b57cec5SDimitry Andric const dwarf::Tag Tag = Die.getTag(); 4805ffd83dbSDimitry Andric // Skip function types. 4815ffd83dbSDimitry Andric if (Tag == dwarf::DW_TAG_subroutine_type) 4825ffd83dbSDimitry Andric return; 4835ffd83dbSDimitry Andric 4845ffd83dbSDimitry Andric // Handle any kind of lexical scope. 485fe6060f1SDimitry Andric const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None; 4860b57cec5SDimitry Andric const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; 4870b57cec5SDimitry Andric const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; 4880b57cec5SDimitry Andric const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; 489fe6060f1SDimitry Andric // We want to know how many variables (with abstract_origin) don't have 490fe6060f1SDimitry Andric // location info. 491fe6060f1SDimitry Andric const bool IsCandidateForZeroLocCovTracking = 492fe6060f1SDimitry Andric (IsInlinedFunction || (IsFunction && HasAbstractOrigin)); 4930b57cec5SDimitry Andric 494fe6060f1SDimitry Andric AbstractOriginVarsTy AbstractOriginVars; 495fe6060f1SDimitry Andric 496fe6060f1SDimitry Andric // Get the vars of the inlined fn, so the locstats 497fe6060f1SDimitry Andric // reports the missing vars (with coverage 0%). 498fe6060f1SDimitry Andric if (IsCandidateForZeroLocCovTracking) { 499fe6060f1SDimitry Andric auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin); 500fe6060f1SDimitry Andric if (OffsetFn) { 501fe6060f1SDimitry Andric uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue(); 502fe6060f1SDimitry Andric if (GlobalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) { 503fe6060f1SDimitry Andric AbstractOriginVars = GlobalAbstractOriginFnInfo[OffsetOfInlineFnCopy]; 504fe6060f1SDimitry Andric AbstractOriginVarsPtr = &AbstractOriginVars; 505fe6060f1SDimitry Andric } else { 506fe6060f1SDimitry Andric // This means that the DW_AT_inline fn copy is out of order, 507fe6060f1SDimitry Andric // so this abstract origin instance will be processed later. 508fe6060f1SDimitry Andric FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset()); 509fe6060f1SDimitry Andric AbstractOriginVarsPtr = nullptr; 510fe6060f1SDimitry Andric } 511fe6060f1SDimitry Andric } 512fe6060f1SDimitry Andric } 513fe6060f1SDimitry Andric 514fe6060f1SDimitry Andric if (IsFunction || IsInlinedFunction || IsBlock) { 5150b57cec5SDimitry Andric // Reset VarPrefix when entering a new function. 516fe6060f1SDimitry Andric if (IsFunction || IsInlinedFunction) 5170b57cec5SDimitry Andric VarPrefix = "v"; 5180b57cec5SDimitry Andric 5190b57cec5SDimitry Andric // Ignore forward declarations. 5200b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_declaration)) 5210b57cec5SDimitry Andric return; 5220b57cec5SDimitry Andric 5230b57cec5SDimitry Andric // Check for call sites. 5240b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line)) 5250b57cec5SDimitry Andric GlobalStats.CallSiteEntries++; 5260b57cec5SDimitry Andric 5270b57cec5SDimitry Andric // PC Ranges. 5280b57cec5SDimitry Andric auto RangesOrError = Die.getAddressRanges(); 5290b57cec5SDimitry Andric if (!RangesOrError) { 5300b57cec5SDimitry Andric llvm::consumeError(RangesOrError.takeError()); 5310b57cec5SDimitry Andric return; 5320b57cec5SDimitry Andric } 5330b57cec5SDimitry Andric 5340b57cec5SDimitry Andric auto Ranges = RangesOrError.get(); 5350b57cec5SDimitry Andric uint64_t BytesInThisScope = 0; 5360b57cec5SDimitry Andric for (auto Range : Ranges) 5370b57cec5SDimitry Andric BytesInThisScope += Range.HighPC - Range.LowPC; 5380b57cec5SDimitry Andric 5390b57cec5SDimitry Andric // Count the function. 5400b57cec5SDimitry Andric if (!IsBlock) { 541fe6060f1SDimitry Andric // Skip over abstract origins, but collect variables 542fe6060f1SDimitry Andric // from it so it can be used for location statistics 543fe6060f1SDimitry Andric // for inlined instancies. 544fe6060f1SDimitry Andric if (Die.find(dwarf::DW_AT_inline)) { 545fe6060f1SDimitry Andric uint64_t SPOffset = Die.getOffset(); 546fe6060f1SDimitry Andric collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo); 5470b57cec5SDimitry Andric return; 548fe6060f1SDimitry Andric } 549fe6060f1SDimitry Andric 5505ffd83dbSDimitry Andric std::string FnID = constructDieID(Die); 5515ffd83dbSDimitry Andric // We've seen an instance of this function. 5525ffd83dbSDimitry Andric auto &FnStats = FnStatMap[FnID]; 5535ffd83dbSDimitry Andric FnStats.IsFunction = true; 5540b57cec5SDimitry Andric if (IsInlinedFunction) { 5550b57cec5SDimitry Andric FnStats.NumFnInlined++; 5560b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) 5570b57cec5SDimitry Andric FnStats.NumAbstractOrigins++; 5585ffd83dbSDimitry Andric } else { 5595ffd83dbSDimitry Andric FnStats.NumFnOutOfLine++; 5600b57cec5SDimitry Andric } 5610b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_decl_file) && 5620b57cec5SDimitry Andric Die.findRecursively(dwarf::DW_AT_decl_line)) 5630b57cec5SDimitry Andric FnStats.HasSourceLocation = true; 5645ffd83dbSDimitry Andric // Update function prefix. 5655ffd83dbSDimitry Andric FnPrefix = FnID; 5660b57cec5SDimitry Andric } 5670b57cec5SDimitry Andric 5680b57cec5SDimitry Andric if (BytesInThisScope) { 5690b57cec5SDimitry Andric BytesInScope = BytesInThisScope; 5700b57cec5SDimitry Andric if (IsFunction) 5710b57cec5SDimitry Andric GlobalStats.FunctionSize += BytesInThisScope; 5720b57cec5SDimitry Andric else if (IsInlinedFunction && InlineDepth == 0) 5730b57cec5SDimitry Andric GlobalStats.InlineFunctionSize += BytesInThisScope; 5740b57cec5SDimitry Andric } 5750b57cec5SDimitry Andric } else { 5760b57cec5SDimitry Andric // Not a scope, visit the Die itself. It could be a variable. 577480093f4SDimitry Andric collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, 578fe6060f1SDimitry Andric FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr); 5790b57cec5SDimitry Andric } 5800b57cec5SDimitry Andric 5810b57cec5SDimitry Andric // Set InlineDepth correctly for child recursion 5820b57cec5SDimitry Andric if (IsFunction) 5830b57cec5SDimitry Andric InlineDepth = 0; 5840b57cec5SDimitry Andric else if (IsInlinedFunction) 5850b57cec5SDimitry Andric ++InlineDepth; 5860b57cec5SDimitry Andric 5870b57cec5SDimitry Andric // Traverse children. 5880b57cec5SDimitry Andric unsigned LexicalBlockIndex = 0; 5895ffd83dbSDimitry Andric unsigned FormalParameterIndex = 0; 5900b57cec5SDimitry Andric DWARFDie Child = Die.getFirstChild(); 5910b57cec5SDimitry Andric while (Child) { 5920b57cec5SDimitry Andric std::string ChildVarPrefix = VarPrefix; 5930b57cec5SDimitry Andric if (Child.getTag() == dwarf::DW_TAG_lexical_block) 5940b57cec5SDimitry Andric ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; 5955ffd83dbSDimitry Andric if (Child.getTag() == dwarf::DW_TAG_formal_parameter) 5965ffd83dbSDimitry Andric ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; 5970b57cec5SDimitry Andric 598fe6060f1SDimitry Andric collectStatsRecursive( 599fe6060f1SDimitry Andric Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap, 600fe6060f1SDimitry Andric GlobalStats, LocStats, GlobalAbstractOriginFnInfo, 601fe6060f1SDimitry Andric FnsWithAbstractOriginToBeProcessed, AbstractOriginVarsPtr); 6020b57cec5SDimitry Andric Child = Child.getSibling(); 6030b57cec5SDimitry Andric } 604fe6060f1SDimitry Andric 605fe6060f1SDimitry Andric if (!IsCandidateForZeroLocCovTracking) 606fe6060f1SDimitry Andric return; 607fe6060f1SDimitry Andric 608fe6060f1SDimitry Andric // After we have processed all vars of the inlined function (or function with 609fe6060f1SDimitry Andric // an abstract_origin), we want to know how many variables have no location. 610fe6060f1SDimitry Andric for (auto Offset : AbstractOriginVars) { 611fe6060f1SDimitry Andric LocStats.NumVarParam++; 612fe6060f1SDimitry Andric LocStats.VarParamLocStats[ZeroCoverageBucket]++; 613fe6060f1SDimitry Andric auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset); 614fe6060f1SDimitry Andric if (!FnDie) 615fe6060f1SDimitry Andric continue; 616fe6060f1SDimitry Andric auto Tag = FnDie.getTag(); 617fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_formal_parameter) { 618fe6060f1SDimitry Andric LocStats.NumParam++; 619fe6060f1SDimitry Andric LocStats.ParamLocStats[ZeroCoverageBucket]++; 620fe6060f1SDimitry Andric } else if (Tag == dwarf::DW_TAG_variable) { 621fe6060f1SDimitry Andric LocStats.NumVar++; 622fe6060f1SDimitry Andric LocStats.LocalVarLocStats[ZeroCoverageBucket]++; 623fe6060f1SDimitry Andric } 624fe6060f1SDimitry Andric } 6250b57cec5SDimitry Andric } 6260b57cec5SDimitry Andric 627e8d8bef9SDimitry Andric /// Print human-readable output. 6280b57cec5SDimitry Andric /// \{ 629e8d8bef9SDimitry Andric static void printDatum(json::OStream &J, const char *Key, json::Value Value) { 630*349cc55cSDimitry Andric if (Value == OverflowValue) 631*349cc55cSDimitry Andric J.attribute(Key, "overflowed"); 632*349cc55cSDimitry Andric else 633e8d8bef9SDimitry Andric J.attribute(Key, Value); 634*349cc55cSDimitry Andric 6350b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); 6360b57cec5SDimitry Andric } 6375ffd83dbSDimitry Andric 638e8d8bef9SDimitry Andric static void printLocationStats(json::OStream &J, const char *Key, 639*349cc55cSDimitry Andric std::vector<SaturatingUINT64> &LocationStats) { 640*349cc55cSDimitry Andric if (LocationStats[0].Value == OverflowValue) 641*349cc55cSDimitry Andric J.attribute((Twine(Key) + 642*349cc55cSDimitry Andric " with (0%,10%) of parent scope covered by DW_AT_location") 643*349cc55cSDimitry Andric .str(), 644*349cc55cSDimitry Andric "overflowed"); 645*349cc55cSDimitry Andric else 646e8d8bef9SDimitry Andric J.attribute( 647*349cc55cSDimitry Andric (Twine(Key) + " with 0% of parent scope covered by DW_AT_location") 648*349cc55cSDimitry Andric .str(), 649*349cc55cSDimitry Andric LocationStats[0].Value); 6505ffd83dbSDimitry Andric LLVM_DEBUG( 6515ffd83dbSDimitry Andric llvm::dbgs() << Key 6525ffd83dbSDimitry Andric << " with 0% of parent scope covered by DW_AT_location: \\" 653*349cc55cSDimitry Andric << LocationStats[0].Value << '\n'); 654*349cc55cSDimitry Andric 655*349cc55cSDimitry Andric if (LocationStats[1].Value == OverflowValue) 656*349cc55cSDimitry Andric J.attribute((Twine(Key) + 657*349cc55cSDimitry Andric " with (0%,10%) of parent scope covered by DW_AT_location") 658e8d8bef9SDimitry Andric .str(), 659*349cc55cSDimitry Andric "overflowed"); 660*349cc55cSDimitry Andric else 661*349cc55cSDimitry Andric J.attribute((Twine(Key) + 662*349cc55cSDimitry Andric " with (0%,10%) of parent scope covered by DW_AT_location") 663*349cc55cSDimitry Andric .str(), 664*349cc55cSDimitry Andric LocationStats[1].Value); 6655ffd83dbSDimitry Andric LLVM_DEBUG(llvm::dbgs() 6665ffd83dbSDimitry Andric << Key 6675ffd83dbSDimitry Andric << " with (0%,10%) of parent scope covered by DW_AT_location: " 668*349cc55cSDimitry Andric << LocationStats[1].Value << '\n'); 669*349cc55cSDimitry Andric 6708bcb0991SDimitry Andric for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { 671*349cc55cSDimitry Andric if (LocationStats[i].Value == OverflowValue) 672e8d8bef9SDimitry Andric J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," + 673*349cc55cSDimitry Andric Twine(i * 10) + 674*349cc55cSDimitry Andric "%) of parent scope covered by DW_AT_location") 675e8d8bef9SDimitry Andric .str(), 676*349cc55cSDimitry Andric "overflowed"); 677*349cc55cSDimitry Andric else 678*349cc55cSDimitry Andric J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," + 679*349cc55cSDimitry Andric Twine(i * 10) + 680*349cc55cSDimitry Andric "%) of parent scope covered by DW_AT_location") 681*349cc55cSDimitry Andric .str(), 682*349cc55cSDimitry Andric LocationStats[i].Value); 6838bcb0991SDimitry Andric LLVM_DEBUG(llvm::dbgs() 684480093f4SDimitry Andric << Key << " with [" << (i - 1) * 10 << "%," << i * 10 6855ffd83dbSDimitry Andric << "%) of parent scope covered by DW_AT_location: " 686*349cc55cSDimitry Andric << LocationStats[i].Value); 6878bcb0991SDimitry Andric } 688*349cc55cSDimitry Andric if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue) 689e8d8bef9SDimitry Andric J.attribute( 690e8d8bef9SDimitry Andric (Twine(Key) + " with 100% of parent scope covered by DW_AT_location") 691e8d8bef9SDimitry Andric .str(), 692*349cc55cSDimitry Andric "overflowed"); 693*349cc55cSDimitry Andric else 694*349cc55cSDimitry Andric J.attribute( 695*349cc55cSDimitry Andric (Twine(Key) + " with 100% of parent scope covered by DW_AT_location") 696*349cc55cSDimitry Andric .str(), 697*349cc55cSDimitry Andric LocationStats[NumOfCoverageCategories - 1].Value); 6985ffd83dbSDimitry Andric LLVM_DEBUG( 6995ffd83dbSDimitry Andric llvm::dbgs() << Key 7005ffd83dbSDimitry Andric << " with 100% of parent scope covered by DW_AT_location: " 701*349cc55cSDimitry Andric << LocationStats[NumOfCoverageCategories - 1].Value); 7028bcb0991SDimitry Andric } 7035ffd83dbSDimitry Andric 704e8d8bef9SDimitry Andric static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) { 705fe6060f1SDimitry Andric for (const auto &It : Sizes.DebugSectionSizes) 706fe6060f1SDimitry Andric J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second)); 707fe6060f1SDimitry Andric } 708fe6060f1SDimitry Andric 709fe6060f1SDimitry Andric /// Stop tracking variables that contain abstract_origin with a location. 710fe6060f1SDimitry Andric /// This is used for out-of-order DW_AT_inline subprograms only. 711fe6060f1SDimitry Andric static void updateVarsWithAbstractOriginLocCovInfo( 712fe6060f1SDimitry Andric DWARFDie FnDieWithAbstractOrigin, 713fe6060f1SDimitry Andric AbstractOriginVarsTy &AbstractOriginVars) { 714fe6060f1SDimitry Andric DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild(); 715fe6060f1SDimitry Andric while (Child) { 716fe6060f1SDimitry Andric const dwarf::Tag ChildTag = Child.getTag(); 717fe6060f1SDimitry Andric if ((ChildTag == dwarf::DW_TAG_formal_parameter || 718fe6060f1SDimitry Andric ChildTag == dwarf::DW_TAG_variable) && 719fe6060f1SDimitry Andric (Child.find(dwarf::DW_AT_location) || 720fe6060f1SDimitry Andric Child.find(dwarf::DW_AT_const_value))) { 721fe6060f1SDimitry Andric auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin); 722fe6060f1SDimitry Andric if (OffsetVar) 723fe6060f1SDimitry Andric llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue()); 724fe6060f1SDimitry Andric } else if (ChildTag == dwarf::DW_TAG_lexical_block) 725fe6060f1SDimitry Andric updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars); 726fe6060f1SDimitry Andric Child = Child.getSibling(); 727fe6060f1SDimitry Andric } 728fe6060f1SDimitry Andric } 729fe6060f1SDimitry Andric 730fe6060f1SDimitry Andric /// Collect zero location coverage for inlined variables which refer to 731fe6060f1SDimitry Andric /// a DW_AT_inline copy of subprogram that is out of order in the DWARF. 732fe6060f1SDimitry Andric /// Also cover the variables of a concrete function (represented with 733fe6060f1SDimitry Andric /// the DW_TAG_subprogram) with an abstract_origin attribute. 734fe6060f1SDimitry Andric static void collectZeroLocCovForVarsWithAbstractOrigin( 735fe6060f1SDimitry Andric DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats, 736fe6060f1SDimitry Andric AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, 737fe6060f1SDimitry Andric FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) { 738fe6060f1SDimitry Andric for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) { 739fe6060f1SDimitry Andric DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset); 740fe6060f1SDimitry Andric auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin); 741fe6060f1SDimitry Andric AbstractOriginVarsTy AbstractOriginVars; 742fe6060f1SDimitry Andric if (!FnCopy) 743fe6060f1SDimitry Andric continue; 744fe6060f1SDimitry Andric 745fe6060f1SDimitry Andric AbstractOriginVars = GlobalAbstractOriginFnInfo[(*FnCopy).getRawUValue()]; 746fe6060f1SDimitry Andric updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin, 747fe6060f1SDimitry Andric AbstractOriginVars); 748fe6060f1SDimitry Andric 749fe6060f1SDimitry Andric for (auto Offset : AbstractOriginVars) { 750fe6060f1SDimitry Andric LocStats.NumVarParam++; 751fe6060f1SDimitry Andric LocStats.VarParamLocStats[ZeroCoverageBucket]++; 752fe6060f1SDimitry Andric auto Tag = DwUnit->getDIEForOffset(Offset).getTag(); 753fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_formal_parameter) { 754fe6060f1SDimitry Andric LocStats.NumParam++; 755fe6060f1SDimitry Andric LocStats.ParamLocStats[ZeroCoverageBucket]++; 756fe6060f1SDimitry Andric } else if (Tag == dwarf::DW_TAG_variable) { 757fe6060f1SDimitry Andric LocStats.NumVar++; 758fe6060f1SDimitry Andric LocStats.LocalVarLocStats[ZeroCoverageBucket]++; 759fe6060f1SDimitry Andric } 760fe6060f1SDimitry Andric } 761fe6060f1SDimitry Andric } 7625ffd83dbSDimitry Andric } 7635ffd83dbSDimitry Andric 7640b57cec5SDimitry Andric /// \} 7650b57cec5SDimitry Andric 7660b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext. 7670b57cec5SDimitry Andric /// 7680b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few 7690b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked 7700b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect 7710b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly 7720b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different 7730b57cec5SDimitry Andric /// compilers is. 7745ffd83dbSDimitry Andric bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 7755ffd83dbSDimitry Andric const Twine &Filename, 7765ffd83dbSDimitry Andric raw_ostream &OS) { 7770b57cec5SDimitry Andric StringRef FormatName = Obj.getFileFormatName(); 7780b57cec5SDimitry Andric GlobalStats GlobalStats; 7798bcb0991SDimitry Andric LocationStats LocStats; 7800b57cec5SDimitry Andric StringMap<PerFunctionStats> Statistics; 781fe6060f1SDimitry Andric for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) { 782fe6060f1SDimitry Andric if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) { 783fe6060f1SDimitry Andric // These variables are being reset for each CU, since there could be 784fe6060f1SDimitry Andric // a situation where we have two subprogram DIEs with the same offsets 785fe6060f1SDimitry Andric // in two diferent CUs, and we can end up using wrong variables info 786fe6060f1SDimitry Andric // when trying to resolve abstract_origin attribute. 787fe6060f1SDimitry Andric // TODO: Handle LTO cases where the abstract origin of 788fe6060f1SDimitry Andric // the function is in a different CU than the one it's 789fe6060f1SDimitry Andric // referenced from or inlined into. 790fe6060f1SDimitry Andric AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo; 791fe6060f1SDimitry Andric FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed; 792fe6060f1SDimitry Andric 793480093f4SDimitry Andric collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, 794fe6060f1SDimitry Andric LocStats, GlobalAbstractOriginFnInfo, 795fe6060f1SDimitry Andric FnsWithAbstractOriginToBeProcessed); 796fe6060f1SDimitry Andric 797fe6060f1SDimitry Andric collectZeroLocCovForVarsWithAbstractOrigin( 798fe6060f1SDimitry Andric CUDie.getDwarfUnit(), GlobalStats, LocStats, 799fe6060f1SDimitry Andric GlobalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed); 800fe6060f1SDimitry Andric } 801fe6060f1SDimitry Andric } 8020b57cec5SDimitry Andric 8035ffd83dbSDimitry Andric /// Collect the sizes of debug sections. 8045ffd83dbSDimitry Andric SectionSizes Sizes; 8055ffd83dbSDimitry Andric calculateSectionSizes(Obj, Sizes, Filename); 8065ffd83dbSDimitry Andric 8070b57cec5SDimitry Andric /// The version number should be increased every time the algorithm is changed 8080b57cec5SDimitry Andric /// (including bug fixes). New metrics may be added without increasing the 8090b57cec5SDimitry Andric /// version. 810*349cc55cSDimitry Andric unsigned Version = 9; 811*349cc55cSDimitry Andric SaturatingUINT64 VarParamTotal = 0; 812*349cc55cSDimitry Andric SaturatingUINT64 VarParamUnique = 0; 813*349cc55cSDimitry Andric SaturatingUINT64 VarParamWithLoc = 0; 814*349cc55cSDimitry Andric SaturatingUINT64 NumFunctions = 0; 815*349cc55cSDimitry Andric SaturatingUINT64 NumInlinedFunctions = 0; 816*349cc55cSDimitry Andric SaturatingUINT64 NumFuncsWithSrcLoc = 0; 817*349cc55cSDimitry Andric SaturatingUINT64 NumAbstractOrigins = 0; 818*349cc55cSDimitry Andric SaturatingUINT64 ParamTotal = 0; 819*349cc55cSDimitry Andric SaturatingUINT64 ParamWithType = 0; 820*349cc55cSDimitry Andric SaturatingUINT64 ParamWithLoc = 0; 821*349cc55cSDimitry Andric SaturatingUINT64 ParamWithSrcLoc = 0; 822*349cc55cSDimitry Andric SaturatingUINT64 LocalVarTotal = 0; 823*349cc55cSDimitry Andric SaturatingUINT64 LocalVarWithType = 0; 824*349cc55cSDimitry Andric SaturatingUINT64 LocalVarWithSrcLoc = 0; 825*349cc55cSDimitry Andric SaturatingUINT64 LocalVarWithLoc = 0; 8260b57cec5SDimitry Andric for (auto &Entry : Statistics) { 8270b57cec5SDimitry Andric PerFunctionStats &Stats = Entry.getValue(); 828*349cc55cSDimitry Andric uint64_t TotalVars = Stats.VarsInFunction.size() * 8295ffd83dbSDimitry Andric (Stats.NumFnInlined + Stats.NumFnOutOfLine); 8305ffd83dbSDimitry Andric // Count variables in global scope. 8315ffd83dbSDimitry Andric if (!Stats.IsFunction) 8325ffd83dbSDimitry Andric TotalVars = 8335ffd83dbSDimitry Andric Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial; 834*349cc55cSDimitry Andric uint64_t Constants = Stats.ConstantMembers; 8350b57cec5SDimitry Andric VarParamWithLoc += Stats.TotalVarWithLoc + Constants; 8360b57cec5SDimitry Andric VarParamTotal += TotalVars; 8370b57cec5SDimitry Andric VarParamUnique += Stats.VarsInFunction.size(); 8380b57cec5SDimitry Andric LLVM_DEBUG(for (auto &V 8390b57cec5SDimitry Andric : Stats.VarsInFunction) llvm::dbgs() 8400b57cec5SDimitry Andric << Entry.getKey() << ": " << V.getKey() << "\n"); 8410b57cec5SDimitry Andric NumFunctions += Stats.IsFunction; 8420b57cec5SDimitry Andric NumFuncsWithSrcLoc += Stats.HasSourceLocation; 8430b57cec5SDimitry Andric NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; 8440b57cec5SDimitry Andric NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; 8450b57cec5SDimitry Andric ParamTotal += Stats.NumParams; 8460b57cec5SDimitry Andric ParamWithType += Stats.NumParamTypes; 8470b57cec5SDimitry Andric ParamWithLoc += Stats.NumParamLocations; 8480b57cec5SDimitry Andric ParamWithSrcLoc += Stats.NumParamSourceLocations; 8495ffd83dbSDimitry Andric LocalVarTotal += Stats.NumLocalVars; 8505ffd83dbSDimitry Andric LocalVarWithType += Stats.NumLocalVarTypes; 8515ffd83dbSDimitry Andric LocalVarWithLoc += Stats.NumLocalVarLocations; 8525ffd83dbSDimitry Andric LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations; 8530b57cec5SDimitry Andric } 8540b57cec5SDimitry Andric 8550b57cec5SDimitry Andric // Print summary. 8560b57cec5SDimitry Andric OS.SetBufferSize(1024); 857e8d8bef9SDimitry Andric json::OStream J(OS, 2); 858e8d8bef9SDimitry Andric J.objectBegin(); 859e8d8bef9SDimitry Andric J.attribute("version", Version); 8600b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; 8610b57cec5SDimitry Andric llvm::dbgs() << "---------------------------------\n"); 8625ffd83dbSDimitry Andric 863e8d8bef9SDimitry Andric printDatum(J, "file", Filename.str()); 864e8d8bef9SDimitry Andric printDatum(J, "format", FormatName); 8655ffd83dbSDimitry Andric 866*349cc55cSDimitry Andric printDatum(J, "#functions", NumFunctions.Value); 867*349cc55cSDimitry Andric printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value); 868*349cc55cSDimitry Andric printDatum(J, "#inlined functions", NumInlinedFunctions.Value); 869*349cc55cSDimitry Andric printDatum(J, "#inlined functions with abstract origins", 870*349cc55cSDimitry Andric NumAbstractOrigins.Value); 8715ffd83dbSDimitry Andric 8725ffd83dbSDimitry Andric // This includes local variables and formal parameters. 873*349cc55cSDimitry Andric printDatum(J, "#unique source variables", VarParamUnique.Value); 874*349cc55cSDimitry Andric printDatum(J, "#source variables", VarParamTotal.Value); 875*349cc55cSDimitry Andric printDatum(J, "#source variables with location", VarParamWithLoc.Value); 8765ffd83dbSDimitry Andric 877*349cc55cSDimitry Andric printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value); 878*349cc55cSDimitry Andric printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value); 879*349cc55cSDimitry Andric printDatum(J, "#call site parameter DIEs", 880*349cc55cSDimitry Andric GlobalStats.CallSiteParamDIEs.Value); 8815ffd83dbSDimitry Andric 882e8d8bef9SDimitry Andric printDatum(J, "sum_all_variables(#bytes in parent scope)", 883*349cc55cSDimitry Andric GlobalStats.ScopeBytes.Value); 884e8d8bef9SDimitry Andric printDatum(J, 885e8d8bef9SDimitry Andric "sum_all_variables(#bytes in any scope covered by DW_AT_location)", 886*349cc55cSDimitry Andric GlobalStats.TotalBytesCovered.Value); 887e8d8bef9SDimitry Andric printDatum(J, 8885ffd83dbSDimitry Andric "sum_all_variables(#bytes in parent scope covered by " 8895ffd83dbSDimitry Andric "DW_AT_location)", 890*349cc55cSDimitry Andric GlobalStats.ScopeBytesCovered.Value); 891e8d8bef9SDimitry Andric printDatum(J, 8925ffd83dbSDimitry Andric "sum_all_variables(#bytes in parent scope covered by " 8935ffd83dbSDimitry Andric "DW_OP_entry_value)", 894*349cc55cSDimitry Andric GlobalStats.ScopeEntryValueBytesCovered.Value); 8955ffd83dbSDimitry Andric 896e8d8bef9SDimitry Andric printDatum(J, "sum_all_params(#bytes in parent scope)", 897*349cc55cSDimitry Andric GlobalStats.ParamScopeBytes.Value); 898e8d8bef9SDimitry Andric printDatum(J, 8995ffd83dbSDimitry Andric "sum_all_params(#bytes in parent scope covered by DW_AT_location)", 900*349cc55cSDimitry Andric GlobalStats.ParamScopeBytesCovered.Value); 901e8d8bef9SDimitry Andric printDatum(J, 9025ffd83dbSDimitry Andric "sum_all_params(#bytes in parent scope covered by " 9035ffd83dbSDimitry Andric "DW_OP_entry_value)", 904*349cc55cSDimitry Andric GlobalStats.ParamScopeEntryValueBytesCovered.Value); 9055ffd83dbSDimitry Andric 906e8d8bef9SDimitry Andric printDatum(J, "sum_all_local_vars(#bytes in parent scope)", 907*349cc55cSDimitry Andric GlobalStats.LocalVarScopeBytes.Value); 908e8d8bef9SDimitry Andric printDatum(J, 9095ffd83dbSDimitry Andric "sum_all_local_vars(#bytes in parent scope covered by " 9105ffd83dbSDimitry Andric "DW_AT_location)", 911*349cc55cSDimitry Andric GlobalStats.LocalVarScopeBytesCovered.Value); 912e8d8bef9SDimitry Andric printDatum(J, 9135ffd83dbSDimitry Andric "sum_all_local_vars(#bytes in parent scope covered by " 9145ffd83dbSDimitry Andric "DW_OP_entry_value)", 915*349cc55cSDimitry Andric GlobalStats.LocalVarScopeEntryValueBytesCovered.Value); 9165ffd83dbSDimitry Andric 917*349cc55cSDimitry Andric printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value); 918e8d8bef9SDimitry Andric printDatum(J, "#bytes within inlined functions", 919*349cc55cSDimitry Andric GlobalStats.InlineFunctionSize.Value); 9205ffd83dbSDimitry Andric 9215ffd83dbSDimitry Andric // Print the summary for formal parameters. 922*349cc55cSDimitry Andric printDatum(J, "#params", ParamTotal.Value); 923*349cc55cSDimitry Andric printDatum(J, "#params with source location", ParamWithSrcLoc.Value); 924*349cc55cSDimitry Andric printDatum(J, "#params with type", ParamWithType.Value); 925*349cc55cSDimitry Andric printDatum(J, "#params with binary location", ParamWithLoc.Value); 9265ffd83dbSDimitry Andric 9275ffd83dbSDimitry Andric // Print the summary for local variables. 928*349cc55cSDimitry Andric printDatum(J, "#local vars", LocalVarTotal.Value); 929*349cc55cSDimitry Andric printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value); 930*349cc55cSDimitry Andric printDatum(J, "#local vars with type", LocalVarWithType.Value); 931*349cc55cSDimitry Andric printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value); 9325ffd83dbSDimitry Andric 9335ffd83dbSDimitry Andric // Print the debug section sizes. 934e8d8bef9SDimitry Andric printSectionSizes(J, Sizes); 9355ffd83dbSDimitry Andric 9365ffd83dbSDimitry Andric // Print the location statistics for variables (includes local variables 9375ffd83dbSDimitry Andric // and formal parameters). 938e8d8bef9SDimitry Andric printDatum(J, "#variables processed by location statistics", 939*349cc55cSDimitry Andric LocStats.NumVarParam.Value); 940e8d8bef9SDimitry Andric printLocationStats(J, "#variables", LocStats.VarParamLocStats); 941e8d8bef9SDimitry Andric printLocationStats(J, "#variables - entry values", 9428bcb0991SDimitry Andric LocStats.VarParamNonEntryValLocStats); 9435ffd83dbSDimitry Andric 9445ffd83dbSDimitry Andric // Print the location statistics for formal parameters. 945*349cc55cSDimitry Andric printDatum(J, "#params processed by location statistics", 946*349cc55cSDimitry Andric LocStats.NumParam.Value); 947e8d8bef9SDimitry Andric printLocationStats(J, "#params", LocStats.ParamLocStats); 948e8d8bef9SDimitry Andric printLocationStats(J, "#params - entry values", 9498bcb0991SDimitry Andric LocStats.ParamNonEntryValLocStats); 9505ffd83dbSDimitry Andric 9515ffd83dbSDimitry Andric // Print the location statistics for local variables. 952e8d8bef9SDimitry Andric printDatum(J, "#local vars processed by location statistics", 953*349cc55cSDimitry Andric LocStats.NumVar.Value); 954e8d8bef9SDimitry Andric printLocationStats(J, "#local vars", LocStats.LocalVarLocStats); 955e8d8bef9SDimitry Andric printLocationStats(J, "#local vars - entry values", 9565ffd83dbSDimitry Andric LocStats.LocalVarNonEntryValLocStats); 957e8d8bef9SDimitry Andric J.objectEnd(); 958e8d8bef9SDimitry Andric OS << '\n'; 959*349cc55cSDimitry Andric LLVM_DEBUG(llvm::dbgs() << "Total Availability: " 960*349cc55cSDimitry Andric << (int)std::round((VarParamWithLoc.Value * 100.0) / 961*349cc55cSDimitry Andric VarParamTotal.Value) 9620b57cec5SDimitry Andric << "%\n"; 9630b57cec5SDimitry Andric llvm::dbgs() << "PC Ranges covered: " 964*349cc55cSDimitry Andric << (int)std::round( 965*349cc55cSDimitry Andric (GlobalStats.ScopeBytesCovered.Value * 100.0) / 966*349cc55cSDimitry Andric GlobalStats.ScopeBytes.Value) 9670b57cec5SDimitry Andric << "%\n"); 9680b57cec5SDimitry Andric return true; 9690b57cec5SDimitry Andric } 970