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