xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp (revision 4824e7fd18a1223177218d4aec1b3c6c5c4a444e)
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/StringSet.h"
120b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h"
130b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
140b57cec5SDimitry Andric #include "llvm/Object/ObjectFile.h"
158bcb0991SDimitry Andric #include "llvm/Support/JSON.h"
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfdump"
180b57cec5SDimitry Andric using namespace llvm;
195ffd83dbSDimitry Andric using namespace llvm::dwarfdump;
205ffd83dbSDimitry Andric using namespace llvm::object;
210b57cec5SDimitry Andric 
22fe6060f1SDimitry Andric namespace {
238bcb0991SDimitry Andric /// This represents the number of categories of debug location coverage being
248bcb0991SDimitry Andric /// calculated. The first category is the number of variables with 0% location
258bcb0991SDimitry Andric /// coverage, but the last category is the number of variables with 100%
268bcb0991SDimitry Andric /// location coverage.
278bcb0991SDimitry Andric constexpr int NumOfCoverageCategories = 12;
288bcb0991SDimitry Andric 
29fe6060f1SDimitry Andric /// This is used for zero location coverage bucket.
30fe6060f1SDimitry Andric constexpr unsigned ZeroCoverageBucket = 0;
31fe6060f1SDimitry Andric 
32349cc55cSDimitry Andric /// The UINT64_MAX is used as an indication of the overflow.
33349cc55cSDimitry Andric constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
34349cc55cSDimitry Andric 
35fe6060f1SDimitry Andric /// This represents variables DIE offsets.
36fe6060f1SDimitry Andric using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
37fe6060f1SDimitry Andric /// This maps function DIE offset to its variables.
38fe6060f1SDimitry Andric using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
39fe6060f1SDimitry Andric /// This represents function DIE offsets containing an abstract_origin.
40fe6060f1SDimitry Andric using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
41fe6060f1SDimitry Andric 
42349cc55cSDimitry Andric /// This represents a data type for the stats and it helps us to
43349cc55cSDimitry Andric /// detect an overflow.
44349cc55cSDimitry Andric /// NOTE: This can be implemented as a template if there is an another type
45349cc55cSDimitry Andric /// needing this.
46349cc55cSDimitry Andric struct SaturatingUINT64 {
47349cc55cSDimitry Andric   /// Number that represents the stats.
48349cc55cSDimitry Andric   uint64_t Value;
49349cc55cSDimitry Andric 
50349cc55cSDimitry Andric   SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
51349cc55cSDimitry Andric 
52349cc55cSDimitry Andric   void operator++(int) { return *this += 1; }
53349cc55cSDimitry Andric   void operator+=(uint64_t Value_) {
54349cc55cSDimitry Andric     if (Value != OverflowValue) {
55349cc55cSDimitry Andric       if (Value < OverflowValue - Value_)
56349cc55cSDimitry Andric         Value += Value_;
57349cc55cSDimitry Andric       else
58349cc55cSDimitry Andric         Value = OverflowValue;
59349cc55cSDimitry Andric     }
60349cc55cSDimitry Andric   }
61349cc55cSDimitry Andric };
62349cc55cSDimitry Andric 
63*4824e7fdSDimitry Andric /// Utility struct to store the full location of a DIE - its CU and offset.
64*4824e7fdSDimitry Andric struct DIELocation {
65*4824e7fdSDimitry Andric   DWARFUnit *DwUnit;
66*4824e7fdSDimitry Andric   uint64_t DIEOffset;
67*4824e7fdSDimitry Andric   DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
68*4824e7fdSDimitry Andric       : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
69*4824e7fdSDimitry Andric };
70*4824e7fdSDimitry Andric /// This represents DWARF locations of CrossCU referencing DIEs.
71*4824e7fdSDimitry Andric using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
72*4824e7fdSDimitry Andric 
73*4824e7fdSDimitry Andric /// This maps function DIE offset to its DWARF CU.
74*4824e7fdSDimitry Andric using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
75*4824e7fdSDimitry Andric 
760b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and
770b57cec5SDimitry Andric /// contains variables, such as a compile unit).
780b57cec5SDimitry Andric struct PerFunctionStats {
790b57cec5SDimitry Andric   /// Number of inlined instances of this function.
80349cc55cSDimitry Andric   uint64_t NumFnInlined = 0;
815ffd83dbSDimitry Andric   /// Number of out-of-line instances of this function.
82349cc55cSDimitry Andric   uint64_t NumFnOutOfLine = 0;
830b57cec5SDimitry Andric   /// Number of inlined instances that have abstract origins.
84349cc55cSDimitry Andric   uint64_t NumAbstractOrigins = 0;
850b57cec5SDimitry Andric   /// Number of variables and parameters with location across all inlined
860b57cec5SDimitry Andric   /// instances.
87349cc55cSDimitry Andric   uint64_t TotalVarWithLoc = 0;
880b57cec5SDimitry Andric   /// Number of constants with location across all inlined instances.
89349cc55cSDimitry Andric   uint64_t ConstantMembers = 0;
905ffd83dbSDimitry Andric   /// Number of arificial variables, parameters or members across all instances.
91349cc55cSDimitry Andric   uint64_t NumArtificial = 0;
920b57cec5SDimitry Andric   /// List of all Variables and parameters in this function.
930b57cec5SDimitry Andric   StringSet<> VarsInFunction;
940b57cec5SDimitry Andric   /// Compile units also cover a PC range, but have this flag set to false.
950b57cec5SDimitry Andric   bool IsFunction = false;
960b57cec5SDimitry Andric   /// Function has source location information.
970b57cec5SDimitry Andric   bool HasSourceLocation = false;
980b57cec5SDimitry Andric   /// Number of function parameters.
99349cc55cSDimitry Andric   uint64_t NumParams = 0;
1000b57cec5SDimitry Andric   /// Number of function parameters with source location.
101349cc55cSDimitry Andric   uint64_t NumParamSourceLocations = 0;
1020b57cec5SDimitry Andric   /// Number of function parameters with type.
103349cc55cSDimitry Andric   uint64_t NumParamTypes = 0;
1040b57cec5SDimitry Andric   /// Number of function parameters with a DW_AT_location.
105349cc55cSDimitry Andric   uint64_t NumParamLocations = 0;
1065ffd83dbSDimitry Andric   /// Number of local variables.
107349cc55cSDimitry Andric   uint64_t NumLocalVars = 0;
1085ffd83dbSDimitry Andric   /// Number of local variables with source location.
109349cc55cSDimitry Andric   uint64_t NumLocalVarSourceLocations = 0;
1105ffd83dbSDimitry Andric   /// Number of local variables with type.
111349cc55cSDimitry Andric   uint64_t NumLocalVarTypes = 0;
1125ffd83dbSDimitry Andric   /// Number of local variables with DW_AT_location.
113349cc55cSDimitry Andric   uint64_t NumLocalVarLocations = 0;
1140b57cec5SDimitry Andric };
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs.
1170b57cec5SDimitry Andric struct GlobalStats {
1180b57cec5SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations.
119349cc55cSDimitry Andric   SaturatingUINT64 TotalBytesCovered = 0;
120e8d8bef9SDimitry Andric   /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
121349cc55cSDimitry Andric   SaturatingUINT64 ScopeBytesCovered = 0;
122480093f4SDimitry Andric   /// Total number of PC range bytes in each variable's enclosing scope.
123349cc55cSDimitry Andric   SaturatingUINT64 ScopeBytes = 0;
1248bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1258bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value).
126349cc55cSDimitry Andric   SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
1278bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations of
1288bcb0991SDimitry Andric   /// formal parameters.
129349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeBytesCovered = 0;
1305ffd83dbSDimitry Andric   /// Total number of PC range bytes in each parameter's enclosing scope.
131349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeBytes = 0;
1328bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1338bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value) (only for parameters).
134349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
1358bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations (only for local
1368bcb0991SDimitry Andric   /// variables).
137349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeBytesCovered = 0;
1385ffd83dbSDimitry Andric   /// Total number of PC range bytes in each local variable's enclosing scope.
139349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeBytes = 0;
1408bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1418bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value) (only for local variables).
142349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
1438bcb0991SDimitry Andric   /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
144349cc55cSDimitry Andric   SaturatingUINT64 CallSiteEntries = 0;
1458bcb0991SDimitry Andric   /// Total number of call site DIEs (DW_TAG_call_site).
146349cc55cSDimitry Andric   SaturatingUINT64 CallSiteDIEs = 0;
1478bcb0991SDimitry Andric   /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
148349cc55cSDimitry Andric   SaturatingUINT64 CallSiteParamDIEs = 0;
1490b57cec5SDimitry Andric   /// Total byte size of concrete functions. This byte size includes
1500b57cec5SDimitry Andric   /// inline functions contained in the concrete functions.
151349cc55cSDimitry Andric   SaturatingUINT64 FunctionSize = 0;
1520b57cec5SDimitry Andric   /// Total byte size of inlined functions. This is the total number of bytes
1530b57cec5SDimitry Andric   /// for the top inline functions within concrete functions. This can help
1540b57cec5SDimitry Andric   /// tune the inline settings when compiling to match user expectations.
155349cc55cSDimitry Andric   SaturatingUINT64 InlineFunctionSize = 0;
1568bcb0991SDimitry Andric };
1578bcb0991SDimitry Andric 
1588bcb0991SDimitry Andric /// Holds accumulated debug location statistics about local variables and
1598bcb0991SDimitry Andric /// formal parameters.
1608bcb0991SDimitry Andric struct LocationStats {
1618bcb0991SDimitry Andric   /// Map the scope coverage decile to the number of variables in the decile.
1628bcb0991SDimitry Andric   /// The first element of the array (at the index zero) represents the number
1638bcb0991SDimitry Andric   /// of variables with the no debug location at all, but the last element
1648bcb0991SDimitry Andric   /// in the vector represents the number of fully covered variables within
1658bcb0991SDimitry Andric   /// its scope.
166349cc55cSDimitry Andric   std::vector<SaturatingUINT64> VarParamLocStats{
167349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1688bcb0991SDimitry Andric   /// Map non debug entry values coverage.
169349cc55cSDimitry Andric   std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
170349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1718bcb0991SDimitry Andric   /// The debug location statistics for formal parameters.
172349cc55cSDimitry Andric   std::vector<SaturatingUINT64> ParamLocStats{
173349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1748bcb0991SDimitry Andric   /// Map non debug entry values coverage for formal parameters.
175349cc55cSDimitry Andric   std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
176349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1778bcb0991SDimitry Andric   /// The debug location statistics for local variables.
178349cc55cSDimitry Andric   std::vector<SaturatingUINT64> LocalVarLocStats{
179349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1808bcb0991SDimitry Andric   /// Map non debug entry values coverage for local variables.
181349cc55cSDimitry Andric   std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
182349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1838bcb0991SDimitry Andric   /// Total number of local variables and function parameters processed.
184349cc55cSDimitry Andric   SaturatingUINT64 NumVarParam = 0;
1858bcb0991SDimitry Andric   /// Total number of formal parameters processed.
186349cc55cSDimitry Andric   SaturatingUINT64 NumParam = 0;
1878bcb0991SDimitry Andric   /// Total number of local variables processed.
188349cc55cSDimitry Andric   SaturatingUINT64 NumVar = 0;
1890b57cec5SDimitry Andric };
1905ffd83dbSDimitry Andric } // namespace
1910b57cec5SDimitry Andric 
1928bcb0991SDimitry Andric /// Collect debug location statistics for one DIE.
193e8d8bef9SDimitry Andric static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
194349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &VarParamLocStats,
195349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &ParamLocStats,
196349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &LocalVarLocStats,
1975ffd83dbSDimitry Andric                             bool IsParam, bool IsLocalVar) {
198e8d8bef9SDimitry Andric   auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
1998bcb0991SDimitry Andric     // No debug location at all for the variable.
200e8d8bef9SDimitry Andric     if (ScopeBytesCovered == 0)
2018bcb0991SDimitry Andric       return 0;
2028bcb0991SDimitry Andric     // Fully covered variable within its scope.
203e8d8bef9SDimitry Andric     if (ScopeBytesCovered >= BytesInScope)
2048bcb0991SDimitry Andric       return NumOfCoverageCategories - 1;
2058bcb0991SDimitry Andric     // Get covered range (e.g. 20%-29%).
206e8d8bef9SDimitry Andric     unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
2078bcb0991SDimitry Andric     LocBucket /= 10;
2088bcb0991SDimitry Andric     return LocBucket + 1;
2098bcb0991SDimitry Andric   };
2108bcb0991SDimitry Andric 
2118bcb0991SDimitry Andric   unsigned CoverageBucket = getCoverageBucket();
212fe6060f1SDimitry Andric 
213349cc55cSDimitry Andric   VarParamLocStats[CoverageBucket].Value++;
2148bcb0991SDimitry Andric   if (IsParam)
215349cc55cSDimitry Andric     ParamLocStats[CoverageBucket].Value++;
2168bcb0991SDimitry Andric   else if (IsLocalVar)
217349cc55cSDimitry Andric     LocalVarLocStats[CoverageBucket].Value++;
2185ffd83dbSDimitry Andric }
219fe6060f1SDimitry Andric 
2205ffd83dbSDimitry Andric /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
2215ffd83dbSDimitry Andric /// and DeclLine. The identifier aims to be unique for any unique entities,
2225ffd83dbSDimitry Andric /// but keeping the same among different instances of the same entity.
2235ffd83dbSDimitry Andric static std::string constructDieID(DWARFDie Die,
2245ffd83dbSDimitry Andric                                   StringRef Prefix = StringRef()) {
2255ffd83dbSDimitry Andric   std::string IDStr;
2265ffd83dbSDimitry Andric   llvm::raw_string_ostream ID(IDStr);
2275ffd83dbSDimitry Andric   ID << Prefix
2285ffd83dbSDimitry Andric      << Die.getName(DINameKind::LinkageName);
2295ffd83dbSDimitry Andric 
2305ffd83dbSDimitry Andric   // Prefix + Name is enough for local variables and parameters.
2315ffd83dbSDimitry Andric   if (!Prefix.empty() && !Prefix.equals("g"))
2325ffd83dbSDimitry Andric     return ID.str();
2335ffd83dbSDimitry Andric 
2345ffd83dbSDimitry Andric   auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
2355ffd83dbSDimitry Andric   std::string File;
2365ffd83dbSDimitry Andric   if (DeclFile) {
2375ffd83dbSDimitry Andric     DWARFUnit *U = Die.getDwarfUnit();
2385ffd83dbSDimitry Andric     if (const auto *LT = U->getContext().getLineTableForUnit(U))
2395ffd83dbSDimitry Andric       if (LT->getFileNameByIndex(
2405ffd83dbSDimitry Andric               dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
2415ffd83dbSDimitry Andric               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
2425ffd83dbSDimitry Andric         File = std::string(sys::path::filename(File));
2435ffd83dbSDimitry Andric   }
2445ffd83dbSDimitry Andric   ID << ":" << (File.empty() ? "/" : File);
2455ffd83dbSDimitry Andric   ID << ":"
2465ffd83dbSDimitry Andric      << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
2475ffd83dbSDimitry Andric   return ID.str();
2488bcb0991SDimitry Andric }
2498bcb0991SDimitry Andric 
250e8d8bef9SDimitry Andric /// Return the number of bytes in the overlap of ranges A and B.
251e8d8bef9SDimitry Andric static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
252e8d8bef9SDimitry Andric   uint64_t Lower = std::max(A.LowPC, B.LowPC);
253e8d8bef9SDimitry Andric   uint64_t Upper = std::min(A.HighPC, B.HighPC);
254e8d8bef9SDimitry Andric   if (Lower >= Upper)
255e8d8bef9SDimitry Andric     return 0;
256e8d8bef9SDimitry Andric   return Upper - Lower;
257e8d8bef9SDimitry Andric }
258e8d8bef9SDimitry Andric 
2590b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE.
260fe6060f1SDimitry Andric static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
261fe6060f1SDimitry Andric                                const std::string &VarPrefix,
262fe6060f1SDimitry Andric                                uint64_t BytesInScope, uint32_t InlineDepth,
2630b57cec5SDimitry Andric                                StringMap<PerFunctionStats> &FnStatMap,
2648bcb0991SDimitry Andric                                GlobalStats &GlobalStats,
265fe6060f1SDimitry Andric                                LocationStats &LocStats,
266fe6060f1SDimitry Andric                                AbstractOriginVarsTy *AbstractOriginVariables) {
267fe6060f1SDimitry Andric   const dwarf::Tag Tag = Die.getTag();
268fe6060f1SDimitry Andric   // Skip CU node.
269fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_compile_unit)
270fe6060f1SDimitry Andric     return;
271fe6060f1SDimitry Andric 
2720b57cec5SDimitry Andric   bool HasLoc = false;
2730b57cec5SDimitry Andric   bool HasSrcLoc = false;
2740b57cec5SDimitry Andric   bool HasType = false;
275e8d8bef9SDimitry Andric   uint64_t TotalBytesCovered = 0;
276e8d8bef9SDimitry Andric   uint64_t ScopeBytesCovered = 0;
2778bcb0991SDimitry Andric   uint64_t BytesEntryValuesCovered = 0;
2788bcb0991SDimitry Andric   auto &FnStats = FnStatMap[FnPrefix];
279fe6060f1SDimitry Andric   bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
280fe6060f1SDimitry Andric   bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
281fe6060f1SDimitry Andric   bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
2825ffd83dbSDimitry Andric                           Die.find(dwarf::DW_AT_const_value);
2830b57cec5SDimitry Andric 
284fe6060f1SDimitry Andric   // For zero covered inlined variables the locstats will be
285fe6060f1SDimitry Andric   // calculated later.
286fe6060f1SDimitry Andric   bool DeferLocStats = false;
287fe6060f1SDimitry Andric 
288fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
2898bcb0991SDimitry Andric     GlobalStats.CallSiteDIEs++;
2900b57cec5SDimitry Andric     return;
2910b57cec5SDimitry Andric   }
2920b57cec5SDimitry Andric 
293fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_call_site_parameter ||
294fe6060f1SDimitry Andric       Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
2958bcb0991SDimitry Andric     GlobalStats.CallSiteParamDIEs++;
2968bcb0991SDimitry Andric     return;
2978bcb0991SDimitry Andric   }
2988bcb0991SDimitry Andric 
2995ffd83dbSDimitry Andric   if (!IsParam && !IsLocalVar && !IsConstantMember) {
3000b57cec5SDimitry Andric     // Not a variable or constant member.
3010b57cec5SDimitry Andric     return;
3020b57cec5SDimitry Andric   }
3030b57cec5SDimitry Andric 
3045ffd83dbSDimitry Andric   // Ignore declarations of global variables.
3055ffd83dbSDimitry Andric   if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
3065ffd83dbSDimitry Andric     return;
3075ffd83dbSDimitry Andric 
3080b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
3090b57cec5SDimitry Andric       Die.findRecursively(dwarf::DW_AT_decl_line))
3100b57cec5SDimitry Andric     HasSrcLoc = true;
3110b57cec5SDimitry Andric 
3120b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_type))
3130b57cec5SDimitry Andric     HasType = true;
3140b57cec5SDimitry Andric 
315fe6060f1SDimitry Andric   if (Die.find(dwarf::DW_AT_abstract_origin)) {
316fe6060f1SDimitry Andric     if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
317fe6060f1SDimitry Andric       if (AbstractOriginVariables) {
318fe6060f1SDimitry Andric         auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
319fe6060f1SDimitry Andric         // Do not track this variable any more, since it has location
320fe6060f1SDimitry Andric         // coverage.
321fe6060f1SDimitry Andric         llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue());
322fe6060f1SDimitry Andric       }
323fe6060f1SDimitry Andric     } else {
324fe6060f1SDimitry Andric       // The locstats will be handled at the end of
325fe6060f1SDimitry Andric       // the collectStatsRecursive().
326fe6060f1SDimitry Andric       DeferLocStats = true;
327fe6060f1SDimitry Andric     }
328fe6060f1SDimitry Andric   }
329fe6060f1SDimitry Andric 
3308bcb0991SDimitry Andric   auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
3318bcb0991SDimitry Andric     DWARFUnit *U = Die.getDwarfUnit();
3328bcb0991SDimitry Andric     DataExtractor Data(toStringRef(D),
3338bcb0991SDimitry Andric                        Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
3345ffd83dbSDimitry Andric     DWARFExpression Expression(Data, U->getAddressByteSize(),
3355ffd83dbSDimitry Andric                                U->getFormParams().Format);
3368bcb0991SDimitry Andric     // Consider the expression containing the DW_OP_entry_value as
3378bcb0991SDimitry Andric     // an entry value.
338349cc55cSDimitry Andric     return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
3398bcb0991SDimitry Andric       return Op.getCode() == dwarf::DW_OP_entry_value ||
3408bcb0991SDimitry Andric              Op.getCode() == dwarf::DW_OP_GNU_entry_value;
3418bcb0991SDimitry Andric     });
3428bcb0991SDimitry Andric   };
3438bcb0991SDimitry Andric 
3440b57cec5SDimitry Andric   if (Die.find(dwarf::DW_AT_const_value)) {
3450b57cec5SDimitry Andric     // This catches constant members *and* variables.
3460b57cec5SDimitry Andric     HasLoc = true;
347e8d8bef9SDimitry Andric     ScopeBytesCovered = BytesInScope;
348e8d8bef9SDimitry Andric     TotalBytesCovered = BytesInScope;
3490b57cec5SDimitry Andric   } else {
3500b57cec5SDimitry Andric     // Handle variables and function arguments.
351480093f4SDimitry Andric     Expected<std::vector<DWARFLocationExpression>> Loc =
352480093f4SDimitry Andric         Die.getLocations(dwarf::DW_AT_location);
353480093f4SDimitry Andric     if (!Loc) {
354480093f4SDimitry Andric       consumeError(Loc.takeError());
3550b57cec5SDimitry Andric     } else {
356480093f4SDimitry Andric       HasLoc = true;
357480093f4SDimitry Andric       // Get PC coverage.
358480093f4SDimitry Andric       auto Default = find_if(
359480093f4SDimitry Andric           *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
360480093f4SDimitry Andric       if (Default != Loc->end()) {
3610b57cec5SDimitry Andric         // Assume the entire range is covered by a single location.
362e8d8bef9SDimitry Andric         ScopeBytesCovered = BytesInScope;
363e8d8bef9SDimitry Andric         TotalBytesCovered = BytesInScope;
364480093f4SDimitry Andric       } else {
365e8d8bef9SDimitry Andric         // Caller checks this Expected result already, it cannot fail.
366e8d8bef9SDimitry Andric         auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
367480093f4SDimitry Andric         for (auto Entry : *Loc) {
368e8d8bef9SDimitry Andric           TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
369e8d8bef9SDimitry Andric           uint64_t ScopeBytesCoveredByEntry = 0;
370e8d8bef9SDimitry Andric           // Calculate how many bytes of the parent scope this entry covers.
371e8d8bef9SDimitry Andric           // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
372e8d8bef9SDimitry Andric           // address ranges defined by the bounded location descriptions of a
373e8d8bef9SDimitry Andric           // location list may overlap". So in theory a variable can have
374e8d8bef9SDimitry Andric           // multiple simultaneous locations, which would make this calculation
375e8d8bef9SDimitry Andric           // misleading because we will count the overlapped areas
376e8d8bef9SDimitry Andric           // twice. However, clang does not currently emit DWARF like this.
377e8d8bef9SDimitry Andric           for (DWARFAddressRange R : ScopeRanges) {
378e8d8bef9SDimitry Andric             ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
379e8d8bef9SDimitry Andric           }
380e8d8bef9SDimitry Andric           ScopeBytesCovered += ScopeBytesCoveredByEntry;
381480093f4SDimitry Andric           if (IsEntryValue(Entry.Expr))
382e8d8bef9SDimitry Andric             BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
383480093f4SDimitry Andric         }
3840b57cec5SDimitry Andric       }
3850b57cec5SDimitry Andric     }
3860b57cec5SDimitry Andric   }
3870b57cec5SDimitry Andric 
3888bcb0991SDimitry Andric   // Calculate the debug location statistics.
389fe6060f1SDimitry Andric   if (BytesInScope && !DeferLocStats) {
390349cc55cSDimitry Andric     LocStats.NumVarParam.Value++;
3918bcb0991SDimitry Andric     if (IsParam)
392349cc55cSDimitry Andric       LocStats.NumParam.Value++;
3938bcb0991SDimitry Andric     else if (IsLocalVar)
394349cc55cSDimitry Andric       LocStats.NumVar.Value++;
3958bcb0991SDimitry Andric 
396e8d8bef9SDimitry Andric     collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
3975ffd83dbSDimitry Andric                     LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
3988bcb0991SDimitry Andric                     IsLocalVar);
3998bcb0991SDimitry Andric     // Non debug entry values coverage statistics.
400e8d8bef9SDimitry Andric     collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
4018bcb0991SDimitry Andric                     LocStats.VarParamNonEntryValLocStats,
4028bcb0991SDimitry Andric                     LocStats.ParamNonEntryValLocStats,
4035ffd83dbSDimitry Andric                     LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
4048bcb0991SDimitry Andric   }
4058bcb0991SDimitry Andric 
4060b57cec5SDimitry Andric   // Collect PC range coverage data.
4070b57cec5SDimitry Andric   if (DWARFDie D =
4080b57cec5SDimitry Andric           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
4090b57cec5SDimitry Andric     Die = D;
4105ffd83dbSDimitry Andric 
4115ffd83dbSDimitry Andric   std::string VarID = constructDieID(Die, VarPrefix);
4125ffd83dbSDimitry Andric   FnStats.VarsInFunction.insert(VarID);
4135ffd83dbSDimitry Andric 
414e8d8bef9SDimitry Andric   GlobalStats.TotalBytesCovered += TotalBytesCovered;
4150b57cec5SDimitry Andric   if (BytesInScope) {
416e8d8bef9SDimitry Andric     GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
417480093f4SDimitry Andric     GlobalStats.ScopeBytes += BytesInScope;
4188bcb0991SDimitry Andric     GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
4198bcb0991SDimitry Andric     if (IsParam) {
420e8d8bef9SDimitry Andric       GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
421480093f4SDimitry Andric       GlobalStats.ParamScopeBytes += BytesInScope;
4228bcb0991SDimitry Andric       GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
4238bcb0991SDimitry Andric     } else if (IsLocalVar) {
424e8d8bef9SDimitry Andric       GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
4255ffd83dbSDimitry Andric       GlobalStats.LocalVarScopeBytes += BytesInScope;
4265ffd83dbSDimitry Andric       GlobalStats.LocalVarScopeEntryValueBytesCovered +=
4275ffd83dbSDimitry Andric           BytesEntryValuesCovered;
4288bcb0991SDimitry Andric     }
429349cc55cSDimitry Andric     assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
4300b57cec5SDimitry Andric   }
4315ffd83dbSDimitry Andric 
4325ffd83dbSDimitry Andric   if (IsConstantMember) {
4335ffd83dbSDimitry Andric     FnStats.ConstantMembers++;
4345ffd83dbSDimitry Andric     return;
4355ffd83dbSDimitry Andric   }
4365ffd83dbSDimitry Andric 
4375ffd83dbSDimitry Andric   FnStats.TotalVarWithLoc += (unsigned)HasLoc;
4385ffd83dbSDimitry Andric 
4395ffd83dbSDimitry Andric   if (Die.find(dwarf::DW_AT_artificial)) {
4405ffd83dbSDimitry Andric     FnStats.NumArtificial++;
4415ffd83dbSDimitry Andric     return;
4425ffd83dbSDimitry Andric   }
4435ffd83dbSDimitry Andric 
4448bcb0991SDimitry Andric   if (IsParam) {
4450b57cec5SDimitry Andric     FnStats.NumParams++;
4460b57cec5SDimitry Andric     if (HasType)
4470b57cec5SDimitry Andric       FnStats.NumParamTypes++;
4480b57cec5SDimitry Andric     if (HasSrcLoc)
4490b57cec5SDimitry Andric       FnStats.NumParamSourceLocations++;
4500b57cec5SDimitry Andric     if (HasLoc)
4510b57cec5SDimitry Andric       FnStats.NumParamLocations++;
4528bcb0991SDimitry Andric   } else if (IsLocalVar) {
4535ffd83dbSDimitry Andric     FnStats.NumLocalVars++;
4540b57cec5SDimitry Andric     if (HasType)
4555ffd83dbSDimitry Andric       FnStats.NumLocalVarTypes++;
4560b57cec5SDimitry Andric     if (HasSrcLoc)
4575ffd83dbSDimitry Andric       FnStats.NumLocalVarSourceLocations++;
4580b57cec5SDimitry Andric     if (HasLoc)
4595ffd83dbSDimitry Andric       FnStats.NumLocalVarLocations++;
4600b57cec5SDimitry Andric   }
4610b57cec5SDimitry Andric }
4620b57cec5SDimitry Andric 
463fe6060f1SDimitry Andric /// Recursively collect variables from subprogram with DW_AT_inline attribute.
464fe6060f1SDimitry Andric static void collectAbstractOriginFnInfo(
465fe6060f1SDimitry Andric     DWARFDie Die, uint64_t SPOffset,
466*4824e7fdSDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
467*4824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
468fe6060f1SDimitry Andric   DWARFDie Child = Die.getFirstChild();
469fe6060f1SDimitry Andric   while (Child) {
470fe6060f1SDimitry Andric     const dwarf::Tag ChildTag = Child.getTag();
471fe6060f1SDimitry Andric     if (ChildTag == dwarf::DW_TAG_formal_parameter ||
472*4824e7fdSDimitry Andric         ChildTag == dwarf::DW_TAG_variable) {
473fe6060f1SDimitry Andric       GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
474*4824e7fdSDimitry Andric       LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
475*4824e7fdSDimitry Andric     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
476*4824e7fdSDimitry Andric       collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
477*4824e7fdSDimitry Andric                                   LocalAbstractOriginFnInfo);
478fe6060f1SDimitry Andric     Child = Child.getSibling();
479fe6060f1SDimitry Andric   }
480fe6060f1SDimitry Andric }
481fe6060f1SDimitry Andric 
4820b57cec5SDimitry Andric /// Recursively collect debug info quality metrics.
483fe6060f1SDimitry Andric static void collectStatsRecursive(
484fe6060f1SDimitry Andric     DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
485fe6060f1SDimitry Andric     uint64_t BytesInScope, uint32_t InlineDepth,
486fe6060f1SDimitry Andric     StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
487*4824e7fdSDimitry Andric     LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
488fe6060f1SDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
489*4824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
490fe6060f1SDimitry Andric     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
491fe6060f1SDimitry Andric     AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
492fe6060f1SDimitry Andric   // Skip NULL nodes.
493fe6060f1SDimitry Andric   if (Die.isNULL())
494fe6060f1SDimitry Andric     return;
495fe6060f1SDimitry Andric 
4960b57cec5SDimitry Andric   const dwarf::Tag Tag = Die.getTag();
4975ffd83dbSDimitry Andric   // Skip function types.
4985ffd83dbSDimitry Andric   if (Tag == dwarf::DW_TAG_subroutine_type)
4995ffd83dbSDimitry Andric     return;
5005ffd83dbSDimitry Andric 
5015ffd83dbSDimitry Andric   // Handle any kind of lexical scope.
502fe6060f1SDimitry Andric   const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None;
5030b57cec5SDimitry Andric   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
5040b57cec5SDimitry Andric   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
5050b57cec5SDimitry Andric   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
506fe6060f1SDimitry Andric   // We want to know how many variables (with abstract_origin) don't have
507fe6060f1SDimitry Andric   // location info.
508fe6060f1SDimitry Andric   const bool IsCandidateForZeroLocCovTracking =
509fe6060f1SDimitry Andric       (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
5100b57cec5SDimitry Andric 
511fe6060f1SDimitry Andric   AbstractOriginVarsTy AbstractOriginVars;
512fe6060f1SDimitry Andric 
513fe6060f1SDimitry Andric   // Get the vars of the inlined fn, so the locstats
514fe6060f1SDimitry Andric   // reports the missing vars (with coverage 0%).
515fe6060f1SDimitry Andric   if (IsCandidateForZeroLocCovTracking) {
516fe6060f1SDimitry Andric     auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
517fe6060f1SDimitry Andric     if (OffsetFn) {
518fe6060f1SDimitry Andric       uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
519*4824e7fdSDimitry Andric       if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
520*4824e7fdSDimitry Andric         AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
521fe6060f1SDimitry Andric         AbstractOriginVarsPtr = &AbstractOriginVars;
522fe6060f1SDimitry Andric       } else {
523*4824e7fdSDimitry Andric         // This means that the DW_AT_inline fn copy is out of order
524*4824e7fdSDimitry Andric         // or that the abstract_origin references another CU,
525fe6060f1SDimitry Andric         // so this abstract origin instance will be processed later.
526fe6060f1SDimitry Andric         FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
527fe6060f1SDimitry Andric         AbstractOriginVarsPtr = nullptr;
528fe6060f1SDimitry Andric       }
529fe6060f1SDimitry Andric     }
530fe6060f1SDimitry Andric   }
531fe6060f1SDimitry Andric 
532fe6060f1SDimitry Andric   if (IsFunction || IsInlinedFunction || IsBlock) {
5330b57cec5SDimitry Andric     // Reset VarPrefix when entering a new function.
534fe6060f1SDimitry Andric     if (IsFunction || IsInlinedFunction)
5350b57cec5SDimitry Andric       VarPrefix = "v";
5360b57cec5SDimitry Andric 
5370b57cec5SDimitry Andric     // Ignore forward declarations.
5380b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_declaration))
5390b57cec5SDimitry Andric       return;
5400b57cec5SDimitry Andric 
5410b57cec5SDimitry Andric     // Check for call sites.
5420b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
5430b57cec5SDimitry Andric       GlobalStats.CallSiteEntries++;
5440b57cec5SDimitry Andric 
5450b57cec5SDimitry Andric     // PC Ranges.
5460b57cec5SDimitry Andric     auto RangesOrError = Die.getAddressRanges();
5470b57cec5SDimitry Andric     if (!RangesOrError) {
5480b57cec5SDimitry Andric       llvm::consumeError(RangesOrError.takeError());
5490b57cec5SDimitry Andric       return;
5500b57cec5SDimitry Andric     }
5510b57cec5SDimitry Andric 
5520b57cec5SDimitry Andric     auto Ranges = RangesOrError.get();
5530b57cec5SDimitry Andric     uint64_t BytesInThisScope = 0;
5540b57cec5SDimitry Andric     for (auto Range : Ranges)
5550b57cec5SDimitry Andric       BytesInThisScope += Range.HighPC - Range.LowPC;
5560b57cec5SDimitry Andric 
5570b57cec5SDimitry Andric     // Count the function.
5580b57cec5SDimitry Andric     if (!IsBlock) {
559fe6060f1SDimitry Andric       // Skip over abstract origins, but collect variables
560fe6060f1SDimitry Andric       // from it so it can be used for location statistics
561fe6060f1SDimitry Andric       // for inlined instancies.
562fe6060f1SDimitry Andric       if (Die.find(dwarf::DW_AT_inline)) {
563fe6060f1SDimitry Andric         uint64_t SPOffset = Die.getOffset();
564*4824e7fdSDimitry Andric         AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
565*4824e7fdSDimitry Andric         collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
566*4824e7fdSDimitry Andric                                     LocalAbstractOriginFnInfo);
5670b57cec5SDimitry Andric         return;
568fe6060f1SDimitry Andric       }
569fe6060f1SDimitry Andric 
5705ffd83dbSDimitry Andric       std::string FnID = constructDieID(Die);
5715ffd83dbSDimitry Andric       // We've seen an instance of this function.
5725ffd83dbSDimitry Andric       auto &FnStats = FnStatMap[FnID];
5735ffd83dbSDimitry Andric       FnStats.IsFunction = true;
5740b57cec5SDimitry Andric       if (IsInlinedFunction) {
5750b57cec5SDimitry Andric         FnStats.NumFnInlined++;
5760b57cec5SDimitry Andric         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
5770b57cec5SDimitry Andric           FnStats.NumAbstractOrigins++;
5785ffd83dbSDimitry Andric       } else {
5795ffd83dbSDimitry Andric         FnStats.NumFnOutOfLine++;
5800b57cec5SDimitry Andric       }
5810b57cec5SDimitry Andric       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
5820b57cec5SDimitry Andric           Die.findRecursively(dwarf::DW_AT_decl_line))
5830b57cec5SDimitry Andric         FnStats.HasSourceLocation = true;
5845ffd83dbSDimitry Andric       // Update function prefix.
5855ffd83dbSDimitry Andric       FnPrefix = FnID;
5860b57cec5SDimitry Andric     }
5870b57cec5SDimitry Andric 
5880b57cec5SDimitry Andric     if (BytesInThisScope) {
5890b57cec5SDimitry Andric       BytesInScope = BytesInThisScope;
5900b57cec5SDimitry Andric       if (IsFunction)
5910b57cec5SDimitry Andric         GlobalStats.FunctionSize += BytesInThisScope;
5920b57cec5SDimitry Andric       else if (IsInlinedFunction && InlineDepth == 0)
5930b57cec5SDimitry Andric         GlobalStats.InlineFunctionSize += BytesInThisScope;
5940b57cec5SDimitry Andric     }
5950b57cec5SDimitry Andric   } else {
5960b57cec5SDimitry Andric     // Not a scope, visit the Die itself. It could be a variable.
597480093f4SDimitry Andric     collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
598fe6060f1SDimitry Andric                        FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
5990b57cec5SDimitry Andric   }
6000b57cec5SDimitry Andric 
6010b57cec5SDimitry Andric   // Set InlineDepth correctly for child recursion
6020b57cec5SDimitry Andric   if (IsFunction)
6030b57cec5SDimitry Andric     InlineDepth = 0;
6040b57cec5SDimitry Andric   else if (IsInlinedFunction)
6050b57cec5SDimitry Andric     ++InlineDepth;
6060b57cec5SDimitry Andric 
6070b57cec5SDimitry Andric   // Traverse children.
6080b57cec5SDimitry Andric   unsigned LexicalBlockIndex = 0;
6095ffd83dbSDimitry Andric   unsigned FormalParameterIndex = 0;
6100b57cec5SDimitry Andric   DWARFDie Child = Die.getFirstChild();
6110b57cec5SDimitry Andric   while (Child) {
6120b57cec5SDimitry Andric     std::string ChildVarPrefix = VarPrefix;
6130b57cec5SDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
6140b57cec5SDimitry Andric       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
6155ffd83dbSDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
6165ffd83dbSDimitry Andric       ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
6170b57cec5SDimitry Andric 
618fe6060f1SDimitry Andric     collectStatsRecursive(
619fe6060f1SDimitry Andric         Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
620*4824e7fdSDimitry Andric         GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
621*4824e7fdSDimitry Andric         LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
622*4824e7fdSDimitry Andric         AbstractOriginVarsPtr);
6230b57cec5SDimitry Andric     Child = Child.getSibling();
6240b57cec5SDimitry Andric   }
625fe6060f1SDimitry Andric 
626fe6060f1SDimitry Andric   if (!IsCandidateForZeroLocCovTracking)
627fe6060f1SDimitry Andric     return;
628fe6060f1SDimitry Andric 
629fe6060f1SDimitry Andric   // After we have processed all vars of the inlined function (or function with
630fe6060f1SDimitry Andric   // an abstract_origin), we want to know how many variables have no location.
631fe6060f1SDimitry Andric   for (auto Offset : AbstractOriginVars) {
632fe6060f1SDimitry Andric     LocStats.NumVarParam++;
633fe6060f1SDimitry Andric     LocStats.VarParamLocStats[ZeroCoverageBucket]++;
634fe6060f1SDimitry Andric     auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
635fe6060f1SDimitry Andric     if (!FnDie)
636fe6060f1SDimitry Andric       continue;
637fe6060f1SDimitry Andric     auto Tag = FnDie.getTag();
638fe6060f1SDimitry Andric     if (Tag == dwarf::DW_TAG_formal_parameter) {
639fe6060f1SDimitry Andric       LocStats.NumParam++;
640fe6060f1SDimitry Andric       LocStats.ParamLocStats[ZeroCoverageBucket]++;
641fe6060f1SDimitry Andric     } else if (Tag == dwarf::DW_TAG_variable) {
642fe6060f1SDimitry Andric       LocStats.NumVar++;
643fe6060f1SDimitry Andric       LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
644fe6060f1SDimitry Andric     }
645fe6060f1SDimitry Andric   }
6460b57cec5SDimitry Andric }
6470b57cec5SDimitry Andric 
648e8d8bef9SDimitry Andric /// Print human-readable output.
6490b57cec5SDimitry Andric /// \{
650e8d8bef9SDimitry Andric static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
651349cc55cSDimitry Andric   if (Value == OverflowValue)
652349cc55cSDimitry Andric     J.attribute(Key, "overflowed");
653349cc55cSDimitry Andric   else
654e8d8bef9SDimitry Andric     J.attribute(Key, Value);
655349cc55cSDimitry Andric 
6560b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
6570b57cec5SDimitry Andric }
6585ffd83dbSDimitry Andric 
659e8d8bef9SDimitry Andric static void printLocationStats(json::OStream &J, const char *Key,
660349cc55cSDimitry Andric                                std::vector<SaturatingUINT64> &LocationStats) {
661349cc55cSDimitry Andric   if (LocationStats[0].Value == OverflowValue)
662349cc55cSDimitry Andric     J.attribute((Twine(Key) +
663349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
664349cc55cSDimitry Andric                     .str(),
665349cc55cSDimitry Andric                 "overflowed");
666349cc55cSDimitry Andric   else
667e8d8bef9SDimitry Andric     J.attribute(
668349cc55cSDimitry Andric         (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
669349cc55cSDimitry Andric             .str(),
670349cc55cSDimitry Andric         LocationStats[0].Value);
6715ffd83dbSDimitry Andric   LLVM_DEBUG(
6725ffd83dbSDimitry Andric       llvm::dbgs() << Key
6735ffd83dbSDimitry Andric                    << " with 0% of parent scope covered by DW_AT_location: \\"
674349cc55cSDimitry Andric                    << LocationStats[0].Value << '\n');
675349cc55cSDimitry Andric 
676349cc55cSDimitry Andric   if (LocationStats[1].Value == OverflowValue)
677349cc55cSDimitry Andric     J.attribute((Twine(Key) +
678349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
679e8d8bef9SDimitry Andric                     .str(),
680349cc55cSDimitry Andric                 "overflowed");
681349cc55cSDimitry Andric   else
682349cc55cSDimitry Andric     J.attribute((Twine(Key) +
683349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
684349cc55cSDimitry Andric                     .str(),
685349cc55cSDimitry Andric                 LocationStats[1].Value);
6865ffd83dbSDimitry Andric   LLVM_DEBUG(llvm::dbgs()
6875ffd83dbSDimitry Andric              << Key
6885ffd83dbSDimitry Andric              << " with (0%,10%) of parent scope covered by DW_AT_location: "
689349cc55cSDimitry Andric              << LocationStats[1].Value << '\n');
690349cc55cSDimitry Andric 
6918bcb0991SDimitry Andric   for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
692349cc55cSDimitry Andric     if (LocationStats[i].Value == OverflowValue)
693e8d8bef9SDimitry Andric       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
694349cc55cSDimitry Andric                    Twine(i * 10) +
695349cc55cSDimitry Andric                    "%) of parent scope covered by DW_AT_location")
696e8d8bef9SDimitry Andric                       .str(),
697349cc55cSDimitry Andric                   "overflowed");
698349cc55cSDimitry Andric     else
699349cc55cSDimitry Andric       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
700349cc55cSDimitry Andric                    Twine(i * 10) +
701349cc55cSDimitry Andric                    "%) of parent scope covered by DW_AT_location")
702349cc55cSDimitry Andric                       .str(),
703349cc55cSDimitry Andric                   LocationStats[i].Value);
7048bcb0991SDimitry Andric     LLVM_DEBUG(llvm::dbgs()
705480093f4SDimitry Andric                << Key << " with [" << (i - 1) * 10 << "%," << i * 10
7065ffd83dbSDimitry Andric                << "%) of parent scope covered by DW_AT_location: "
707349cc55cSDimitry Andric                << LocationStats[i].Value);
7088bcb0991SDimitry Andric   }
709349cc55cSDimitry Andric   if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
710e8d8bef9SDimitry Andric     J.attribute(
711e8d8bef9SDimitry Andric         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
712e8d8bef9SDimitry Andric             .str(),
713349cc55cSDimitry Andric         "overflowed");
714349cc55cSDimitry Andric   else
715349cc55cSDimitry Andric     J.attribute(
716349cc55cSDimitry Andric         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
717349cc55cSDimitry Andric             .str(),
718349cc55cSDimitry Andric         LocationStats[NumOfCoverageCategories - 1].Value);
7195ffd83dbSDimitry Andric   LLVM_DEBUG(
7205ffd83dbSDimitry Andric       llvm::dbgs() << Key
7215ffd83dbSDimitry Andric                    << " with 100% of parent scope covered by DW_AT_location: "
722349cc55cSDimitry Andric                    << LocationStats[NumOfCoverageCategories - 1].Value);
7238bcb0991SDimitry Andric }
7245ffd83dbSDimitry Andric 
725e8d8bef9SDimitry Andric static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
726fe6060f1SDimitry Andric   for (const auto &It : Sizes.DebugSectionSizes)
727fe6060f1SDimitry Andric     J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
728fe6060f1SDimitry Andric }
729fe6060f1SDimitry Andric 
730fe6060f1SDimitry Andric /// Stop tracking variables that contain abstract_origin with a location.
731fe6060f1SDimitry Andric /// This is used for out-of-order DW_AT_inline subprograms only.
732fe6060f1SDimitry Andric static void updateVarsWithAbstractOriginLocCovInfo(
733fe6060f1SDimitry Andric     DWARFDie FnDieWithAbstractOrigin,
734fe6060f1SDimitry Andric     AbstractOriginVarsTy &AbstractOriginVars) {
735fe6060f1SDimitry Andric   DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
736fe6060f1SDimitry Andric   while (Child) {
737fe6060f1SDimitry Andric     const dwarf::Tag ChildTag = Child.getTag();
738fe6060f1SDimitry Andric     if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
739fe6060f1SDimitry Andric          ChildTag == dwarf::DW_TAG_variable) &&
740fe6060f1SDimitry Andric         (Child.find(dwarf::DW_AT_location) ||
741fe6060f1SDimitry Andric          Child.find(dwarf::DW_AT_const_value))) {
742fe6060f1SDimitry Andric       auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
743fe6060f1SDimitry Andric       if (OffsetVar)
744fe6060f1SDimitry Andric         llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue());
745fe6060f1SDimitry Andric     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
746fe6060f1SDimitry Andric       updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
747fe6060f1SDimitry Andric     Child = Child.getSibling();
748fe6060f1SDimitry Andric   }
749fe6060f1SDimitry Andric }
750fe6060f1SDimitry Andric 
751fe6060f1SDimitry Andric /// Collect zero location coverage for inlined variables which refer to
752fe6060f1SDimitry Andric /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
753fe6060f1SDimitry Andric /// Also cover the variables of a concrete function (represented with
754fe6060f1SDimitry Andric /// the DW_TAG_subprogram) with an abstract_origin attribute.
755fe6060f1SDimitry Andric static void collectZeroLocCovForVarsWithAbstractOrigin(
756fe6060f1SDimitry Andric     DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
757*4824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
758fe6060f1SDimitry Andric     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
759*4824e7fdSDimitry Andric   // The next variable is used to filter out functions that have been processed,
760*4824e7fdSDimitry Andric   // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
761*4824e7fdSDimitry Andric   FunctionsWithAbstractOriginTy ProcessedFns;
762fe6060f1SDimitry Andric   for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
763fe6060f1SDimitry Andric     DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
764fe6060f1SDimitry Andric     auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
765fe6060f1SDimitry Andric     AbstractOriginVarsTy AbstractOriginVars;
766fe6060f1SDimitry Andric     if (!FnCopy)
767fe6060f1SDimitry Andric       continue;
768*4824e7fdSDimitry Andric     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
769*4824e7fdSDimitry Andric     // If there is no entry within LocalAbstractOriginFnInfo for the given
770*4824e7fdSDimitry Andric     // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
771*4824e7fdSDimitry Andric     // CrossCU referencing.
772*4824e7fdSDimitry Andric     if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
773*4824e7fdSDimitry Andric       continue;
774*4824e7fdSDimitry Andric     AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
775fe6060f1SDimitry Andric     updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
776fe6060f1SDimitry Andric                                            AbstractOriginVars);
777fe6060f1SDimitry Andric 
778fe6060f1SDimitry Andric     for (auto Offset : AbstractOriginVars) {
779fe6060f1SDimitry Andric       LocStats.NumVarParam++;
780fe6060f1SDimitry Andric       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
781fe6060f1SDimitry Andric       auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
782fe6060f1SDimitry Andric       if (Tag == dwarf::DW_TAG_formal_parameter) {
783fe6060f1SDimitry Andric         LocStats.NumParam++;
784fe6060f1SDimitry Andric         LocStats.ParamLocStats[ZeroCoverageBucket]++;
785fe6060f1SDimitry Andric       } else if (Tag == dwarf::DW_TAG_variable) {
786fe6060f1SDimitry Andric         LocStats.NumVar++;
787fe6060f1SDimitry Andric         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
788fe6060f1SDimitry Andric       }
789fe6060f1SDimitry Andric     }
790*4824e7fdSDimitry Andric     ProcessedFns.push_back(FnOffset);
791*4824e7fdSDimitry Andric   }
792*4824e7fdSDimitry Andric   for (auto ProcessedFn : ProcessedFns)
793*4824e7fdSDimitry Andric     llvm::erase_value(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
794*4824e7fdSDimitry Andric }
795*4824e7fdSDimitry Andric 
796*4824e7fdSDimitry Andric /// Collect zero location coverage for inlined variables which refer to
797*4824e7fdSDimitry Andric /// a DW_AT_inline copy of subprogram that is in a different CU.
798*4824e7fdSDimitry Andric static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
799*4824e7fdSDimitry Andric     LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
800*4824e7fdSDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
801*4824e7fdSDimitry Andric     CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
802*4824e7fdSDimitry Andric   for (const auto &CrossCUReferenceToBeResolved :
803*4824e7fdSDimitry Andric        CrossCUReferencesToBeResolved) {
804*4824e7fdSDimitry Andric     DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
805*4824e7fdSDimitry Andric     DWARFDie FnDIEWithCrossCUReferencing =
806*4824e7fdSDimitry Andric         DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
807*4824e7fdSDimitry Andric     auto FnCopy =
808*4824e7fdSDimitry Andric         FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
809*4824e7fdSDimitry Andric     if (!FnCopy)
810*4824e7fdSDimitry Andric       continue;
811*4824e7fdSDimitry Andric     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
812*4824e7fdSDimitry Andric     AbstractOriginVarsTy AbstractOriginVars =
813*4824e7fdSDimitry Andric         GlobalAbstractOriginFnInfo[FnCopyRawUValue];
814*4824e7fdSDimitry Andric     updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
815*4824e7fdSDimitry Andric                                            AbstractOriginVars);
816*4824e7fdSDimitry Andric     for (auto Offset : AbstractOriginVars) {
817*4824e7fdSDimitry Andric       LocStats.NumVarParam++;
818*4824e7fdSDimitry Andric       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
819*4824e7fdSDimitry Andric       auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
820*4824e7fdSDimitry Andric                      ->getDIEForOffset(Offset)
821*4824e7fdSDimitry Andric                      .getTag();
822*4824e7fdSDimitry Andric       if (Tag == dwarf::DW_TAG_formal_parameter) {
823*4824e7fdSDimitry Andric         LocStats.NumParam++;
824*4824e7fdSDimitry Andric         LocStats.ParamLocStats[ZeroCoverageBucket]++;
825*4824e7fdSDimitry Andric       } else if (Tag == dwarf::DW_TAG_variable) {
826*4824e7fdSDimitry Andric         LocStats.NumVar++;
827*4824e7fdSDimitry Andric         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
828*4824e7fdSDimitry Andric       }
829*4824e7fdSDimitry Andric     }
830fe6060f1SDimitry Andric   }
8315ffd83dbSDimitry Andric }
8325ffd83dbSDimitry Andric 
8330b57cec5SDimitry Andric /// \}
8340b57cec5SDimitry Andric 
8350b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext.
8360b57cec5SDimitry Andric ///
8370b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few
8380b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked
8390b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect
8400b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly
8410b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different
8420b57cec5SDimitry Andric /// compilers is.
8435ffd83dbSDimitry Andric bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
8445ffd83dbSDimitry Andric                                           const Twine &Filename,
8455ffd83dbSDimitry Andric                                           raw_ostream &OS) {
8460b57cec5SDimitry Andric   StringRef FormatName = Obj.getFileFormatName();
8470b57cec5SDimitry Andric   GlobalStats GlobalStats;
8488bcb0991SDimitry Andric   LocationStats LocStats;
8490b57cec5SDimitry Andric   StringMap<PerFunctionStats> Statistics;
850*4824e7fdSDimitry Andric   // This variable holds variable information for functions with
851*4824e7fdSDimitry Andric   // abstract_origin globally, across all CUs.
852*4824e7fdSDimitry Andric   AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
853*4824e7fdSDimitry Andric   // This variable holds information about the CU of a function with
854*4824e7fdSDimitry Andric   // abstract_origin.
855*4824e7fdSDimitry Andric   FunctionDIECUTyMap AbstractOriginFnCUs;
856*4824e7fdSDimitry Andric   CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
857fe6060f1SDimitry Andric   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
858fe6060f1SDimitry Andric     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
859*4824e7fdSDimitry Andric       // This variable holds variable information for functions with
860*4824e7fdSDimitry Andric       // abstract_origin, but just for the current CU.
861*4824e7fdSDimitry Andric       AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
862fe6060f1SDimitry Andric       FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
863fe6060f1SDimitry Andric 
864*4824e7fdSDimitry Andric       collectStatsRecursive(
865*4824e7fdSDimitry Andric           CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
866*4824e7fdSDimitry Andric           AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
867*4824e7fdSDimitry Andric           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
868fe6060f1SDimitry Andric 
869*4824e7fdSDimitry Andric       // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
870*4824e7fdSDimitry Andric       // out-of-order DWARF functions that have been processed within it,
871*4824e7fdSDimitry Andric       // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
872*4824e7fdSDimitry Andric       // references.
873fe6060f1SDimitry Andric       collectZeroLocCovForVarsWithAbstractOrigin(
874fe6060f1SDimitry Andric           CUDie.getDwarfUnit(), GlobalStats, LocStats,
875*4824e7fdSDimitry Andric           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
876*4824e7fdSDimitry Andric 
877*4824e7fdSDimitry Andric       // Collect all CrossCU references into CrossCUReferencesToBeResolved.
878*4824e7fdSDimitry Andric       for (auto CrossCUReferencingDIEOffset :
879*4824e7fdSDimitry Andric            FnsWithAbstractOriginToBeProcessed)
880*4824e7fdSDimitry Andric         CrossCUReferencesToBeResolved.push_back(
881*4824e7fdSDimitry Andric             DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
882fe6060f1SDimitry Andric     }
883fe6060f1SDimitry Andric   }
8840b57cec5SDimitry Andric 
885*4824e7fdSDimitry Andric   /// Resolve CrossCU references.
886*4824e7fdSDimitry Andric   collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
887*4824e7fdSDimitry Andric       LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
888*4824e7fdSDimitry Andric       CrossCUReferencesToBeResolved);
889*4824e7fdSDimitry Andric 
8905ffd83dbSDimitry Andric   /// Collect the sizes of debug sections.
8915ffd83dbSDimitry Andric   SectionSizes Sizes;
8925ffd83dbSDimitry Andric   calculateSectionSizes(Obj, Sizes, Filename);
8935ffd83dbSDimitry Andric 
8940b57cec5SDimitry Andric   /// The version number should be increased every time the algorithm is changed
8950b57cec5SDimitry Andric   /// (including bug fixes). New metrics may be added without increasing the
8960b57cec5SDimitry Andric   /// version.
897349cc55cSDimitry Andric   unsigned Version = 9;
898349cc55cSDimitry Andric   SaturatingUINT64 VarParamTotal = 0;
899349cc55cSDimitry Andric   SaturatingUINT64 VarParamUnique = 0;
900349cc55cSDimitry Andric   SaturatingUINT64 VarParamWithLoc = 0;
901349cc55cSDimitry Andric   SaturatingUINT64 NumFunctions = 0;
902349cc55cSDimitry Andric   SaturatingUINT64 NumInlinedFunctions = 0;
903349cc55cSDimitry Andric   SaturatingUINT64 NumFuncsWithSrcLoc = 0;
904349cc55cSDimitry Andric   SaturatingUINT64 NumAbstractOrigins = 0;
905349cc55cSDimitry Andric   SaturatingUINT64 ParamTotal = 0;
906349cc55cSDimitry Andric   SaturatingUINT64 ParamWithType = 0;
907349cc55cSDimitry Andric   SaturatingUINT64 ParamWithLoc = 0;
908349cc55cSDimitry Andric   SaturatingUINT64 ParamWithSrcLoc = 0;
909349cc55cSDimitry Andric   SaturatingUINT64 LocalVarTotal = 0;
910349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithType = 0;
911349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithSrcLoc = 0;
912349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithLoc = 0;
9130b57cec5SDimitry Andric   for (auto &Entry : Statistics) {
9140b57cec5SDimitry Andric     PerFunctionStats &Stats = Entry.getValue();
915349cc55cSDimitry Andric     uint64_t TotalVars = Stats.VarsInFunction.size() *
9165ffd83dbSDimitry Andric                          (Stats.NumFnInlined + Stats.NumFnOutOfLine);
9175ffd83dbSDimitry Andric     // Count variables in global scope.
9185ffd83dbSDimitry Andric     if (!Stats.IsFunction)
9195ffd83dbSDimitry Andric       TotalVars =
9205ffd83dbSDimitry Andric           Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
921349cc55cSDimitry Andric     uint64_t Constants = Stats.ConstantMembers;
9220b57cec5SDimitry Andric     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
9230b57cec5SDimitry Andric     VarParamTotal += TotalVars;
9240b57cec5SDimitry Andric     VarParamUnique += Stats.VarsInFunction.size();
9250b57cec5SDimitry Andric     LLVM_DEBUG(for (auto &V
9260b57cec5SDimitry Andric                     : Stats.VarsInFunction) llvm::dbgs()
9270b57cec5SDimitry Andric                << Entry.getKey() << ": " << V.getKey() << "\n");
9280b57cec5SDimitry Andric     NumFunctions += Stats.IsFunction;
9290b57cec5SDimitry Andric     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
9300b57cec5SDimitry Andric     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
9310b57cec5SDimitry Andric     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
9320b57cec5SDimitry Andric     ParamTotal += Stats.NumParams;
9330b57cec5SDimitry Andric     ParamWithType += Stats.NumParamTypes;
9340b57cec5SDimitry Andric     ParamWithLoc += Stats.NumParamLocations;
9350b57cec5SDimitry Andric     ParamWithSrcLoc += Stats.NumParamSourceLocations;
9365ffd83dbSDimitry Andric     LocalVarTotal += Stats.NumLocalVars;
9375ffd83dbSDimitry Andric     LocalVarWithType += Stats.NumLocalVarTypes;
9385ffd83dbSDimitry Andric     LocalVarWithLoc += Stats.NumLocalVarLocations;
9395ffd83dbSDimitry Andric     LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
9400b57cec5SDimitry Andric   }
9410b57cec5SDimitry Andric 
9420b57cec5SDimitry Andric   // Print summary.
9430b57cec5SDimitry Andric   OS.SetBufferSize(1024);
944e8d8bef9SDimitry Andric   json::OStream J(OS, 2);
945e8d8bef9SDimitry Andric   J.objectBegin();
946e8d8bef9SDimitry Andric   J.attribute("version", Version);
9470b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
9480b57cec5SDimitry Andric              llvm::dbgs() << "---------------------------------\n");
9495ffd83dbSDimitry Andric 
950e8d8bef9SDimitry Andric   printDatum(J, "file", Filename.str());
951e8d8bef9SDimitry Andric   printDatum(J, "format", FormatName);
9525ffd83dbSDimitry Andric 
953349cc55cSDimitry Andric   printDatum(J, "#functions", NumFunctions.Value);
954349cc55cSDimitry Andric   printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
955349cc55cSDimitry Andric   printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
956349cc55cSDimitry Andric   printDatum(J, "#inlined functions with abstract origins",
957349cc55cSDimitry Andric              NumAbstractOrigins.Value);
9585ffd83dbSDimitry Andric 
9595ffd83dbSDimitry Andric   // This includes local variables and formal parameters.
960349cc55cSDimitry Andric   printDatum(J, "#unique source variables", VarParamUnique.Value);
961349cc55cSDimitry Andric   printDatum(J, "#source variables", VarParamTotal.Value);
962349cc55cSDimitry Andric   printDatum(J, "#source variables with location", VarParamWithLoc.Value);
9635ffd83dbSDimitry Andric 
964349cc55cSDimitry Andric   printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
965349cc55cSDimitry Andric   printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
966349cc55cSDimitry Andric   printDatum(J, "#call site parameter DIEs",
967349cc55cSDimitry Andric              GlobalStats.CallSiteParamDIEs.Value);
9685ffd83dbSDimitry Andric 
969e8d8bef9SDimitry Andric   printDatum(J, "sum_all_variables(#bytes in parent scope)",
970349cc55cSDimitry Andric              GlobalStats.ScopeBytes.Value);
971e8d8bef9SDimitry Andric   printDatum(J,
972e8d8bef9SDimitry Andric              "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
973349cc55cSDimitry Andric              GlobalStats.TotalBytesCovered.Value);
974e8d8bef9SDimitry Andric   printDatum(J,
9755ffd83dbSDimitry Andric              "sum_all_variables(#bytes in parent scope covered by "
9765ffd83dbSDimitry Andric              "DW_AT_location)",
977349cc55cSDimitry Andric              GlobalStats.ScopeBytesCovered.Value);
978e8d8bef9SDimitry Andric   printDatum(J,
9795ffd83dbSDimitry Andric              "sum_all_variables(#bytes in parent scope covered by "
9805ffd83dbSDimitry Andric              "DW_OP_entry_value)",
981349cc55cSDimitry Andric              GlobalStats.ScopeEntryValueBytesCovered.Value);
9825ffd83dbSDimitry Andric 
983e8d8bef9SDimitry Andric   printDatum(J, "sum_all_params(#bytes in parent scope)",
984349cc55cSDimitry Andric              GlobalStats.ParamScopeBytes.Value);
985e8d8bef9SDimitry Andric   printDatum(J,
9865ffd83dbSDimitry Andric              "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
987349cc55cSDimitry Andric              GlobalStats.ParamScopeBytesCovered.Value);
988e8d8bef9SDimitry Andric   printDatum(J,
9895ffd83dbSDimitry Andric              "sum_all_params(#bytes in parent scope covered by "
9905ffd83dbSDimitry Andric              "DW_OP_entry_value)",
991349cc55cSDimitry Andric              GlobalStats.ParamScopeEntryValueBytesCovered.Value);
9925ffd83dbSDimitry Andric 
993e8d8bef9SDimitry Andric   printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
994349cc55cSDimitry Andric              GlobalStats.LocalVarScopeBytes.Value);
995e8d8bef9SDimitry Andric   printDatum(J,
9965ffd83dbSDimitry Andric              "sum_all_local_vars(#bytes in parent scope covered by "
9975ffd83dbSDimitry Andric              "DW_AT_location)",
998349cc55cSDimitry Andric              GlobalStats.LocalVarScopeBytesCovered.Value);
999e8d8bef9SDimitry Andric   printDatum(J,
10005ffd83dbSDimitry Andric              "sum_all_local_vars(#bytes in parent scope covered by "
10015ffd83dbSDimitry Andric              "DW_OP_entry_value)",
1002349cc55cSDimitry Andric              GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
10035ffd83dbSDimitry Andric 
1004349cc55cSDimitry Andric   printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
1005e8d8bef9SDimitry Andric   printDatum(J, "#bytes within inlined functions",
1006349cc55cSDimitry Andric              GlobalStats.InlineFunctionSize.Value);
10075ffd83dbSDimitry Andric 
10085ffd83dbSDimitry Andric   // Print the summary for formal parameters.
1009349cc55cSDimitry Andric   printDatum(J, "#params", ParamTotal.Value);
1010349cc55cSDimitry Andric   printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
1011349cc55cSDimitry Andric   printDatum(J, "#params with type", ParamWithType.Value);
1012349cc55cSDimitry Andric   printDatum(J, "#params with binary location", ParamWithLoc.Value);
10135ffd83dbSDimitry Andric 
10145ffd83dbSDimitry Andric   // Print the summary for local variables.
1015349cc55cSDimitry Andric   printDatum(J, "#local vars", LocalVarTotal.Value);
1016349cc55cSDimitry Andric   printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
1017349cc55cSDimitry Andric   printDatum(J, "#local vars with type", LocalVarWithType.Value);
1018349cc55cSDimitry Andric   printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
10195ffd83dbSDimitry Andric 
10205ffd83dbSDimitry Andric   // Print the debug section sizes.
1021e8d8bef9SDimitry Andric   printSectionSizes(J, Sizes);
10225ffd83dbSDimitry Andric 
10235ffd83dbSDimitry Andric   // Print the location statistics for variables (includes local variables
10245ffd83dbSDimitry Andric   // and formal parameters).
1025e8d8bef9SDimitry Andric   printDatum(J, "#variables processed by location statistics",
1026349cc55cSDimitry Andric              LocStats.NumVarParam.Value);
1027e8d8bef9SDimitry Andric   printLocationStats(J, "#variables", LocStats.VarParamLocStats);
1028e8d8bef9SDimitry Andric   printLocationStats(J, "#variables - entry values",
10298bcb0991SDimitry Andric                      LocStats.VarParamNonEntryValLocStats);
10305ffd83dbSDimitry Andric 
10315ffd83dbSDimitry Andric   // Print the location statistics for formal parameters.
1032349cc55cSDimitry Andric   printDatum(J, "#params processed by location statistics",
1033349cc55cSDimitry Andric              LocStats.NumParam.Value);
1034e8d8bef9SDimitry Andric   printLocationStats(J, "#params", LocStats.ParamLocStats);
1035e8d8bef9SDimitry Andric   printLocationStats(J, "#params - entry values",
10368bcb0991SDimitry Andric                      LocStats.ParamNonEntryValLocStats);
10375ffd83dbSDimitry Andric 
10385ffd83dbSDimitry Andric   // Print the location statistics for local variables.
1039e8d8bef9SDimitry Andric   printDatum(J, "#local vars processed by location statistics",
1040349cc55cSDimitry Andric              LocStats.NumVar.Value);
1041e8d8bef9SDimitry Andric   printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
1042e8d8bef9SDimitry Andric   printLocationStats(J, "#local vars - entry values",
10435ffd83dbSDimitry Andric                      LocStats.LocalVarNonEntryValLocStats);
1044e8d8bef9SDimitry Andric   J.objectEnd();
1045e8d8bef9SDimitry Andric   OS << '\n';
1046349cc55cSDimitry Andric   LLVM_DEBUG(llvm::dbgs() << "Total Availability: "
1047349cc55cSDimitry Andric                           << (int)std::round((VarParamWithLoc.Value * 100.0) /
1048349cc55cSDimitry Andric                                              VarParamTotal.Value)
10490b57cec5SDimitry Andric                           << "%\n";
10500b57cec5SDimitry Andric              llvm::dbgs() << "PC Ranges covered: "
1051349cc55cSDimitry Andric                           << (int)std::round(
1052349cc55cSDimitry Andric                                  (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1053349cc55cSDimitry Andric                                  GlobalStats.ScopeBytes.Value)
10540b57cec5SDimitry Andric                           << "%\n");
10550b57cec5SDimitry Andric   return true;
10560b57cec5SDimitry Andric }
1057