10b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h" 20b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h" 30b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h" 40b57cec5SDimitry Andric #include "llvm/DebugInfo/DIContext.h" 50b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h" 60b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" 70b57cec5SDimitry Andric #include "llvm/Object/ObjectFile.h" 88bcb0991SDimitry Andric #include "llvm/Support/JSON.h" 90b57cec5SDimitry Andric 100b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfdump" 110b57cec5SDimitry Andric using namespace llvm; 120b57cec5SDimitry Andric using namespace object; 130b57cec5SDimitry Andric 148bcb0991SDimitry Andric /// This represents the number of categories of debug location coverage being 158bcb0991SDimitry Andric /// calculated. The first category is the number of variables with 0% location 168bcb0991SDimitry Andric /// coverage, but the last category is the number of variables with 100% 178bcb0991SDimitry Andric /// location coverage. 188bcb0991SDimitry Andric constexpr int NumOfCoverageCategories = 12; 198bcb0991SDimitry Andric 200b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and 210b57cec5SDimitry Andric /// contains variables, such as a compile unit). 220b57cec5SDimitry Andric struct PerFunctionStats { 230b57cec5SDimitry Andric /// Number of inlined instances of this function. 240b57cec5SDimitry Andric unsigned NumFnInlined = 0; 250b57cec5SDimitry Andric /// Number of inlined instances that have abstract origins. 260b57cec5SDimitry Andric unsigned NumAbstractOrigins = 0; 270b57cec5SDimitry Andric /// Number of variables and parameters with location across all inlined 280b57cec5SDimitry Andric /// instances. 290b57cec5SDimitry Andric unsigned TotalVarWithLoc = 0; 300b57cec5SDimitry Andric /// Number of constants with location across all inlined instances. 310b57cec5SDimitry Andric unsigned ConstantMembers = 0; 320b57cec5SDimitry Andric /// List of all Variables and parameters in this function. 330b57cec5SDimitry Andric StringSet<> VarsInFunction; 340b57cec5SDimitry Andric /// Compile units also cover a PC range, but have this flag set to false. 350b57cec5SDimitry Andric bool IsFunction = false; 360b57cec5SDimitry Andric /// Verify function definition has PC addresses (for detecting when 370b57cec5SDimitry Andric /// a function has been inlined everywhere). 380b57cec5SDimitry Andric bool HasPCAddresses = false; 390b57cec5SDimitry Andric /// Function has source location information. 400b57cec5SDimitry Andric bool HasSourceLocation = false; 410b57cec5SDimitry Andric /// Number of function parameters. 420b57cec5SDimitry Andric unsigned NumParams = 0; 430b57cec5SDimitry Andric /// Number of function parameters with source location. 440b57cec5SDimitry Andric unsigned NumParamSourceLocations = 0; 450b57cec5SDimitry Andric /// Number of function parameters with type. 460b57cec5SDimitry Andric unsigned NumParamTypes = 0; 470b57cec5SDimitry Andric /// Number of function parameters with a DW_AT_location. 480b57cec5SDimitry Andric unsigned NumParamLocations = 0; 490b57cec5SDimitry Andric /// Number of variables. 500b57cec5SDimitry Andric unsigned NumVars = 0; 510b57cec5SDimitry Andric /// Number of variables with source location. 520b57cec5SDimitry Andric unsigned NumVarSourceLocations = 0; 538bcb0991SDimitry Andric /// Number of variables with type. 540b57cec5SDimitry Andric unsigned NumVarTypes = 0; 558bcb0991SDimitry Andric /// Number of variables with DW_AT_location. 560b57cec5SDimitry Andric unsigned NumVarLocations = 0; 570b57cec5SDimitry Andric }; 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs. 600b57cec5SDimitry Andric struct GlobalStats { 610b57cec5SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations. 620b57cec5SDimitry Andric unsigned ScopeBytesCovered = 0; 63*480093f4SDimitry Andric /// Total number of PC range bytes in each variable's enclosing scope. 64*480093f4SDimitry Andric unsigned ScopeBytes = 0; 658bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 668bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value). 678bcb0991SDimitry Andric unsigned ScopeEntryValueBytesCovered = 0; 688bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations of 698bcb0991SDimitry Andric /// formal parameters. 708bcb0991SDimitry Andric unsigned ParamScopeBytesCovered = 0; 71*480093f4SDimitry Andric /// Total number of PC range bytes in each variable's enclosing scope 72*480093f4SDimitry Andric /// (only for parameters). 73*480093f4SDimitry Andric unsigned ParamScopeBytes = 0; 748bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 758bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value) (only for parameters). 768bcb0991SDimitry Andric unsigned ParamScopeEntryValueBytesCovered = 0; 778bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations (only for local 788bcb0991SDimitry Andric /// variables). 798bcb0991SDimitry Andric unsigned VarScopeBytesCovered = 0; 80*480093f4SDimitry Andric /// Total number of PC range bytes in each variable's enclosing scope 81*480093f4SDimitry Andric /// (only for local variables). 82*480093f4SDimitry Andric unsigned VarScopeBytes = 0; 838bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 848bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value) (only for local variables). 858bcb0991SDimitry Andric unsigned VarScopeEntryValueBytesCovered = 0; 868bcb0991SDimitry Andric /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line). 870b57cec5SDimitry Andric unsigned CallSiteEntries = 0; 888bcb0991SDimitry Andric /// Total number of call site DIEs (DW_TAG_call_site). 898bcb0991SDimitry Andric unsigned CallSiteDIEs = 0; 908bcb0991SDimitry Andric /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter). 918bcb0991SDimitry Andric unsigned CallSiteParamDIEs = 0; 920b57cec5SDimitry Andric /// Total byte size of concrete functions. This byte size includes 930b57cec5SDimitry Andric /// inline functions contained in the concrete functions. 948bcb0991SDimitry Andric unsigned FunctionSize = 0; 950b57cec5SDimitry Andric /// Total byte size of inlined functions. This is the total number of bytes 960b57cec5SDimitry Andric /// for the top inline functions within concrete functions. This can help 970b57cec5SDimitry Andric /// tune the inline settings when compiling to match user expectations. 988bcb0991SDimitry Andric unsigned InlineFunctionSize = 0; 998bcb0991SDimitry Andric }; 1008bcb0991SDimitry Andric 1018bcb0991SDimitry Andric /// Holds accumulated debug location statistics about local variables and 1028bcb0991SDimitry Andric /// formal parameters. 1038bcb0991SDimitry Andric struct LocationStats { 1048bcb0991SDimitry Andric /// Map the scope coverage decile to the number of variables in the decile. 1058bcb0991SDimitry Andric /// The first element of the array (at the index zero) represents the number 1068bcb0991SDimitry Andric /// of variables with the no debug location at all, but the last element 1078bcb0991SDimitry Andric /// in the vector represents the number of fully covered variables within 1088bcb0991SDimitry Andric /// its scope. 1098bcb0991SDimitry Andric std::vector<unsigned> VarParamLocStats{ 1108bcb0991SDimitry Andric std::vector<unsigned>(NumOfCoverageCategories, 0)}; 1118bcb0991SDimitry Andric /// Map non debug entry values coverage. 1128bcb0991SDimitry Andric std::vector<unsigned> VarParamNonEntryValLocStats{ 1138bcb0991SDimitry Andric std::vector<unsigned>(NumOfCoverageCategories, 0)}; 1148bcb0991SDimitry Andric /// The debug location statistics for formal parameters. 1158bcb0991SDimitry Andric std::vector<unsigned> ParamLocStats{ 1168bcb0991SDimitry Andric std::vector<unsigned>(NumOfCoverageCategories, 0)}; 1178bcb0991SDimitry Andric /// Map non debug entry values coverage for formal parameters. 1188bcb0991SDimitry Andric std::vector<unsigned> ParamNonEntryValLocStats{ 1198bcb0991SDimitry Andric std::vector<unsigned>(NumOfCoverageCategories, 0)}; 1208bcb0991SDimitry Andric /// The debug location statistics for local variables. 1218bcb0991SDimitry Andric std::vector<unsigned> VarLocStats{ 1228bcb0991SDimitry Andric std::vector<unsigned>(NumOfCoverageCategories, 0)}; 1238bcb0991SDimitry Andric /// Map non debug entry values coverage for local variables. 1248bcb0991SDimitry Andric std::vector<unsigned> VarNonEntryValLocStats{ 1258bcb0991SDimitry Andric std::vector<unsigned>(NumOfCoverageCategories, 0)}; 1268bcb0991SDimitry Andric /// Total number of local variables and function parameters processed. 1278bcb0991SDimitry Andric unsigned NumVarParam = 0; 1288bcb0991SDimitry Andric /// Total number of formal parameters processed. 1298bcb0991SDimitry Andric unsigned NumParam = 0; 1308bcb0991SDimitry Andric /// Total number of local variables processed. 1318bcb0991SDimitry Andric unsigned NumVar = 0; 1320b57cec5SDimitry Andric }; 1330b57cec5SDimitry Andric 1348bcb0991SDimitry Andric /// Collect debug location statistics for one DIE. 1358bcb0991SDimitry Andric static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope, 1368bcb0991SDimitry Andric std::vector<unsigned> &VarParamLocStats, 1378bcb0991SDimitry Andric std::vector<unsigned> &ParamLocStats, 1388bcb0991SDimitry Andric std::vector<unsigned> &VarLocStats, bool IsParam, 1398bcb0991SDimitry Andric bool IsLocalVar) { 1408bcb0991SDimitry Andric auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned { 1418bcb0991SDimitry Andric // No debug location at all for the variable. 142*480093f4SDimitry Andric if (BytesCovered == 0) 1438bcb0991SDimitry Andric return 0; 1448bcb0991SDimitry Andric // Fully covered variable within its scope. 145*480093f4SDimitry Andric if (BytesCovered >= BytesInScope) 1468bcb0991SDimitry Andric return NumOfCoverageCategories - 1; 1478bcb0991SDimitry Andric // Get covered range (e.g. 20%-29%). 148*480093f4SDimitry Andric unsigned LocBucket = 100 * (double)BytesCovered / BytesInScope; 1498bcb0991SDimitry Andric LocBucket /= 10; 1508bcb0991SDimitry Andric return LocBucket + 1; 1518bcb0991SDimitry Andric }; 1528bcb0991SDimitry Andric 1538bcb0991SDimitry Andric unsigned CoverageBucket = getCoverageBucket(); 1548bcb0991SDimitry Andric VarParamLocStats[CoverageBucket]++; 1558bcb0991SDimitry Andric if (IsParam) 1568bcb0991SDimitry Andric ParamLocStats[CoverageBucket]++; 1578bcb0991SDimitry Andric else if (IsLocalVar) 1588bcb0991SDimitry Andric VarLocStats[CoverageBucket]++; 1598bcb0991SDimitry Andric } 1608bcb0991SDimitry Andric 1610b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE. 162*480093f4SDimitry Andric static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, 163*480093f4SDimitry Andric std::string VarPrefix, uint64_t BytesInScope, 164*480093f4SDimitry Andric uint32_t InlineDepth, 1650b57cec5SDimitry Andric StringMap<PerFunctionStats> &FnStatMap, 1668bcb0991SDimitry Andric GlobalStats &GlobalStats, 1678bcb0991SDimitry Andric LocationStats &LocStats) { 1680b57cec5SDimitry Andric bool HasLoc = false; 1690b57cec5SDimitry Andric bool HasSrcLoc = false; 1700b57cec5SDimitry Andric bool HasType = false; 1710b57cec5SDimitry Andric bool IsArtificial = false; 1720b57cec5SDimitry Andric uint64_t BytesCovered = 0; 1738bcb0991SDimitry Andric uint64_t BytesEntryValuesCovered = 0; 1748bcb0991SDimitry Andric auto &FnStats = FnStatMap[FnPrefix]; 1758bcb0991SDimitry Andric bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter; 1768bcb0991SDimitry Andric bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable; 1770b57cec5SDimitry Andric 1788bcb0991SDimitry Andric if (Die.getTag() == dwarf::DW_TAG_call_site || 1798bcb0991SDimitry Andric Die.getTag() == dwarf::DW_TAG_GNU_call_site) { 1808bcb0991SDimitry Andric GlobalStats.CallSiteDIEs++; 1810b57cec5SDimitry Andric return; 1820b57cec5SDimitry Andric } 1830b57cec5SDimitry Andric 1848bcb0991SDimitry Andric if (Die.getTag() == dwarf::DW_TAG_call_site_parameter || 1858bcb0991SDimitry Andric Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) { 1868bcb0991SDimitry Andric GlobalStats.CallSiteParamDIEs++; 1878bcb0991SDimitry Andric return; 1888bcb0991SDimitry Andric } 1898bcb0991SDimitry Andric 1908bcb0991SDimitry Andric if (!IsParam && !IsLocalVar && Die.getTag() != dwarf::DW_TAG_member) { 1910b57cec5SDimitry Andric // Not a variable or constant member. 1920b57cec5SDimitry Andric return; 1930b57cec5SDimitry Andric } 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_decl_file) && 1960b57cec5SDimitry Andric Die.findRecursively(dwarf::DW_AT_decl_line)) 1970b57cec5SDimitry Andric HasSrcLoc = true; 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_type)) 2000b57cec5SDimitry Andric HasType = true; 2010b57cec5SDimitry Andric 2020b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_artificial)) 2030b57cec5SDimitry Andric IsArtificial = true; 2040b57cec5SDimitry Andric 2058bcb0991SDimitry Andric auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { 2068bcb0991SDimitry Andric DWARFUnit *U = Die.getDwarfUnit(); 2078bcb0991SDimitry Andric DataExtractor Data(toStringRef(D), 2088bcb0991SDimitry Andric Die.getDwarfUnit()->getContext().isLittleEndian(), 0); 2098bcb0991SDimitry Andric DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); 2108bcb0991SDimitry Andric // Consider the expression containing the DW_OP_entry_value as 2118bcb0991SDimitry Andric // an entry value. 2128bcb0991SDimitry Andric return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { 2138bcb0991SDimitry Andric return Op.getCode() == dwarf::DW_OP_entry_value || 2148bcb0991SDimitry Andric Op.getCode() == dwarf::DW_OP_GNU_entry_value; 2158bcb0991SDimitry Andric }); 2168bcb0991SDimitry Andric }; 2178bcb0991SDimitry Andric 2180b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_const_value)) { 2190b57cec5SDimitry Andric // This catches constant members *and* variables. 2200b57cec5SDimitry Andric HasLoc = true; 2210b57cec5SDimitry Andric BytesCovered = BytesInScope; 2220b57cec5SDimitry Andric } else { 2230b57cec5SDimitry Andric if (Die.getTag() == dwarf::DW_TAG_member) { 2240b57cec5SDimitry Andric // Non-const member. 2250b57cec5SDimitry Andric return; 2260b57cec5SDimitry Andric } 2270b57cec5SDimitry Andric // Handle variables and function arguments. 228*480093f4SDimitry Andric Expected<std::vector<DWARFLocationExpression>> Loc = 229*480093f4SDimitry Andric Die.getLocations(dwarf::DW_AT_location); 230*480093f4SDimitry Andric if (!Loc) { 231*480093f4SDimitry Andric consumeError(Loc.takeError()); 2320b57cec5SDimitry Andric } else { 233*480093f4SDimitry Andric HasLoc = true; 234*480093f4SDimitry Andric // Get PC coverage. 235*480093f4SDimitry Andric auto Default = find_if( 236*480093f4SDimitry Andric *Loc, [](const DWARFLocationExpression &L) { return !L.Range; }); 237*480093f4SDimitry Andric if (Default != Loc->end()) { 2380b57cec5SDimitry Andric // Assume the entire range is covered by a single location. 2390b57cec5SDimitry Andric BytesCovered = BytesInScope; 240*480093f4SDimitry Andric } else { 241*480093f4SDimitry Andric for (auto Entry : *Loc) { 242*480093f4SDimitry Andric uint64_t BytesEntryCovered = Entry.Range->HighPC - Entry.Range->LowPC; 243*480093f4SDimitry Andric BytesCovered += BytesEntryCovered; 244*480093f4SDimitry Andric if (IsEntryValue(Entry.Expr)) 245*480093f4SDimitry Andric BytesEntryValuesCovered += BytesEntryCovered; 246*480093f4SDimitry Andric } 2470b57cec5SDimitry Andric } 2480b57cec5SDimitry Andric } 2490b57cec5SDimitry Andric } 2500b57cec5SDimitry Andric 2518bcb0991SDimitry Andric // Calculate the debug location statistics. 2528bcb0991SDimitry Andric if (BytesInScope) { 2538bcb0991SDimitry Andric LocStats.NumVarParam++; 2548bcb0991SDimitry Andric if (IsParam) 2558bcb0991SDimitry Andric LocStats.NumParam++; 2568bcb0991SDimitry Andric else if (IsLocalVar) 2578bcb0991SDimitry Andric LocStats.NumVar++; 2588bcb0991SDimitry Andric 2598bcb0991SDimitry Andric collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats, 2608bcb0991SDimitry Andric LocStats.ParamLocStats, LocStats.VarLocStats, IsParam, 2618bcb0991SDimitry Andric IsLocalVar); 2628bcb0991SDimitry Andric // Non debug entry values coverage statistics. 2638bcb0991SDimitry Andric collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope, 2648bcb0991SDimitry Andric LocStats.VarParamNonEntryValLocStats, 2658bcb0991SDimitry Andric LocStats.ParamNonEntryValLocStats, 2668bcb0991SDimitry Andric LocStats.VarNonEntryValLocStats, IsParam, IsLocalVar); 2678bcb0991SDimitry Andric } 2688bcb0991SDimitry Andric 2690b57cec5SDimitry Andric // Collect PC range coverage data. 2700b57cec5SDimitry Andric if (DWARFDie D = 2710b57cec5SDimitry Andric Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) 2720b57cec5SDimitry Andric Die = D; 2730b57cec5SDimitry Andric // By using the variable name + the path through the lexical block tree, the 2740b57cec5SDimitry Andric // keys are consistent across duplicate abstract origins in different CUs. 2750b57cec5SDimitry Andric std::string VarName = StringRef(Die.getName(DINameKind::ShortName)); 2760b57cec5SDimitry Andric FnStats.VarsInFunction.insert(VarPrefix + VarName); 2770b57cec5SDimitry Andric if (BytesInScope) { 2780b57cec5SDimitry Andric FnStats.TotalVarWithLoc += (unsigned)HasLoc; 2790b57cec5SDimitry Andric // Turns out we have a lot of ranges that extend past the lexical scope. 2800b57cec5SDimitry Andric GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered); 281*480093f4SDimitry Andric GlobalStats.ScopeBytes += BytesInScope; 2828bcb0991SDimitry Andric GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered; 2838bcb0991SDimitry Andric if (IsParam) { 2848bcb0991SDimitry Andric GlobalStats.ParamScopeBytesCovered += 2858bcb0991SDimitry Andric std::min(BytesInScope, BytesCovered); 286*480093f4SDimitry Andric GlobalStats.ParamScopeBytes += BytesInScope; 2878bcb0991SDimitry Andric GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; 2888bcb0991SDimitry Andric } else if (IsLocalVar) { 2898bcb0991SDimitry Andric GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered); 290*480093f4SDimitry Andric GlobalStats.VarScopeBytes += BytesInScope; 2918bcb0991SDimitry Andric GlobalStats.VarScopeEntryValueBytesCovered += BytesEntryValuesCovered; 2928bcb0991SDimitry Andric } 293*480093f4SDimitry Andric assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes); 2940b57cec5SDimitry Andric } else if (Die.getTag() == dwarf::DW_TAG_member) { 2950b57cec5SDimitry Andric FnStats.ConstantMembers++; 2960b57cec5SDimitry Andric } else { 2970b57cec5SDimitry Andric FnStats.TotalVarWithLoc += (unsigned)HasLoc; 2980b57cec5SDimitry Andric } 2990b57cec5SDimitry Andric if (!IsArtificial) { 3008bcb0991SDimitry Andric if (IsParam) { 3010b57cec5SDimitry Andric FnStats.NumParams++; 3020b57cec5SDimitry Andric if (HasType) 3030b57cec5SDimitry Andric FnStats.NumParamTypes++; 3040b57cec5SDimitry Andric if (HasSrcLoc) 3050b57cec5SDimitry Andric FnStats.NumParamSourceLocations++; 3060b57cec5SDimitry Andric if (HasLoc) 3070b57cec5SDimitry Andric FnStats.NumParamLocations++; 3088bcb0991SDimitry Andric } else if (IsLocalVar) { 3090b57cec5SDimitry Andric FnStats.NumVars++; 3100b57cec5SDimitry Andric if (HasType) 3110b57cec5SDimitry Andric FnStats.NumVarTypes++; 3120b57cec5SDimitry Andric if (HasSrcLoc) 3130b57cec5SDimitry Andric FnStats.NumVarSourceLocations++; 3140b57cec5SDimitry Andric if (HasLoc) 3150b57cec5SDimitry Andric FnStats.NumVarLocations++; 3160b57cec5SDimitry Andric } 3170b57cec5SDimitry Andric } 3180b57cec5SDimitry Andric } 3190b57cec5SDimitry Andric 3200b57cec5SDimitry Andric /// Recursively collect debug info quality metrics. 321*480093f4SDimitry Andric static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, 322*480093f4SDimitry Andric std::string VarPrefix, uint64_t BytesInScope, 323*480093f4SDimitry Andric uint32_t InlineDepth, 3240b57cec5SDimitry Andric StringMap<PerFunctionStats> &FnStatMap, 3258bcb0991SDimitry Andric GlobalStats &GlobalStats, 3268bcb0991SDimitry Andric LocationStats &LocStats) { 3270b57cec5SDimitry Andric // Handle any kind of lexical scope. 3280b57cec5SDimitry Andric const dwarf::Tag Tag = Die.getTag(); 3290b57cec5SDimitry Andric const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; 3300b57cec5SDimitry Andric const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; 3310b57cec5SDimitry Andric const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; 3320b57cec5SDimitry Andric if (IsFunction || IsInlinedFunction || IsBlock) { 3330b57cec5SDimitry Andric 3340b57cec5SDimitry Andric // Reset VarPrefix when entering a new function. 3350b57cec5SDimitry Andric if (Die.getTag() == dwarf::DW_TAG_subprogram || 3360b57cec5SDimitry Andric Die.getTag() == dwarf::DW_TAG_inlined_subroutine) 3370b57cec5SDimitry Andric VarPrefix = "v"; 3380b57cec5SDimitry Andric 3390b57cec5SDimitry Andric // Ignore forward declarations. 3400b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_declaration)) 3410b57cec5SDimitry Andric return; 3420b57cec5SDimitry Andric 3430b57cec5SDimitry Andric // Check for call sites. 3440b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line)) 3450b57cec5SDimitry Andric GlobalStats.CallSiteEntries++; 3460b57cec5SDimitry Andric 3470b57cec5SDimitry Andric // PC Ranges. 3480b57cec5SDimitry Andric auto RangesOrError = Die.getAddressRanges(); 3490b57cec5SDimitry Andric if (!RangesOrError) { 3500b57cec5SDimitry Andric llvm::consumeError(RangesOrError.takeError()); 3510b57cec5SDimitry Andric return; 3520b57cec5SDimitry Andric } 3530b57cec5SDimitry Andric 3540b57cec5SDimitry Andric auto Ranges = RangesOrError.get(); 3550b57cec5SDimitry Andric uint64_t BytesInThisScope = 0; 3560b57cec5SDimitry Andric for (auto Range : Ranges) 3570b57cec5SDimitry Andric BytesInThisScope += Range.HighPC - Range.LowPC; 3580b57cec5SDimitry Andric 3590b57cec5SDimitry Andric // Count the function. 3600b57cec5SDimitry Andric if (!IsBlock) { 3610b57cec5SDimitry Andric StringRef Name = Die.getName(DINameKind::LinkageName); 3620b57cec5SDimitry Andric if (Name.empty()) 3630b57cec5SDimitry Andric Name = Die.getName(DINameKind::ShortName); 3640b57cec5SDimitry Andric FnPrefix = Name; 3650b57cec5SDimitry Andric // Skip over abstract origins. 3660b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_inline)) 3670b57cec5SDimitry Andric return; 3680b57cec5SDimitry Andric // We've seen an (inlined) instance of this function. 3690b57cec5SDimitry Andric auto &FnStats = FnStatMap[Name]; 3700b57cec5SDimitry Andric if (IsInlinedFunction) { 3710b57cec5SDimitry Andric FnStats.NumFnInlined++; 3720b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) 3730b57cec5SDimitry Andric FnStats.NumAbstractOrigins++; 3740b57cec5SDimitry Andric } 3750b57cec5SDimitry Andric FnStats.IsFunction = true; 3760b57cec5SDimitry Andric if (BytesInThisScope && !IsInlinedFunction) 3770b57cec5SDimitry Andric FnStats.HasPCAddresses = true; 3780b57cec5SDimitry Andric std::string FnName = StringRef(Die.getName(DINameKind::ShortName)); 3790b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_decl_file) && 3800b57cec5SDimitry Andric Die.findRecursively(dwarf::DW_AT_decl_line)) 3810b57cec5SDimitry Andric FnStats.HasSourceLocation = true; 3820b57cec5SDimitry Andric } 3830b57cec5SDimitry Andric 3840b57cec5SDimitry Andric if (BytesInThisScope) { 3850b57cec5SDimitry Andric BytesInScope = BytesInThisScope; 3860b57cec5SDimitry Andric if (IsFunction) 3870b57cec5SDimitry Andric GlobalStats.FunctionSize += BytesInThisScope; 3880b57cec5SDimitry Andric else if (IsInlinedFunction && InlineDepth == 0) 3890b57cec5SDimitry Andric GlobalStats.InlineFunctionSize += BytesInThisScope; 3900b57cec5SDimitry Andric } 3910b57cec5SDimitry Andric } else { 3920b57cec5SDimitry Andric // Not a scope, visit the Die itself. It could be a variable. 393*480093f4SDimitry Andric collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, 394*480093f4SDimitry Andric FnStatMap, GlobalStats, LocStats); 3950b57cec5SDimitry Andric } 3960b57cec5SDimitry Andric 3970b57cec5SDimitry Andric // Set InlineDepth correctly for child recursion 3980b57cec5SDimitry Andric if (IsFunction) 3990b57cec5SDimitry Andric InlineDepth = 0; 4000b57cec5SDimitry Andric else if (IsInlinedFunction) 4010b57cec5SDimitry Andric ++InlineDepth; 4020b57cec5SDimitry Andric 4030b57cec5SDimitry Andric // Traverse children. 4040b57cec5SDimitry Andric unsigned LexicalBlockIndex = 0; 4050b57cec5SDimitry Andric DWARFDie Child = Die.getFirstChild(); 4060b57cec5SDimitry Andric while (Child) { 4070b57cec5SDimitry Andric std::string ChildVarPrefix = VarPrefix; 4080b57cec5SDimitry Andric if (Child.getTag() == dwarf::DW_TAG_lexical_block) 4090b57cec5SDimitry Andric ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; 4100b57cec5SDimitry Andric 411*480093f4SDimitry Andric collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, 412*480093f4SDimitry Andric InlineDepth, FnStatMap, GlobalStats, LocStats); 4130b57cec5SDimitry Andric Child = Child.getSibling(); 4140b57cec5SDimitry Andric } 4150b57cec5SDimitry Andric } 4160b57cec5SDimitry Andric 4170b57cec5SDimitry Andric /// Print machine-readable output. 4180b57cec5SDimitry Andric /// The machine-readable format is single-line JSON output. 4190b57cec5SDimitry Andric /// \{ 4208bcb0991SDimitry Andric static void printDatum(raw_ostream &OS, const char *Key, json::Value Value) { 4210b57cec5SDimitry Andric OS << ",\"" << Key << "\":" << Value; 4220b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); 4230b57cec5SDimitry Andric } 4248bcb0991SDimitry Andric static void printLocationStats(raw_ostream &OS, 4258bcb0991SDimitry Andric const char *Key, 4268bcb0991SDimitry Andric std::vector<unsigned> &LocationStats) { 4278bcb0991SDimitry Andric OS << ",\"" << Key << " with 0% of its scope covered\":" 4288bcb0991SDimitry Andric << LocationStats[0]; 4298bcb0991SDimitry Andric LLVM_DEBUG(llvm::dbgs() << Key << " with 0% of its scope covered: " 4308bcb0991SDimitry Andric << LocationStats[0] << '\n'); 431*480093f4SDimitry Andric OS << ",\"" << Key << " with (0%,10%) of its scope covered\":" 4328bcb0991SDimitry Andric << LocationStats[1]; 433*480093f4SDimitry Andric LLVM_DEBUG(llvm::dbgs() << Key << " with (0%,10%) of its scope covered: " 4348bcb0991SDimitry Andric << LocationStats[1] << '\n'); 4358bcb0991SDimitry Andric for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { 436*480093f4SDimitry Andric OS << ",\"" << Key << " with [" << (i - 1) * 10 << "%," << i * 10 437*480093f4SDimitry Andric << "%) of its scope covered\":" << LocationStats[i]; 4388bcb0991SDimitry Andric LLVM_DEBUG(llvm::dbgs() 439*480093f4SDimitry Andric << Key << " with [" << (i - 1) * 10 << "%," << i * 10 440*480093f4SDimitry Andric << "%) of its scope covered: " << LocationStats[i]); 4418bcb0991SDimitry Andric } 4428bcb0991SDimitry Andric OS << ",\"" << Key << " with 100% of its scope covered\":" 4438bcb0991SDimitry Andric << LocationStats[NumOfCoverageCategories - 1]; 4448bcb0991SDimitry Andric LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: " 4458bcb0991SDimitry Andric << LocationStats[NumOfCoverageCategories - 1]); 4468bcb0991SDimitry Andric } 4470b57cec5SDimitry Andric /// \} 4480b57cec5SDimitry Andric 4490b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext. 4500b57cec5SDimitry Andric /// 4510b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few 4520b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked 4530b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect 4540b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly 4550b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different 4560b57cec5SDimitry Andric /// compilers is. 4570b57cec5SDimitry Andric bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 4580b57cec5SDimitry Andric Twine Filename, raw_ostream &OS) { 4590b57cec5SDimitry Andric StringRef FormatName = Obj.getFileFormatName(); 4600b57cec5SDimitry Andric GlobalStats GlobalStats; 4618bcb0991SDimitry Andric LocationStats LocStats; 4620b57cec5SDimitry Andric StringMap<PerFunctionStats> Statistics; 4630b57cec5SDimitry Andric for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) 4640b57cec5SDimitry Andric if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) 465*480093f4SDimitry Andric collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, 466*480093f4SDimitry Andric LocStats); 4670b57cec5SDimitry Andric 4680b57cec5SDimitry Andric /// The version number should be increased every time the algorithm is changed 4690b57cec5SDimitry Andric /// (including bug fixes). New metrics may be added without increasing the 4700b57cec5SDimitry Andric /// version. 471*480093f4SDimitry Andric unsigned Version = 4; 4720b57cec5SDimitry Andric unsigned VarParamTotal = 0; 4730b57cec5SDimitry Andric unsigned VarParamUnique = 0; 4740b57cec5SDimitry Andric unsigned VarParamWithLoc = 0; 4750b57cec5SDimitry Andric unsigned NumFunctions = 0; 4760b57cec5SDimitry Andric unsigned NumInlinedFunctions = 0; 4770b57cec5SDimitry Andric unsigned NumFuncsWithSrcLoc = 0; 4780b57cec5SDimitry Andric unsigned NumAbstractOrigins = 0; 4790b57cec5SDimitry Andric unsigned ParamTotal = 0; 4800b57cec5SDimitry Andric unsigned ParamWithType = 0; 4810b57cec5SDimitry Andric unsigned ParamWithLoc = 0; 4820b57cec5SDimitry Andric unsigned ParamWithSrcLoc = 0; 4830b57cec5SDimitry Andric unsigned VarTotal = 0; 4840b57cec5SDimitry Andric unsigned VarWithType = 0; 4850b57cec5SDimitry Andric unsigned VarWithSrcLoc = 0; 4860b57cec5SDimitry Andric unsigned VarWithLoc = 0; 4870b57cec5SDimitry Andric for (auto &Entry : Statistics) { 4880b57cec5SDimitry Andric PerFunctionStats &Stats = Entry.getValue(); 4890b57cec5SDimitry Andric unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined; 4900b57cec5SDimitry Andric // Count variables in concrete out-of-line functions and in global scope. 4910b57cec5SDimitry Andric if (Stats.HasPCAddresses || !Stats.IsFunction) 4920b57cec5SDimitry Andric TotalVars += Stats.VarsInFunction.size(); 4930b57cec5SDimitry Andric unsigned Constants = Stats.ConstantMembers; 4940b57cec5SDimitry Andric VarParamWithLoc += Stats.TotalVarWithLoc + Constants; 4950b57cec5SDimitry Andric VarParamTotal += TotalVars; 4960b57cec5SDimitry Andric VarParamUnique += Stats.VarsInFunction.size(); 4970b57cec5SDimitry Andric LLVM_DEBUG(for (auto &V 4980b57cec5SDimitry Andric : Stats.VarsInFunction) llvm::dbgs() 4990b57cec5SDimitry Andric << Entry.getKey() << ": " << V.getKey() << "\n"); 5000b57cec5SDimitry Andric NumFunctions += Stats.IsFunction; 5010b57cec5SDimitry Andric NumFuncsWithSrcLoc += Stats.HasSourceLocation; 5020b57cec5SDimitry Andric NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; 5030b57cec5SDimitry Andric NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; 5040b57cec5SDimitry Andric ParamTotal += Stats.NumParams; 5050b57cec5SDimitry Andric ParamWithType += Stats.NumParamTypes; 5060b57cec5SDimitry Andric ParamWithLoc += Stats.NumParamLocations; 5070b57cec5SDimitry Andric ParamWithSrcLoc += Stats.NumParamSourceLocations; 5080b57cec5SDimitry Andric VarTotal += Stats.NumVars; 5090b57cec5SDimitry Andric VarWithType += Stats.NumVarTypes; 5100b57cec5SDimitry Andric VarWithLoc += Stats.NumVarLocations; 5110b57cec5SDimitry Andric VarWithSrcLoc += Stats.NumVarSourceLocations; 5120b57cec5SDimitry Andric } 5130b57cec5SDimitry Andric 5140b57cec5SDimitry Andric // Print summary. 5150b57cec5SDimitry Andric OS.SetBufferSize(1024); 5160b57cec5SDimitry Andric OS << "{\"version\":" << Version; 5170b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; 5180b57cec5SDimitry Andric llvm::dbgs() << "---------------------------------\n"); 5190b57cec5SDimitry Andric printDatum(OS, "file", Filename.str()); 5200b57cec5SDimitry Andric printDatum(OS, "format", FormatName); 5210b57cec5SDimitry Andric printDatum(OS, "source functions", NumFunctions); 5220b57cec5SDimitry Andric printDatum(OS, "source functions with location", NumFuncsWithSrcLoc); 5230b57cec5SDimitry Andric printDatum(OS, "inlined functions", NumInlinedFunctions); 5240b57cec5SDimitry Andric printDatum(OS, "inlined funcs with abstract origins", NumAbstractOrigins); 5250b57cec5SDimitry Andric printDatum(OS, "unique source variables", VarParamUnique); 5260b57cec5SDimitry Andric printDatum(OS, "source variables", VarParamTotal); 5270b57cec5SDimitry Andric printDatum(OS, "variables with location", VarParamWithLoc); 5280b57cec5SDimitry Andric printDatum(OS, "call site entries", GlobalStats.CallSiteEntries); 5298bcb0991SDimitry Andric printDatum(OS, "call site DIEs", GlobalStats.CallSiteDIEs); 5308bcb0991SDimitry Andric printDatum(OS, "call site parameter DIEs", GlobalStats.CallSiteParamDIEs); 531*480093f4SDimitry Andric printDatum(OS, "scope bytes total", GlobalStats.ScopeBytes); 5320b57cec5SDimitry Andric printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); 5338bcb0991SDimitry Andric printDatum(OS, "entry value scope bytes covered", 5348bcb0991SDimitry Andric GlobalStats.ScopeEntryValueBytesCovered); 5358bcb0991SDimitry Andric printDatum(OS, "formal params scope bytes total", 536*480093f4SDimitry Andric GlobalStats.ParamScopeBytes); 5378bcb0991SDimitry Andric printDatum(OS, "formal params scope bytes covered", 5388bcb0991SDimitry Andric GlobalStats.ParamScopeBytesCovered); 5398bcb0991SDimitry Andric printDatum(OS, "formal params entry value scope bytes covered", 5408bcb0991SDimitry Andric GlobalStats.ParamScopeEntryValueBytesCovered); 541*480093f4SDimitry Andric printDatum(OS, "vars scope bytes total", GlobalStats.VarScopeBytes); 5428bcb0991SDimitry Andric printDatum(OS, "vars scope bytes covered", GlobalStats.VarScopeBytesCovered); 5438bcb0991SDimitry Andric printDatum(OS, "vars entry value scope bytes covered", 5448bcb0991SDimitry Andric GlobalStats.VarScopeEntryValueBytesCovered); 5450b57cec5SDimitry Andric printDatum(OS, "total function size", GlobalStats.FunctionSize); 5460b57cec5SDimitry Andric printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize); 5470b57cec5SDimitry Andric printDatum(OS, "total formal params", ParamTotal); 5480b57cec5SDimitry Andric printDatum(OS, "formal params with source location", ParamWithSrcLoc); 5490b57cec5SDimitry Andric printDatum(OS, "formal params with type", ParamWithType); 5500b57cec5SDimitry Andric printDatum(OS, "formal params with binary location", ParamWithLoc); 5510b57cec5SDimitry Andric printDatum(OS, "total vars", VarTotal); 5520b57cec5SDimitry Andric printDatum(OS, "vars with source location", VarWithSrcLoc); 5530b57cec5SDimitry Andric printDatum(OS, "vars with type", VarWithType); 5540b57cec5SDimitry Andric printDatum(OS, "vars with binary location", VarWithLoc); 5558bcb0991SDimitry Andric printDatum(OS, "total variables procesed by location statistics", 5568bcb0991SDimitry Andric LocStats.NumVarParam); 5578bcb0991SDimitry Andric printLocationStats(OS, "variables", LocStats.VarParamLocStats); 5588bcb0991SDimitry Andric printLocationStats(OS, "variables (excluding the debug entry values)", 5598bcb0991SDimitry Andric LocStats.VarParamNonEntryValLocStats); 5608bcb0991SDimitry Andric printDatum(OS, "total params procesed by location statistics", 5618bcb0991SDimitry Andric LocStats.NumParam); 5628bcb0991SDimitry Andric printLocationStats(OS, "params", LocStats.ParamLocStats); 5638bcb0991SDimitry Andric printLocationStats(OS, "params (excluding the debug entry values)", 5648bcb0991SDimitry Andric LocStats.ParamNonEntryValLocStats); 5658bcb0991SDimitry Andric printDatum(OS, "total vars procesed by location statistics", LocStats.NumVar); 5668bcb0991SDimitry Andric printLocationStats(OS, "vars", LocStats.VarLocStats); 5678bcb0991SDimitry Andric printLocationStats(OS, "vars (excluding the debug entry values)", 5688bcb0991SDimitry Andric LocStats.VarNonEntryValLocStats); 5690b57cec5SDimitry Andric OS << "}\n"; 5700b57cec5SDimitry Andric LLVM_DEBUG( 5710b57cec5SDimitry Andric llvm::dbgs() << "Total Availability: " 5720b57cec5SDimitry Andric << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal) 5730b57cec5SDimitry Andric << "%\n"; 5740b57cec5SDimitry Andric llvm::dbgs() << "PC Ranges covered: " 5750b57cec5SDimitry Andric << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) / 576*480093f4SDimitry Andric GlobalStats.ScopeBytes) 5770b57cec5SDimitry Andric << "%\n"); 5780b57cec5SDimitry Andric return true; 5790b57cec5SDimitry Andric } 580