1*0b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h" 2*0b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h" 3*0b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h" 4*0b57cec5SDimitry Andric #include "llvm/DebugInfo/DIContext.h" 5*0b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h" 6*0b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" 7*0b57cec5SDimitry Andric #include "llvm/Object/ObjectFile.h" 8*0b57cec5SDimitry Andric 9*0b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfdump" 10*0b57cec5SDimitry Andric using namespace llvm; 11*0b57cec5SDimitry Andric using namespace object; 12*0b57cec5SDimitry Andric 13*0b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and 14*0b57cec5SDimitry Andric /// contains variables, such as a compile unit). 15*0b57cec5SDimitry Andric struct PerFunctionStats { 16*0b57cec5SDimitry Andric /// Number of inlined instances of this function. 17*0b57cec5SDimitry Andric unsigned NumFnInlined = 0; 18*0b57cec5SDimitry Andric /// Number of inlined instances that have abstract origins. 19*0b57cec5SDimitry Andric unsigned NumAbstractOrigins = 0; 20*0b57cec5SDimitry Andric /// Number of variables and parameters with location across all inlined 21*0b57cec5SDimitry Andric /// instances. 22*0b57cec5SDimitry Andric unsigned TotalVarWithLoc = 0; 23*0b57cec5SDimitry Andric /// Number of constants with location across all inlined instances. 24*0b57cec5SDimitry Andric unsigned ConstantMembers = 0; 25*0b57cec5SDimitry Andric /// List of all Variables and parameters in this function. 26*0b57cec5SDimitry Andric StringSet<> VarsInFunction; 27*0b57cec5SDimitry Andric /// Compile units also cover a PC range, but have this flag set to false. 28*0b57cec5SDimitry Andric bool IsFunction = false; 29*0b57cec5SDimitry Andric /// Verify function definition has PC addresses (for detecting when 30*0b57cec5SDimitry Andric /// a function has been inlined everywhere). 31*0b57cec5SDimitry Andric bool HasPCAddresses = false; 32*0b57cec5SDimitry Andric /// Function has source location information. 33*0b57cec5SDimitry Andric bool HasSourceLocation = false; 34*0b57cec5SDimitry Andric /// Number of function parameters. 35*0b57cec5SDimitry Andric unsigned NumParams = 0; 36*0b57cec5SDimitry Andric /// Number of function parameters with source location. 37*0b57cec5SDimitry Andric unsigned NumParamSourceLocations = 0; 38*0b57cec5SDimitry Andric /// Number of function parameters with type. 39*0b57cec5SDimitry Andric unsigned NumParamTypes = 0; 40*0b57cec5SDimitry Andric /// Number of function parameters with a DW_AT_location. 41*0b57cec5SDimitry Andric unsigned NumParamLocations = 0; 42*0b57cec5SDimitry Andric /// Number of variables. 43*0b57cec5SDimitry Andric unsigned NumVars = 0; 44*0b57cec5SDimitry Andric /// Number of variables with source location. 45*0b57cec5SDimitry Andric unsigned NumVarSourceLocations = 0; 46*0b57cec5SDimitry Andric /// Number of variables wtih type. 47*0b57cec5SDimitry Andric unsigned NumVarTypes = 0; 48*0b57cec5SDimitry Andric /// Number of variables wtih DW_AT_location. 49*0b57cec5SDimitry Andric unsigned NumVarLocations = 0; 50*0b57cec5SDimitry Andric }; 51*0b57cec5SDimitry Andric 52*0b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs. 53*0b57cec5SDimitry Andric struct GlobalStats { 54*0b57cec5SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations. 55*0b57cec5SDimitry Andric unsigned ScopeBytesCovered = 0; 56*0b57cec5SDimitry Andric /// Total number of PC range bytes in each variable's enclosing scope, 57*0b57cec5SDimitry Andric /// starting from the first definition of the variable. 58*0b57cec5SDimitry Andric unsigned ScopeBytesFromFirstDefinition = 0; 59*0b57cec5SDimitry Andric /// Total number of call site entries (DW_TAG_call_site) or 60*0b57cec5SDimitry Andric /// (DW_AT_call_file & DW_AT_call_line). 61*0b57cec5SDimitry Andric unsigned CallSiteEntries = 0; 62*0b57cec5SDimitry Andric /// Total byte size of concrete functions. This byte size includes 63*0b57cec5SDimitry Andric /// inline functions contained in the concrete functions. 64*0b57cec5SDimitry Andric uint64_t FunctionSize = 0; 65*0b57cec5SDimitry Andric /// Total byte size of inlined functions. This is the total number of bytes 66*0b57cec5SDimitry Andric /// for the top inline functions within concrete functions. This can help 67*0b57cec5SDimitry Andric /// tune the inline settings when compiling to match user expectations. 68*0b57cec5SDimitry Andric uint64_t InlineFunctionSize = 0; 69*0b57cec5SDimitry Andric }; 70*0b57cec5SDimitry Andric 71*0b57cec5SDimitry Andric /// Extract the low pc from a Die. 72*0b57cec5SDimitry Andric static uint64_t getLowPC(DWARFDie Die) { 73*0b57cec5SDimitry Andric auto RangesOrError = Die.getAddressRanges(); 74*0b57cec5SDimitry Andric DWARFAddressRangesVector Ranges; 75*0b57cec5SDimitry Andric if (RangesOrError) 76*0b57cec5SDimitry Andric Ranges = RangesOrError.get(); 77*0b57cec5SDimitry Andric else 78*0b57cec5SDimitry Andric llvm::consumeError(RangesOrError.takeError()); 79*0b57cec5SDimitry Andric if (Ranges.size()) 80*0b57cec5SDimitry Andric return Ranges[0].LowPC; 81*0b57cec5SDimitry Andric return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0); 82*0b57cec5SDimitry Andric } 83*0b57cec5SDimitry Andric 84*0b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE. 85*0b57cec5SDimitry Andric static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, 86*0b57cec5SDimitry Andric std::string VarPrefix, uint64_t ScopeLowPC, 87*0b57cec5SDimitry Andric uint64_t BytesInScope, uint32_t InlineDepth, 88*0b57cec5SDimitry Andric StringMap<PerFunctionStats> &FnStatMap, 89*0b57cec5SDimitry Andric GlobalStats &GlobalStats) { 90*0b57cec5SDimitry Andric bool HasLoc = false; 91*0b57cec5SDimitry Andric bool HasSrcLoc = false; 92*0b57cec5SDimitry Andric bool HasType = false; 93*0b57cec5SDimitry Andric bool IsArtificial = false; 94*0b57cec5SDimitry Andric uint64_t BytesCovered = 0; 95*0b57cec5SDimitry Andric uint64_t OffsetToFirstDefinition = 0; 96*0b57cec5SDimitry Andric 97*0b57cec5SDimitry Andric if (Die.getTag() == dwarf::DW_TAG_call_site) { 98*0b57cec5SDimitry Andric GlobalStats.CallSiteEntries++; 99*0b57cec5SDimitry Andric return; 100*0b57cec5SDimitry Andric } 101*0b57cec5SDimitry Andric 102*0b57cec5SDimitry Andric if (Die.getTag() != dwarf::DW_TAG_formal_parameter && 103*0b57cec5SDimitry Andric Die.getTag() != dwarf::DW_TAG_variable && 104*0b57cec5SDimitry Andric Die.getTag() != dwarf::DW_TAG_member) { 105*0b57cec5SDimitry Andric // Not a variable or constant member. 106*0b57cec5SDimitry Andric return; 107*0b57cec5SDimitry Andric } 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_decl_file) && 110*0b57cec5SDimitry Andric Die.findRecursively(dwarf::DW_AT_decl_line)) 111*0b57cec5SDimitry Andric HasSrcLoc = true; 112*0b57cec5SDimitry Andric 113*0b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_type)) 114*0b57cec5SDimitry Andric HasType = true; 115*0b57cec5SDimitry Andric 116*0b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_artificial)) 117*0b57cec5SDimitry Andric IsArtificial = true; 118*0b57cec5SDimitry Andric 119*0b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_const_value)) { 120*0b57cec5SDimitry Andric // This catches constant members *and* variables. 121*0b57cec5SDimitry Andric HasLoc = true; 122*0b57cec5SDimitry Andric BytesCovered = BytesInScope; 123*0b57cec5SDimitry Andric } else { 124*0b57cec5SDimitry Andric if (Die.getTag() == dwarf::DW_TAG_member) { 125*0b57cec5SDimitry Andric // Non-const member. 126*0b57cec5SDimitry Andric return; 127*0b57cec5SDimitry Andric } 128*0b57cec5SDimitry Andric // Handle variables and function arguments. 129*0b57cec5SDimitry Andric auto FormValue = Die.find(dwarf::DW_AT_location); 130*0b57cec5SDimitry Andric HasLoc = FormValue.hasValue(); 131*0b57cec5SDimitry Andric if (HasLoc) { 132*0b57cec5SDimitry Andric // Get PC coverage. 133*0b57cec5SDimitry Andric if (auto DebugLocOffset = FormValue->getAsSectionOffset()) { 134*0b57cec5SDimitry Andric auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc(); 135*0b57cec5SDimitry Andric if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) { 136*0b57cec5SDimitry Andric for (auto Entry : List->Entries) 137*0b57cec5SDimitry Andric BytesCovered += Entry.End - Entry.Begin; 138*0b57cec5SDimitry Andric if (List->Entries.size()) { 139*0b57cec5SDimitry Andric uint64_t FirstDef = List->Entries[0].Begin; 140*0b57cec5SDimitry Andric uint64_t UnitOfs = getLowPC(Die.getDwarfUnit()->getUnitDIE()); 141*0b57cec5SDimitry Andric // Ranges sometimes start before the lexical scope. 142*0b57cec5SDimitry Andric if (UnitOfs + FirstDef >= ScopeLowPC) 143*0b57cec5SDimitry Andric OffsetToFirstDefinition = UnitOfs + FirstDef - ScopeLowPC; 144*0b57cec5SDimitry Andric // Or even after it. Count that as a failure. 145*0b57cec5SDimitry Andric if (OffsetToFirstDefinition > BytesInScope) 146*0b57cec5SDimitry Andric OffsetToFirstDefinition = 0; 147*0b57cec5SDimitry Andric } 148*0b57cec5SDimitry Andric } 149*0b57cec5SDimitry Andric assert(BytesInScope); 150*0b57cec5SDimitry Andric } else { 151*0b57cec5SDimitry Andric // Assume the entire range is covered by a single location. 152*0b57cec5SDimitry Andric BytesCovered = BytesInScope; 153*0b57cec5SDimitry Andric } 154*0b57cec5SDimitry Andric } 155*0b57cec5SDimitry Andric } 156*0b57cec5SDimitry Andric 157*0b57cec5SDimitry Andric // Collect PC range coverage data. 158*0b57cec5SDimitry Andric auto &FnStats = FnStatMap[FnPrefix]; 159*0b57cec5SDimitry Andric if (DWARFDie D = 160*0b57cec5SDimitry Andric Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) 161*0b57cec5SDimitry Andric Die = D; 162*0b57cec5SDimitry Andric // By using the variable name + the path through the lexical block tree, the 163*0b57cec5SDimitry Andric // keys are consistent across duplicate abstract origins in different CUs. 164*0b57cec5SDimitry Andric std::string VarName = StringRef(Die.getName(DINameKind::ShortName)); 165*0b57cec5SDimitry Andric FnStats.VarsInFunction.insert(VarPrefix + VarName); 166*0b57cec5SDimitry Andric if (BytesInScope) { 167*0b57cec5SDimitry Andric FnStats.TotalVarWithLoc += (unsigned)HasLoc; 168*0b57cec5SDimitry Andric // Adjust for the fact the variables often start their lifetime in the 169*0b57cec5SDimitry Andric // middle of the scope. 170*0b57cec5SDimitry Andric BytesInScope -= OffsetToFirstDefinition; 171*0b57cec5SDimitry Andric // Turns out we have a lot of ranges that extend past the lexical scope. 172*0b57cec5SDimitry Andric GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered); 173*0b57cec5SDimitry Andric GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope; 174*0b57cec5SDimitry Andric assert(GlobalStats.ScopeBytesCovered <= 175*0b57cec5SDimitry Andric GlobalStats.ScopeBytesFromFirstDefinition); 176*0b57cec5SDimitry Andric } else if (Die.getTag() == dwarf::DW_TAG_member) { 177*0b57cec5SDimitry Andric FnStats.ConstantMembers++; 178*0b57cec5SDimitry Andric } else { 179*0b57cec5SDimitry Andric FnStats.TotalVarWithLoc += (unsigned)HasLoc; 180*0b57cec5SDimitry Andric } 181*0b57cec5SDimitry Andric if (!IsArtificial) { 182*0b57cec5SDimitry Andric if (Die.getTag() == dwarf::DW_TAG_formal_parameter) { 183*0b57cec5SDimitry Andric FnStats.NumParams++; 184*0b57cec5SDimitry Andric if (HasType) 185*0b57cec5SDimitry Andric FnStats.NumParamTypes++; 186*0b57cec5SDimitry Andric if (HasSrcLoc) 187*0b57cec5SDimitry Andric FnStats.NumParamSourceLocations++; 188*0b57cec5SDimitry Andric if (HasLoc) 189*0b57cec5SDimitry Andric FnStats.NumParamLocations++; 190*0b57cec5SDimitry Andric } else if (Die.getTag() == dwarf::DW_TAG_variable) { 191*0b57cec5SDimitry Andric FnStats.NumVars++; 192*0b57cec5SDimitry Andric if (HasType) 193*0b57cec5SDimitry Andric FnStats.NumVarTypes++; 194*0b57cec5SDimitry Andric if (HasSrcLoc) 195*0b57cec5SDimitry Andric FnStats.NumVarSourceLocations++; 196*0b57cec5SDimitry Andric if (HasLoc) 197*0b57cec5SDimitry Andric FnStats.NumVarLocations++; 198*0b57cec5SDimitry Andric } 199*0b57cec5SDimitry Andric } 200*0b57cec5SDimitry Andric } 201*0b57cec5SDimitry Andric 202*0b57cec5SDimitry Andric /// Recursively collect debug info quality metrics. 203*0b57cec5SDimitry Andric static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, 204*0b57cec5SDimitry Andric std::string VarPrefix, uint64_t ScopeLowPC, 205*0b57cec5SDimitry Andric uint64_t BytesInScope, uint32_t InlineDepth, 206*0b57cec5SDimitry Andric StringMap<PerFunctionStats> &FnStatMap, 207*0b57cec5SDimitry Andric GlobalStats &GlobalStats) { 208*0b57cec5SDimitry Andric // Handle any kind of lexical scope. 209*0b57cec5SDimitry Andric const dwarf::Tag Tag = Die.getTag(); 210*0b57cec5SDimitry Andric const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; 211*0b57cec5SDimitry Andric const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; 212*0b57cec5SDimitry Andric const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; 213*0b57cec5SDimitry Andric if (IsFunction || IsInlinedFunction || IsBlock) { 214*0b57cec5SDimitry Andric 215*0b57cec5SDimitry Andric // Reset VarPrefix when entering a new function. 216*0b57cec5SDimitry Andric if (Die.getTag() == dwarf::DW_TAG_subprogram || 217*0b57cec5SDimitry Andric Die.getTag() == dwarf::DW_TAG_inlined_subroutine) 218*0b57cec5SDimitry Andric VarPrefix = "v"; 219*0b57cec5SDimitry Andric 220*0b57cec5SDimitry Andric // Ignore forward declarations. 221*0b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_declaration)) 222*0b57cec5SDimitry Andric return; 223*0b57cec5SDimitry Andric 224*0b57cec5SDimitry Andric // Check for call sites. 225*0b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line)) 226*0b57cec5SDimitry Andric GlobalStats.CallSiteEntries++; 227*0b57cec5SDimitry Andric 228*0b57cec5SDimitry Andric // PC Ranges. 229*0b57cec5SDimitry Andric auto RangesOrError = Die.getAddressRanges(); 230*0b57cec5SDimitry Andric if (!RangesOrError) { 231*0b57cec5SDimitry Andric llvm::consumeError(RangesOrError.takeError()); 232*0b57cec5SDimitry Andric return; 233*0b57cec5SDimitry Andric } 234*0b57cec5SDimitry Andric 235*0b57cec5SDimitry Andric auto Ranges = RangesOrError.get(); 236*0b57cec5SDimitry Andric uint64_t BytesInThisScope = 0; 237*0b57cec5SDimitry Andric for (auto Range : Ranges) 238*0b57cec5SDimitry Andric BytesInThisScope += Range.HighPC - Range.LowPC; 239*0b57cec5SDimitry Andric ScopeLowPC = getLowPC(Die); 240*0b57cec5SDimitry Andric 241*0b57cec5SDimitry Andric // Count the function. 242*0b57cec5SDimitry Andric if (!IsBlock) { 243*0b57cec5SDimitry Andric StringRef Name = Die.getName(DINameKind::LinkageName); 244*0b57cec5SDimitry Andric if (Name.empty()) 245*0b57cec5SDimitry Andric Name = Die.getName(DINameKind::ShortName); 246*0b57cec5SDimitry Andric FnPrefix = Name; 247*0b57cec5SDimitry Andric // Skip over abstract origins. 248*0b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_inline)) 249*0b57cec5SDimitry Andric return; 250*0b57cec5SDimitry Andric // We've seen an (inlined) instance of this function. 251*0b57cec5SDimitry Andric auto &FnStats = FnStatMap[Name]; 252*0b57cec5SDimitry Andric if (IsInlinedFunction) { 253*0b57cec5SDimitry Andric FnStats.NumFnInlined++; 254*0b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) 255*0b57cec5SDimitry Andric FnStats.NumAbstractOrigins++; 256*0b57cec5SDimitry Andric } 257*0b57cec5SDimitry Andric FnStats.IsFunction = true; 258*0b57cec5SDimitry Andric if (BytesInThisScope && !IsInlinedFunction) 259*0b57cec5SDimitry Andric FnStats.HasPCAddresses = true; 260*0b57cec5SDimitry Andric std::string FnName = StringRef(Die.getName(DINameKind::ShortName)); 261*0b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_decl_file) && 262*0b57cec5SDimitry Andric Die.findRecursively(dwarf::DW_AT_decl_line)) 263*0b57cec5SDimitry Andric FnStats.HasSourceLocation = true; 264*0b57cec5SDimitry Andric } 265*0b57cec5SDimitry Andric 266*0b57cec5SDimitry Andric if (BytesInThisScope) { 267*0b57cec5SDimitry Andric BytesInScope = BytesInThisScope; 268*0b57cec5SDimitry Andric if (IsFunction) 269*0b57cec5SDimitry Andric GlobalStats.FunctionSize += BytesInThisScope; 270*0b57cec5SDimitry Andric else if (IsInlinedFunction && InlineDepth == 0) 271*0b57cec5SDimitry Andric GlobalStats.InlineFunctionSize += BytesInThisScope; 272*0b57cec5SDimitry Andric } 273*0b57cec5SDimitry Andric } else { 274*0b57cec5SDimitry Andric // Not a scope, visit the Die itself. It could be a variable. 275*0b57cec5SDimitry Andric collectStatsForDie(Die, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope, 276*0b57cec5SDimitry Andric InlineDepth, FnStatMap, GlobalStats); 277*0b57cec5SDimitry Andric } 278*0b57cec5SDimitry Andric 279*0b57cec5SDimitry Andric // Set InlineDepth correctly for child recursion 280*0b57cec5SDimitry Andric if (IsFunction) 281*0b57cec5SDimitry Andric InlineDepth = 0; 282*0b57cec5SDimitry Andric else if (IsInlinedFunction) 283*0b57cec5SDimitry Andric ++InlineDepth; 284*0b57cec5SDimitry Andric 285*0b57cec5SDimitry Andric // Traverse children. 286*0b57cec5SDimitry Andric unsigned LexicalBlockIndex = 0; 287*0b57cec5SDimitry Andric DWARFDie Child = Die.getFirstChild(); 288*0b57cec5SDimitry Andric while (Child) { 289*0b57cec5SDimitry Andric std::string ChildVarPrefix = VarPrefix; 290*0b57cec5SDimitry Andric if (Child.getTag() == dwarf::DW_TAG_lexical_block) 291*0b57cec5SDimitry Andric ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; 292*0b57cec5SDimitry Andric 293*0b57cec5SDimitry Andric collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, ScopeLowPC, 294*0b57cec5SDimitry Andric BytesInScope, InlineDepth, FnStatMap, GlobalStats); 295*0b57cec5SDimitry Andric Child = Child.getSibling(); 296*0b57cec5SDimitry Andric } 297*0b57cec5SDimitry Andric } 298*0b57cec5SDimitry Andric 299*0b57cec5SDimitry Andric /// Print machine-readable output. 300*0b57cec5SDimitry Andric /// The machine-readable format is single-line JSON output. 301*0b57cec5SDimitry Andric /// \{ 302*0b57cec5SDimitry Andric static void printDatum(raw_ostream &OS, const char *Key, StringRef Value) { 303*0b57cec5SDimitry Andric OS << ",\"" << Key << "\":\"" << Value << '"'; 304*0b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); 305*0b57cec5SDimitry Andric } 306*0b57cec5SDimitry Andric static void printDatum(raw_ostream &OS, const char *Key, uint64_t Value) { 307*0b57cec5SDimitry Andric OS << ",\"" << Key << "\":" << Value; 308*0b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); 309*0b57cec5SDimitry Andric } 310*0b57cec5SDimitry Andric /// \} 311*0b57cec5SDimitry Andric 312*0b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext. 313*0b57cec5SDimitry Andric /// 314*0b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few 315*0b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked 316*0b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect 317*0b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly 318*0b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different 319*0b57cec5SDimitry Andric /// compilers is. 320*0b57cec5SDimitry Andric bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 321*0b57cec5SDimitry Andric Twine Filename, raw_ostream &OS) { 322*0b57cec5SDimitry Andric StringRef FormatName = Obj.getFileFormatName(); 323*0b57cec5SDimitry Andric GlobalStats GlobalStats; 324*0b57cec5SDimitry Andric StringMap<PerFunctionStats> Statistics; 325*0b57cec5SDimitry Andric for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) 326*0b57cec5SDimitry Andric if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) 327*0b57cec5SDimitry Andric collectStatsRecursive(CUDie, "/", "g", 0, 0, 0, Statistics, GlobalStats); 328*0b57cec5SDimitry Andric 329*0b57cec5SDimitry Andric /// The version number should be increased every time the algorithm is changed 330*0b57cec5SDimitry Andric /// (including bug fixes). New metrics may be added without increasing the 331*0b57cec5SDimitry Andric /// version. 332*0b57cec5SDimitry Andric unsigned Version = 3; 333*0b57cec5SDimitry Andric unsigned VarParamTotal = 0; 334*0b57cec5SDimitry Andric unsigned VarParamUnique = 0; 335*0b57cec5SDimitry Andric unsigned VarParamWithLoc = 0; 336*0b57cec5SDimitry Andric unsigned NumFunctions = 0; 337*0b57cec5SDimitry Andric unsigned NumInlinedFunctions = 0; 338*0b57cec5SDimitry Andric unsigned NumFuncsWithSrcLoc = 0; 339*0b57cec5SDimitry Andric unsigned NumAbstractOrigins = 0; 340*0b57cec5SDimitry Andric unsigned ParamTotal = 0; 341*0b57cec5SDimitry Andric unsigned ParamWithType = 0; 342*0b57cec5SDimitry Andric unsigned ParamWithLoc = 0; 343*0b57cec5SDimitry Andric unsigned ParamWithSrcLoc = 0; 344*0b57cec5SDimitry Andric unsigned VarTotal = 0; 345*0b57cec5SDimitry Andric unsigned VarWithType = 0; 346*0b57cec5SDimitry Andric unsigned VarWithSrcLoc = 0; 347*0b57cec5SDimitry Andric unsigned VarWithLoc = 0; 348*0b57cec5SDimitry Andric for (auto &Entry : Statistics) { 349*0b57cec5SDimitry Andric PerFunctionStats &Stats = Entry.getValue(); 350*0b57cec5SDimitry Andric unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined; 351*0b57cec5SDimitry Andric // Count variables in concrete out-of-line functions and in global scope. 352*0b57cec5SDimitry Andric if (Stats.HasPCAddresses || !Stats.IsFunction) 353*0b57cec5SDimitry Andric TotalVars += Stats.VarsInFunction.size(); 354*0b57cec5SDimitry Andric unsigned Constants = Stats.ConstantMembers; 355*0b57cec5SDimitry Andric VarParamWithLoc += Stats.TotalVarWithLoc + Constants; 356*0b57cec5SDimitry Andric VarParamTotal += TotalVars; 357*0b57cec5SDimitry Andric VarParamUnique += Stats.VarsInFunction.size(); 358*0b57cec5SDimitry Andric LLVM_DEBUG(for (auto &V 359*0b57cec5SDimitry Andric : Stats.VarsInFunction) llvm::dbgs() 360*0b57cec5SDimitry Andric << Entry.getKey() << ": " << V.getKey() << "\n"); 361*0b57cec5SDimitry Andric NumFunctions += Stats.IsFunction; 362*0b57cec5SDimitry Andric NumFuncsWithSrcLoc += Stats.HasSourceLocation; 363*0b57cec5SDimitry Andric NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; 364*0b57cec5SDimitry Andric NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; 365*0b57cec5SDimitry Andric ParamTotal += Stats.NumParams; 366*0b57cec5SDimitry Andric ParamWithType += Stats.NumParamTypes; 367*0b57cec5SDimitry Andric ParamWithLoc += Stats.NumParamLocations; 368*0b57cec5SDimitry Andric ParamWithSrcLoc += Stats.NumParamSourceLocations; 369*0b57cec5SDimitry Andric VarTotal += Stats.NumVars; 370*0b57cec5SDimitry Andric VarWithType += Stats.NumVarTypes; 371*0b57cec5SDimitry Andric VarWithLoc += Stats.NumVarLocations; 372*0b57cec5SDimitry Andric VarWithSrcLoc += Stats.NumVarSourceLocations; 373*0b57cec5SDimitry Andric } 374*0b57cec5SDimitry Andric 375*0b57cec5SDimitry Andric // Print summary. 376*0b57cec5SDimitry Andric OS.SetBufferSize(1024); 377*0b57cec5SDimitry Andric OS << "{\"version\":" << Version; 378*0b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; 379*0b57cec5SDimitry Andric llvm::dbgs() << "---------------------------------\n"); 380*0b57cec5SDimitry Andric printDatum(OS, "file", Filename.str()); 381*0b57cec5SDimitry Andric printDatum(OS, "format", FormatName); 382*0b57cec5SDimitry Andric printDatum(OS, "source functions", NumFunctions); 383*0b57cec5SDimitry Andric printDatum(OS, "source functions with location", NumFuncsWithSrcLoc); 384*0b57cec5SDimitry Andric printDatum(OS, "inlined functions", NumInlinedFunctions); 385*0b57cec5SDimitry Andric printDatum(OS, "inlined funcs with abstract origins", NumAbstractOrigins); 386*0b57cec5SDimitry Andric printDatum(OS, "unique source variables", VarParamUnique); 387*0b57cec5SDimitry Andric printDatum(OS, "source variables", VarParamTotal); 388*0b57cec5SDimitry Andric printDatum(OS, "variables with location", VarParamWithLoc); 389*0b57cec5SDimitry Andric printDatum(OS, "call site entries", GlobalStats.CallSiteEntries); 390*0b57cec5SDimitry Andric printDatum(OS, "scope bytes total", 391*0b57cec5SDimitry Andric GlobalStats.ScopeBytesFromFirstDefinition); 392*0b57cec5SDimitry Andric printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); 393*0b57cec5SDimitry Andric printDatum(OS, "total function size", GlobalStats.FunctionSize); 394*0b57cec5SDimitry Andric printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize); 395*0b57cec5SDimitry Andric printDatum(OS, "total formal params", ParamTotal); 396*0b57cec5SDimitry Andric printDatum(OS, "formal params with source location", ParamWithSrcLoc); 397*0b57cec5SDimitry Andric printDatum(OS, "formal params with type", ParamWithType); 398*0b57cec5SDimitry Andric printDatum(OS, "formal params with binary location", ParamWithLoc); 399*0b57cec5SDimitry Andric printDatum(OS, "total vars", VarTotal); 400*0b57cec5SDimitry Andric printDatum(OS, "vars with source location", VarWithSrcLoc); 401*0b57cec5SDimitry Andric printDatum(OS, "vars with type", VarWithType); 402*0b57cec5SDimitry Andric printDatum(OS, "vars with binary location", VarWithLoc); 403*0b57cec5SDimitry Andric OS << "}\n"; 404*0b57cec5SDimitry Andric LLVM_DEBUG( 405*0b57cec5SDimitry Andric llvm::dbgs() << "Total Availability: " 406*0b57cec5SDimitry Andric << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal) 407*0b57cec5SDimitry Andric << "%\n"; 408*0b57cec5SDimitry Andric llvm::dbgs() << "PC Ranges covered: " 409*0b57cec5SDimitry Andric << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) / 410*0b57cec5SDimitry Andric GlobalStats.ScopeBytesFromFirstDefinition) 411*0b57cec5SDimitry Andric << "%\n"); 412*0b57cec5SDimitry Andric return true; 413*0b57cec5SDimitry Andric } 414