xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm-dwarfdump.h"
10 #include "llvm/ADT/DenseMap.h"
11 #include "llvm/ADT/DenseSet.h"
12 #include "llvm/ADT/StringSet.h"
13 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
14 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
15 #include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
16 #include "llvm/Object/ObjectFile.h"
17 #include "llvm/Support/JSON.h"
18 
19 #define DEBUG_TYPE "dwarfdump"
20 using namespace llvm;
21 using namespace llvm::dwarfdump;
22 using namespace llvm::object;
23 
24 namespace {
25 /// This represents the number of categories of debug location coverage being
26 /// calculated. The first category is the number of variables with 0% location
27 /// coverage, but the last category is the number of variables with 100%
28 /// location coverage.
29 constexpr int NumOfCoverageCategories = 12;
30 
31 /// This is used for zero location coverage bucket.
32 constexpr unsigned ZeroCoverageBucket = 0;
33 
34 /// The UINT64_MAX is used as an indication of the overflow.
35 constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
36 
37 /// This represents variables DIE offsets.
38 using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
39 /// This maps function DIE offset to its variables.
40 using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
41 /// This represents function DIE offsets containing an abstract_origin.
42 using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
43 
44 /// This represents a data type for the stats and it helps us to
45 /// detect an overflow.
46 /// NOTE: This can be implemented as a template if there is an another type
47 /// needing this.
48 struct SaturatingUINT64 {
49   /// Number that represents the stats.
50   uint64_t Value;
51 
SaturatingUINT64__anond5eb1a760111::SaturatingUINT6452   SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
53 
operator ++__anond5eb1a760111::SaturatingUINT6454   void operator++(int) { return *this += 1; }
operator +=__anond5eb1a760111::SaturatingUINT6455   void operator+=(uint64_t Value_) {
56     if (Value != OverflowValue) {
57       if (Value < OverflowValue - Value_)
58         Value += Value_;
59       else
60         Value = OverflowValue;
61     }
62   }
63 };
64 
65 /// Utility struct to store the full location of a DIE - its CU and offset.
66 struct DIELocation {
67   DWARFUnit *DwUnit;
68   uint64_t DIEOffset;
DIELocation__anond5eb1a760111::DIELocation69   DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
70       : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
71 };
72 /// This represents DWARF locations of CrossCU referencing DIEs.
73 using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
74 
75 /// This maps function DIE offset to its DWARF CU.
76 using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
77 
78 /// Holds statistics for one function (or other entity that has a PC range and
79 /// contains variables, such as a compile unit).
80 struct PerFunctionStats {
81   /// Number of inlined instances of this function.
82   uint64_t NumFnInlined = 0;
83   /// Number of out-of-line instances of this function.
84   uint64_t NumFnOutOfLine = 0;
85   /// Number of inlined instances that have abstract origins.
86   uint64_t NumAbstractOrigins = 0;
87   /// Number of variables and parameters with location across all inlined
88   /// instances.
89   uint64_t TotalVarWithLoc = 0;
90   /// Number of constants with location across all inlined instances.
91   uint64_t ConstantMembers = 0;
92   /// Number of arificial variables, parameters or members across all instances.
93   uint64_t NumArtificial = 0;
94   /// List of all Variables and parameters in this function.
95   StringSet<> VarsInFunction;
96   /// Compile units also cover a PC range, but have this flag set to false.
97   bool IsFunction = false;
98   /// Function has source location information.
99   bool HasSourceLocation = false;
100   /// Number of function parameters.
101   uint64_t NumParams = 0;
102   /// Number of function parameters with source location.
103   uint64_t NumParamSourceLocations = 0;
104   /// Number of function parameters with type.
105   uint64_t NumParamTypes = 0;
106   /// Number of function parameters with a DW_AT_location.
107   uint64_t NumParamLocations = 0;
108   /// Number of local variables.
109   uint64_t NumLocalVars = 0;
110   /// Number of local variables with source location.
111   uint64_t NumLocalVarSourceLocations = 0;
112   /// Number of local variables with type.
113   uint64_t NumLocalVarTypes = 0;
114   /// Number of local variables with DW_AT_location.
115   uint64_t NumLocalVarLocations = 0;
116 };
117 
118 /// Holds accumulated global statistics about DIEs.
119 struct GlobalStats {
120   /// Total number of PC range bytes covered by DW_AT_locations.
121   SaturatingUINT64 TotalBytesCovered = 0;
122   /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
123   SaturatingUINT64 ScopeBytesCovered = 0;
124   /// Total number of PC range bytes in each variable's enclosing scope.
125   SaturatingUINT64 ScopeBytes = 0;
126   /// Total number of PC range bytes covered by DW_AT_locations with
127   /// the debug entry values (DW_OP_entry_value).
128   SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
129   /// Total number of PC range bytes covered by DW_AT_locations of
130   /// formal parameters.
131   SaturatingUINT64 ParamScopeBytesCovered = 0;
132   /// Total number of PC range bytes in each parameter's enclosing scope.
133   SaturatingUINT64 ParamScopeBytes = 0;
134   /// Total number of PC range bytes covered by DW_AT_locations with
135   /// the debug entry values (DW_OP_entry_value) (only for parameters).
136   SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
137   /// Total number of PC range bytes covered by DW_AT_locations (only for local
138   /// variables).
139   SaturatingUINT64 LocalVarScopeBytesCovered = 0;
140   /// Total number of PC range bytes in each local variable's enclosing scope.
141   SaturatingUINT64 LocalVarScopeBytes = 0;
142   /// Total number of PC range bytes covered by DW_AT_locations with
143   /// the debug entry values (DW_OP_entry_value) (only for local variables).
144   SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
145   /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
146   SaturatingUINT64 CallSiteEntries = 0;
147   /// Total number of call site DIEs (DW_TAG_call_site).
148   SaturatingUINT64 CallSiteDIEs = 0;
149   /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
150   SaturatingUINT64 CallSiteParamDIEs = 0;
151   /// Total byte size of concrete functions. This byte size includes
152   /// inline functions contained in the concrete functions.
153   SaturatingUINT64 FunctionSize = 0;
154   /// Total byte size of inlined functions. This is the total number of bytes
155   /// for the top inline functions within concrete functions. This can help
156   /// tune the inline settings when compiling to match user expectations.
157   SaturatingUINT64 InlineFunctionSize = 0;
158 };
159 
160 /// Holds accumulated debug location statistics about local variables and
161 /// formal parameters.
162 struct LocationStats {
163   /// Map the scope coverage decile to the number of variables in the decile.
164   /// The first element of the array (at the index zero) represents the number
165   /// of variables with the no debug location at all, but the last element
166   /// in the vector represents the number of fully covered variables within
167   /// its scope.
168   std::vector<SaturatingUINT64> VarParamLocStats{
169       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
170   /// Map non debug entry values coverage.
171   std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
172       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
173   /// The debug location statistics for formal parameters.
174   std::vector<SaturatingUINT64> ParamLocStats{
175       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
176   /// Map non debug entry values coverage for formal parameters.
177   std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
178       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
179   /// The debug location statistics for local variables.
180   std::vector<SaturatingUINT64> LocalVarLocStats{
181       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
182   /// Map non debug entry values coverage for local variables.
183   std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
184       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
185   /// Total number of local variables and function parameters processed.
186   SaturatingUINT64 NumVarParam = 0;
187   /// Total number of formal parameters processed.
188   SaturatingUINT64 NumParam = 0;
189   /// Total number of local variables processed.
190   SaturatingUINT64 NumVar = 0;
191 };
192 
193 /// Holds accumulated debug line statistics across all CUs.
194 struct LineStats {
195   SaturatingUINT64 NumBytes = 0;
196   SaturatingUINT64 NumLineZeroBytes = 0;
197   SaturatingUINT64 NumEntries = 0;
198   SaturatingUINT64 NumIsStmtEntries = 0;
199   SaturatingUINT64 NumUniqueEntries = 0;
200   SaturatingUINT64 NumUniqueNonZeroEntries = 0;
201 };
202 } // namespace
203 
204 /// Collect debug location statistics for one DIE.
collectLocStats(uint64_t ScopeBytesCovered,uint64_t BytesInScope,std::vector<SaturatingUINT64> & VarParamLocStats,std::vector<SaturatingUINT64> & ParamLocStats,std::vector<SaturatingUINT64> & LocalVarLocStats,bool IsParam,bool IsLocalVar)205 static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
206                             std::vector<SaturatingUINT64> &VarParamLocStats,
207                             std::vector<SaturatingUINT64> &ParamLocStats,
208                             std::vector<SaturatingUINT64> &LocalVarLocStats,
209                             bool IsParam, bool IsLocalVar) {
210   auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
211     // No debug location at all for the variable.
212     if (ScopeBytesCovered == 0)
213       return 0;
214     // Fully covered variable within its scope.
215     if (ScopeBytesCovered >= BytesInScope)
216       return NumOfCoverageCategories - 1;
217     // Get covered range (e.g. 20%-29%).
218     unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
219     LocBucket /= 10;
220     return LocBucket + 1;
221   };
222 
223   unsigned CoverageBucket = getCoverageBucket();
224 
225   VarParamLocStats[CoverageBucket].Value++;
226   if (IsParam)
227     ParamLocStats[CoverageBucket].Value++;
228   else if (IsLocalVar)
229     LocalVarLocStats[CoverageBucket].Value++;
230 }
231 
232 /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
233 /// and DeclLine. The identifier aims to be unique for any unique entities,
234 /// but keeping the same among different instances of the same entity.
constructDieID(DWARFDie Die,StringRef Prefix=StringRef ())235 static std::string constructDieID(DWARFDie Die,
236                                   StringRef Prefix = StringRef()) {
237   std::string IDStr;
238   llvm::raw_string_ostream ID(IDStr);
239   ID << Prefix
240      << Die.getName(DINameKind::LinkageName);
241 
242   // Prefix + Name is enough for local variables and parameters.
243   if (!Prefix.empty() && Prefix != "g")
244     return IDStr;
245 
246   auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
247   std::string File;
248   if (DeclFile) {
249     DWARFUnit *U = Die.getDwarfUnit();
250     if (const auto *LT = U->getContext().getLineTableForUnit(U))
251       if (LT->getFileNameByIndex(
252               dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
253               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
254         File = std::string(sys::path::filename(File));
255   }
256   ID << ":" << (File.empty() ? "/" : File);
257   ID << ":"
258      << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
259   return IDStr;
260 }
261 
262 /// Return the number of bytes in the overlap of ranges A and B.
calculateOverlap(DWARFAddressRange A,DWARFAddressRange B)263 static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
264   uint64_t Lower = std::max(A.LowPC, B.LowPC);
265   uint64_t Upper = std::min(A.HighPC, B.HighPC);
266   if (Lower >= Upper)
267     return 0;
268   return Upper - Lower;
269 }
270 
271 /// Collect debug info quality metrics for one DIE.
collectStatsForDie(DWARFDie Die,const std::string & FnPrefix,const std::string & VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTy * AbstractOriginVariables)272 static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
273                                const std::string &VarPrefix,
274                                uint64_t BytesInScope, uint32_t InlineDepth,
275                                StringMap<PerFunctionStats> &FnStatMap,
276                                GlobalStats &GlobalStats,
277                                LocationStats &LocStats,
278                                AbstractOriginVarsTy *AbstractOriginVariables) {
279   const dwarf::Tag Tag = Die.getTag();
280   // Skip CU node.
281   if (Tag == dwarf::DW_TAG_compile_unit)
282     return;
283 
284   bool HasLoc = false;
285   bool HasSrcLoc = false;
286   bool HasType = false;
287   uint64_t TotalBytesCovered = 0;
288   uint64_t ScopeBytesCovered = 0;
289   uint64_t BytesEntryValuesCovered = 0;
290   auto &FnStats = FnStatMap[FnPrefix];
291   bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
292   bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
293   bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
294                           Die.find(dwarf::DW_AT_const_value);
295 
296   // For zero covered inlined variables the locstats will be
297   // calculated later.
298   bool DeferLocStats = false;
299 
300   if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
301     GlobalStats.CallSiteDIEs++;
302     return;
303   }
304 
305   if (Tag == dwarf::DW_TAG_call_site_parameter ||
306       Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
307     GlobalStats.CallSiteParamDIEs++;
308     return;
309   }
310 
311   if (!IsParam && !IsLocalVar && !IsConstantMember) {
312     // Not a variable or constant member.
313     return;
314   }
315 
316   // Ignore declarations of global variables.
317   if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
318     return;
319 
320   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
321       Die.findRecursively(dwarf::DW_AT_decl_line))
322     HasSrcLoc = true;
323 
324   if (Die.findRecursively(dwarf::DW_AT_type))
325     HasType = true;
326 
327   if (Die.find(dwarf::DW_AT_abstract_origin)) {
328     if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
329       if (AbstractOriginVariables) {
330         auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
331         // Do not track this variable any more, since it has location
332         // coverage.
333         llvm::erase(*AbstractOriginVariables, (*Offset).getRawUValue());
334       }
335     } else {
336       // The locstats will be handled at the end of
337       // the collectStatsRecursive().
338       DeferLocStats = true;
339     }
340   }
341 
342   auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
343     DWARFUnit *U = Die.getDwarfUnit();
344     DataExtractor Data(toStringRef(D),
345                        Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
346     DWARFExpression Expression(Data, U->getAddressByteSize(),
347                                U->getFormParams().Format);
348     // Consider the expression containing the DW_OP_entry_value as
349     // an entry value.
350     return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
351       return Op.getCode() == dwarf::DW_OP_entry_value ||
352              Op.getCode() == dwarf::DW_OP_GNU_entry_value;
353     });
354   };
355 
356   if (Die.find(dwarf::DW_AT_const_value)) {
357     // This catches constant members *and* variables.
358     HasLoc = true;
359     ScopeBytesCovered = BytesInScope;
360     TotalBytesCovered = BytesInScope;
361   } else {
362     // Handle variables and function arguments.
363     Expected<std::vector<DWARFLocationExpression>> Loc =
364         Die.getLocations(dwarf::DW_AT_location);
365     if (!Loc) {
366       consumeError(Loc.takeError());
367     } else {
368       HasLoc = true;
369       // Get PC coverage.
370       auto Default = find_if(
371           *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
372       if (Default != Loc->end()) {
373         // Assume the entire range is covered by a single location.
374         ScopeBytesCovered = BytesInScope;
375         TotalBytesCovered = BytesInScope;
376       } else {
377         // Caller checks this Expected result already, it cannot fail.
378         auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
379         for (auto Entry : *Loc) {
380           TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
381           uint64_t ScopeBytesCoveredByEntry = 0;
382           // Calculate how many bytes of the parent scope this entry covers.
383           // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
384           // address ranges defined by the bounded location descriptions of a
385           // location list may overlap". So in theory a variable can have
386           // multiple simultaneous locations, which would make this calculation
387           // misleading because we will count the overlapped areas
388           // twice. However, clang does not currently emit DWARF like this.
389           for (DWARFAddressRange R : ScopeRanges) {
390             ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
391           }
392           ScopeBytesCovered += ScopeBytesCoveredByEntry;
393           if (IsEntryValue(Entry.Expr))
394             BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
395         }
396       }
397     }
398   }
399 
400   // Calculate the debug location statistics.
401   if (BytesInScope && !DeferLocStats) {
402     LocStats.NumVarParam.Value++;
403     if (IsParam)
404       LocStats.NumParam.Value++;
405     else if (IsLocalVar)
406       LocStats.NumVar.Value++;
407 
408     collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
409                     LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
410                     IsLocalVar);
411     // Non debug entry values coverage statistics.
412     collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
413                     LocStats.VarParamNonEntryValLocStats,
414                     LocStats.ParamNonEntryValLocStats,
415                     LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
416   }
417 
418   // Collect PC range coverage data.
419   if (DWARFDie D =
420           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
421     Die = D;
422 
423   std::string VarID = constructDieID(Die, VarPrefix);
424   FnStats.VarsInFunction.insert(VarID);
425 
426   GlobalStats.TotalBytesCovered += TotalBytesCovered;
427   if (BytesInScope) {
428     GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
429     GlobalStats.ScopeBytes += BytesInScope;
430     GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
431     if (IsParam) {
432       GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
433       GlobalStats.ParamScopeBytes += BytesInScope;
434       GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
435     } else if (IsLocalVar) {
436       GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
437       GlobalStats.LocalVarScopeBytes += BytesInScope;
438       GlobalStats.LocalVarScopeEntryValueBytesCovered +=
439           BytesEntryValuesCovered;
440     }
441     assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
442   }
443 
444   if (IsConstantMember) {
445     FnStats.ConstantMembers++;
446     return;
447   }
448 
449   FnStats.TotalVarWithLoc += (unsigned)HasLoc;
450 
451   if (Die.find(dwarf::DW_AT_artificial)) {
452     FnStats.NumArtificial++;
453     return;
454   }
455 
456   if (IsParam) {
457     FnStats.NumParams++;
458     if (HasType)
459       FnStats.NumParamTypes++;
460     if (HasSrcLoc)
461       FnStats.NumParamSourceLocations++;
462     if (HasLoc)
463       FnStats.NumParamLocations++;
464   } else if (IsLocalVar) {
465     FnStats.NumLocalVars++;
466     if (HasType)
467       FnStats.NumLocalVarTypes++;
468     if (HasSrcLoc)
469       FnStats.NumLocalVarSourceLocations++;
470     if (HasLoc)
471       FnStats.NumLocalVarLocations++;
472   }
473 }
474 
475 /// Recursively collect variables from subprogram with DW_AT_inline attribute.
collectAbstractOriginFnInfo(DWARFDie Die,uint64_t SPOffset,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo)476 static void collectAbstractOriginFnInfo(
477     DWARFDie Die, uint64_t SPOffset,
478     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
479     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
480   DWARFDie Child = Die.getFirstChild();
481   while (Child) {
482     const dwarf::Tag ChildTag = Child.getTag();
483     if (ChildTag == dwarf::DW_TAG_formal_parameter ||
484         ChildTag == dwarf::DW_TAG_variable) {
485       GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
486       LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
487     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
488       collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
489                                   LocalAbstractOriginFnInfo);
490     Child = Child.getSibling();
491   }
492 }
493 
494 /// Recursively collect debug info quality metrics.
collectStatsRecursive(DWARFDie Die,std::string FnPrefix,std::string VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats,FunctionDIECUTyMap & AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed,AbstractOriginVarsTy * AbstractOriginVarsPtr=nullptr)495 static void collectStatsRecursive(
496     DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
497     uint64_t BytesInScope, uint32_t InlineDepth,
498     StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
499     LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
500     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
501     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
502     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
503     AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
504   // Skip NULL nodes.
505   if (Die.isNULL())
506     return;
507 
508   const dwarf::Tag Tag = Die.getTag();
509   // Skip function types.
510   if (Tag == dwarf::DW_TAG_subroutine_type)
511     return;
512 
513   // Handle any kind of lexical scope.
514   const bool HasAbstractOrigin =
515       Die.find(dwarf::DW_AT_abstract_origin) != std::nullopt;
516   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
517   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
518   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
519   // We want to know how many variables (with abstract_origin) don't have
520   // location info.
521   const bool IsCandidateForZeroLocCovTracking =
522       (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
523 
524   AbstractOriginVarsTy AbstractOriginVars;
525 
526   // Get the vars of the inlined fn, so the locstats
527   // reports the missing vars (with coverage 0%).
528   if (IsCandidateForZeroLocCovTracking) {
529     auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
530     if (OffsetFn) {
531       uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
532       if (auto It = LocalAbstractOriginFnInfo.find(OffsetOfInlineFnCopy);
533           It != LocalAbstractOriginFnInfo.end()) {
534         AbstractOriginVars = It->second;
535         AbstractOriginVarsPtr = &AbstractOriginVars;
536       } else {
537         // This means that the DW_AT_inline fn copy is out of order
538         // or that the abstract_origin references another CU,
539         // so this abstract origin instance will be processed later.
540         FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
541         AbstractOriginVarsPtr = nullptr;
542       }
543     }
544   }
545 
546   if (IsFunction || IsInlinedFunction || IsBlock) {
547     // Reset VarPrefix when entering a new function.
548     if (IsFunction || IsInlinedFunction)
549       VarPrefix = "v";
550 
551     // Ignore forward declarations.
552     if (Die.find(dwarf::DW_AT_declaration))
553       return;
554 
555     // Check for call sites.
556     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
557       GlobalStats.CallSiteEntries++;
558 
559     // PC Ranges.
560     auto RangesOrError = Die.getAddressRanges();
561     if (!RangesOrError) {
562       llvm::consumeError(RangesOrError.takeError());
563       return;
564     }
565 
566     auto Ranges = RangesOrError.get();
567     uint64_t BytesInThisScope = 0;
568     for (auto Range : Ranges)
569       BytesInThisScope += Range.HighPC - Range.LowPC;
570 
571     // Count the function.
572     if (!IsBlock) {
573       // Skip over abstract origins, but collect variables
574       // from it so it can be used for location statistics
575       // for inlined instancies.
576       if (Die.find(dwarf::DW_AT_inline)) {
577         uint64_t SPOffset = Die.getOffset();
578         AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
579         collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
580                                     LocalAbstractOriginFnInfo);
581         return;
582       }
583 
584       std::string FnID = constructDieID(Die);
585       // We've seen an instance of this function.
586       auto &FnStats = FnStatMap[FnID];
587       FnStats.IsFunction = true;
588       if (IsInlinedFunction) {
589         FnStats.NumFnInlined++;
590         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
591           FnStats.NumAbstractOrigins++;
592       } else {
593         FnStats.NumFnOutOfLine++;
594       }
595       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
596           Die.findRecursively(dwarf::DW_AT_decl_line))
597         FnStats.HasSourceLocation = true;
598       // Update function prefix.
599       FnPrefix = FnID;
600     }
601 
602     if (BytesInThisScope) {
603       BytesInScope = BytesInThisScope;
604       if (IsFunction)
605         GlobalStats.FunctionSize += BytesInThisScope;
606       else if (IsInlinedFunction && InlineDepth == 0)
607         GlobalStats.InlineFunctionSize += BytesInThisScope;
608     }
609   } else {
610     // Not a scope, visit the Die itself. It could be a variable.
611     collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
612                        FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
613   }
614 
615   // Set InlineDepth correctly for child recursion
616   if (IsFunction)
617     InlineDepth = 0;
618   else if (IsInlinedFunction)
619     ++InlineDepth;
620 
621   // Traverse children.
622   unsigned LexicalBlockIndex = 0;
623   unsigned FormalParameterIndex = 0;
624   DWARFDie Child = Die.getFirstChild();
625   while (Child) {
626     std::string ChildVarPrefix = VarPrefix;
627     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
628       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
629     if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
630       ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
631 
632     collectStatsRecursive(
633         Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
634         GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
635         LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
636         AbstractOriginVarsPtr);
637     Child = Child.getSibling();
638   }
639 
640   if (!IsCandidateForZeroLocCovTracking)
641     return;
642 
643   // After we have processed all vars of the inlined function (or function with
644   // an abstract_origin), we want to know how many variables have no location.
645   for (auto Offset : AbstractOriginVars) {
646     LocStats.NumVarParam++;
647     LocStats.VarParamLocStats[ZeroCoverageBucket]++;
648     auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
649     if (!FnDie)
650       continue;
651     auto Tag = FnDie.getTag();
652     if (Tag == dwarf::DW_TAG_formal_parameter) {
653       LocStats.NumParam++;
654       LocStats.ParamLocStats[ZeroCoverageBucket]++;
655     } else if (Tag == dwarf::DW_TAG_variable) {
656       LocStats.NumVar++;
657       LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
658     }
659   }
660 }
661 
662 /// Print human-readable output.
663 /// \{
printDatum(json::OStream & J,const char * Key,json::Value Value)664 static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
665   if (Value == OverflowValue)
666     J.attribute(Key, "overflowed");
667   else
668     J.attribute(Key, Value);
669 
670   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
671 }
672 
printLocationStats(json::OStream & J,const char * Key,std::vector<SaturatingUINT64> & LocationStats)673 static void printLocationStats(json::OStream &J, const char *Key,
674                                std::vector<SaturatingUINT64> &LocationStats) {
675   if (LocationStats[0].Value == OverflowValue)
676     J.attribute((Twine(Key) +
677                  " with (0%,10%) of parent scope covered by DW_AT_location")
678                     .str(),
679                 "overflowed");
680   else
681     J.attribute(
682         (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
683             .str(),
684         LocationStats[0].Value);
685   LLVM_DEBUG(
686       llvm::dbgs() << Key
687                    << " with 0% of parent scope covered by DW_AT_location: \\"
688                    << LocationStats[0].Value << '\n');
689 
690   if (LocationStats[1].Value == OverflowValue)
691     J.attribute((Twine(Key) +
692                  " with (0%,10%) of parent scope covered by DW_AT_location")
693                     .str(),
694                 "overflowed");
695   else
696     J.attribute((Twine(Key) +
697                  " with (0%,10%) of parent scope covered by DW_AT_location")
698                     .str(),
699                 LocationStats[1].Value);
700   LLVM_DEBUG(llvm::dbgs()
701              << Key
702              << " with (0%,10%) of parent scope covered by DW_AT_location: "
703              << LocationStats[1].Value << '\n');
704 
705   for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
706     if (LocationStats[i].Value == OverflowValue)
707       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
708                    Twine(i * 10) +
709                    "%) of parent scope covered by DW_AT_location")
710                       .str(),
711                   "overflowed");
712     else
713       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
714                    Twine(i * 10) +
715                    "%) of parent scope covered by DW_AT_location")
716                       .str(),
717                   LocationStats[i].Value);
718     LLVM_DEBUG(llvm::dbgs()
719                << Key << " with [" << (i - 1) * 10 << "%," << i * 10
720                << "%) of parent scope covered by DW_AT_location: "
721                << LocationStats[i].Value);
722   }
723   if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
724     J.attribute(
725         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
726             .str(),
727         "overflowed");
728   else
729     J.attribute(
730         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
731             .str(),
732         LocationStats[NumOfCoverageCategories - 1].Value);
733   LLVM_DEBUG(
734       llvm::dbgs() << Key
735                    << " with 100% of parent scope covered by DW_AT_location: "
736                    << LocationStats[NumOfCoverageCategories - 1].Value);
737 }
738 
printSectionSizes(json::OStream & J,const SectionSizes & Sizes)739 static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
740   for (const auto &It : Sizes.DebugSectionSizes)
741     J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
742 }
743 
744 /// Stop tracking variables that contain abstract_origin with a location.
745 /// This is used for out-of-order DW_AT_inline subprograms only.
updateVarsWithAbstractOriginLocCovInfo(DWARFDie FnDieWithAbstractOrigin,AbstractOriginVarsTy & AbstractOriginVars)746 static void updateVarsWithAbstractOriginLocCovInfo(
747     DWARFDie FnDieWithAbstractOrigin,
748     AbstractOriginVarsTy &AbstractOriginVars) {
749   DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
750   while (Child) {
751     const dwarf::Tag ChildTag = Child.getTag();
752     if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
753          ChildTag == dwarf::DW_TAG_variable) &&
754         (Child.find(dwarf::DW_AT_location) ||
755          Child.find(dwarf::DW_AT_const_value))) {
756       auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
757       if (OffsetVar)
758         llvm::erase(AbstractOriginVars, (*OffsetVar).getRawUValue());
759     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
760       updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
761     Child = Child.getSibling();
762   }
763 }
764 
765 /// Collect zero location coverage for inlined variables which refer to
766 /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
767 /// Also cover the variables of a concrete function (represented with
768 /// the DW_TAG_subprogram) with an abstract_origin attribute.
collectZeroLocCovForVarsWithAbstractOrigin(DWARFUnit * DwUnit,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed)769 static void collectZeroLocCovForVarsWithAbstractOrigin(
770     DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
771     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
772     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
773   // The next variable is used to filter out functions that have been processed,
774   // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
775   FunctionsWithAbstractOriginTy ProcessedFns;
776   for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
777     DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
778     auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
779     AbstractOriginVarsTy AbstractOriginVars;
780     if (!FnCopy)
781       continue;
782     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
783     // If there is no entry within LocalAbstractOriginFnInfo for the given
784     // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
785     // CrossCU referencing.
786     auto It = LocalAbstractOriginFnInfo.find(FnCopyRawUValue);
787     if (It == LocalAbstractOriginFnInfo.end())
788       continue;
789     AbstractOriginVars = It->second;
790     updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
791                                            AbstractOriginVars);
792 
793     for (auto Offset : AbstractOriginVars) {
794       LocStats.NumVarParam++;
795       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
796       auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
797       if (Tag == dwarf::DW_TAG_formal_parameter) {
798         LocStats.NumParam++;
799         LocStats.ParamLocStats[ZeroCoverageBucket]++;
800       } else if (Tag == dwarf::DW_TAG_variable) {
801         LocStats.NumVar++;
802         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
803       }
804     }
805     ProcessedFns.push_back(FnOffset);
806   }
807   for (auto ProcessedFn : ProcessedFns)
808     llvm::erase(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
809 }
810 
811 /// Collect zero location coverage for inlined variables which refer to
812 /// a DW_AT_inline copy of subprogram that is in a different CU.
collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(LocationStats & LocStats,FunctionDIECUTyMap AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,CrossCUReferencingDIELocationTy & CrossCUReferencesToBeResolved)813 static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
814     LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
815     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
816     CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
817   for (const auto &CrossCUReferenceToBeResolved :
818        CrossCUReferencesToBeResolved) {
819     DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
820     DWARFDie FnDIEWithCrossCUReferencing =
821         DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
822     auto FnCopy =
823         FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
824     if (!FnCopy)
825       continue;
826     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
827     AbstractOriginVarsTy AbstractOriginVars =
828         GlobalAbstractOriginFnInfo[FnCopyRawUValue];
829     updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
830                                            AbstractOriginVars);
831     for (auto Offset : AbstractOriginVars) {
832       LocStats.NumVarParam++;
833       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
834       auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
835                      ->getDIEForOffset(Offset)
836                      .getTag();
837       if (Tag == dwarf::DW_TAG_formal_parameter) {
838         LocStats.NumParam++;
839         LocStats.ParamLocStats[ZeroCoverageBucket]++;
840       } else if (Tag == dwarf::DW_TAG_variable) {
841         LocStats.NumVar++;
842         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
843       }
844     }
845   }
846 }
847 
848 /// \}
849 
850 /// Collect debug info quality metrics for an entire DIContext.
851 ///
852 /// Do the impossible and reduce the quality of the debug info down to a few
853 /// numbers. The idea is to condense the data into numbers that can be tracked
854 /// over time to identify trends in newer compiler versions and gauge the effect
855 /// of particular optimizations. The raw numbers themselves are not particularly
856 /// useful, only the delta between compiling the same program with different
857 /// compilers is.
collectStatsForObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)858 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
859                                           const Twine &Filename,
860                                           raw_ostream &OS) {
861   StringRef FormatName = Obj.getFileFormatName();
862   GlobalStats GlobalStats;
863   LocationStats LocStats;
864   LineStats LnStats;
865   StringMap<PerFunctionStats> Statistics;
866   // This variable holds variable information for functions with
867   // abstract_origin globally, across all CUs.
868   AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
869   // This variable holds information about the CU of a function with
870   // abstract_origin.
871   FunctionDIECUTyMap AbstractOriginFnCUs;
872   CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
873   // Tuple representing a single source code position in the line table. Fields
874   // are respectively: Line, Col, File, where 'File' is an index into the Files
875   // vector below.
876   using LineTuple = std::tuple<uint32_t, uint16_t, uint16_t>;
877   SmallVector<std::string> Files;
878   DenseSet<LineTuple> UniqueLines;
879   DenseSet<LineTuple> UniqueNonZeroLines;
880 
881   for (const auto &CU : DICtx.compile_units()) {
882     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
883       // This variable holds variable information for functions with
884       // abstract_origin, but just for the current CU.
885       AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
886       FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
887 
888       collectStatsRecursive(
889           CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
890           AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
891           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
892 
893       // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
894       // out-of-order DWARF functions that have been processed within it,
895       // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
896       // references.
897       collectZeroLocCovForVarsWithAbstractOrigin(
898           CUDie.getDwarfUnit(), GlobalStats, LocStats,
899           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
900 
901       // Collect all CrossCU references into CrossCUReferencesToBeResolved.
902       for (auto CrossCUReferencingDIEOffset :
903            FnsWithAbstractOriginToBeProcessed)
904         CrossCUReferencesToBeResolved.push_back(
905             DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
906     }
907     const auto *LineTable = DICtx.getLineTableForUnit(CU.get());
908     std::optional<uint64_t> LastFileIdxOpt;
909     if (LineTable)
910       LastFileIdxOpt = LineTable->getLastValidFileIndex();
911     if (LastFileIdxOpt) {
912       // Each CU has its own file index; in order to track unique line entries
913       // across CUs, we therefore need to map each CU file index to a global
914       // file index, which we store here.
915       DenseMap<uint64_t, uint16_t> CUFileMapping;
916       for (uint64_t FileIdx = 0; FileIdx <= *LastFileIdxOpt; ++FileIdx) {
917         std::string File;
918         if (LineTable->getFileNameByIndex(
919                 FileIdx, CU->getCompilationDir(),
920                 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
921                 File)) {
922           auto ExistingFile = llvm::find(Files, File);
923           if (ExistingFile != Files.end()) {
924             CUFileMapping[FileIdx] = std::distance(Files.begin(), ExistingFile);
925           } else {
926             CUFileMapping[FileIdx] = Files.size();
927             Files.push_back(File);
928           }
929         }
930       }
931       for (const auto &Seq : LineTable->Sequences) {
932         LnStats.NumBytes += Seq.HighPC - Seq.LowPC;
933         // Ignore the `end_sequence` entry, since it's not interesting for us.
934         LnStats.NumEntries += Seq.LastRowIndex - Seq.FirstRowIndex - 1;
935         for (size_t RowIdx = Seq.FirstRowIndex; RowIdx < Seq.LastRowIndex - 1;
936              ++RowIdx) {
937           auto Entry = LineTable->Rows[RowIdx];
938           if (Entry.IsStmt)
939             LnStats.NumIsStmtEntries += 1;
940           assert(CUFileMapping.contains(Entry.File) &&
941                  "Should have been collected earlier!");
942           uint16_t MappedFile = CUFileMapping[Entry.File];
943           UniqueLines.insert({Entry.Line, Entry.Column, MappedFile});
944           if (Entry.Line != 0) {
945             UniqueNonZeroLines.insert({Entry.Line, Entry.Column, MappedFile});
946           } else {
947             auto EntryStartAddress = Entry.Address.Address;
948             auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address;
949             LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress;
950           }
951         }
952       }
953     }
954   }
955 
956   LnStats.NumUniqueEntries = UniqueLines.size();
957   LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size();
958 
959   /// Resolve CrossCU references.
960   collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
961       LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
962       CrossCUReferencesToBeResolved);
963 
964   /// Collect the sizes of debug sections.
965   SectionSizes Sizes;
966   calculateSectionSizes(Obj, Sizes, Filename);
967 
968   /// The version number should be increased every time the algorithm is changed
969   /// (including bug fixes). New metrics may be added without increasing the
970   /// version.
971   unsigned Version = 9;
972   SaturatingUINT64 VarParamTotal = 0;
973   SaturatingUINT64 VarParamUnique = 0;
974   SaturatingUINT64 VarParamWithLoc = 0;
975   SaturatingUINT64 NumFunctions = 0;
976   SaturatingUINT64 NumOutOfLineFunctions = 0;
977   SaturatingUINT64 NumInlinedFunctions = 0;
978   SaturatingUINT64 NumFuncsWithSrcLoc = 0;
979   SaturatingUINT64 NumAbstractOrigins = 0;
980   SaturatingUINT64 ParamTotal = 0;
981   SaturatingUINT64 ParamWithType = 0;
982   SaturatingUINT64 ParamWithLoc = 0;
983   SaturatingUINT64 ParamWithSrcLoc = 0;
984   SaturatingUINT64 LocalVarTotal = 0;
985   SaturatingUINT64 LocalVarWithType = 0;
986   SaturatingUINT64 LocalVarWithSrcLoc = 0;
987   SaturatingUINT64 LocalVarWithLoc = 0;
988   for (auto &Entry : Statistics) {
989     PerFunctionStats &Stats = Entry.getValue();
990     uint64_t TotalVars = Stats.VarsInFunction.size() *
991                          (Stats.NumFnInlined + Stats.NumFnOutOfLine);
992     // Count variables in global scope.
993     if (!Stats.IsFunction)
994       TotalVars =
995           Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
996     uint64_t Constants = Stats.ConstantMembers;
997     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
998     VarParamTotal += TotalVars;
999     VarParamUnique += Stats.VarsInFunction.size();
1000     LLVM_DEBUG(for (auto &V
1001                     : Stats.VarsInFunction) llvm::dbgs()
1002                << Entry.getKey() << ": " << V.getKey() << "\n");
1003     NumFunctions += Stats.IsFunction;
1004     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
1005     NumOutOfLineFunctions += Stats.IsFunction * Stats.NumFnOutOfLine;
1006     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
1007     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
1008     ParamTotal += Stats.NumParams;
1009     ParamWithType += Stats.NumParamTypes;
1010     ParamWithLoc += Stats.NumParamLocations;
1011     ParamWithSrcLoc += Stats.NumParamSourceLocations;
1012     LocalVarTotal += Stats.NumLocalVars;
1013     LocalVarWithType += Stats.NumLocalVarTypes;
1014     LocalVarWithLoc += Stats.NumLocalVarLocations;
1015     LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
1016   }
1017 
1018   // Print summary.
1019   OS.SetBufferSize(1024);
1020   json::OStream J(OS, 2);
1021   J.objectBegin();
1022   J.attribute("version", Version);
1023   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
1024              llvm::dbgs() << "---------------------------------\n");
1025 
1026   printDatum(J, "file", Filename.str());
1027   printDatum(J, "format", FormatName);
1028 
1029   printDatum(J, "#functions", NumFunctions.Value);
1030   printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
1031   printDatum(J, "#out-of-line functions", NumOutOfLineFunctions.Value);
1032   printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
1033   printDatum(J, "#inlined functions with abstract origins",
1034              NumAbstractOrigins.Value);
1035 
1036   // This includes local variables and formal parameters.
1037   printDatum(J, "#unique source variables", VarParamUnique.Value);
1038   printDatum(J, "#source variables", VarParamTotal.Value);
1039   printDatum(J, "#source variables with location", VarParamWithLoc.Value);
1040 
1041   printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
1042   printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
1043   printDatum(J, "#call site parameter DIEs",
1044              GlobalStats.CallSiteParamDIEs.Value);
1045 
1046   printDatum(J, "sum_all_variables(#bytes in parent scope)",
1047              GlobalStats.ScopeBytes.Value);
1048   printDatum(J,
1049              "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
1050              GlobalStats.TotalBytesCovered.Value);
1051   printDatum(J,
1052              "sum_all_variables(#bytes in parent scope covered by "
1053              "DW_AT_location)",
1054              GlobalStats.ScopeBytesCovered.Value);
1055   printDatum(J,
1056              "sum_all_variables(#bytes in parent scope covered by "
1057              "DW_OP_entry_value)",
1058              GlobalStats.ScopeEntryValueBytesCovered.Value);
1059 
1060   printDatum(J, "sum_all_params(#bytes in parent scope)",
1061              GlobalStats.ParamScopeBytes.Value);
1062   printDatum(J,
1063              "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
1064              GlobalStats.ParamScopeBytesCovered.Value);
1065   printDatum(J,
1066              "sum_all_params(#bytes in parent scope covered by "
1067              "DW_OP_entry_value)",
1068              GlobalStats.ParamScopeEntryValueBytesCovered.Value);
1069 
1070   printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
1071              GlobalStats.LocalVarScopeBytes.Value);
1072   printDatum(J,
1073              "sum_all_local_vars(#bytes in parent scope covered by "
1074              "DW_AT_location)",
1075              GlobalStats.LocalVarScopeBytesCovered.Value);
1076   printDatum(J,
1077              "sum_all_local_vars(#bytes in parent scope covered by "
1078              "DW_OP_entry_value)",
1079              GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
1080 
1081   printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
1082   printDatum(J, "#bytes within inlined functions",
1083              GlobalStats.InlineFunctionSize.Value);
1084 
1085   // Print the summary for formal parameters.
1086   printDatum(J, "#params", ParamTotal.Value);
1087   printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
1088   printDatum(J, "#params with type", ParamWithType.Value);
1089   printDatum(J, "#params with binary location", ParamWithLoc.Value);
1090 
1091   // Print the summary for local variables.
1092   printDatum(J, "#local vars", LocalVarTotal.Value);
1093   printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
1094   printDatum(J, "#local vars with type", LocalVarWithType.Value);
1095   printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
1096 
1097   // Print the debug section sizes.
1098   printSectionSizes(J, Sizes);
1099 
1100   // Print the location statistics for variables (includes local variables
1101   // and formal parameters).
1102   printDatum(J, "#variables processed by location statistics",
1103              LocStats.NumVarParam.Value);
1104   printLocationStats(J, "#variables", LocStats.VarParamLocStats);
1105   printLocationStats(J, "#variables - entry values",
1106                      LocStats.VarParamNonEntryValLocStats);
1107 
1108   // Print the location statistics for formal parameters.
1109   printDatum(J, "#params processed by location statistics",
1110              LocStats.NumParam.Value);
1111   printLocationStats(J, "#params", LocStats.ParamLocStats);
1112   printLocationStats(J, "#params - entry values",
1113                      LocStats.ParamNonEntryValLocStats);
1114 
1115   // Print the location statistics for local variables.
1116   printDatum(J, "#local vars processed by location statistics",
1117              LocStats.NumVar.Value);
1118   printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
1119   printLocationStats(J, "#local vars - entry values",
1120                      LocStats.LocalVarNonEntryValLocStats);
1121 
1122   // Print line statistics for the object file.
1123   printDatum(J, "#bytes with line information", LnStats.NumBytes.Value);
1124   printDatum(J, "#bytes with line-0 locations", LnStats.NumLineZeroBytes.Value);
1125   printDatum(J, "#line entries", LnStats.NumEntries.Value);
1126   printDatum(J, "#line entries (is_stmt)", LnStats.NumIsStmtEntries.Value);
1127   printDatum(J, "#line entries (unique)", LnStats.NumUniqueEntries.Value);
1128   printDatum(J, "#line entries (unique non-0)",
1129              LnStats.NumUniqueNonZeroEntries.Value);
1130 
1131   J.objectEnd();
1132   OS << '\n';
1133   LLVM_DEBUG(
1134       llvm::dbgs() << "Total Availability: "
1135                    << (VarParamTotal.Value
1136                            ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1137                                              VarParamTotal.Value)
1138                            : 0)
1139                    << "%\n";
1140       llvm::dbgs() << "PC Ranges covered: "
1141                    << (GlobalStats.ScopeBytes.Value
1142                            ? (int)std::round(
1143                                  (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1144                                  GlobalStats.ScopeBytes.Value)
1145                            : 0)
1146                    << "%\n");
1147   return true;
1148 }
1149