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/StringExtras.h"
12 #include "llvm/ADT/StringSet.h"
13 #include "llvm/DebugInfo/DIContext.h"
14 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
15 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.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 /// This represents the number of categories of debug location coverage being
25 /// calculated. The first category is the number of variables with 0% location
26 /// coverage, but the last category is the number of variables with 100%
27 /// location coverage.
28 constexpr int NumOfCoverageCategories = 12;
29 
30 namespace {
31 /// Holds statistics for one function (or other entity that has a PC range and
32 /// contains variables, such as a compile unit).
33 struct PerFunctionStats {
34   /// Number of inlined instances of this function.
35   unsigned NumFnInlined = 0;
36   /// Number of out-of-line instances of this function.
37   unsigned NumFnOutOfLine = 0;
38   /// Number of inlined instances that have abstract origins.
39   unsigned NumAbstractOrigins = 0;
40   /// Number of variables and parameters with location across all inlined
41   /// instances.
42   unsigned TotalVarWithLoc = 0;
43   /// Number of constants with location across all inlined instances.
44   unsigned ConstantMembers = 0;
45   /// Number of arificial variables, parameters or members across all instances.
46   unsigned NumArtificial = 0;
47   /// List of all Variables and parameters in this function.
48   StringSet<> VarsInFunction;
49   /// Compile units also cover a PC range, but have this flag set to false.
50   bool IsFunction = false;
51   /// Function has source location information.
52   bool HasSourceLocation = false;
53   /// Number of function parameters.
54   unsigned NumParams = 0;
55   /// Number of function parameters with source location.
56   unsigned NumParamSourceLocations = 0;
57   /// Number of function parameters with type.
58   unsigned NumParamTypes = 0;
59   /// Number of function parameters with a DW_AT_location.
60   unsigned NumParamLocations = 0;
61   /// Number of local variables.
62   unsigned NumLocalVars = 0;
63   /// Number of local variables with source location.
64   unsigned NumLocalVarSourceLocations = 0;
65   /// Number of local variables with type.
66   unsigned NumLocalVarTypes = 0;
67   /// Number of local variables with DW_AT_location.
68   unsigned NumLocalVarLocations = 0;
69 };
70 
71 /// Holds accumulated global statistics about DIEs.
72 struct GlobalStats {
73   /// Total number of PC range bytes covered by DW_AT_locations.
74   unsigned ScopeBytesCovered = 0;
75   /// Total number of PC range bytes in each variable's enclosing scope.
76   unsigned ScopeBytes = 0;
77   /// Total number of PC range bytes covered by DW_AT_locations with
78   /// the debug entry values (DW_OP_entry_value).
79   unsigned ScopeEntryValueBytesCovered = 0;
80   /// Total number of PC range bytes covered by DW_AT_locations of
81   /// formal parameters.
82   unsigned ParamScopeBytesCovered = 0;
83   /// Total number of PC range bytes in each parameter's enclosing scope.
84   unsigned ParamScopeBytes = 0;
85   /// Total number of PC range bytes covered by DW_AT_locations with
86   /// the debug entry values (DW_OP_entry_value) (only for parameters).
87   unsigned ParamScopeEntryValueBytesCovered = 0;
88   /// Total number of PC range bytes covered by DW_AT_locations (only for local
89   /// variables).
90   unsigned LocalVarScopeBytesCovered = 0;
91   /// Total number of PC range bytes in each local variable's enclosing scope.
92   unsigned LocalVarScopeBytes = 0;
93   /// Total number of PC range bytes covered by DW_AT_locations with
94   /// the debug entry values (DW_OP_entry_value) (only for local variables).
95   unsigned LocalVarScopeEntryValueBytesCovered = 0;
96   /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
97   unsigned CallSiteEntries = 0;
98   /// Total number of call site DIEs (DW_TAG_call_site).
99   unsigned CallSiteDIEs = 0;
100   /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
101   unsigned CallSiteParamDIEs = 0;
102   /// Total byte size of concrete functions. This byte size includes
103   /// inline functions contained in the concrete functions.
104   unsigned FunctionSize = 0;
105   /// Total byte size of inlined functions. This is the total number of bytes
106   /// for the top inline functions within concrete functions. This can help
107   /// tune the inline settings when compiling to match user expectations.
108   unsigned InlineFunctionSize = 0;
109 };
110 
111 /// Holds accumulated debug location statistics about local variables and
112 /// formal parameters.
113 struct LocationStats {
114   /// Map the scope coverage decile to the number of variables in the decile.
115   /// The first element of the array (at the index zero) represents the number
116   /// of variables with the no debug location at all, but the last element
117   /// in the vector represents the number of fully covered variables within
118   /// its scope.
119   std::vector<unsigned> VarParamLocStats{
120       std::vector<unsigned>(NumOfCoverageCategories, 0)};
121   /// Map non debug entry values coverage.
122   std::vector<unsigned> VarParamNonEntryValLocStats{
123       std::vector<unsigned>(NumOfCoverageCategories, 0)};
124   /// The debug location statistics for formal parameters.
125   std::vector<unsigned> ParamLocStats{
126       std::vector<unsigned>(NumOfCoverageCategories, 0)};
127   /// Map non debug entry values coverage for formal parameters.
128   std::vector<unsigned> ParamNonEntryValLocStats{
129       std::vector<unsigned>(NumOfCoverageCategories, 0)};
130   /// The debug location statistics for local variables.
131   std::vector<unsigned> LocalVarLocStats{
132       std::vector<unsigned>(NumOfCoverageCategories, 0)};
133   /// Map non debug entry values coverage for local variables.
134   std::vector<unsigned> LocalVarNonEntryValLocStats{
135       std::vector<unsigned>(NumOfCoverageCategories, 0)};
136   /// Total number of local variables and function parameters processed.
137   unsigned NumVarParam = 0;
138   /// Total number of formal parameters processed.
139   unsigned NumParam = 0;
140   /// Total number of local variables processed.
141   unsigned NumVar = 0;
142 };
143 } // namespace
144 
145 /// Collect debug location statistics for one DIE.
collectLocStats(uint64_t BytesCovered,uint64_t BytesInScope,std::vector<unsigned> & VarParamLocStats,std::vector<unsigned> & ParamLocStats,std::vector<unsigned> & LocalVarLocStats,bool IsParam,bool IsLocalVar)146 static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope,
147                             std::vector<unsigned> &VarParamLocStats,
148                             std::vector<unsigned> &ParamLocStats,
149                             std::vector<unsigned> &LocalVarLocStats,
150                             bool IsParam, bool IsLocalVar) {
151   auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned {
152     // No debug location at all for the variable.
153     if (BytesCovered == 0)
154       return 0;
155     // Fully covered variable within its scope.
156     if (BytesCovered >= BytesInScope)
157       return NumOfCoverageCategories - 1;
158     // Get covered range (e.g. 20%-29%).
159     unsigned LocBucket = 100 * (double)BytesCovered / BytesInScope;
160     LocBucket /= 10;
161     return LocBucket + 1;
162   };
163 
164   unsigned CoverageBucket = getCoverageBucket();
165   VarParamLocStats[CoverageBucket]++;
166   if (IsParam)
167     ParamLocStats[CoverageBucket]++;
168   else if (IsLocalVar)
169     LocalVarLocStats[CoverageBucket]++;
170 }
171 /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
172 /// and DeclLine. The identifier aims to be unique for any unique entities,
173 /// but keeping the same among different instances of the same entity.
constructDieID(DWARFDie Die,StringRef Prefix=StringRef ())174 static std::string constructDieID(DWARFDie Die,
175                                   StringRef Prefix = StringRef()) {
176   std::string IDStr;
177   llvm::raw_string_ostream ID(IDStr);
178   ID << Prefix
179      << Die.getName(DINameKind::LinkageName);
180 
181   // Prefix + Name is enough for local variables and parameters.
182   if (!Prefix.empty() && !Prefix.equals("g"))
183     return ID.str();
184 
185   auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
186   std::string File;
187   if (DeclFile) {
188     DWARFUnit *U = Die.getDwarfUnit();
189     if (const auto *LT = U->getContext().getLineTableForUnit(U))
190       if (LT->getFileNameByIndex(
191               dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
192               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
193         File = std::string(sys::path::filename(File));
194   }
195   ID << ":" << (File.empty() ? "/" : File);
196   ID << ":"
197      << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
198   return ID.str();
199 }
200 
201 /// Collect debug info quality metrics for one DIE.
collectStatsForDie(DWARFDie Die,std::string FnPrefix,std::string VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats)202 static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
203                                std::string VarPrefix, uint64_t BytesInScope,
204                                uint32_t InlineDepth,
205                                StringMap<PerFunctionStats> &FnStatMap,
206                                GlobalStats &GlobalStats,
207                                LocationStats &LocStats) {
208   bool HasLoc = false;
209   bool HasSrcLoc = false;
210   bool HasType = false;
211   uint64_t BytesCovered = 0;
212   uint64_t BytesEntryValuesCovered = 0;
213   auto &FnStats = FnStatMap[FnPrefix];
214   bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter;
215   bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable;
216   bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member &&
217                           Die.find(dwarf::DW_AT_const_value);
218 
219   if (Die.getTag() == dwarf::DW_TAG_call_site ||
220       Die.getTag() == dwarf::DW_TAG_GNU_call_site) {
221     GlobalStats.CallSiteDIEs++;
222     return;
223   }
224 
225   if (Die.getTag() == dwarf::DW_TAG_call_site_parameter ||
226       Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) {
227     GlobalStats.CallSiteParamDIEs++;
228     return;
229   }
230 
231   if (!IsParam && !IsLocalVar && !IsConstantMember) {
232     // Not a variable or constant member.
233     return;
234   }
235 
236   // Ignore declarations of global variables.
237   if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
238     return;
239 
240   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
241       Die.findRecursively(dwarf::DW_AT_decl_line))
242     HasSrcLoc = true;
243 
244   if (Die.findRecursively(dwarf::DW_AT_type))
245     HasType = true;
246 
247   auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
248     DWARFUnit *U = Die.getDwarfUnit();
249     DataExtractor Data(toStringRef(D),
250                        Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
251     DWARFExpression Expression(Data, U->getAddressByteSize(),
252                                U->getFormParams().Format);
253     // Consider the expression containing the DW_OP_entry_value as
254     // an entry value.
255     return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) {
256       return Op.getCode() == dwarf::DW_OP_entry_value ||
257              Op.getCode() == dwarf::DW_OP_GNU_entry_value;
258     });
259   };
260 
261   if (Die.find(dwarf::DW_AT_const_value)) {
262     // This catches constant members *and* variables.
263     HasLoc = true;
264     BytesCovered = BytesInScope;
265   } else {
266     // Handle variables and function arguments.
267     Expected<std::vector<DWARFLocationExpression>> Loc =
268         Die.getLocations(dwarf::DW_AT_location);
269     if (!Loc) {
270       consumeError(Loc.takeError());
271     } else {
272       HasLoc = true;
273       // Get PC coverage.
274       auto Default = find_if(
275           *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
276       if (Default != Loc->end()) {
277         // Assume the entire range is covered by a single location.
278         BytesCovered = BytesInScope;
279       } else {
280         for (auto Entry : *Loc) {
281           uint64_t BytesEntryCovered = Entry.Range->HighPC - Entry.Range->LowPC;
282           BytesCovered += BytesEntryCovered;
283           if (IsEntryValue(Entry.Expr))
284             BytesEntryValuesCovered += BytesEntryCovered;
285         }
286       }
287     }
288   }
289 
290   // Calculate the debug location statistics.
291   if (BytesInScope) {
292     LocStats.NumVarParam++;
293     if (IsParam)
294       LocStats.NumParam++;
295     else if (IsLocalVar)
296       LocStats.NumVar++;
297 
298     collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats,
299                     LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
300                     IsLocalVar);
301     // Non debug entry values coverage statistics.
302     collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope,
303                     LocStats.VarParamNonEntryValLocStats,
304                     LocStats.ParamNonEntryValLocStats,
305                     LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
306   }
307 
308   // Collect PC range coverage data.
309   if (DWARFDie D =
310           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
311     Die = D;
312 
313   std::string VarID = constructDieID(Die, VarPrefix);
314   FnStats.VarsInFunction.insert(VarID);
315 
316   if (BytesInScope) {
317     // Turns out we have a lot of ranges that extend past the lexical scope.
318     GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered);
319     GlobalStats.ScopeBytes += BytesInScope;
320     GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
321     if (IsParam) {
322       GlobalStats.ParamScopeBytesCovered +=
323           std::min(BytesInScope, BytesCovered);
324       GlobalStats.ParamScopeBytes += BytesInScope;
325       GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
326     } else if (IsLocalVar) {
327       GlobalStats.LocalVarScopeBytesCovered +=
328           std::min(BytesInScope, BytesCovered);
329       GlobalStats.LocalVarScopeBytes += BytesInScope;
330       GlobalStats.LocalVarScopeEntryValueBytesCovered +=
331           BytesEntryValuesCovered;
332     }
333     assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes);
334   }
335 
336   if (IsConstantMember) {
337     FnStats.ConstantMembers++;
338     return;
339   }
340 
341   FnStats.TotalVarWithLoc += (unsigned)HasLoc;
342 
343   if (Die.find(dwarf::DW_AT_artificial)) {
344     FnStats.NumArtificial++;
345     return;
346   }
347 
348   if (IsParam) {
349     FnStats.NumParams++;
350     if (HasType)
351       FnStats.NumParamTypes++;
352     if (HasSrcLoc)
353       FnStats.NumParamSourceLocations++;
354     if (HasLoc)
355       FnStats.NumParamLocations++;
356   } else if (IsLocalVar) {
357     FnStats.NumLocalVars++;
358     if (HasType)
359       FnStats.NumLocalVarTypes++;
360     if (HasSrcLoc)
361       FnStats.NumLocalVarSourceLocations++;
362     if (HasLoc)
363       FnStats.NumLocalVarLocations++;
364   }
365 }
366 
367 /// 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)368 static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
369                                   std::string VarPrefix, uint64_t BytesInScope,
370                                   uint32_t InlineDepth,
371                                   StringMap<PerFunctionStats> &FnStatMap,
372                                   GlobalStats &GlobalStats,
373                                   LocationStats &LocStats) {
374   const dwarf::Tag Tag = Die.getTag();
375   // Skip function types.
376   if (Tag == dwarf::DW_TAG_subroutine_type)
377     return;
378 
379   // Handle any kind of lexical scope.
380   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
381   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
382   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
383   if (IsFunction || IsInlinedFunction || IsBlock) {
384 
385     // Reset VarPrefix when entering a new function.
386     if (Die.getTag() == dwarf::DW_TAG_subprogram ||
387         Die.getTag() == dwarf::DW_TAG_inlined_subroutine)
388       VarPrefix = "v";
389 
390     // Ignore forward declarations.
391     if (Die.find(dwarf::DW_AT_declaration))
392       return;
393 
394     // Check for call sites.
395     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
396       GlobalStats.CallSiteEntries++;
397 
398     // PC Ranges.
399     auto RangesOrError = Die.getAddressRanges();
400     if (!RangesOrError) {
401       llvm::consumeError(RangesOrError.takeError());
402       return;
403     }
404 
405     auto Ranges = RangesOrError.get();
406     uint64_t BytesInThisScope = 0;
407     for (auto Range : Ranges)
408       BytesInThisScope += Range.HighPC - Range.LowPC;
409 
410     // Count the function.
411     if (!IsBlock) {
412       // Skip over abstract origins.
413       if (Die.find(dwarf::DW_AT_inline))
414         return;
415       std::string FnID = constructDieID(Die);
416       // We've seen an instance of this function.
417       auto &FnStats = FnStatMap[FnID];
418       FnStats.IsFunction = true;
419       if (IsInlinedFunction) {
420         FnStats.NumFnInlined++;
421         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
422           FnStats.NumAbstractOrigins++;
423       } else {
424         FnStats.NumFnOutOfLine++;
425       }
426       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
427           Die.findRecursively(dwarf::DW_AT_decl_line))
428         FnStats.HasSourceLocation = true;
429       // Update function prefix.
430       FnPrefix = FnID;
431     }
432 
433     if (BytesInThisScope) {
434       BytesInScope = BytesInThisScope;
435       if (IsFunction)
436         GlobalStats.FunctionSize += BytesInThisScope;
437       else if (IsInlinedFunction && InlineDepth == 0)
438         GlobalStats.InlineFunctionSize += BytesInThisScope;
439     }
440   } else {
441     // Not a scope, visit the Die itself. It could be a variable.
442     collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
443                        FnStatMap, GlobalStats, LocStats);
444   }
445 
446   // Set InlineDepth correctly for child recursion
447   if (IsFunction)
448     InlineDepth = 0;
449   else if (IsInlinedFunction)
450     ++InlineDepth;
451 
452   // Traverse children.
453   unsigned LexicalBlockIndex = 0;
454   unsigned FormalParameterIndex = 0;
455   DWARFDie Child = Die.getFirstChild();
456   while (Child) {
457     std::string ChildVarPrefix = VarPrefix;
458     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
459       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
460     if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
461       ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
462 
463     collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope,
464                           InlineDepth, FnStatMap, GlobalStats, LocStats);
465     Child = Child.getSibling();
466   }
467 }
468 
469 /// Print machine-readable output.
470 /// The machine-readable format is single-line JSON output.
471 /// \{
printDatum(raw_ostream & OS,const char * Key,json::Value Value)472 static void printDatum(raw_ostream &OS, const char *Key, json::Value Value) {
473   OS << ",\"" << Key << "\":" << Value;
474   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
475 }
476 
printLocationStats(raw_ostream & OS,const char * Key,std::vector<unsigned> & LocationStats)477 static void printLocationStats(raw_ostream &OS, const char *Key,
478                                std::vector<unsigned> &LocationStats) {
479   OS << ",\"" << Key << " with 0% of parent scope covered by DW_AT_location\":"
480      << LocationStats[0];
481   LLVM_DEBUG(
482       llvm::dbgs() << Key
483                    << " with 0% of parent scope covered by DW_AT_location: \\"
484                    << LocationStats[0] << '\n');
485   OS << ",\"" << Key
486      << " with (0%,10%) of parent scope covered by DW_AT_location\":"
487      << LocationStats[1];
488   LLVM_DEBUG(llvm::dbgs()
489              << Key
490              << " with (0%,10%) of parent scope covered by DW_AT_location: "
491              << LocationStats[1] << '\n');
492   for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
493     OS << ",\"" << Key << " with [" << (i - 1) * 10 << "%," << i * 10
494        << "%) of parent scope covered by DW_AT_location\":" << LocationStats[i];
495     LLVM_DEBUG(llvm::dbgs()
496                << Key << " with [" << (i - 1) * 10 << "%," << i * 10
497                << "%) of parent scope covered by DW_AT_location: "
498                << LocationStats[i]);
499   }
500   OS << ",\"" << Key
501      << " with 100% of parent scope covered by DW_AT_location\":"
502      << LocationStats[NumOfCoverageCategories - 1];
503   LLVM_DEBUG(
504       llvm::dbgs() << Key
505                    << " with 100% of parent scope covered by DW_AT_location: "
506                    << LocationStats[NumOfCoverageCategories - 1]);
507 }
508 
printSectionSizes(raw_ostream & OS,const SectionSizes & Sizes)509 static void printSectionSizes(raw_ostream &OS, const SectionSizes &Sizes) {
510   for (const auto &DebugSec : Sizes.DebugSectionSizes)
511     OS << ",\"#bytes in " << DebugSec.getKey() << "\":" << DebugSec.getValue();
512 }
513 
514 /// \}
515 
516 /// Collect debug info quality metrics for an entire DIContext.
517 ///
518 /// Do the impossible and reduce the quality of the debug info down to a few
519 /// numbers. The idea is to condense the data into numbers that can be tracked
520 /// over time to identify trends in newer compiler versions and gauge the effect
521 /// of particular optimizations. The raw numbers themselves are not particularly
522 /// useful, only the delta between compiling the same program with different
523 /// compilers is.
collectStatsForObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)524 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
525                                           const Twine &Filename,
526                                           raw_ostream &OS) {
527   StringRef FormatName = Obj.getFileFormatName();
528   GlobalStats GlobalStats;
529   LocationStats LocStats;
530   StringMap<PerFunctionStats> Statistics;
531   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
532     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
533       collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats,
534                             LocStats);
535 
536   /// Collect the sizes of debug sections.
537   SectionSizes Sizes;
538   calculateSectionSizes(Obj, Sizes, Filename);
539 
540   /// The version number should be increased every time the algorithm is changed
541   /// (including bug fixes). New metrics may be added without increasing the
542   /// version.
543   unsigned Version = 5;
544   unsigned VarParamTotal = 0;
545   unsigned VarParamUnique = 0;
546   unsigned VarParamWithLoc = 0;
547   unsigned NumFunctions = 0;
548   unsigned NumInlinedFunctions = 0;
549   unsigned NumFuncsWithSrcLoc = 0;
550   unsigned NumAbstractOrigins = 0;
551   unsigned ParamTotal = 0;
552   unsigned ParamWithType = 0;
553   unsigned ParamWithLoc = 0;
554   unsigned ParamWithSrcLoc = 0;
555   unsigned LocalVarTotal = 0;
556   unsigned LocalVarWithType = 0;
557   unsigned LocalVarWithSrcLoc = 0;
558   unsigned LocalVarWithLoc = 0;
559   for (auto &Entry : Statistics) {
560     PerFunctionStats &Stats = Entry.getValue();
561     unsigned TotalVars = Stats.VarsInFunction.size() *
562                          (Stats.NumFnInlined + Stats.NumFnOutOfLine);
563     // Count variables in global scope.
564     if (!Stats.IsFunction)
565       TotalVars =
566           Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
567     unsigned Constants = Stats.ConstantMembers;
568     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
569     VarParamTotal += TotalVars;
570     VarParamUnique += Stats.VarsInFunction.size();
571     LLVM_DEBUG(for (auto &V
572                     : Stats.VarsInFunction) llvm::dbgs()
573                << Entry.getKey() << ": " << V.getKey() << "\n");
574     NumFunctions += Stats.IsFunction;
575     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
576     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
577     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
578     ParamTotal += Stats.NumParams;
579     ParamWithType += Stats.NumParamTypes;
580     ParamWithLoc += Stats.NumParamLocations;
581     ParamWithSrcLoc += Stats.NumParamSourceLocations;
582     LocalVarTotal += Stats.NumLocalVars;
583     LocalVarWithType += Stats.NumLocalVarTypes;
584     LocalVarWithLoc += Stats.NumLocalVarLocations;
585     LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
586   }
587 
588   // Print summary.
589   OS.SetBufferSize(1024);
590   OS << "{\"version\":" << Version;
591   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
592              llvm::dbgs() << "---------------------------------\n");
593 
594   printDatum(OS, "file", Filename.str());
595   printDatum(OS, "format", FormatName);
596 
597   printDatum(OS, "#functions", NumFunctions);
598   printDatum(OS, "#functions with location", NumFuncsWithSrcLoc);
599   printDatum(OS, "#inlined functions", NumInlinedFunctions);
600   printDatum(OS, "#inlined functions with abstract origins",
601              NumAbstractOrigins);
602 
603   // This includes local variables and formal parameters.
604   printDatum(OS, "#unique source variables", VarParamUnique);
605   printDatum(OS, "#source variables", VarParamTotal);
606   printDatum(OS, "#source variables with location", VarParamWithLoc);
607 
608   printDatum(OS, "#call site entries", GlobalStats.CallSiteEntries);
609   printDatum(OS, "#call site DIEs", GlobalStats.CallSiteDIEs);
610   printDatum(OS, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
611 
612   printDatum(OS, "sum_all_variables(#bytes in parent scope)",
613              GlobalStats.ScopeBytes);
614   printDatum(OS,
615              "sum_all_variables(#bytes in parent scope covered by "
616              "DW_AT_location)",
617              GlobalStats.ScopeBytesCovered);
618   printDatum(OS,
619              "sum_all_variables(#bytes in parent scope covered by "
620              "DW_OP_entry_value)",
621              GlobalStats.ScopeEntryValueBytesCovered);
622 
623   printDatum(OS, "sum_all_params(#bytes in parent scope)",
624              GlobalStats.ParamScopeBytes);
625   printDatum(
626       OS,
627       "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
628       GlobalStats.ParamScopeBytesCovered);
629   printDatum(OS,
630              "sum_all_params(#bytes in parent scope covered by "
631              "DW_OP_entry_value)",
632              GlobalStats.ParamScopeEntryValueBytesCovered);
633 
634   printDatum(OS, "sum_all_local_vars(#bytes in parent scope)",
635              GlobalStats.LocalVarScopeBytes);
636   printDatum(OS,
637              "sum_all_local_vars(#bytes in parent scope covered by "
638              "DW_AT_location)",
639              GlobalStats.LocalVarScopeBytesCovered);
640   printDatum(OS,
641              "sum_all_local_vars(#bytes in parent scope covered by "
642              "DW_OP_entry_value)",
643              GlobalStats.LocalVarScopeEntryValueBytesCovered);
644 
645   printDatum(OS, "#bytes witin functions", GlobalStats.FunctionSize);
646   printDatum(OS, "#bytes witin inlined functions",
647              GlobalStats.InlineFunctionSize);
648 
649   // Print the summary for formal parameters.
650   printDatum(OS, "#params", ParamTotal);
651   printDatum(OS, "#params with source location", ParamWithSrcLoc);
652   printDatum(OS, "#params with type", ParamWithType);
653   printDatum(OS, "#params with binary location", ParamWithLoc);
654 
655   // Print the summary for local variables.
656   printDatum(OS, "#local vars", LocalVarTotal);
657   printDatum(OS, "#local vars with source location", LocalVarWithSrcLoc);
658   printDatum(OS, "#local vars with type", LocalVarWithType);
659   printDatum(OS, "#local vars with binary location", LocalVarWithLoc);
660 
661   // Print the debug section sizes.
662   printSectionSizes(OS, Sizes);
663 
664   // Print the location statistics for variables (includes local variables
665   // and formal parameters).
666   printDatum(OS, "#variables processed by location statistics",
667              LocStats.NumVarParam);
668   printLocationStats(OS, "#variables", LocStats.VarParamLocStats);
669   printLocationStats(OS, "#variables - entry values",
670                      LocStats.VarParamNonEntryValLocStats);
671 
672   // Print the location statistics for formal parameters.
673   printDatum(OS, "#params processed by location statistics", LocStats.NumParam);
674   printLocationStats(OS, "#params", LocStats.ParamLocStats);
675   printLocationStats(OS, "#params - entry values",
676                      LocStats.ParamNonEntryValLocStats);
677 
678   // Print the location statistics for local variables.
679   printDatum(OS, "#local vars processed by location statistics",
680              LocStats.NumVar);
681   printLocationStats(OS, "#local vars", LocStats.LocalVarLocStats);
682   printLocationStats(OS, "#local vars - entry values",
683                      LocStats.LocalVarNonEntryValLocStats);
684   OS << "}\n";
685   LLVM_DEBUG(
686       llvm::dbgs() << "Total Availability: "
687                    << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal)
688                    << "%\n";
689       llvm::dbgs() << "PC Ranges covered: "
690                    << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
691                                       GlobalStats.ScopeBytes)
692                    << "%\n");
693   return true;
694 }
695