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" 11*0fca6ea1SDimitry Andric #include "llvm/ADT/DenseSet.h" 120b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h" 130b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h" 140b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" 1581ad6265SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFExpression.h" 160b57cec5SDimitry Andric #include "llvm/Object/ObjectFile.h" 178bcb0991SDimitry Andric #include "llvm/Support/JSON.h" 180b57cec5SDimitry Andric 190b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfdump" 200b57cec5SDimitry Andric using namespace llvm; 215ffd83dbSDimitry Andric using namespace llvm::dwarfdump; 225ffd83dbSDimitry Andric using namespace llvm::object; 230b57cec5SDimitry Andric 24fe6060f1SDimitry Andric namespace { 258bcb0991SDimitry Andric /// This represents the number of categories of debug location coverage being 268bcb0991SDimitry Andric /// calculated. The first category is the number of variables with 0% location 278bcb0991SDimitry Andric /// coverage, but the last category is the number of variables with 100% 288bcb0991SDimitry Andric /// location coverage. 298bcb0991SDimitry Andric constexpr int NumOfCoverageCategories = 12; 308bcb0991SDimitry Andric 31fe6060f1SDimitry Andric /// This is used for zero location coverage bucket. 32fe6060f1SDimitry Andric constexpr unsigned ZeroCoverageBucket = 0; 33fe6060f1SDimitry Andric 34349cc55cSDimitry Andric /// The UINT64_MAX is used as an indication of the overflow. 35349cc55cSDimitry Andric constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max(); 36349cc55cSDimitry Andric 37fe6060f1SDimitry Andric /// This represents variables DIE offsets. 38fe6060f1SDimitry Andric using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>; 39fe6060f1SDimitry Andric /// This maps function DIE offset to its variables. 40fe6060f1SDimitry Andric using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>; 41fe6060f1SDimitry Andric /// This represents function DIE offsets containing an abstract_origin. 42fe6060f1SDimitry Andric using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>; 43fe6060f1SDimitry Andric 44349cc55cSDimitry Andric /// This represents a data type for the stats and it helps us to 45349cc55cSDimitry Andric /// detect an overflow. 46349cc55cSDimitry Andric /// NOTE: This can be implemented as a template if there is an another type 47349cc55cSDimitry Andric /// needing this. 48349cc55cSDimitry Andric struct SaturatingUINT64 { 49349cc55cSDimitry Andric /// Number that represents the stats. 50349cc55cSDimitry Andric uint64_t Value; 51349cc55cSDimitry Andric 52349cc55cSDimitry Andric SaturatingUINT64(uint64_t Value_) : Value(Value_) {} 53349cc55cSDimitry Andric 54349cc55cSDimitry Andric void operator++(int) { return *this += 1; } 55349cc55cSDimitry Andric void operator+=(uint64_t Value_) { 56349cc55cSDimitry Andric if (Value != OverflowValue) { 57349cc55cSDimitry Andric if (Value < OverflowValue - Value_) 58349cc55cSDimitry Andric Value += Value_; 59349cc55cSDimitry Andric else 60349cc55cSDimitry Andric Value = OverflowValue; 61349cc55cSDimitry Andric } 62349cc55cSDimitry Andric } 63349cc55cSDimitry Andric }; 64349cc55cSDimitry Andric 654824e7fdSDimitry Andric /// Utility struct to store the full location of a DIE - its CU and offset. 664824e7fdSDimitry Andric struct DIELocation { 674824e7fdSDimitry Andric DWARFUnit *DwUnit; 684824e7fdSDimitry Andric uint64_t DIEOffset; 694824e7fdSDimitry Andric DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset) 704824e7fdSDimitry Andric : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {} 714824e7fdSDimitry Andric }; 724824e7fdSDimitry Andric /// This represents DWARF locations of CrossCU referencing DIEs. 734824e7fdSDimitry Andric using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>; 744824e7fdSDimitry Andric 754824e7fdSDimitry Andric /// This maps function DIE offset to its DWARF CU. 764824e7fdSDimitry Andric using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>; 774824e7fdSDimitry Andric 780b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and 790b57cec5SDimitry Andric /// contains variables, such as a compile unit). 800b57cec5SDimitry Andric struct PerFunctionStats { 810b57cec5SDimitry Andric /// Number of inlined instances of this function. 82349cc55cSDimitry Andric uint64_t NumFnInlined = 0; 835ffd83dbSDimitry Andric /// Number of out-of-line instances of this function. 84349cc55cSDimitry Andric uint64_t NumFnOutOfLine = 0; 850b57cec5SDimitry Andric /// Number of inlined instances that have abstract origins. 86349cc55cSDimitry Andric uint64_t NumAbstractOrigins = 0; 870b57cec5SDimitry Andric /// Number of variables and parameters with location across all inlined 880b57cec5SDimitry Andric /// instances. 89349cc55cSDimitry Andric uint64_t TotalVarWithLoc = 0; 900b57cec5SDimitry Andric /// Number of constants with location across all inlined instances. 91349cc55cSDimitry Andric uint64_t ConstantMembers = 0; 925ffd83dbSDimitry Andric /// Number of arificial variables, parameters or members across all instances. 93349cc55cSDimitry Andric uint64_t NumArtificial = 0; 940b57cec5SDimitry Andric /// List of all Variables and parameters in this function. 950b57cec5SDimitry Andric StringSet<> VarsInFunction; 960b57cec5SDimitry Andric /// Compile units also cover a PC range, but have this flag set to false. 970b57cec5SDimitry Andric bool IsFunction = false; 980b57cec5SDimitry Andric /// Function has source location information. 990b57cec5SDimitry Andric bool HasSourceLocation = false; 1000b57cec5SDimitry Andric /// Number of function parameters. 101349cc55cSDimitry Andric uint64_t NumParams = 0; 1020b57cec5SDimitry Andric /// Number of function parameters with source location. 103349cc55cSDimitry Andric uint64_t NumParamSourceLocations = 0; 1040b57cec5SDimitry Andric /// Number of function parameters with type. 105349cc55cSDimitry Andric uint64_t NumParamTypes = 0; 1060b57cec5SDimitry Andric /// Number of function parameters with a DW_AT_location. 107349cc55cSDimitry Andric uint64_t NumParamLocations = 0; 1085ffd83dbSDimitry Andric /// Number of local variables. 109349cc55cSDimitry Andric uint64_t NumLocalVars = 0; 1105ffd83dbSDimitry Andric /// Number of local variables with source location. 111349cc55cSDimitry Andric uint64_t NumLocalVarSourceLocations = 0; 1125ffd83dbSDimitry Andric /// Number of local variables with type. 113349cc55cSDimitry Andric uint64_t NumLocalVarTypes = 0; 1145ffd83dbSDimitry Andric /// Number of local variables with DW_AT_location. 115349cc55cSDimitry Andric uint64_t NumLocalVarLocations = 0; 1160b57cec5SDimitry Andric }; 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs. 1190b57cec5SDimitry Andric struct GlobalStats { 1200b57cec5SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations. 121349cc55cSDimitry Andric SaturatingUINT64 TotalBytesCovered = 0; 122e8d8bef9SDimitry Andric /// Total number of parent DIE PC range bytes covered by DW_AT_Locations. 123349cc55cSDimitry Andric SaturatingUINT64 ScopeBytesCovered = 0; 124480093f4SDimitry Andric /// Total number of PC range bytes in each variable's enclosing scope. 125349cc55cSDimitry Andric SaturatingUINT64 ScopeBytes = 0; 1268bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 1278bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value). 128349cc55cSDimitry Andric SaturatingUINT64 ScopeEntryValueBytesCovered = 0; 1298bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations of 1308bcb0991SDimitry Andric /// formal parameters. 131349cc55cSDimitry Andric SaturatingUINT64 ParamScopeBytesCovered = 0; 1325ffd83dbSDimitry Andric /// Total number of PC range bytes in each parameter's enclosing scope. 133349cc55cSDimitry Andric SaturatingUINT64 ParamScopeBytes = 0; 1348bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 1358bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value) (only for parameters). 136349cc55cSDimitry Andric SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0; 1378bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations (only for local 1388bcb0991SDimitry Andric /// variables). 139349cc55cSDimitry Andric SaturatingUINT64 LocalVarScopeBytesCovered = 0; 1405ffd83dbSDimitry Andric /// Total number of PC range bytes in each local variable's enclosing scope. 141349cc55cSDimitry Andric SaturatingUINT64 LocalVarScopeBytes = 0; 1428bcb0991SDimitry Andric /// Total number of PC range bytes covered by DW_AT_locations with 1438bcb0991SDimitry Andric /// the debug entry values (DW_OP_entry_value) (only for local variables). 144349cc55cSDimitry Andric SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0; 1458bcb0991SDimitry Andric /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line). 146349cc55cSDimitry Andric SaturatingUINT64 CallSiteEntries = 0; 1478bcb0991SDimitry Andric /// Total number of call site DIEs (DW_TAG_call_site). 148349cc55cSDimitry Andric SaturatingUINT64 CallSiteDIEs = 0; 1498bcb0991SDimitry Andric /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter). 150349cc55cSDimitry Andric SaturatingUINT64 CallSiteParamDIEs = 0; 1510b57cec5SDimitry Andric /// Total byte size of concrete functions. This byte size includes 1520b57cec5SDimitry Andric /// inline functions contained in the concrete functions. 153349cc55cSDimitry Andric SaturatingUINT64 FunctionSize = 0; 1540b57cec5SDimitry Andric /// Total byte size of inlined functions. This is the total number of bytes 1550b57cec5SDimitry Andric /// for the top inline functions within concrete functions. This can help 1560b57cec5SDimitry Andric /// tune the inline settings when compiling to match user expectations. 157349cc55cSDimitry Andric SaturatingUINT64 InlineFunctionSize = 0; 1588bcb0991SDimitry Andric }; 1598bcb0991SDimitry Andric 1608bcb0991SDimitry Andric /// Holds accumulated debug location statistics about local variables and 1618bcb0991SDimitry Andric /// formal parameters. 1628bcb0991SDimitry Andric struct LocationStats { 1638bcb0991SDimitry Andric /// Map the scope coverage decile to the number of variables in the decile. 1648bcb0991SDimitry Andric /// The first element of the array (at the index zero) represents the number 1658bcb0991SDimitry Andric /// of variables with the no debug location at all, but the last element 1668bcb0991SDimitry Andric /// in the vector represents the number of fully covered variables within 1678bcb0991SDimitry Andric /// its scope. 168349cc55cSDimitry Andric std::vector<SaturatingUINT64> VarParamLocStats{ 169349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1708bcb0991SDimitry Andric /// Map non debug entry values coverage. 171349cc55cSDimitry Andric std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{ 172349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1738bcb0991SDimitry Andric /// The debug location statistics for formal parameters. 174349cc55cSDimitry Andric std::vector<SaturatingUINT64> ParamLocStats{ 175349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1768bcb0991SDimitry Andric /// Map non debug entry values coverage for formal parameters. 177349cc55cSDimitry Andric std::vector<SaturatingUINT64> ParamNonEntryValLocStats{ 178349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1798bcb0991SDimitry Andric /// The debug location statistics for local variables. 180349cc55cSDimitry Andric std::vector<SaturatingUINT64> LocalVarLocStats{ 181349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1828bcb0991SDimitry Andric /// Map non debug entry values coverage for local variables. 183349cc55cSDimitry Andric std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{ 184349cc55cSDimitry Andric std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)}; 1858bcb0991SDimitry Andric /// Total number of local variables and function parameters processed. 186349cc55cSDimitry Andric SaturatingUINT64 NumVarParam = 0; 1878bcb0991SDimitry Andric /// Total number of formal parameters processed. 188349cc55cSDimitry Andric SaturatingUINT64 NumParam = 0; 1898bcb0991SDimitry Andric /// Total number of local variables processed. 190349cc55cSDimitry Andric SaturatingUINT64 NumVar = 0; 1910b57cec5SDimitry Andric }; 192*0fca6ea1SDimitry Andric 193*0fca6ea1SDimitry Andric /// Holds accumulated debug line statistics across all CUs. 194*0fca6ea1SDimitry Andric struct LineStats { 195*0fca6ea1SDimitry Andric SaturatingUINT64 NumBytes = 0; 196*0fca6ea1SDimitry Andric SaturatingUINT64 NumLineZeroBytes = 0; 197*0fca6ea1SDimitry Andric SaturatingUINT64 NumEntries = 0; 198*0fca6ea1SDimitry Andric SaturatingUINT64 NumIsStmtEntries = 0; 199*0fca6ea1SDimitry Andric SaturatingUINT64 NumUniqueEntries = 0; 200*0fca6ea1SDimitry Andric SaturatingUINT64 NumUniqueNonZeroEntries = 0; 201*0fca6ea1SDimitry Andric }; 2025ffd83dbSDimitry Andric } // namespace 2030b57cec5SDimitry Andric 2048bcb0991SDimitry Andric /// Collect debug location statistics for one DIE. 205e8d8bef9SDimitry Andric static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope, 206349cc55cSDimitry Andric std::vector<SaturatingUINT64> &VarParamLocStats, 207349cc55cSDimitry Andric std::vector<SaturatingUINT64> &ParamLocStats, 208349cc55cSDimitry Andric std::vector<SaturatingUINT64> &LocalVarLocStats, 2095ffd83dbSDimitry Andric bool IsParam, bool IsLocalVar) { 210e8d8bef9SDimitry Andric auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned { 2118bcb0991SDimitry Andric // No debug location at all for the variable. 212e8d8bef9SDimitry Andric if (ScopeBytesCovered == 0) 2138bcb0991SDimitry Andric return 0; 2148bcb0991SDimitry Andric // Fully covered variable within its scope. 215e8d8bef9SDimitry Andric if (ScopeBytesCovered >= BytesInScope) 2168bcb0991SDimitry Andric return NumOfCoverageCategories - 1; 2178bcb0991SDimitry Andric // Get covered range (e.g. 20%-29%). 218e8d8bef9SDimitry Andric unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope; 2198bcb0991SDimitry Andric LocBucket /= 10; 2208bcb0991SDimitry Andric return LocBucket + 1; 2218bcb0991SDimitry Andric }; 2228bcb0991SDimitry Andric 2238bcb0991SDimitry Andric unsigned CoverageBucket = getCoverageBucket(); 224fe6060f1SDimitry Andric 225349cc55cSDimitry Andric VarParamLocStats[CoverageBucket].Value++; 2268bcb0991SDimitry Andric if (IsParam) 227349cc55cSDimitry Andric ParamLocStats[CoverageBucket].Value++; 2288bcb0991SDimitry Andric else if (IsLocalVar) 229349cc55cSDimitry Andric LocalVarLocStats[CoverageBucket].Value++; 2305ffd83dbSDimitry Andric } 231fe6060f1SDimitry Andric 2325ffd83dbSDimitry Andric /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName 2335ffd83dbSDimitry Andric /// and DeclLine. The identifier aims to be unique for any unique entities, 2345ffd83dbSDimitry Andric /// but keeping the same among different instances of the same entity. 2355ffd83dbSDimitry Andric static std::string constructDieID(DWARFDie Die, 2365ffd83dbSDimitry Andric StringRef Prefix = StringRef()) { 2375ffd83dbSDimitry Andric std::string IDStr; 2385ffd83dbSDimitry Andric llvm::raw_string_ostream ID(IDStr); 2395ffd83dbSDimitry Andric ID << Prefix 2405ffd83dbSDimitry Andric << Die.getName(DINameKind::LinkageName); 2415ffd83dbSDimitry Andric 2425ffd83dbSDimitry Andric // Prefix + Name is enough for local variables and parameters. 243*0fca6ea1SDimitry Andric if (!Prefix.empty() && Prefix != "g") 2445ffd83dbSDimitry Andric return ID.str(); 2455ffd83dbSDimitry Andric 2465ffd83dbSDimitry Andric auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file); 2475ffd83dbSDimitry Andric std::string File; 2485ffd83dbSDimitry Andric if (DeclFile) { 2495ffd83dbSDimitry Andric DWARFUnit *U = Die.getDwarfUnit(); 2505ffd83dbSDimitry Andric if (const auto *LT = U->getContext().getLineTableForUnit(U)) 2515ffd83dbSDimitry Andric if (LT->getFileNameByIndex( 2525ffd83dbSDimitry Andric dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(), 2535ffd83dbSDimitry Andric DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) 2545ffd83dbSDimitry Andric File = std::string(sys::path::filename(File)); 2555ffd83dbSDimitry Andric } 2565ffd83dbSDimitry Andric ID << ":" << (File.empty() ? "/" : File); 2575ffd83dbSDimitry Andric ID << ":" 2585ffd83dbSDimitry Andric << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0); 2595ffd83dbSDimitry Andric return ID.str(); 2608bcb0991SDimitry Andric } 2618bcb0991SDimitry Andric 262e8d8bef9SDimitry Andric /// Return the number of bytes in the overlap of ranges A and B. 263e8d8bef9SDimitry Andric static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) { 264e8d8bef9SDimitry Andric uint64_t Lower = std::max(A.LowPC, B.LowPC); 265e8d8bef9SDimitry Andric uint64_t Upper = std::min(A.HighPC, B.HighPC); 266e8d8bef9SDimitry Andric if (Lower >= Upper) 267e8d8bef9SDimitry Andric return 0; 268e8d8bef9SDimitry Andric return Upper - Lower; 269e8d8bef9SDimitry Andric } 270e8d8bef9SDimitry Andric 2710b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE. 272fe6060f1SDimitry Andric static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix, 273fe6060f1SDimitry Andric const std::string &VarPrefix, 274fe6060f1SDimitry Andric uint64_t BytesInScope, uint32_t InlineDepth, 2750b57cec5SDimitry Andric StringMap<PerFunctionStats> &FnStatMap, 2768bcb0991SDimitry Andric GlobalStats &GlobalStats, 277fe6060f1SDimitry Andric LocationStats &LocStats, 278fe6060f1SDimitry Andric AbstractOriginVarsTy *AbstractOriginVariables) { 279fe6060f1SDimitry Andric const dwarf::Tag Tag = Die.getTag(); 280fe6060f1SDimitry Andric // Skip CU node. 281fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_compile_unit) 282fe6060f1SDimitry Andric return; 283fe6060f1SDimitry Andric 2840b57cec5SDimitry Andric bool HasLoc = false; 2850b57cec5SDimitry Andric bool HasSrcLoc = false; 2860b57cec5SDimitry Andric bool HasType = false; 287e8d8bef9SDimitry Andric uint64_t TotalBytesCovered = 0; 288e8d8bef9SDimitry Andric uint64_t ScopeBytesCovered = 0; 2898bcb0991SDimitry Andric uint64_t BytesEntryValuesCovered = 0; 2908bcb0991SDimitry Andric auto &FnStats = FnStatMap[FnPrefix]; 291fe6060f1SDimitry Andric bool IsParam = Tag == dwarf::DW_TAG_formal_parameter; 292fe6060f1SDimitry Andric bool IsLocalVar = Tag == dwarf::DW_TAG_variable; 293fe6060f1SDimitry Andric bool IsConstantMember = Tag == dwarf::DW_TAG_member && 2945ffd83dbSDimitry Andric Die.find(dwarf::DW_AT_const_value); 2950b57cec5SDimitry Andric 296fe6060f1SDimitry Andric // For zero covered inlined variables the locstats will be 297fe6060f1SDimitry Andric // calculated later. 298fe6060f1SDimitry Andric bool DeferLocStats = false; 299fe6060f1SDimitry Andric 300fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) { 3018bcb0991SDimitry Andric GlobalStats.CallSiteDIEs++; 3020b57cec5SDimitry Andric return; 3030b57cec5SDimitry Andric } 3040b57cec5SDimitry Andric 305fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_call_site_parameter || 306fe6060f1SDimitry Andric Tag == dwarf::DW_TAG_GNU_call_site_parameter) { 3078bcb0991SDimitry Andric GlobalStats.CallSiteParamDIEs++; 3088bcb0991SDimitry Andric return; 3098bcb0991SDimitry Andric } 3108bcb0991SDimitry Andric 3115ffd83dbSDimitry Andric if (!IsParam && !IsLocalVar && !IsConstantMember) { 3120b57cec5SDimitry Andric // Not a variable or constant member. 3130b57cec5SDimitry Andric return; 3140b57cec5SDimitry Andric } 3150b57cec5SDimitry Andric 3165ffd83dbSDimitry Andric // Ignore declarations of global variables. 3175ffd83dbSDimitry Andric if (IsLocalVar && Die.find(dwarf::DW_AT_declaration)) 3185ffd83dbSDimitry Andric return; 3195ffd83dbSDimitry Andric 3200b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_decl_file) && 3210b57cec5SDimitry Andric Die.findRecursively(dwarf::DW_AT_decl_line)) 3220b57cec5SDimitry Andric HasSrcLoc = true; 3230b57cec5SDimitry Andric 3240b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_type)) 3250b57cec5SDimitry Andric HasType = true; 3260b57cec5SDimitry Andric 327fe6060f1SDimitry Andric if (Die.find(dwarf::DW_AT_abstract_origin)) { 328fe6060f1SDimitry Andric if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) { 329fe6060f1SDimitry Andric if (AbstractOriginVariables) { 330fe6060f1SDimitry Andric auto Offset = Die.find(dwarf::DW_AT_abstract_origin); 331fe6060f1SDimitry Andric // Do not track this variable any more, since it has location 332fe6060f1SDimitry Andric // coverage. 3335f757f3fSDimitry Andric llvm::erase(*AbstractOriginVariables, (*Offset).getRawUValue()); 334fe6060f1SDimitry Andric } 335fe6060f1SDimitry Andric } else { 336fe6060f1SDimitry Andric // The locstats will be handled at the end of 337fe6060f1SDimitry Andric // the collectStatsRecursive(). 338fe6060f1SDimitry Andric DeferLocStats = true; 339fe6060f1SDimitry Andric } 340fe6060f1SDimitry Andric } 341fe6060f1SDimitry Andric 3428bcb0991SDimitry Andric auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { 3438bcb0991SDimitry Andric DWARFUnit *U = Die.getDwarfUnit(); 3448bcb0991SDimitry Andric DataExtractor Data(toStringRef(D), 3458bcb0991SDimitry Andric Die.getDwarfUnit()->getContext().isLittleEndian(), 0); 3465ffd83dbSDimitry Andric DWARFExpression Expression(Data, U->getAddressByteSize(), 3475ffd83dbSDimitry Andric U->getFormParams().Format); 3488bcb0991SDimitry Andric // Consider the expression containing the DW_OP_entry_value as 3498bcb0991SDimitry Andric // an entry value. 350349cc55cSDimitry Andric return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) { 3518bcb0991SDimitry Andric return Op.getCode() == dwarf::DW_OP_entry_value || 3528bcb0991SDimitry Andric Op.getCode() == dwarf::DW_OP_GNU_entry_value; 3538bcb0991SDimitry Andric }); 3548bcb0991SDimitry Andric }; 3558bcb0991SDimitry Andric 3560b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_const_value)) { 3570b57cec5SDimitry Andric // This catches constant members *and* variables. 3580b57cec5SDimitry Andric HasLoc = true; 359e8d8bef9SDimitry Andric ScopeBytesCovered = BytesInScope; 360e8d8bef9SDimitry Andric TotalBytesCovered = BytesInScope; 3610b57cec5SDimitry Andric } else { 3620b57cec5SDimitry Andric // Handle variables and function arguments. 363480093f4SDimitry Andric Expected<std::vector<DWARFLocationExpression>> Loc = 364480093f4SDimitry Andric Die.getLocations(dwarf::DW_AT_location); 365480093f4SDimitry Andric if (!Loc) { 366480093f4SDimitry Andric consumeError(Loc.takeError()); 3670b57cec5SDimitry Andric } else { 368480093f4SDimitry Andric HasLoc = true; 369480093f4SDimitry Andric // Get PC coverage. 370480093f4SDimitry Andric auto Default = find_if( 371480093f4SDimitry Andric *Loc, [](const DWARFLocationExpression &L) { return !L.Range; }); 372480093f4SDimitry Andric if (Default != Loc->end()) { 3730b57cec5SDimitry Andric // Assume the entire range is covered by a single location. 374e8d8bef9SDimitry Andric ScopeBytesCovered = BytesInScope; 375e8d8bef9SDimitry Andric TotalBytesCovered = BytesInScope; 376480093f4SDimitry Andric } else { 377e8d8bef9SDimitry Andric // Caller checks this Expected result already, it cannot fail. 378e8d8bef9SDimitry Andric auto ScopeRanges = cantFail(Die.getParent().getAddressRanges()); 379480093f4SDimitry Andric for (auto Entry : *Loc) { 380e8d8bef9SDimitry Andric TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC; 381e8d8bef9SDimitry Andric uint64_t ScopeBytesCoveredByEntry = 0; 382e8d8bef9SDimitry Andric // Calculate how many bytes of the parent scope this entry covers. 383e8d8bef9SDimitry Andric // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The 384e8d8bef9SDimitry Andric // address ranges defined by the bounded location descriptions of a 385e8d8bef9SDimitry Andric // location list may overlap". So in theory a variable can have 386e8d8bef9SDimitry Andric // multiple simultaneous locations, which would make this calculation 387e8d8bef9SDimitry Andric // misleading because we will count the overlapped areas 388e8d8bef9SDimitry Andric // twice. However, clang does not currently emit DWARF like this. 389e8d8bef9SDimitry Andric for (DWARFAddressRange R : ScopeRanges) { 390e8d8bef9SDimitry Andric ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R); 391e8d8bef9SDimitry Andric } 392e8d8bef9SDimitry Andric ScopeBytesCovered += ScopeBytesCoveredByEntry; 393480093f4SDimitry Andric if (IsEntryValue(Entry.Expr)) 394e8d8bef9SDimitry Andric BytesEntryValuesCovered += ScopeBytesCoveredByEntry; 395480093f4SDimitry Andric } 3960b57cec5SDimitry Andric } 3970b57cec5SDimitry Andric } 3980b57cec5SDimitry Andric } 3990b57cec5SDimitry Andric 4008bcb0991SDimitry Andric // Calculate the debug location statistics. 401fe6060f1SDimitry Andric if (BytesInScope && !DeferLocStats) { 402349cc55cSDimitry Andric LocStats.NumVarParam.Value++; 4038bcb0991SDimitry Andric if (IsParam) 404349cc55cSDimitry Andric LocStats.NumParam.Value++; 4058bcb0991SDimitry Andric else if (IsLocalVar) 406349cc55cSDimitry Andric LocStats.NumVar.Value++; 4078bcb0991SDimitry Andric 408e8d8bef9SDimitry Andric collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats, 4095ffd83dbSDimitry Andric LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam, 4108bcb0991SDimitry Andric IsLocalVar); 4118bcb0991SDimitry Andric // Non debug entry values coverage statistics. 412e8d8bef9SDimitry Andric collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope, 4138bcb0991SDimitry Andric LocStats.VarParamNonEntryValLocStats, 4148bcb0991SDimitry Andric LocStats.ParamNonEntryValLocStats, 4155ffd83dbSDimitry Andric LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar); 4168bcb0991SDimitry Andric } 4178bcb0991SDimitry Andric 4180b57cec5SDimitry Andric // Collect PC range coverage data. 4190b57cec5SDimitry Andric if (DWARFDie D = 4200b57cec5SDimitry Andric Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) 4210b57cec5SDimitry Andric Die = D; 4225ffd83dbSDimitry Andric 4235ffd83dbSDimitry Andric std::string VarID = constructDieID(Die, VarPrefix); 4245ffd83dbSDimitry Andric FnStats.VarsInFunction.insert(VarID); 4255ffd83dbSDimitry Andric 426e8d8bef9SDimitry Andric GlobalStats.TotalBytesCovered += TotalBytesCovered; 4270b57cec5SDimitry Andric if (BytesInScope) { 428e8d8bef9SDimitry Andric GlobalStats.ScopeBytesCovered += ScopeBytesCovered; 429480093f4SDimitry Andric GlobalStats.ScopeBytes += BytesInScope; 4308bcb0991SDimitry Andric GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered; 4318bcb0991SDimitry Andric if (IsParam) { 432e8d8bef9SDimitry Andric GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered; 433480093f4SDimitry Andric GlobalStats.ParamScopeBytes += BytesInScope; 4348bcb0991SDimitry Andric GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; 4358bcb0991SDimitry Andric } else if (IsLocalVar) { 436e8d8bef9SDimitry Andric GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered; 4375ffd83dbSDimitry Andric GlobalStats.LocalVarScopeBytes += BytesInScope; 4385ffd83dbSDimitry Andric GlobalStats.LocalVarScopeEntryValueBytesCovered += 4395ffd83dbSDimitry Andric BytesEntryValuesCovered; 4408bcb0991SDimitry Andric } 441349cc55cSDimitry Andric assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value); 4420b57cec5SDimitry Andric } 4435ffd83dbSDimitry Andric 4445ffd83dbSDimitry Andric if (IsConstantMember) { 4455ffd83dbSDimitry Andric FnStats.ConstantMembers++; 4465ffd83dbSDimitry Andric return; 4475ffd83dbSDimitry Andric } 4485ffd83dbSDimitry Andric 4495ffd83dbSDimitry Andric FnStats.TotalVarWithLoc += (unsigned)HasLoc; 4505ffd83dbSDimitry Andric 4515ffd83dbSDimitry Andric if (Die.find(dwarf::DW_AT_artificial)) { 4525ffd83dbSDimitry Andric FnStats.NumArtificial++; 4535ffd83dbSDimitry Andric return; 4545ffd83dbSDimitry Andric } 4555ffd83dbSDimitry Andric 4568bcb0991SDimitry Andric if (IsParam) { 4570b57cec5SDimitry Andric FnStats.NumParams++; 4580b57cec5SDimitry Andric if (HasType) 4590b57cec5SDimitry Andric FnStats.NumParamTypes++; 4600b57cec5SDimitry Andric if (HasSrcLoc) 4610b57cec5SDimitry Andric FnStats.NumParamSourceLocations++; 4620b57cec5SDimitry Andric if (HasLoc) 4630b57cec5SDimitry Andric FnStats.NumParamLocations++; 4648bcb0991SDimitry Andric } else if (IsLocalVar) { 4655ffd83dbSDimitry Andric FnStats.NumLocalVars++; 4660b57cec5SDimitry Andric if (HasType) 4675ffd83dbSDimitry Andric FnStats.NumLocalVarTypes++; 4680b57cec5SDimitry Andric if (HasSrcLoc) 4695ffd83dbSDimitry Andric FnStats.NumLocalVarSourceLocations++; 4700b57cec5SDimitry Andric if (HasLoc) 4715ffd83dbSDimitry Andric FnStats.NumLocalVarLocations++; 4720b57cec5SDimitry Andric } 4730b57cec5SDimitry Andric } 4740b57cec5SDimitry Andric 475fe6060f1SDimitry Andric /// Recursively collect variables from subprogram with DW_AT_inline attribute. 476fe6060f1SDimitry Andric static void collectAbstractOriginFnInfo( 477fe6060f1SDimitry Andric DWARFDie Die, uint64_t SPOffset, 4784824e7fdSDimitry Andric AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, 4794824e7fdSDimitry Andric AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) { 480fe6060f1SDimitry Andric DWARFDie Child = Die.getFirstChild(); 481fe6060f1SDimitry Andric while (Child) { 482fe6060f1SDimitry Andric const dwarf::Tag ChildTag = Child.getTag(); 483fe6060f1SDimitry Andric if (ChildTag == dwarf::DW_TAG_formal_parameter || 4844824e7fdSDimitry Andric ChildTag == dwarf::DW_TAG_variable) { 485fe6060f1SDimitry Andric GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset()); 4864824e7fdSDimitry Andric LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset()); 4874824e7fdSDimitry Andric } else if (ChildTag == dwarf::DW_TAG_lexical_block) 4884824e7fdSDimitry Andric collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo, 4894824e7fdSDimitry Andric LocalAbstractOriginFnInfo); 490fe6060f1SDimitry Andric Child = Child.getSibling(); 491fe6060f1SDimitry Andric } 492fe6060f1SDimitry Andric } 493fe6060f1SDimitry Andric 4940b57cec5SDimitry Andric /// Recursively collect debug info quality metrics. 495fe6060f1SDimitry Andric static void collectStatsRecursive( 496fe6060f1SDimitry Andric DWARFDie Die, std::string FnPrefix, std::string VarPrefix, 497fe6060f1SDimitry Andric uint64_t BytesInScope, uint32_t InlineDepth, 498fe6060f1SDimitry Andric StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats, 4994824e7fdSDimitry Andric LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs, 500fe6060f1SDimitry Andric AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, 5014824e7fdSDimitry Andric AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo, 502fe6060f1SDimitry Andric FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed, 503fe6060f1SDimitry Andric AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) { 504fe6060f1SDimitry Andric // Skip NULL nodes. 505fe6060f1SDimitry Andric if (Die.isNULL()) 506fe6060f1SDimitry Andric return; 507fe6060f1SDimitry Andric 5080b57cec5SDimitry Andric const dwarf::Tag Tag = Die.getTag(); 5095ffd83dbSDimitry Andric // Skip function types. 5105ffd83dbSDimitry Andric if (Tag == dwarf::DW_TAG_subroutine_type) 5115ffd83dbSDimitry Andric return; 5125ffd83dbSDimitry Andric 5135ffd83dbSDimitry Andric // Handle any kind of lexical scope. 514bdd1243dSDimitry Andric const bool HasAbstractOrigin = 515bdd1243dSDimitry Andric Die.find(dwarf::DW_AT_abstract_origin) != std::nullopt; 5160b57cec5SDimitry Andric const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; 5170b57cec5SDimitry Andric const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; 5180b57cec5SDimitry Andric const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; 519fe6060f1SDimitry Andric // We want to know how many variables (with abstract_origin) don't have 520fe6060f1SDimitry Andric // location info. 521fe6060f1SDimitry Andric const bool IsCandidateForZeroLocCovTracking = 522fe6060f1SDimitry Andric (IsInlinedFunction || (IsFunction && HasAbstractOrigin)); 5230b57cec5SDimitry Andric 524fe6060f1SDimitry Andric AbstractOriginVarsTy AbstractOriginVars; 525fe6060f1SDimitry Andric 526fe6060f1SDimitry Andric // Get the vars of the inlined fn, so the locstats 527fe6060f1SDimitry Andric // reports the missing vars (with coverage 0%). 528fe6060f1SDimitry Andric if (IsCandidateForZeroLocCovTracking) { 529fe6060f1SDimitry Andric auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin); 530fe6060f1SDimitry Andric if (OffsetFn) { 531fe6060f1SDimitry Andric uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue(); 5324824e7fdSDimitry Andric if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) { 5334824e7fdSDimitry Andric AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy]; 534fe6060f1SDimitry Andric AbstractOriginVarsPtr = &AbstractOriginVars; 535fe6060f1SDimitry Andric } else { 5364824e7fdSDimitry Andric // This means that the DW_AT_inline fn copy is out of order 5374824e7fdSDimitry Andric // or that the abstract_origin references another CU, 538fe6060f1SDimitry Andric // so this abstract origin instance will be processed later. 539fe6060f1SDimitry Andric FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset()); 540fe6060f1SDimitry Andric AbstractOriginVarsPtr = nullptr; 541fe6060f1SDimitry Andric } 542fe6060f1SDimitry Andric } 543fe6060f1SDimitry Andric } 544fe6060f1SDimitry Andric 545fe6060f1SDimitry Andric if (IsFunction || IsInlinedFunction || IsBlock) { 5460b57cec5SDimitry Andric // Reset VarPrefix when entering a new function. 547fe6060f1SDimitry Andric if (IsFunction || IsInlinedFunction) 5480b57cec5SDimitry Andric VarPrefix = "v"; 5490b57cec5SDimitry Andric 5500b57cec5SDimitry Andric // Ignore forward declarations. 5510b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_declaration)) 5520b57cec5SDimitry Andric return; 5530b57cec5SDimitry Andric 5540b57cec5SDimitry Andric // Check for call sites. 5550b57cec5SDimitry Andric if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line)) 5560b57cec5SDimitry Andric GlobalStats.CallSiteEntries++; 5570b57cec5SDimitry Andric 5580b57cec5SDimitry Andric // PC Ranges. 5590b57cec5SDimitry Andric auto RangesOrError = Die.getAddressRanges(); 5600b57cec5SDimitry Andric if (!RangesOrError) { 5610b57cec5SDimitry Andric llvm::consumeError(RangesOrError.takeError()); 5620b57cec5SDimitry Andric return; 5630b57cec5SDimitry Andric } 5640b57cec5SDimitry Andric 5650b57cec5SDimitry Andric auto Ranges = RangesOrError.get(); 5660b57cec5SDimitry Andric uint64_t BytesInThisScope = 0; 5670b57cec5SDimitry Andric for (auto Range : Ranges) 5680b57cec5SDimitry Andric BytesInThisScope += Range.HighPC - Range.LowPC; 5690b57cec5SDimitry Andric 5700b57cec5SDimitry Andric // Count the function. 5710b57cec5SDimitry Andric if (!IsBlock) { 572fe6060f1SDimitry Andric // Skip over abstract origins, but collect variables 573fe6060f1SDimitry Andric // from it so it can be used for location statistics 574fe6060f1SDimitry Andric // for inlined instancies. 575fe6060f1SDimitry Andric if (Die.find(dwarf::DW_AT_inline)) { 576fe6060f1SDimitry Andric uint64_t SPOffset = Die.getOffset(); 5774824e7fdSDimitry Andric AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit(); 5784824e7fdSDimitry Andric collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo, 5794824e7fdSDimitry Andric LocalAbstractOriginFnInfo); 5800b57cec5SDimitry Andric return; 581fe6060f1SDimitry Andric } 582fe6060f1SDimitry Andric 5835ffd83dbSDimitry Andric std::string FnID = constructDieID(Die); 5845ffd83dbSDimitry Andric // We've seen an instance of this function. 5855ffd83dbSDimitry Andric auto &FnStats = FnStatMap[FnID]; 5865ffd83dbSDimitry Andric FnStats.IsFunction = true; 5870b57cec5SDimitry Andric if (IsInlinedFunction) { 5880b57cec5SDimitry Andric FnStats.NumFnInlined++; 5890b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) 5900b57cec5SDimitry Andric FnStats.NumAbstractOrigins++; 5915ffd83dbSDimitry Andric } else { 5925ffd83dbSDimitry Andric FnStats.NumFnOutOfLine++; 5930b57cec5SDimitry Andric } 5940b57cec5SDimitry Andric if (Die.findRecursively(dwarf::DW_AT_decl_file) && 5950b57cec5SDimitry Andric Die.findRecursively(dwarf::DW_AT_decl_line)) 5960b57cec5SDimitry Andric FnStats.HasSourceLocation = true; 5975ffd83dbSDimitry Andric // Update function prefix. 5985ffd83dbSDimitry Andric FnPrefix = FnID; 5990b57cec5SDimitry Andric } 6000b57cec5SDimitry Andric 6010b57cec5SDimitry Andric if (BytesInThisScope) { 6020b57cec5SDimitry Andric BytesInScope = BytesInThisScope; 6030b57cec5SDimitry Andric if (IsFunction) 6040b57cec5SDimitry Andric GlobalStats.FunctionSize += BytesInThisScope; 6050b57cec5SDimitry Andric else if (IsInlinedFunction && InlineDepth == 0) 6060b57cec5SDimitry Andric GlobalStats.InlineFunctionSize += BytesInThisScope; 6070b57cec5SDimitry Andric } 6080b57cec5SDimitry Andric } else { 6090b57cec5SDimitry Andric // Not a scope, visit the Die itself. It could be a variable. 610480093f4SDimitry Andric collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, 611fe6060f1SDimitry Andric FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr); 6120b57cec5SDimitry Andric } 6130b57cec5SDimitry Andric 6140b57cec5SDimitry Andric // Set InlineDepth correctly for child recursion 6150b57cec5SDimitry Andric if (IsFunction) 6160b57cec5SDimitry Andric InlineDepth = 0; 6170b57cec5SDimitry Andric else if (IsInlinedFunction) 6180b57cec5SDimitry Andric ++InlineDepth; 6190b57cec5SDimitry Andric 6200b57cec5SDimitry Andric // Traverse children. 6210b57cec5SDimitry Andric unsigned LexicalBlockIndex = 0; 6225ffd83dbSDimitry Andric unsigned FormalParameterIndex = 0; 6230b57cec5SDimitry Andric DWARFDie Child = Die.getFirstChild(); 6240b57cec5SDimitry Andric while (Child) { 6250b57cec5SDimitry Andric std::string ChildVarPrefix = VarPrefix; 6260b57cec5SDimitry Andric if (Child.getTag() == dwarf::DW_TAG_lexical_block) 6270b57cec5SDimitry Andric ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; 6285ffd83dbSDimitry Andric if (Child.getTag() == dwarf::DW_TAG_formal_parameter) 6295ffd83dbSDimitry Andric ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; 6300b57cec5SDimitry Andric 631fe6060f1SDimitry Andric collectStatsRecursive( 632fe6060f1SDimitry Andric Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap, 6334824e7fdSDimitry Andric GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo, 6344824e7fdSDimitry Andric LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed, 6354824e7fdSDimitry Andric AbstractOriginVarsPtr); 6360b57cec5SDimitry Andric Child = Child.getSibling(); 6370b57cec5SDimitry Andric } 638fe6060f1SDimitry Andric 639fe6060f1SDimitry Andric if (!IsCandidateForZeroLocCovTracking) 640fe6060f1SDimitry Andric return; 641fe6060f1SDimitry Andric 642fe6060f1SDimitry Andric // After we have processed all vars of the inlined function (or function with 643fe6060f1SDimitry Andric // an abstract_origin), we want to know how many variables have no location. 644fe6060f1SDimitry Andric for (auto Offset : AbstractOriginVars) { 645fe6060f1SDimitry Andric LocStats.NumVarParam++; 646fe6060f1SDimitry Andric LocStats.VarParamLocStats[ZeroCoverageBucket]++; 647fe6060f1SDimitry Andric auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset); 648fe6060f1SDimitry Andric if (!FnDie) 649fe6060f1SDimitry Andric continue; 650fe6060f1SDimitry Andric auto Tag = FnDie.getTag(); 651fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_formal_parameter) { 652fe6060f1SDimitry Andric LocStats.NumParam++; 653fe6060f1SDimitry Andric LocStats.ParamLocStats[ZeroCoverageBucket]++; 654fe6060f1SDimitry Andric } else if (Tag == dwarf::DW_TAG_variable) { 655fe6060f1SDimitry Andric LocStats.NumVar++; 656fe6060f1SDimitry Andric LocStats.LocalVarLocStats[ZeroCoverageBucket]++; 657fe6060f1SDimitry Andric } 658fe6060f1SDimitry Andric } 6590b57cec5SDimitry Andric } 6600b57cec5SDimitry Andric 661e8d8bef9SDimitry Andric /// Print human-readable output. 6620b57cec5SDimitry Andric /// \{ 663e8d8bef9SDimitry Andric static void printDatum(json::OStream &J, const char *Key, json::Value Value) { 664349cc55cSDimitry Andric if (Value == OverflowValue) 665349cc55cSDimitry Andric J.attribute(Key, "overflowed"); 666349cc55cSDimitry Andric else 667e8d8bef9SDimitry Andric J.attribute(Key, Value); 668349cc55cSDimitry Andric 6690b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); 6700b57cec5SDimitry Andric } 6715ffd83dbSDimitry Andric 672e8d8bef9SDimitry Andric static void printLocationStats(json::OStream &J, const char *Key, 673349cc55cSDimitry Andric std::vector<SaturatingUINT64> &LocationStats) { 674349cc55cSDimitry Andric if (LocationStats[0].Value == OverflowValue) 675349cc55cSDimitry Andric J.attribute((Twine(Key) + 676349cc55cSDimitry Andric " with (0%,10%) of parent scope covered by DW_AT_location") 677349cc55cSDimitry Andric .str(), 678349cc55cSDimitry Andric "overflowed"); 679349cc55cSDimitry Andric else 680e8d8bef9SDimitry Andric J.attribute( 681349cc55cSDimitry Andric (Twine(Key) + " with 0% of parent scope covered by DW_AT_location") 682349cc55cSDimitry Andric .str(), 683349cc55cSDimitry Andric LocationStats[0].Value); 6845ffd83dbSDimitry Andric LLVM_DEBUG( 6855ffd83dbSDimitry Andric llvm::dbgs() << Key 6865ffd83dbSDimitry Andric << " with 0% of parent scope covered by DW_AT_location: \\" 687349cc55cSDimitry Andric << LocationStats[0].Value << '\n'); 688349cc55cSDimitry Andric 689349cc55cSDimitry Andric if (LocationStats[1].Value == OverflowValue) 690349cc55cSDimitry Andric J.attribute((Twine(Key) + 691349cc55cSDimitry Andric " with (0%,10%) of parent scope covered by DW_AT_location") 692e8d8bef9SDimitry Andric .str(), 693349cc55cSDimitry Andric "overflowed"); 694349cc55cSDimitry Andric else 695349cc55cSDimitry Andric J.attribute((Twine(Key) + 696349cc55cSDimitry Andric " with (0%,10%) of parent scope covered by DW_AT_location") 697349cc55cSDimitry Andric .str(), 698349cc55cSDimitry Andric LocationStats[1].Value); 6995ffd83dbSDimitry Andric LLVM_DEBUG(llvm::dbgs() 7005ffd83dbSDimitry Andric << Key 7015ffd83dbSDimitry Andric << " with (0%,10%) of parent scope covered by DW_AT_location: " 702349cc55cSDimitry Andric << LocationStats[1].Value << '\n'); 703349cc55cSDimitry Andric 7048bcb0991SDimitry Andric for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { 705349cc55cSDimitry Andric if (LocationStats[i].Value == OverflowValue) 706e8d8bef9SDimitry Andric J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," + 707349cc55cSDimitry Andric Twine(i * 10) + 708349cc55cSDimitry Andric "%) of parent scope covered by DW_AT_location") 709e8d8bef9SDimitry Andric .str(), 710349cc55cSDimitry Andric "overflowed"); 711349cc55cSDimitry Andric else 712349cc55cSDimitry Andric J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," + 713349cc55cSDimitry Andric Twine(i * 10) + 714349cc55cSDimitry Andric "%) of parent scope covered by DW_AT_location") 715349cc55cSDimitry Andric .str(), 716349cc55cSDimitry Andric LocationStats[i].Value); 7178bcb0991SDimitry Andric LLVM_DEBUG(llvm::dbgs() 718480093f4SDimitry Andric << Key << " with [" << (i - 1) * 10 << "%," << i * 10 7195ffd83dbSDimitry Andric << "%) of parent scope covered by DW_AT_location: " 720349cc55cSDimitry Andric << LocationStats[i].Value); 7218bcb0991SDimitry Andric } 722349cc55cSDimitry Andric if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue) 723e8d8bef9SDimitry Andric J.attribute( 724e8d8bef9SDimitry Andric (Twine(Key) + " with 100% of parent scope covered by DW_AT_location") 725e8d8bef9SDimitry Andric .str(), 726349cc55cSDimitry Andric "overflowed"); 727349cc55cSDimitry Andric else 728349cc55cSDimitry Andric J.attribute( 729349cc55cSDimitry Andric (Twine(Key) + " with 100% of parent scope covered by DW_AT_location") 730349cc55cSDimitry Andric .str(), 731349cc55cSDimitry Andric LocationStats[NumOfCoverageCategories - 1].Value); 7325ffd83dbSDimitry Andric LLVM_DEBUG( 7335ffd83dbSDimitry Andric llvm::dbgs() << Key 7345ffd83dbSDimitry Andric << " with 100% of parent scope covered by DW_AT_location: " 735349cc55cSDimitry Andric << LocationStats[NumOfCoverageCategories - 1].Value); 7368bcb0991SDimitry Andric } 7375ffd83dbSDimitry Andric 738e8d8bef9SDimitry Andric static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) { 739fe6060f1SDimitry Andric for (const auto &It : Sizes.DebugSectionSizes) 740fe6060f1SDimitry Andric J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second)); 741fe6060f1SDimitry Andric } 742fe6060f1SDimitry Andric 743fe6060f1SDimitry Andric /// Stop tracking variables that contain abstract_origin with a location. 744fe6060f1SDimitry Andric /// This is used for out-of-order DW_AT_inline subprograms only. 745fe6060f1SDimitry Andric static void updateVarsWithAbstractOriginLocCovInfo( 746fe6060f1SDimitry Andric DWARFDie FnDieWithAbstractOrigin, 747fe6060f1SDimitry Andric AbstractOriginVarsTy &AbstractOriginVars) { 748fe6060f1SDimitry Andric DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild(); 749fe6060f1SDimitry Andric while (Child) { 750fe6060f1SDimitry Andric const dwarf::Tag ChildTag = Child.getTag(); 751fe6060f1SDimitry Andric if ((ChildTag == dwarf::DW_TAG_formal_parameter || 752fe6060f1SDimitry Andric ChildTag == dwarf::DW_TAG_variable) && 753fe6060f1SDimitry Andric (Child.find(dwarf::DW_AT_location) || 754fe6060f1SDimitry Andric Child.find(dwarf::DW_AT_const_value))) { 755fe6060f1SDimitry Andric auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin); 756fe6060f1SDimitry Andric if (OffsetVar) 7575f757f3fSDimitry Andric llvm::erase(AbstractOriginVars, (*OffsetVar).getRawUValue()); 758fe6060f1SDimitry Andric } else if (ChildTag == dwarf::DW_TAG_lexical_block) 759fe6060f1SDimitry Andric updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars); 760fe6060f1SDimitry Andric Child = Child.getSibling(); 761fe6060f1SDimitry Andric } 762fe6060f1SDimitry Andric } 763fe6060f1SDimitry Andric 764fe6060f1SDimitry Andric /// Collect zero location coverage for inlined variables which refer to 765fe6060f1SDimitry Andric /// a DW_AT_inline copy of subprogram that is out of order in the DWARF. 766fe6060f1SDimitry Andric /// Also cover the variables of a concrete function (represented with 767fe6060f1SDimitry Andric /// the DW_TAG_subprogram) with an abstract_origin attribute. 768fe6060f1SDimitry Andric static void collectZeroLocCovForVarsWithAbstractOrigin( 769fe6060f1SDimitry Andric DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats, 7704824e7fdSDimitry Andric AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo, 771fe6060f1SDimitry Andric FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) { 7724824e7fdSDimitry Andric // The next variable is used to filter out functions that have been processed, 7734824e7fdSDimitry Andric // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references. 7744824e7fdSDimitry Andric FunctionsWithAbstractOriginTy ProcessedFns; 775fe6060f1SDimitry Andric for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) { 776fe6060f1SDimitry Andric DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset); 777fe6060f1SDimitry Andric auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin); 778fe6060f1SDimitry Andric AbstractOriginVarsTy AbstractOriginVars; 779fe6060f1SDimitry Andric if (!FnCopy) 780fe6060f1SDimitry Andric continue; 7814824e7fdSDimitry Andric uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue(); 7824824e7fdSDimitry Andric // If there is no entry within LocalAbstractOriginFnInfo for the given 7834824e7fdSDimitry Andric // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have 7844824e7fdSDimitry Andric // CrossCU referencing. 7854824e7fdSDimitry Andric if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue)) 7864824e7fdSDimitry Andric continue; 7874824e7fdSDimitry Andric AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue]; 788fe6060f1SDimitry Andric updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin, 789fe6060f1SDimitry Andric AbstractOriginVars); 790fe6060f1SDimitry Andric 791fe6060f1SDimitry Andric for (auto Offset : AbstractOriginVars) { 792fe6060f1SDimitry Andric LocStats.NumVarParam++; 793fe6060f1SDimitry Andric LocStats.VarParamLocStats[ZeroCoverageBucket]++; 794fe6060f1SDimitry Andric auto Tag = DwUnit->getDIEForOffset(Offset).getTag(); 795fe6060f1SDimitry Andric if (Tag == dwarf::DW_TAG_formal_parameter) { 796fe6060f1SDimitry Andric LocStats.NumParam++; 797fe6060f1SDimitry Andric LocStats.ParamLocStats[ZeroCoverageBucket]++; 798fe6060f1SDimitry Andric } else if (Tag == dwarf::DW_TAG_variable) { 799fe6060f1SDimitry Andric LocStats.NumVar++; 800fe6060f1SDimitry Andric LocStats.LocalVarLocStats[ZeroCoverageBucket]++; 801fe6060f1SDimitry Andric } 802fe6060f1SDimitry Andric } 8034824e7fdSDimitry Andric ProcessedFns.push_back(FnOffset); 8044824e7fdSDimitry Andric } 8054824e7fdSDimitry Andric for (auto ProcessedFn : ProcessedFns) 8065f757f3fSDimitry Andric llvm::erase(FnsWithAbstractOriginToBeProcessed, ProcessedFn); 8074824e7fdSDimitry Andric } 8084824e7fdSDimitry Andric 8094824e7fdSDimitry Andric /// Collect zero location coverage for inlined variables which refer to 8104824e7fdSDimitry Andric /// a DW_AT_inline copy of subprogram that is in a different CU. 8114824e7fdSDimitry Andric static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin( 8124824e7fdSDimitry Andric LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs, 8134824e7fdSDimitry Andric AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, 8144824e7fdSDimitry Andric CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) { 8154824e7fdSDimitry Andric for (const auto &CrossCUReferenceToBeResolved : 8164824e7fdSDimitry Andric CrossCUReferencesToBeResolved) { 8174824e7fdSDimitry Andric DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit; 8184824e7fdSDimitry Andric DWARFDie FnDIEWithCrossCUReferencing = 8194824e7fdSDimitry Andric DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset); 8204824e7fdSDimitry Andric auto FnCopy = 8214824e7fdSDimitry Andric FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin); 8224824e7fdSDimitry Andric if (!FnCopy) 8234824e7fdSDimitry Andric continue; 8244824e7fdSDimitry Andric uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue(); 8254824e7fdSDimitry Andric AbstractOriginVarsTy AbstractOriginVars = 8264824e7fdSDimitry Andric GlobalAbstractOriginFnInfo[FnCopyRawUValue]; 8274824e7fdSDimitry Andric updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing, 8284824e7fdSDimitry Andric AbstractOriginVars); 8294824e7fdSDimitry Andric for (auto Offset : AbstractOriginVars) { 8304824e7fdSDimitry Andric LocStats.NumVarParam++; 8314824e7fdSDimitry Andric LocStats.VarParamLocStats[ZeroCoverageBucket]++; 8324824e7fdSDimitry Andric auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue]) 8334824e7fdSDimitry Andric ->getDIEForOffset(Offset) 8344824e7fdSDimitry Andric .getTag(); 8354824e7fdSDimitry Andric if (Tag == dwarf::DW_TAG_formal_parameter) { 8364824e7fdSDimitry Andric LocStats.NumParam++; 8374824e7fdSDimitry Andric LocStats.ParamLocStats[ZeroCoverageBucket]++; 8384824e7fdSDimitry Andric } else if (Tag == dwarf::DW_TAG_variable) { 8394824e7fdSDimitry Andric LocStats.NumVar++; 8404824e7fdSDimitry Andric LocStats.LocalVarLocStats[ZeroCoverageBucket]++; 8414824e7fdSDimitry Andric } 8424824e7fdSDimitry Andric } 843fe6060f1SDimitry Andric } 8445ffd83dbSDimitry Andric } 8455ffd83dbSDimitry Andric 8460b57cec5SDimitry Andric /// \} 8470b57cec5SDimitry Andric 8480b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext. 8490b57cec5SDimitry Andric /// 8500b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few 8510b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked 8520b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect 8530b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly 8540b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different 8550b57cec5SDimitry Andric /// compilers is. 8565ffd83dbSDimitry Andric bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 8575ffd83dbSDimitry Andric const Twine &Filename, 8585ffd83dbSDimitry Andric raw_ostream &OS) { 8590b57cec5SDimitry Andric StringRef FormatName = Obj.getFileFormatName(); 8600b57cec5SDimitry Andric GlobalStats GlobalStats; 8618bcb0991SDimitry Andric LocationStats LocStats; 862*0fca6ea1SDimitry Andric LineStats LnStats; 8630b57cec5SDimitry Andric StringMap<PerFunctionStats> Statistics; 8644824e7fdSDimitry Andric // This variable holds variable information for functions with 8654824e7fdSDimitry Andric // abstract_origin globally, across all CUs. 8664824e7fdSDimitry Andric AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo; 8674824e7fdSDimitry Andric // This variable holds information about the CU of a function with 8684824e7fdSDimitry Andric // abstract_origin. 8694824e7fdSDimitry Andric FunctionDIECUTyMap AbstractOriginFnCUs; 8704824e7fdSDimitry Andric CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved; 871*0fca6ea1SDimitry Andric // Tuple representing a single source code position in the line table. Fields 872*0fca6ea1SDimitry Andric // are respectively: Line, Col, File, where 'File' is an index into the Files 873*0fca6ea1SDimitry Andric // vector below. 874*0fca6ea1SDimitry Andric using LineTuple = std::tuple<uint32_t, uint16_t, uint16_t>; 875*0fca6ea1SDimitry Andric SmallVector<std::string> Files; 876*0fca6ea1SDimitry Andric DenseSet<LineTuple> UniqueLines; 877*0fca6ea1SDimitry Andric DenseSet<LineTuple> UniqueNonZeroLines; 878*0fca6ea1SDimitry Andric 879fe6060f1SDimitry Andric for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) { 880fe6060f1SDimitry Andric if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) { 8814824e7fdSDimitry Andric // This variable holds variable information for functions with 8824824e7fdSDimitry Andric // abstract_origin, but just for the current CU. 8834824e7fdSDimitry Andric AbstractOriginVarsTyMap LocalAbstractOriginFnInfo; 884fe6060f1SDimitry Andric FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed; 885fe6060f1SDimitry Andric 8864824e7fdSDimitry Andric collectStatsRecursive( 8874824e7fdSDimitry Andric CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats, 8884824e7fdSDimitry Andric AbstractOriginFnCUs, GlobalAbstractOriginFnInfo, 8894824e7fdSDimitry Andric LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed); 890fe6060f1SDimitry Andric 8914824e7fdSDimitry Andric // collectZeroLocCovForVarsWithAbstractOrigin will filter out all 8924824e7fdSDimitry Andric // out-of-order DWARF functions that have been processed within it, 8934824e7fdSDimitry Andric // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU 8944824e7fdSDimitry Andric // references. 895fe6060f1SDimitry Andric collectZeroLocCovForVarsWithAbstractOrigin( 896fe6060f1SDimitry Andric CUDie.getDwarfUnit(), GlobalStats, LocStats, 8974824e7fdSDimitry Andric LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed); 8984824e7fdSDimitry Andric 8994824e7fdSDimitry Andric // Collect all CrossCU references into CrossCUReferencesToBeResolved. 9004824e7fdSDimitry Andric for (auto CrossCUReferencingDIEOffset : 9014824e7fdSDimitry Andric FnsWithAbstractOriginToBeProcessed) 9024824e7fdSDimitry Andric CrossCUReferencesToBeResolved.push_back( 9034824e7fdSDimitry Andric DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset)); 904fe6060f1SDimitry Andric } 905*0fca6ea1SDimitry Andric const auto *LineTable = DICtx.getLineTableForUnit(CU.get()); 906*0fca6ea1SDimitry Andric std::optional<uint64_t> LastFileIdxOpt; 907*0fca6ea1SDimitry Andric if (LineTable) 908*0fca6ea1SDimitry Andric LastFileIdxOpt = LineTable->getLastValidFileIndex(); 909*0fca6ea1SDimitry Andric if (LastFileIdxOpt) { 910*0fca6ea1SDimitry Andric // Each CU has its own file index; in order to track unique line entries 911*0fca6ea1SDimitry Andric // across CUs, we therefore need to map each CU file index to a global 912*0fca6ea1SDimitry Andric // file index, which we store here. 913*0fca6ea1SDimitry Andric DenseMap<uint64_t, uint16_t> CUFileMapping; 914*0fca6ea1SDimitry Andric for (uint64_t FileIdx = 0; FileIdx <= *LastFileIdxOpt; ++FileIdx) { 915*0fca6ea1SDimitry Andric std::string File; 916*0fca6ea1SDimitry Andric if (LineTable->getFileNameByIndex( 917*0fca6ea1SDimitry Andric FileIdx, CU->getCompilationDir(), 918*0fca6ea1SDimitry Andric DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, 919*0fca6ea1SDimitry Andric File)) { 920*0fca6ea1SDimitry Andric auto ExistingFile = llvm::find(Files, File); 921*0fca6ea1SDimitry Andric if (ExistingFile != Files.end()) { 922*0fca6ea1SDimitry Andric CUFileMapping[FileIdx] = std::distance(Files.begin(), ExistingFile); 923*0fca6ea1SDimitry Andric } else { 924*0fca6ea1SDimitry Andric CUFileMapping[FileIdx] = Files.size(); 925*0fca6ea1SDimitry Andric Files.push_back(File); 926fe6060f1SDimitry Andric } 927*0fca6ea1SDimitry Andric } 928*0fca6ea1SDimitry Andric } 929*0fca6ea1SDimitry Andric for (const auto &Seq : LineTable->Sequences) { 930*0fca6ea1SDimitry Andric LnStats.NumBytes += Seq.HighPC - Seq.LowPC; 931*0fca6ea1SDimitry Andric // Ignore the `end_sequence` entry, since it's not interesting for us. 932*0fca6ea1SDimitry Andric LnStats.NumEntries += Seq.LastRowIndex - Seq.FirstRowIndex - 1; 933*0fca6ea1SDimitry Andric for (size_t RowIdx = Seq.FirstRowIndex; RowIdx < Seq.LastRowIndex - 1; 934*0fca6ea1SDimitry Andric ++RowIdx) { 935*0fca6ea1SDimitry Andric auto Entry = LineTable->Rows[RowIdx]; 936*0fca6ea1SDimitry Andric if (Entry.IsStmt) 937*0fca6ea1SDimitry Andric LnStats.NumIsStmtEntries += 1; 938*0fca6ea1SDimitry Andric assert(CUFileMapping.contains(Entry.File) && 939*0fca6ea1SDimitry Andric "Should have been collected earlier!"); 940*0fca6ea1SDimitry Andric uint16_t MappedFile = CUFileMapping[Entry.File]; 941*0fca6ea1SDimitry Andric UniqueLines.insert({Entry.Line, Entry.Column, MappedFile}); 942*0fca6ea1SDimitry Andric if (Entry.Line != 0) { 943*0fca6ea1SDimitry Andric UniqueNonZeroLines.insert({Entry.Line, Entry.Column, MappedFile}); 944*0fca6ea1SDimitry Andric } else { 945*0fca6ea1SDimitry Andric auto EntryStartAddress = Entry.Address.Address; 946*0fca6ea1SDimitry Andric auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address; 947*0fca6ea1SDimitry Andric LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress; 948*0fca6ea1SDimitry Andric } 949*0fca6ea1SDimitry Andric } 950*0fca6ea1SDimitry Andric } 951*0fca6ea1SDimitry Andric } 952*0fca6ea1SDimitry Andric } 953*0fca6ea1SDimitry Andric 954*0fca6ea1SDimitry Andric LnStats.NumUniqueEntries = UniqueLines.size(); 955*0fca6ea1SDimitry Andric LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size(); 9560b57cec5SDimitry Andric 9574824e7fdSDimitry Andric /// Resolve CrossCU references. 9584824e7fdSDimitry Andric collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin( 9594824e7fdSDimitry Andric LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo, 9604824e7fdSDimitry Andric CrossCUReferencesToBeResolved); 9614824e7fdSDimitry Andric 9625ffd83dbSDimitry Andric /// Collect the sizes of debug sections. 9635ffd83dbSDimitry Andric SectionSizes Sizes; 9645ffd83dbSDimitry Andric calculateSectionSizes(Obj, Sizes, Filename); 9655ffd83dbSDimitry Andric 9660b57cec5SDimitry Andric /// The version number should be increased every time the algorithm is changed 9670b57cec5SDimitry Andric /// (including bug fixes). New metrics may be added without increasing the 9680b57cec5SDimitry Andric /// version. 969349cc55cSDimitry Andric unsigned Version = 9; 970349cc55cSDimitry Andric SaturatingUINT64 VarParamTotal = 0; 971349cc55cSDimitry Andric SaturatingUINT64 VarParamUnique = 0; 972349cc55cSDimitry Andric SaturatingUINT64 VarParamWithLoc = 0; 973349cc55cSDimitry Andric SaturatingUINT64 NumFunctions = 0; 974349cc55cSDimitry Andric SaturatingUINT64 NumInlinedFunctions = 0; 975349cc55cSDimitry Andric SaturatingUINT64 NumFuncsWithSrcLoc = 0; 976349cc55cSDimitry Andric SaturatingUINT64 NumAbstractOrigins = 0; 977349cc55cSDimitry Andric SaturatingUINT64 ParamTotal = 0; 978349cc55cSDimitry Andric SaturatingUINT64 ParamWithType = 0; 979349cc55cSDimitry Andric SaturatingUINT64 ParamWithLoc = 0; 980349cc55cSDimitry Andric SaturatingUINT64 ParamWithSrcLoc = 0; 981349cc55cSDimitry Andric SaturatingUINT64 LocalVarTotal = 0; 982349cc55cSDimitry Andric SaturatingUINT64 LocalVarWithType = 0; 983349cc55cSDimitry Andric SaturatingUINT64 LocalVarWithSrcLoc = 0; 984349cc55cSDimitry Andric SaturatingUINT64 LocalVarWithLoc = 0; 9850b57cec5SDimitry Andric for (auto &Entry : Statistics) { 9860b57cec5SDimitry Andric PerFunctionStats &Stats = Entry.getValue(); 987349cc55cSDimitry Andric uint64_t TotalVars = Stats.VarsInFunction.size() * 9885ffd83dbSDimitry Andric (Stats.NumFnInlined + Stats.NumFnOutOfLine); 9895ffd83dbSDimitry Andric // Count variables in global scope. 9905ffd83dbSDimitry Andric if (!Stats.IsFunction) 9915ffd83dbSDimitry Andric TotalVars = 9925ffd83dbSDimitry Andric Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial; 993349cc55cSDimitry Andric uint64_t Constants = Stats.ConstantMembers; 9940b57cec5SDimitry Andric VarParamWithLoc += Stats.TotalVarWithLoc + Constants; 9950b57cec5SDimitry Andric VarParamTotal += TotalVars; 9960b57cec5SDimitry Andric VarParamUnique += Stats.VarsInFunction.size(); 9970b57cec5SDimitry Andric LLVM_DEBUG(for (auto &V 9980b57cec5SDimitry Andric : Stats.VarsInFunction) llvm::dbgs() 9990b57cec5SDimitry Andric << Entry.getKey() << ": " << V.getKey() << "\n"); 10000b57cec5SDimitry Andric NumFunctions += Stats.IsFunction; 10010b57cec5SDimitry Andric NumFuncsWithSrcLoc += Stats.HasSourceLocation; 10020b57cec5SDimitry Andric NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; 10030b57cec5SDimitry Andric NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; 10040b57cec5SDimitry Andric ParamTotal += Stats.NumParams; 10050b57cec5SDimitry Andric ParamWithType += Stats.NumParamTypes; 10060b57cec5SDimitry Andric ParamWithLoc += Stats.NumParamLocations; 10070b57cec5SDimitry Andric ParamWithSrcLoc += Stats.NumParamSourceLocations; 10085ffd83dbSDimitry Andric LocalVarTotal += Stats.NumLocalVars; 10095ffd83dbSDimitry Andric LocalVarWithType += Stats.NumLocalVarTypes; 10105ffd83dbSDimitry Andric LocalVarWithLoc += Stats.NumLocalVarLocations; 10115ffd83dbSDimitry Andric LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations; 10120b57cec5SDimitry Andric } 10130b57cec5SDimitry Andric 10140b57cec5SDimitry Andric // Print summary. 10150b57cec5SDimitry Andric OS.SetBufferSize(1024); 1016e8d8bef9SDimitry Andric json::OStream J(OS, 2); 1017e8d8bef9SDimitry Andric J.objectBegin(); 1018e8d8bef9SDimitry Andric J.attribute("version", Version); 10190b57cec5SDimitry Andric LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; 10200b57cec5SDimitry Andric llvm::dbgs() << "---------------------------------\n"); 10215ffd83dbSDimitry Andric 1022e8d8bef9SDimitry Andric printDatum(J, "file", Filename.str()); 1023e8d8bef9SDimitry Andric printDatum(J, "format", FormatName); 10245ffd83dbSDimitry Andric 1025349cc55cSDimitry Andric printDatum(J, "#functions", NumFunctions.Value); 1026349cc55cSDimitry Andric printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value); 1027349cc55cSDimitry Andric printDatum(J, "#inlined functions", NumInlinedFunctions.Value); 1028349cc55cSDimitry Andric printDatum(J, "#inlined functions with abstract origins", 1029349cc55cSDimitry Andric NumAbstractOrigins.Value); 10305ffd83dbSDimitry Andric 10315ffd83dbSDimitry Andric // This includes local variables and formal parameters. 1032349cc55cSDimitry Andric printDatum(J, "#unique source variables", VarParamUnique.Value); 1033349cc55cSDimitry Andric printDatum(J, "#source variables", VarParamTotal.Value); 1034349cc55cSDimitry Andric printDatum(J, "#source variables with location", VarParamWithLoc.Value); 10355ffd83dbSDimitry Andric 1036349cc55cSDimitry Andric printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value); 1037349cc55cSDimitry Andric printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value); 1038349cc55cSDimitry Andric printDatum(J, "#call site parameter DIEs", 1039349cc55cSDimitry Andric GlobalStats.CallSiteParamDIEs.Value); 10405ffd83dbSDimitry Andric 1041e8d8bef9SDimitry Andric printDatum(J, "sum_all_variables(#bytes in parent scope)", 1042349cc55cSDimitry Andric GlobalStats.ScopeBytes.Value); 1043e8d8bef9SDimitry Andric printDatum(J, 1044e8d8bef9SDimitry Andric "sum_all_variables(#bytes in any scope covered by DW_AT_location)", 1045349cc55cSDimitry Andric GlobalStats.TotalBytesCovered.Value); 1046e8d8bef9SDimitry Andric printDatum(J, 10475ffd83dbSDimitry Andric "sum_all_variables(#bytes in parent scope covered by " 10485ffd83dbSDimitry Andric "DW_AT_location)", 1049349cc55cSDimitry Andric GlobalStats.ScopeBytesCovered.Value); 1050e8d8bef9SDimitry Andric printDatum(J, 10515ffd83dbSDimitry Andric "sum_all_variables(#bytes in parent scope covered by " 10525ffd83dbSDimitry Andric "DW_OP_entry_value)", 1053349cc55cSDimitry Andric GlobalStats.ScopeEntryValueBytesCovered.Value); 10545ffd83dbSDimitry Andric 1055e8d8bef9SDimitry Andric printDatum(J, "sum_all_params(#bytes in parent scope)", 1056349cc55cSDimitry Andric GlobalStats.ParamScopeBytes.Value); 1057e8d8bef9SDimitry Andric printDatum(J, 10585ffd83dbSDimitry Andric "sum_all_params(#bytes in parent scope covered by DW_AT_location)", 1059349cc55cSDimitry Andric GlobalStats.ParamScopeBytesCovered.Value); 1060e8d8bef9SDimitry Andric printDatum(J, 10615ffd83dbSDimitry Andric "sum_all_params(#bytes in parent scope covered by " 10625ffd83dbSDimitry Andric "DW_OP_entry_value)", 1063349cc55cSDimitry Andric GlobalStats.ParamScopeEntryValueBytesCovered.Value); 10645ffd83dbSDimitry Andric 1065e8d8bef9SDimitry Andric printDatum(J, "sum_all_local_vars(#bytes in parent scope)", 1066349cc55cSDimitry Andric GlobalStats.LocalVarScopeBytes.Value); 1067e8d8bef9SDimitry Andric printDatum(J, 10685ffd83dbSDimitry Andric "sum_all_local_vars(#bytes in parent scope covered by " 10695ffd83dbSDimitry Andric "DW_AT_location)", 1070349cc55cSDimitry Andric GlobalStats.LocalVarScopeBytesCovered.Value); 1071e8d8bef9SDimitry Andric printDatum(J, 10725ffd83dbSDimitry Andric "sum_all_local_vars(#bytes in parent scope covered by " 10735ffd83dbSDimitry Andric "DW_OP_entry_value)", 1074349cc55cSDimitry Andric GlobalStats.LocalVarScopeEntryValueBytesCovered.Value); 10755ffd83dbSDimitry Andric 1076349cc55cSDimitry Andric printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value); 1077e8d8bef9SDimitry Andric printDatum(J, "#bytes within inlined functions", 1078349cc55cSDimitry Andric GlobalStats.InlineFunctionSize.Value); 10795ffd83dbSDimitry Andric 10805ffd83dbSDimitry Andric // Print the summary for formal parameters. 1081349cc55cSDimitry Andric printDatum(J, "#params", ParamTotal.Value); 1082349cc55cSDimitry Andric printDatum(J, "#params with source location", ParamWithSrcLoc.Value); 1083349cc55cSDimitry Andric printDatum(J, "#params with type", ParamWithType.Value); 1084349cc55cSDimitry Andric printDatum(J, "#params with binary location", ParamWithLoc.Value); 10855ffd83dbSDimitry Andric 10865ffd83dbSDimitry Andric // Print the summary for local variables. 1087349cc55cSDimitry Andric printDatum(J, "#local vars", LocalVarTotal.Value); 1088349cc55cSDimitry Andric printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value); 1089349cc55cSDimitry Andric printDatum(J, "#local vars with type", LocalVarWithType.Value); 1090349cc55cSDimitry Andric printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value); 10915ffd83dbSDimitry Andric 10925ffd83dbSDimitry Andric // Print the debug section sizes. 1093e8d8bef9SDimitry Andric printSectionSizes(J, Sizes); 10945ffd83dbSDimitry Andric 10955ffd83dbSDimitry Andric // Print the location statistics for variables (includes local variables 10965ffd83dbSDimitry Andric // and formal parameters). 1097e8d8bef9SDimitry Andric printDatum(J, "#variables processed by location statistics", 1098349cc55cSDimitry Andric LocStats.NumVarParam.Value); 1099e8d8bef9SDimitry Andric printLocationStats(J, "#variables", LocStats.VarParamLocStats); 1100e8d8bef9SDimitry Andric printLocationStats(J, "#variables - entry values", 11018bcb0991SDimitry Andric LocStats.VarParamNonEntryValLocStats); 11025ffd83dbSDimitry Andric 11035ffd83dbSDimitry Andric // Print the location statistics for formal parameters. 1104349cc55cSDimitry Andric printDatum(J, "#params processed by location statistics", 1105349cc55cSDimitry Andric LocStats.NumParam.Value); 1106e8d8bef9SDimitry Andric printLocationStats(J, "#params", LocStats.ParamLocStats); 1107e8d8bef9SDimitry Andric printLocationStats(J, "#params - entry values", 11088bcb0991SDimitry Andric LocStats.ParamNonEntryValLocStats); 11095ffd83dbSDimitry Andric 11105ffd83dbSDimitry Andric // Print the location statistics for local variables. 1111e8d8bef9SDimitry Andric printDatum(J, "#local vars processed by location statistics", 1112349cc55cSDimitry Andric LocStats.NumVar.Value); 1113e8d8bef9SDimitry Andric printLocationStats(J, "#local vars", LocStats.LocalVarLocStats); 1114e8d8bef9SDimitry Andric printLocationStats(J, "#local vars - entry values", 11155ffd83dbSDimitry Andric LocStats.LocalVarNonEntryValLocStats); 1116*0fca6ea1SDimitry Andric 1117*0fca6ea1SDimitry Andric // Print line statistics for the object file. 1118*0fca6ea1SDimitry Andric printDatum(J, "#bytes with line information", LnStats.NumBytes.Value); 1119*0fca6ea1SDimitry Andric printDatum(J, "#bytes with line-0 locations", LnStats.NumLineZeroBytes.Value); 1120*0fca6ea1SDimitry Andric printDatum(J, "#line entries", LnStats.NumEntries.Value); 1121*0fca6ea1SDimitry Andric printDatum(J, "#line entries (is_stmt)", LnStats.NumIsStmtEntries.Value); 1122*0fca6ea1SDimitry Andric printDatum(J, "#line entries (unique)", LnStats.NumUniqueEntries.Value); 1123*0fca6ea1SDimitry Andric printDatum(J, "#line entries (unique non-0)", 1124*0fca6ea1SDimitry Andric LnStats.NumUniqueNonZeroEntries.Value); 1125*0fca6ea1SDimitry Andric 1126e8d8bef9SDimitry Andric J.objectEnd(); 1127e8d8bef9SDimitry Andric OS << '\n'; 112881ad6265SDimitry Andric LLVM_DEBUG( 112981ad6265SDimitry Andric llvm::dbgs() << "Total Availability: " 113081ad6265SDimitry Andric << (VarParamTotal.Value 113181ad6265SDimitry Andric ? (int)std::round((VarParamWithLoc.Value * 100.0) / 1132349cc55cSDimitry Andric VarParamTotal.Value) 113381ad6265SDimitry Andric : 0) 11340b57cec5SDimitry Andric << "%\n"; 11350b57cec5SDimitry Andric llvm::dbgs() << "PC Ranges covered: " 113681ad6265SDimitry Andric << (GlobalStats.ScopeBytes.Value 113781ad6265SDimitry Andric ? (int)std::round( 1138349cc55cSDimitry Andric (GlobalStats.ScopeBytesCovered.Value * 100.0) / 1139349cc55cSDimitry Andric GlobalStats.ScopeBytes.Value) 114081ad6265SDimitry Andric : 0) 11410b57cec5SDimitry Andric << "%\n"); 11420b57cec5SDimitry Andric return true; 11430b57cec5SDimitry Andric } 1144