xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
15ffd83dbSDimitry Andric //===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "llvm-dwarfdump.h"
100b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
110b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h"
120b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h"
130b57cec5SDimitry Andric #include "llvm/DebugInfo/DIContext.h"
140b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h"
150b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.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 
248bcb0991SDimitry Andric /// This represents the number of categories of debug location coverage being
258bcb0991SDimitry Andric /// calculated. The first category is the number of variables with 0% location
268bcb0991SDimitry Andric /// coverage, but the last category is the number of variables with 100%
278bcb0991SDimitry Andric /// location coverage.
288bcb0991SDimitry Andric constexpr int NumOfCoverageCategories = 12;
298bcb0991SDimitry Andric 
305ffd83dbSDimitry Andric namespace {
310b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and
320b57cec5SDimitry Andric /// contains variables, such as a compile unit).
330b57cec5SDimitry Andric struct PerFunctionStats {
340b57cec5SDimitry Andric   /// Number of inlined instances of this function.
350b57cec5SDimitry Andric   unsigned NumFnInlined = 0;
365ffd83dbSDimitry Andric   /// Number of out-of-line instances of this function.
375ffd83dbSDimitry Andric   unsigned NumFnOutOfLine = 0;
380b57cec5SDimitry Andric   /// Number of inlined instances that have abstract origins.
390b57cec5SDimitry Andric   unsigned NumAbstractOrigins = 0;
400b57cec5SDimitry Andric   /// Number of variables and parameters with location across all inlined
410b57cec5SDimitry Andric   /// instances.
420b57cec5SDimitry Andric   unsigned TotalVarWithLoc = 0;
430b57cec5SDimitry Andric   /// Number of constants with location across all inlined instances.
440b57cec5SDimitry Andric   unsigned ConstantMembers = 0;
455ffd83dbSDimitry Andric   /// Number of arificial variables, parameters or members across all instances.
465ffd83dbSDimitry Andric   unsigned NumArtificial = 0;
470b57cec5SDimitry Andric   /// List of all Variables and parameters in this function.
480b57cec5SDimitry Andric   StringSet<> VarsInFunction;
490b57cec5SDimitry Andric   /// Compile units also cover a PC range, but have this flag set to false.
500b57cec5SDimitry Andric   bool IsFunction = false;
510b57cec5SDimitry Andric   /// Function has source location information.
520b57cec5SDimitry Andric   bool HasSourceLocation = false;
530b57cec5SDimitry Andric   /// Number of function parameters.
540b57cec5SDimitry Andric   unsigned NumParams = 0;
550b57cec5SDimitry Andric   /// Number of function parameters with source location.
560b57cec5SDimitry Andric   unsigned NumParamSourceLocations = 0;
570b57cec5SDimitry Andric   /// Number of function parameters with type.
580b57cec5SDimitry Andric   unsigned NumParamTypes = 0;
590b57cec5SDimitry Andric   /// Number of function parameters with a DW_AT_location.
600b57cec5SDimitry Andric   unsigned NumParamLocations = 0;
615ffd83dbSDimitry Andric   /// Number of local variables.
625ffd83dbSDimitry Andric   unsigned NumLocalVars = 0;
635ffd83dbSDimitry Andric   /// Number of local variables with source location.
645ffd83dbSDimitry Andric   unsigned NumLocalVarSourceLocations = 0;
655ffd83dbSDimitry Andric   /// Number of local variables with type.
665ffd83dbSDimitry Andric   unsigned NumLocalVarTypes = 0;
675ffd83dbSDimitry Andric   /// Number of local variables with DW_AT_location.
685ffd83dbSDimitry Andric   unsigned NumLocalVarLocations = 0;
690b57cec5SDimitry Andric };
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs.
720b57cec5SDimitry Andric struct GlobalStats {
730b57cec5SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations.
74*e8d8bef9SDimitry Andric   unsigned TotalBytesCovered = 0;
75*e8d8bef9SDimitry Andric   /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
760b57cec5SDimitry Andric   unsigned ScopeBytesCovered = 0;
77480093f4SDimitry Andric   /// Total number of PC range bytes in each variable's enclosing scope.
78480093f4SDimitry Andric   unsigned ScopeBytes = 0;
798bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
808bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value).
818bcb0991SDimitry Andric   unsigned ScopeEntryValueBytesCovered = 0;
828bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations of
838bcb0991SDimitry Andric   /// formal parameters.
848bcb0991SDimitry Andric   unsigned ParamScopeBytesCovered = 0;
855ffd83dbSDimitry Andric   /// Total number of PC range bytes in each parameter's enclosing scope.
86480093f4SDimitry Andric   unsigned ParamScopeBytes = 0;
878bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
888bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value) (only for parameters).
898bcb0991SDimitry Andric   unsigned ParamScopeEntryValueBytesCovered = 0;
908bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations (only for local
918bcb0991SDimitry Andric   /// variables).
925ffd83dbSDimitry Andric   unsigned LocalVarScopeBytesCovered = 0;
935ffd83dbSDimitry Andric   /// Total number of PC range bytes in each local variable's enclosing scope.
945ffd83dbSDimitry Andric   unsigned LocalVarScopeBytes = 0;
958bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
968bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value) (only for local variables).
975ffd83dbSDimitry Andric   unsigned LocalVarScopeEntryValueBytesCovered = 0;
988bcb0991SDimitry Andric   /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
990b57cec5SDimitry Andric   unsigned CallSiteEntries = 0;
1008bcb0991SDimitry Andric   /// Total number of call site DIEs (DW_TAG_call_site).
1018bcb0991SDimitry Andric   unsigned CallSiteDIEs = 0;
1028bcb0991SDimitry Andric   /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
1038bcb0991SDimitry Andric   unsigned CallSiteParamDIEs = 0;
1040b57cec5SDimitry Andric   /// Total byte size of concrete functions. This byte size includes
1050b57cec5SDimitry Andric   /// inline functions contained in the concrete functions.
1068bcb0991SDimitry Andric   unsigned FunctionSize = 0;
1070b57cec5SDimitry Andric   /// Total byte size of inlined functions. This is the total number of bytes
1080b57cec5SDimitry Andric   /// for the top inline functions within concrete functions. This can help
1090b57cec5SDimitry Andric   /// tune the inline settings when compiling to match user expectations.
1108bcb0991SDimitry Andric   unsigned InlineFunctionSize = 0;
1118bcb0991SDimitry Andric };
1128bcb0991SDimitry Andric 
1138bcb0991SDimitry Andric /// Holds accumulated debug location statistics about local variables and
1148bcb0991SDimitry Andric /// formal parameters.
1158bcb0991SDimitry Andric struct LocationStats {
1168bcb0991SDimitry Andric   /// Map the scope coverage decile to the number of variables in the decile.
1178bcb0991SDimitry Andric   /// The first element of the array (at the index zero) represents the number
1188bcb0991SDimitry Andric   /// of variables with the no debug location at all, but the last element
1198bcb0991SDimitry Andric   /// in the vector represents the number of fully covered variables within
1208bcb0991SDimitry Andric   /// its scope.
1218bcb0991SDimitry Andric   std::vector<unsigned> VarParamLocStats{
1228bcb0991SDimitry Andric       std::vector<unsigned>(NumOfCoverageCategories, 0)};
1238bcb0991SDimitry Andric   /// Map non debug entry values coverage.
1248bcb0991SDimitry Andric   std::vector<unsigned> VarParamNonEntryValLocStats{
1258bcb0991SDimitry Andric       std::vector<unsigned>(NumOfCoverageCategories, 0)};
1268bcb0991SDimitry Andric   /// The debug location statistics for formal parameters.
1278bcb0991SDimitry Andric   std::vector<unsigned> ParamLocStats{
1288bcb0991SDimitry Andric       std::vector<unsigned>(NumOfCoverageCategories, 0)};
1298bcb0991SDimitry Andric   /// Map non debug entry values coverage for formal parameters.
1308bcb0991SDimitry Andric   std::vector<unsigned> ParamNonEntryValLocStats{
1318bcb0991SDimitry Andric       std::vector<unsigned>(NumOfCoverageCategories, 0)};
1328bcb0991SDimitry Andric   /// The debug location statistics for local variables.
1335ffd83dbSDimitry Andric   std::vector<unsigned> LocalVarLocStats{
1348bcb0991SDimitry Andric       std::vector<unsigned>(NumOfCoverageCategories, 0)};
1358bcb0991SDimitry Andric   /// Map non debug entry values coverage for local variables.
1365ffd83dbSDimitry Andric   std::vector<unsigned> LocalVarNonEntryValLocStats{
1378bcb0991SDimitry Andric       std::vector<unsigned>(NumOfCoverageCategories, 0)};
1388bcb0991SDimitry Andric   /// Total number of local variables and function parameters processed.
1398bcb0991SDimitry Andric   unsigned NumVarParam = 0;
1408bcb0991SDimitry Andric   /// Total number of formal parameters processed.
1418bcb0991SDimitry Andric   unsigned NumParam = 0;
1428bcb0991SDimitry Andric   /// Total number of local variables processed.
1438bcb0991SDimitry Andric   unsigned NumVar = 0;
1440b57cec5SDimitry Andric };
1455ffd83dbSDimitry Andric } // namespace
1460b57cec5SDimitry Andric 
1478bcb0991SDimitry Andric /// Collect debug location statistics for one DIE.
148*e8d8bef9SDimitry Andric static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
1498bcb0991SDimitry Andric                             std::vector<unsigned> &VarParamLocStats,
1508bcb0991SDimitry Andric                             std::vector<unsigned> &ParamLocStats,
1515ffd83dbSDimitry Andric                             std::vector<unsigned> &LocalVarLocStats,
1525ffd83dbSDimitry Andric                             bool IsParam, bool IsLocalVar) {
153*e8d8bef9SDimitry Andric   auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
1548bcb0991SDimitry Andric     // No debug location at all for the variable.
155*e8d8bef9SDimitry Andric     if (ScopeBytesCovered == 0)
1568bcb0991SDimitry Andric       return 0;
1578bcb0991SDimitry Andric     // Fully covered variable within its scope.
158*e8d8bef9SDimitry Andric     if (ScopeBytesCovered >= BytesInScope)
1598bcb0991SDimitry Andric       return NumOfCoverageCategories - 1;
1608bcb0991SDimitry Andric     // Get covered range (e.g. 20%-29%).
161*e8d8bef9SDimitry Andric     unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
1628bcb0991SDimitry Andric     LocBucket /= 10;
1638bcb0991SDimitry Andric     return LocBucket + 1;
1648bcb0991SDimitry Andric   };
1658bcb0991SDimitry Andric 
1668bcb0991SDimitry Andric   unsigned CoverageBucket = getCoverageBucket();
1678bcb0991SDimitry Andric   VarParamLocStats[CoverageBucket]++;
1688bcb0991SDimitry Andric   if (IsParam)
1698bcb0991SDimitry Andric     ParamLocStats[CoverageBucket]++;
1708bcb0991SDimitry Andric   else if (IsLocalVar)
1715ffd83dbSDimitry Andric     LocalVarLocStats[CoverageBucket]++;
1725ffd83dbSDimitry Andric }
1735ffd83dbSDimitry Andric /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
1745ffd83dbSDimitry Andric /// and DeclLine. The identifier aims to be unique for any unique entities,
1755ffd83dbSDimitry Andric /// but keeping the same among different instances of the same entity.
1765ffd83dbSDimitry Andric static std::string constructDieID(DWARFDie Die,
1775ffd83dbSDimitry Andric                                   StringRef Prefix = StringRef()) {
1785ffd83dbSDimitry Andric   std::string IDStr;
1795ffd83dbSDimitry Andric   llvm::raw_string_ostream ID(IDStr);
1805ffd83dbSDimitry Andric   ID << Prefix
1815ffd83dbSDimitry Andric      << Die.getName(DINameKind::LinkageName);
1825ffd83dbSDimitry Andric 
1835ffd83dbSDimitry Andric   // Prefix + Name is enough for local variables and parameters.
1845ffd83dbSDimitry Andric   if (!Prefix.empty() && !Prefix.equals("g"))
1855ffd83dbSDimitry Andric     return ID.str();
1865ffd83dbSDimitry Andric 
1875ffd83dbSDimitry Andric   auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
1885ffd83dbSDimitry Andric   std::string File;
1895ffd83dbSDimitry Andric   if (DeclFile) {
1905ffd83dbSDimitry Andric     DWARFUnit *U = Die.getDwarfUnit();
1915ffd83dbSDimitry Andric     if (const auto *LT = U->getContext().getLineTableForUnit(U))
1925ffd83dbSDimitry Andric       if (LT->getFileNameByIndex(
1935ffd83dbSDimitry Andric               dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
1945ffd83dbSDimitry Andric               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
1955ffd83dbSDimitry Andric         File = std::string(sys::path::filename(File));
1965ffd83dbSDimitry Andric   }
1975ffd83dbSDimitry Andric   ID << ":" << (File.empty() ? "/" : File);
1985ffd83dbSDimitry Andric   ID << ":"
1995ffd83dbSDimitry Andric      << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
2005ffd83dbSDimitry Andric   return ID.str();
2018bcb0991SDimitry Andric }
2028bcb0991SDimitry Andric 
203*e8d8bef9SDimitry Andric /// Return the number of bytes in the overlap of ranges A and B.
204*e8d8bef9SDimitry Andric static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
205*e8d8bef9SDimitry Andric   uint64_t Lower = std::max(A.LowPC, B.LowPC);
206*e8d8bef9SDimitry Andric   uint64_t Upper = std::min(A.HighPC, B.HighPC);
207*e8d8bef9SDimitry Andric   if (Lower >= Upper)
208*e8d8bef9SDimitry Andric     return 0;
209*e8d8bef9SDimitry Andric   return Upper - Lower;
210*e8d8bef9SDimitry Andric }
211*e8d8bef9SDimitry Andric 
2120b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE.
213480093f4SDimitry Andric static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
214480093f4SDimitry Andric                                std::string VarPrefix, uint64_t BytesInScope,
215480093f4SDimitry Andric                                uint32_t InlineDepth,
2160b57cec5SDimitry Andric                                StringMap<PerFunctionStats> &FnStatMap,
2178bcb0991SDimitry Andric                                GlobalStats &GlobalStats,
2188bcb0991SDimitry Andric                                LocationStats &LocStats) {
2190b57cec5SDimitry Andric   bool HasLoc = false;
2200b57cec5SDimitry Andric   bool HasSrcLoc = false;
2210b57cec5SDimitry Andric   bool HasType = false;
222*e8d8bef9SDimitry Andric   uint64_t TotalBytesCovered = 0;
223*e8d8bef9SDimitry Andric   uint64_t ScopeBytesCovered = 0;
2248bcb0991SDimitry Andric   uint64_t BytesEntryValuesCovered = 0;
2258bcb0991SDimitry Andric   auto &FnStats = FnStatMap[FnPrefix];
2268bcb0991SDimitry Andric   bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter;
2278bcb0991SDimitry Andric   bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable;
2285ffd83dbSDimitry Andric   bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member &&
2295ffd83dbSDimitry Andric                           Die.find(dwarf::DW_AT_const_value);
2300b57cec5SDimitry Andric 
2318bcb0991SDimitry Andric   if (Die.getTag() == dwarf::DW_TAG_call_site ||
2328bcb0991SDimitry Andric       Die.getTag() == dwarf::DW_TAG_GNU_call_site) {
2338bcb0991SDimitry Andric     GlobalStats.CallSiteDIEs++;
2340b57cec5SDimitry Andric     return;
2350b57cec5SDimitry Andric   }
2360b57cec5SDimitry Andric 
2378bcb0991SDimitry Andric   if (Die.getTag() == dwarf::DW_TAG_call_site_parameter ||
2388bcb0991SDimitry Andric       Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) {
2398bcb0991SDimitry Andric     GlobalStats.CallSiteParamDIEs++;
2408bcb0991SDimitry Andric     return;
2418bcb0991SDimitry Andric   }
2428bcb0991SDimitry Andric 
2435ffd83dbSDimitry Andric   if (!IsParam && !IsLocalVar && !IsConstantMember) {
2440b57cec5SDimitry Andric     // Not a variable or constant member.
2450b57cec5SDimitry Andric     return;
2460b57cec5SDimitry Andric   }
2470b57cec5SDimitry Andric 
2485ffd83dbSDimitry Andric   // Ignore declarations of global variables.
2495ffd83dbSDimitry Andric   if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
2505ffd83dbSDimitry Andric     return;
2515ffd83dbSDimitry Andric 
2520b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
2530b57cec5SDimitry Andric       Die.findRecursively(dwarf::DW_AT_decl_line))
2540b57cec5SDimitry Andric     HasSrcLoc = true;
2550b57cec5SDimitry Andric 
2560b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_type))
2570b57cec5SDimitry Andric     HasType = true;
2580b57cec5SDimitry Andric 
2598bcb0991SDimitry Andric   auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
2608bcb0991SDimitry Andric     DWARFUnit *U = Die.getDwarfUnit();
2618bcb0991SDimitry Andric     DataExtractor Data(toStringRef(D),
2628bcb0991SDimitry Andric                        Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
2635ffd83dbSDimitry Andric     DWARFExpression Expression(Data, U->getAddressByteSize(),
2645ffd83dbSDimitry Andric                                U->getFormParams().Format);
2658bcb0991SDimitry Andric     // Consider the expression containing the DW_OP_entry_value as
2668bcb0991SDimitry Andric     // an entry value.
2678bcb0991SDimitry Andric     return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) {
2688bcb0991SDimitry Andric       return Op.getCode() == dwarf::DW_OP_entry_value ||
2698bcb0991SDimitry Andric              Op.getCode() == dwarf::DW_OP_GNU_entry_value;
2708bcb0991SDimitry Andric     });
2718bcb0991SDimitry Andric   };
2728bcb0991SDimitry Andric 
2730b57cec5SDimitry Andric   if (Die.find(dwarf::DW_AT_const_value)) {
2740b57cec5SDimitry Andric     // This catches constant members *and* variables.
2750b57cec5SDimitry Andric     HasLoc = true;
276*e8d8bef9SDimitry Andric     ScopeBytesCovered = BytesInScope;
277*e8d8bef9SDimitry Andric     TotalBytesCovered = BytesInScope;
2780b57cec5SDimitry Andric   } else {
2790b57cec5SDimitry Andric     // Handle variables and function arguments.
280480093f4SDimitry Andric     Expected<std::vector<DWARFLocationExpression>> Loc =
281480093f4SDimitry Andric         Die.getLocations(dwarf::DW_AT_location);
282480093f4SDimitry Andric     if (!Loc) {
283480093f4SDimitry Andric       consumeError(Loc.takeError());
2840b57cec5SDimitry Andric     } else {
285480093f4SDimitry Andric       HasLoc = true;
286480093f4SDimitry Andric       // Get PC coverage.
287480093f4SDimitry Andric       auto Default = find_if(
288480093f4SDimitry Andric           *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
289480093f4SDimitry Andric       if (Default != Loc->end()) {
2900b57cec5SDimitry Andric         // Assume the entire range is covered by a single location.
291*e8d8bef9SDimitry Andric         ScopeBytesCovered = BytesInScope;
292*e8d8bef9SDimitry Andric         TotalBytesCovered = BytesInScope;
293480093f4SDimitry Andric       } else {
294*e8d8bef9SDimitry Andric         // Caller checks this Expected result already, it cannot fail.
295*e8d8bef9SDimitry Andric         auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
296480093f4SDimitry Andric         for (auto Entry : *Loc) {
297*e8d8bef9SDimitry Andric           TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
298*e8d8bef9SDimitry Andric           uint64_t ScopeBytesCoveredByEntry = 0;
299*e8d8bef9SDimitry Andric           // Calculate how many bytes of the parent scope this entry covers.
300*e8d8bef9SDimitry Andric           // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
301*e8d8bef9SDimitry Andric           // address ranges defined by the bounded location descriptions of a
302*e8d8bef9SDimitry Andric           // location list may overlap". So in theory a variable can have
303*e8d8bef9SDimitry Andric           // multiple simultaneous locations, which would make this calculation
304*e8d8bef9SDimitry Andric           // misleading because we will count the overlapped areas
305*e8d8bef9SDimitry Andric           // twice. However, clang does not currently emit DWARF like this.
306*e8d8bef9SDimitry Andric           for (DWARFAddressRange R : ScopeRanges) {
307*e8d8bef9SDimitry Andric             ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
308*e8d8bef9SDimitry Andric           }
309*e8d8bef9SDimitry Andric           ScopeBytesCovered += ScopeBytesCoveredByEntry;
310480093f4SDimitry Andric           if (IsEntryValue(Entry.Expr))
311*e8d8bef9SDimitry Andric             BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
312480093f4SDimitry Andric         }
3130b57cec5SDimitry Andric       }
3140b57cec5SDimitry Andric     }
3150b57cec5SDimitry Andric   }
3160b57cec5SDimitry Andric 
3178bcb0991SDimitry Andric   // Calculate the debug location statistics.
3188bcb0991SDimitry Andric   if (BytesInScope) {
3198bcb0991SDimitry Andric     LocStats.NumVarParam++;
3208bcb0991SDimitry Andric     if (IsParam)
3218bcb0991SDimitry Andric       LocStats.NumParam++;
3228bcb0991SDimitry Andric     else if (IsLocalVar)
3238bcb0991SDimitry Andric       LocStats.NumVar++;
3248bcb0991SDimitry Andric 
325*e8d8bef9SDimitry Andric     collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
3265ffd83dbSDimitry Andric                     LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
3278bcb0991SDimitry Andric                     IsLocalVar);
3288bcb0991SDimitry Andric     // Non debug entry values coverage statistics.
329*e8d8bef9SDimitry Andric     collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
3308bcb0991SDimitry Andric                     LocStats.VarParamNonEntryValLocStats,
3318bcb0991SDimitry Andric                     LocStats.ParamNonEntryValLocStats,
3325ffd83dbSDimitry Andric                     LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
3338bcb0991SDimitry Andric   }
3348bcb0991SDimitry Andric 
3350b57cec5SDimitry Andric   // Collect PC range coverage data.
3360b57cec5SDimitry Andric   if (DWARFDie D =
3370b57cec5SDimitry Andric           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
3380b57cec5SDimitry Andric     Die = D;
3395ffd83dbSDimitry Andric 
3405ffd83dbSDimitry Andric   std::string VarID = constructDieID(Die, VarPrefix);
3415ffd83dbSDimitry Andric   FnStats.VarsInFunction.insert(VarID);
3425ffd83dbSDimitry Andric 
343*e8d8bef9SDimitry Andric   GlobalStats.TotalBytesCovered += TotalBytesCovered;
3440b57cec5SDimitry Andric   if (BytesInScope) {
345*e8d8bef9SDimitry Andric     GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
346480093f4SDimitry Andric     GlobalStats.ScopeBytes += BytesInScope;
3478bcb0991SDimitry Andric     GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
3488bcb0991SDimitry Andric     if (IsParam) {
349*e8d8bef9SDimitry Andric       GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
350480093f4SDimitry Andric       GlobalStats.ParamScopeBytes += BytesInScope;
3518bcb0991SDimitry Andric       GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
3528bcb0991SDimitry Andric     } else if (IsLocalVar) {
353*e8d8bef9SDimitry Andric       GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
3545ffd83dbSDimitry Andric       GlobalStats.LocalVarScopeBytes += BytesInScope;
3555ffd83dbSDimitry Andric       GlobalStats.LocalVarScopeEntryValueBytesCovered +=
3565ffd83dbSDimitry Andric           BytesEntryValuesCovered;
3578bcb0991SDimitry Andric     }
358480093f4SDimitry Andric     assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes);
3590b57cec5SDimitry Andric   }
3605ffd83dbSDimitry Andric 
3615ffd83dbSDimitry Andric   if (IsConstantMember) {
3625ffd83dbSDimitry Andric     FnStats.ConstantMembers++;
3635ffd83dbSDimitry Andric     return;
3645ffd83dbSDimitry Andric   }
3655ffd83dbSDimitry Andric 
3665ffd83dbSDimitry Andric   FnStats.TotalVarWithLoc += (unsigned)HasLoc;
3675ffd83dbSDimitry Andric 
3685ffd83dbSDimitry Andric   if (Die.find(dwarf::DW_AT_artificial)) {
3695ffd83dbSDimitry Andric     FnStats.NumArtificial++;
3705ffd83dbSDimitry Andric     return;
3715ffd83dbSDimitry Andric   }
3725ffd83dbSDimitry Andric 
3738bcb0991SDimitry Andric   if (IsParam) {
3740b57cec5SDimitry Andric     FnStats.NumParams++;
3750b57cec5SDimitry Andric     if (HasType)
3760b57cec5SDimitry Andric       FnStats.NumParamTypes++;
3770b57cec5SDimitry Andric     if (HasSrcLoc)
3780b57cec5SDimitry Andric       FnStats.NumParamSourceLocations++;
3790b57cec5SDimitry Andric     if (HasLoc)
3800b57cec5SDimitry Andric       FnStats.NumParamLocations++;
3818bcb0991SDimitry Andric   } else if (IsLocalVar) {
3825ffd83dbSDimitry Andric     FnStats.NumLocalVars++;
3830b57cec5SDimitry Andric     if (HasType)
3845ffd83dbSDimitry Andric       FnStats.NumLocalVarTypes++;
3850b57cec5SDimitry Andric     if (HasSrcLoc)
3865ffd83dbSDimitry Andric       FnStats.NumLocalVarSourceLocations++;
3870b57cec5SDimitry Andric     if (HasLoc)
3885ffd83dbSDimitry Andric       FnStats.NumLocalVarLocations++;
3890b57cec5SDimitry Andric   }
3900b57cec5SDimitry Andric }
3910b57cec5SDimitry Andric 
3920b57cec5SDimitry Andric /// Recursively collect debug info quality metrics.
393480093f4SDimitry Andric static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
394480093f4SDimitry Andric                                   std::string VarPrefix, uint64_t BytesInScope,
395480093f4SDimitry Andric                                   uint32_t InlineDepth,
3960b57cec5SDimitry Andric                                   StringMap<PerFunctionStats> &FnStatMap,
3978bcb0991SDimitry Andric                                   GlobalStats &GlobalStats,
3988bcb0991SDimitry Andric                                   LocationStats &LocStats) {
3990b57cec5SDimitry Andric   const dwarf::Tag Tag = Die.getTag();
4005ffd83dbSDimitry Andric   // Skip function types.
4015ffd83dbSDimitry Andric   if (Tag == dwarf::DW_TAG_subroutine_type)
4025ffd83dbSDimitry Andric     return;
4035ffd83dbSDimitry Andric 
4045ffd83dbSDimitry Andric   // Handle any kind of lexical scope.
4050b57cec5SDimitry Andric   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
4060b57cec5SDimitry Andric   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
4070b57cec5SDimitry Andric   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
4080b57cec5SDimitry Andric   if (IsFunction || IsInlinedFunction || IsBlock) {
4090b57cec5SDimitry Andric 
4100b57cec5SDimitry Andric     // Reset VarPrefix when entering a new function.
4110b57cec5SDimitry Andric     if (Die.getTag() == dwarf::DW_TAG_subprogram ||
4120b57cec5SDimitry Andric         Die.getTag() == dwarf::DW_TAG_inlined_subroutine)
4130b57cec5SDimitry Andric       VarPrefix = "v";
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric     // Ignore forward declarations.
4160b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_declaration))
4170b57cec5SDimitry Andric       return;
4180b57cec5SDimitry Andric 
4190b57cec5SDimitry Andric     // Check for call sites.
4200b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
4210b57cec5SDimitry Andric       GlobalStats.CallSiteEntries++;
4220b57cec5SDimitry Andric 
4230b57cec5SDimitry Andric     // PC Ranges.
4240b57cec5SDimitry Andric     auto RangesOrError = Die.getAddressRanges();
4250b57cec5SDimitry Andric     if (!RangesOrError) {
4260b57cec5SDimitry Andric       llvm::consumeError(RangesOrError.takeError());
4270b57cec5SDimitry Andric       return;
4280b57cec5SDimitry Andric     }
4290b57cec5SDimitry Andric 
4300b57cec5SDimitry Andric     auto Ranges = RangesOrError.get();
4310b57cec5SDimitry Andric     uint64_t BytesInThisScope = 0;
4320b57cec5SDimitry Andric     for (auto Range : Ranges)
4330b57cec5SDimitry Andric       BytesInThisScope += Range.HighPC - Range.LowPC;
4340b57cec5SDimitry Andric 
4350b57cec5SDimitry Andric     // Count the function.
4360b57cec5SDimitry Andric     if (!IsBlock) {
4370b57cec5SDimitry Andric       // Skip over abstract origins.
4380b57cec5SDimitry Andric       if (Die.find(dwarf::DW_AT_inline))
4390b57cec5SDimitry Andric         return;
4405ffd83dbSDimitry Andric       std::string FnID = constructDieID(Die);
4415ffd83dbSDimitry Andric       // We've seen an instance of this function.
4425ffd83dbSDimitry Andric       auto &FnStats = FnStatMap[FnID];
4435ffd83dbSDimitry Andric       FnStats.IsFunction = true;
4440b57cec5SDimitry Andric       if (IsInlinedFunction) {
4450b57cec5SDimitry Andric         FnStats.NumFnInlined++;
4460b57cec5SDimitry Andric         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
4470b57cec5SDimitry Andric           FnStats.NumAbstractOrigins++;
4485ffd83dbSDimitry Andric       } else {
4495ffd83dbSDimitry Andric         FnStats.NumFnOutOfLine++;
4500b57cec5SDimitry Andric       }
4510b57cec5SDimitry Andric       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
4520b57cec5SDimitry Andric           Die.findRecursively(dwarf::DW_AT_decl_line))
4530b57cec5SDimitry Andric         FnStats.HasSourceLocation = true;
4545ffd83dbSDimitry Andric       // Update function prefix.
4555ffd83dbSDimitry Andric       FnPrefix = FnID;
4560b57cec5SDimitry Andric     }
4570b57cec5SDimitry Andric 
4580b57cec5SDimitry Andric     if (BytesInThisScope) {
4590b57cec5SDimitry Andric       BytesInScope = BytesInThisScope;
4600b57cec5SDimitry Andric       if (IsFunction)
4610b57cec5SDimitry Andric         GlobalStats.FunctionSize += BytesInThisScope;
4620b57cec5SDimitry Andric       else if (IsInlinedFunction && InlineDepth == 0)
4630b57cec5SDimitry Andric         GlobalStats.InlineFunctionSize += BytesInThisScope;
4640b57cec5SDimitry Andric     }
4650b57cec5SDimitry Andric   } else {
4660b57cec5SDimitry Andric     // Not a scope, visit the Die itself. It could be a variable.
467480093f4SDimitry Andric     collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
468480093f4SDimitry Andric                        FnStatMap, GlobalStats, LocStats);
4690b57cec5SDimitry Andric   }
4700b57cec5SDimitry Andric 
4710b57cec5SDimitry Andric   // Set InlineDepth correctly for child recursion
4720b57cec5SDimitry Andric   if (IsFunction)
4730b57cec5SDimitry Andric     InlineDepth = 0;
4740b57cec5SDimitry Andric   else if (IsInlinedFunction)
4750b57cec5SDimitry Andric     ++InlineDepth;
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric   // Traverse children.
4780b57cec5SDimitry Andric   unsigned LexicalBlockIndex = 0;
4795ffd83dbSDimitry Andric   unsigned FormalParameterIndex = 0;
4800b57cec5SDimitry Andric   DWARFDie Child = Die.getFirstChild();
4810b57cec5SDimitry Andric   while (Child) {
4820b57cec5SDimitry Andric     std::string ChildVarPrefix = VarPrefix;
4830b57cec5SDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
4840b57cec5SDimitry Andric       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
4855ffd83dbSDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
4865ffd83dbSDimitry Andric       ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
4870b57cec5SDimitry Andric 
488480093f4SDimitry Andric     collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope,
489480093f4SDimitry Andric                           InlineDepth, FnStatMap, GlobalStats, LocStats);
4900b57cec5SDimitry Andric     Child = Child.getSibling();
4910b57cec5SDimitry Andric   }
4920b57cec5SDimitry Andric }
4930b57cec5SDimitry Andric 
494*e8d8bef9SDimitry Andric /// Print human-readable output.
4950b57cec5SDimitry Andric /// \{
496*e8d8bef9SDimitry Andric static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
497*e8d8bef9SDimitry Andric   J.attribute(Key, Value);
4980b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
4990b57cec5SDimitry Andric }
5005ffd83dbSDimitry Andric 
501*e8d8bef9SDimitry Andric static void printLocationStats(json::OStream &J, const char *Key,
5028bcb0991SDimitry Andric                                std::vector<unsigned> &LocationStats) {
503*e8d8bef9SDimitry Andric   J.attribute(
504*e8d8bef9SDimitry Andric       (Twine(Key) + " with 0% of parent scope covered by DW_AT_location").str(),
505*e8d8bef9SDimitry Andric       LocationStats[0]);
5065ffd83dbSDimitry Andric   LLVM_DEBUG(
5075ffd83dbSDimitry Andric       llvm::dbgs() << Key
5085ffd83dbSDimitry Andric                    << " with 0% of parent scope covered by DW_AT_location: \\"
5098bcb0991SDimitry Andric                    << LocationStats[0] << '\n');
510*e8d8bef9SDimitry Andric   J.attribute(
511*e8d8bef9SDimitry Andric       (Twine(Key) + " with (0%,10%) of parent scope covered by DW_AT_location")
512*e8d8bef9SDimitry Andric           .str(),
513*e8d8bef9SDimitry Andric       LocationStats[1]);
5145ffd83dbSDimitry Andric   LLVM_DEBUG(llvm::dbgs()
5155ffd83dbSDimitry Andric              << Key
5165ffd83dbSDimitry Andric              << " with (0%,10%) of parent scope covered by DW_AT_location: "
5178bcb0991SDimitry Andric              << LocationStats[1] << '\n');
5188bcb0991SDimitry Andric   for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
519*e8d8bef9SDimitry Andric     J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
520*e8d8bef9SDimitry Andric                  Twine(i * 10) + "%) of parent scope covered by DW_AT_location")
521*e8d8bef9SDimitry Andric                     .str(),
522*e8d8bef9SDimitry Andric                 LocationStats[i]);
5238bcb0991SDimitry Andric     LLVM_DEBUG(llvm::dbgs()
524480093f4SDimitry Andric                << Key << " with [" << (i - 1) * 10 << "%," << i * 10
5255ffd83dbSDimitry Andric                << "%) of parent scope covered by DW_AT_location: "
5265ffd83dbSDimitry Andric                << LocationStats[i]);
5278bcb0991SDimitry Andric   }
528*e8d8bef9SDimitry Andric   J.attribute(
529*e8d8bef9SDimitry Andric       (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
530*e8d8bef9SDimitry Andric           .str(),
531*e8d8bef9SDimitry Andric       LocationStats[NumOfCoverageCategories - 1]);
5325ffd83dbSDimitry Andric   LLVM_DEBUG(
5335ffd83dbSDimitry Andric       llvm::dbgs() << Key
5345ffd83dbSDimitry Andric                    << " with 100% of parent scope covered by DW_AT_location: "
5358bcb0991SDimitry Andric                    << LocationStats[NumOfCoverageCategories - 1]);
5368bcb0991SDimitry Andric }
5375ffd83dbSDimitry Andric 
538*e8d8bef9SDimitry Andric static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
5395ffd83dbSDimitry Andric   for (const auto &DebugSec : Sizes.DebugSectionSizes)
540*e8d8bef9SDimitry Andric     J.attribute((Twine("#bytes in ") + DebugSec.getKey()).str(),
541*e8d8bef9SDimitry Andric                 int64_t(DebugSec.getValue()));
5425ffd83dbSDimitry Andric }
5435ffd83dbSDimitry Andric 
5440b57cec5SDimitry Andric /// \}
5450b57cec5SDimitry Andric 
5460b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext.
5470b57cec5SDimitry Andric ///
5480b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few
5490b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked
5500b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect
5510b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly
5520b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different
5530b57cec5SDimitry Andric /// compilers is.
5545ffd83dbSDimitry Andric bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
5555ffd83dbSDimitry Andric                                           const Twine &Filename,
5565ffd83dbSDimitry Andric                                           raw_ostream &OS) {
5570b57cec5SDimitry Andric   StringRef FormatName = Obj.getFileFormatName();
5580b57cec5SDimitry Andric   GlobalStats GlobalStats;
5598bcb0991SDimitry Andric   LocationStats LocStats;
5600b57cec5SDimitry Andric   StringMap<PerFunctionStats> Statistics;
5610b57cec5SDimitry Andric   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
5620b57cec5SDimitry Andric     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
563480093f4SDimitry Andric       collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats,
564480093f4SDimitry Andric                             LocStats);
5650b57cec5SDimitry Andric 
5665ffd83dbSDimitry Andric   /// Collect the sizes of debug sections.
5675ffd83dbSDimitry Andric   SectionSizes Sizes;
5685ffd83dbSDimitry Andric   calculateSectionSizes(Obj, Sizes, Filename);
5695ffd83dbSDimitry Andric 
5700b57cec5SDimitry Andric   /// The version number should be increased every time the algorithm is changed
5710b57cec5SDimitry Andric   /// (including bug fixes). New metrics may be added without increasing the
5720b57cec5SDimitry Andric   /// version.
573*e8d8bef9SDimitry Andric   unsigned Version = 6;
5740b57cec5SDimitry Andric   unsigned VarParamTotal = 0;
5750b57cec5SDimitry Andric   unsigned VarParamUnique = 0;
5760b57cec5SDimitry Andric   unsigned VarParamWithLoc = 0;
5770b57cec5SDimitry Andric   unsigned NumFunctions = 0;
5780b57cec5SDimitry Andric   unsigned NumInlinedFunctions = 0;
5790b57cec5SDimitry Andric   unsigned NumFuncsWithSrcLoc = 0;
5800b57cec5SDimitry Andric   unsigned NumAbstractOrigins = 0;
5810b57cec5SDimitry Andric   unsigned ParamTotal = 0;
5820b57cec5SDimitry Andric   unsigned ParamWithType = 0;
5830b57cec5SDimitry Andric   unsigned ParamWithLoc = 0;
5840b57cec5SDimitry Andric   unsigned ParamWithSrcLoc = 0;
5855ffd83dbSDimitry Andric   unsigned LocalVarTotal = 0;
5865ffd83dbSDimitry Andric   unsigned LocalVarWithType = 0;
5875ffd83dbSDimitry Andric   unsigned LocalVarWithSrcLoc = 0;
5885ffd83dbSDimitry Andric   unsigned LocalVarWithLoc = 0;
5890b57cec5SDimitry Andric   for (auto &Entry : Statistics) {
5900b57cec5SDimitry Andric     PerFunctionStats &Stats = Entry.getValue();
5915ffd83dbSDimitry Andric     unsigned TotalVars = Stats.VarsInFunction.size() *
5925ffd83dbSDimitry Andric                          (Stats.NumFnInlined + Stats.NumFnOutOfLine);
5935ffd83dbSDimitry Andric     // Count variables in global scope.
5945ffd83dbSDimitry Andric     if (!Stats.IsFunction)
5955ffd83dbSDimitry Andric       TotalVars =
5965ffd83dbSDimitry Andric           Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
5970b57cec5SDimitry Andric     unsigned Constants = Stats.ConstantMembers;
5980b57cec5SDimitry Andric     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
5990b57cec5SDimitry Andric     VarParamTotal += TotalVars;
6000b57cec5SDimitry Andric     VarParamUnique += Stats.VarsInFunction.size();
6010b57cec5SDimitry Andric     LLVM_DEBUG(for (auto &V
6020b57cec5SDimitry Andric                     : Stats.VarsInFunction) llvm::dbgs()
6030b57cec5SDimitry Andric                << Entry.getKey() << ": " << V.getKey() << "\n");
6040b57cec5SDimitry Andric     NumFunctions += Stats.IsFunction;
6050b57cec5SDimitry Andric     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
6060b57cec5SDimitry Andric     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
6070b57cec5SDimitry Andric     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
6080b57cec5SDimitry Andric     ParamTotal += Stats.NumParams;
6090b57cec5SDimitry Andric     ParamWithType += Stats.NumParamTypes;
6100b57cec5SDimitry Andric     ParamWithLoc += Stats.NumParamLocations;
6110b57cec5SDimitry Andric     ParamWithSrcLoc += Stats.NumParamSourceLocations;
6125ffd83dbSDimitry Andric     LocalVarTotal += Stats.NumLocalVars;
6135ffd83dbSDimitry Andric     LocalVarWithType += Stats.NumLocalVarTypes;
6145ffd83dbSDimitry Andric     LocalVarWithLoc += Stats.NumLocalVarLocations;
6155ffd83dbSDimitry Andric     LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
6160b57cec5SDimitry Andric   }
6170b57cec5SDimitry Andric 
6180b57cec5SDimitry Andric   // Print summary.
6190b57cec5SDimitry Andric   OS.SetBufferSize(1024);
620*e8d8bef9SDimitry Andric   json::OStream J(OS, 2);
621*e8d8bef9SDimitry Andric   J.objectBegin();
622*e8d8bef9SDimitry Andric   J.attribute("version", Version);
6230b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
6240b57cec5SDimitry Andric              llvm::dbgs() << "---------------------------------\n");
6255ffd83dbSDimitry Andric 
626*e8d8bef9SDimitry Andric   printDatum(J, "file", Filename.str());
627*e8d8bef9SDimitry Andric   printDatum(J, "format", FormatName);
6285ffd83dbSDimitry Andric 
629*e8d8bef9SDimitry Andric   printDatum(J, "#functions", NumFunctions);
630*e8d8bef9SDimitry Andric   printDatum(J, "#functions with location", NumFuncsWithSrcLoc);
631*e8d8bef9SDimitry Andric   printDatum(J, "#inlined functions", NumInlinedFunctions);
632*e8d8bef9SDimitry Andric   printDatum(J, "#inlined functions with abstract origins", NumAbstractOrigins);
6335ffd83dbSDimitry Andric 
6345ffd83dbSDimitry Andric   // This includes local variables and formal parameters.
635*e8d8bef9SDimitry Andric   printDatum(J, "#unique source variables", VarParamUnique);
636*e8d8bef9SDimitry Andric   printDatum(J, "#source variables", VarParamTotal);
637*e8d8bef9SDimitry Andric   printDatum(J, "#source variables with location", VarParamWithLoc);
6385ffd83dbSDimitry Andric 
639*e8d8bef9SDimitry Andric   printDatum(J, "#call site entries", GlobalStats.CallSiteEntries);
640*e8d8bef9SDimitry Andric   printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs);
641*e8d8bef9SDimitry Andric   printDatum(J, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
6425ffd83dbSDimitry Andric 
643*e8d8bef9SDimitry Andric   printDatum(J, "sum_all_variables(#bytes in parent scope)",
6445ffd83dbSDimitry Andric              GlobalStats.ScopeBytes);
645*e8d8bef9SDimitry Andric   printDatum(J,
646*e8d8bef9SDimitry Andric              "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
647*e8d8bef9SDimitry Andric              GlobalStats.TotalBytesCovered);
648*e8d8bef9SDimitry Andric   printDatum(J,
6495ffd83dbSDimitry Andric              "sum_all_variables(#bytes in parent scope covered by "
6505ffd83dbSDimitry Andric              "DW_AT_location)",
6515ffd83dbSDimitry Andric              GlobalStats.ScopeBytesCovered);
652*e8d8bef9SDimitry Andric   printDatum(J,
6535ffd83dbSDimitry Andric              "sum_all_variables(#bytes in parent scope covered by "
6545ffd83dbSDimitry Andric              "DW_OP_entry_value)",
6558bcb0991SDimitry Andric              GlobalStats.ScopeEntryValueBytesCovered);
6565ffd83dbSDimitry Andric 
657*e8d8bef9SDimitry Andric   printDatum(J, "sum_all_params(#bytes in parent scope)",
658480093f4SDimitry Andric              GlobalStats.ParamScopeBytes);
659*e8d8bef9SDimitry Andric   printDatum(J,
6605ffd83dbSDimitry Andric              "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
6618bcb0991SDimitry Andric              GlobalStats.ParamScopeBytesCovered);
662*e8d8bef9SDimitry Andric   printDatum(J,
6635ffd83dbSDimitry Andric              "sum_all_params(#bytes in parent scope covered by "
6645ffd83dbSDimitry Andric              "DW_OP_entry_value)",
6658bcb0991SDimitry Andric              GlobalStats.ParamScopeEntryValueBytesCovered);
6665ffd83dbSDimitry Andric 
667*e8d8bef9SDimitry Andric   printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
6685ffd83dbSDimitry Andric              GlobalStats.LocalVarScopeBytes);
669*e8d8bef9SDimitry Andric   printDatum(J,
6705ffd83dbSDimitry Andric              "sum_all_local_vars(#bytes in parent scope covered by "
6715ffd83dbSDimitry Andric              "DW_AT_location)",
6725ffd83dbSDimitry Andric              GlobalStats.LocalVarScopeBytesCovered);
673*e8d8bef9SDimitry Andric   printDatum(J,
6745ffd83dbSDimitry Andric              "sum_all_local_vars(#bytes in parent scope covered by "
6755ffd83dbSDimitry Andric              "DW_OP_entry_value)",
6765ffd83dbSDimitry Andric              GlobalStats.LocalVarScopeEntryValueBytesCovered);
6775ffd83dbSDimitry Andric 
678*e8d8bef9SDimitry Andric   printDatum(J, "#bytes within functions", GlobalStats.FunctionSize);
679*e8d8bef9SDimitry Andric   printDatum(J, "#bytes within inlined functions",
6805ffd83dbSDimitry Andric              GlobalStats.InlineFunctionSize);
6815ffd83dbSDimitry Andric 
6825ffd83dbSDimitry Andric   // Print the summary for formal parameters.
683*e8d8bef9SDimitry Andric   printDatum(J, "#params", ParamTotal);
684*e8d8bef9SDimitry Andric   printDatum(J, "#params with source location", ParamWithSrcLoc);
685*e8d8bef9SDimitry Andric   printDatum(J, "#params with type", ParamWithType);
686*e8d8bef9SDimitry Andric   printDatum(J, "#params with binary location", ParamWithLoc);
6875ffd83dbSDimitry Andric 
6885ffd83dbSDimitry Andric   // Print the summary for local variables.
689*e8d8bef9SDimitry Andric   printDatum(J, "#local vars", LocalVarTotal);
690*e8d8bef9SDimitry Andric   printDatum(J, "#local vars with source location", LocalVarWithSrcLoc);
691*e8d8bef9SDimitry Andric   printDatum(J, "#local vars with type", LocalVarWithType);
692*e8d8bef9SDimitry Andric   printDatum(J, "#local vars with binary location", LocalVarWithLoc);
6935ffd83dbSDimitry Andric 
6945ffd83dbSDimitry Andric   // Print the debug section sizes.
695*e8d8bef9SDimitry Andric   printSectionSizes(J, Sizes);
6965ffd83dbSDimitry Andric 
6975ffd83dbSDimitry Andric   // Print the location statistics for variables (includes local variables
6985ffd83dbSDimitry Andric   // and formal parameters).
699*e8d8bef9SDimitry Andric   printDatum(J, "#variables processed by location statistics",
7008bcb0991SDimitry Andric              LocStats.NumVarParam);
701*e8d8bef9SDimitry Andric   printLocationStats(J, "#variables", LocStats.VarParamLocStats);
702*e8d8bef9SDimitry Andric   printLocationStats(J, "#variables - entry values",
7038bcb0991SDimitry Andric                      LocStats.VarParamNonEntryValLocStats);
7045ffd83dbSDimitry Andric 
7055ffd83dbSDimitry Andric   // Print the location statistics for formal parameters.
706*e8d8bef9SDimitry Andric   printDatum(J, "#params processed by location statistics", LocStats.NumParam);
707*e8d8bef9SDimitry Andric   printLocationStats(J, "#params", LocStats.ParamLocStats);
708*e8d8bef9SDimitry Andric   printLocationStats(J, "#params - entry values",
7098bcb0991SDimitry Andric                      LocStats.ParamNonEntryValLocStats);
7105ffd83dbSDimitry Andric 
7115ffd83dbSDimitry Andric   // Print the location statistics for local variables.
712*e8d8bef9SDimitry Andric   printDatum(J, "#local vars processed by location statistics",
7135ffd83dbSDimitry Andric              LocStats.NumVar);
714*e8d8bef9SDimitry Andric   printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
715*e8d8bef9SDimitry Andric   printLocationStats(J, "#local vars - entry values",
7165ffd83dbSDimitry Andric                      LocStats.LocalVarNonEntryValLocStats);
717*e8d8bef9SDimitry Andric   J.objectEnd();
718*e8d8bef9SDimitry Andric   OS << '\n';
7190b57cec5SDimitry Andric   LLVM_DEBUG(
7200b57cec5SDimitry Andric       llvm::dbgs() << "Total Availability: "
7210b57cec5SDimitry Andric                    << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal)
7220b57cec5SDimitry Andric                    << "%\n";
7230b57cec5SDimitry Andric       llvm::dbgs() << "PC Ranges covered: "
7240b57cec5SDimitry Andric                    << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
725480093f4SDimitry Andric                                       GlobalStats.ScopeBytes)
7260b57cec5SDimitry Andric                    << "%\n");
7270b57cec5SDimitry Andric   return true;
7280b57cec5SDimitry Andric }
729