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/StringSet.h"
12 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
13 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
14 #include "llvm/Object/ObjectFile.h"
15 #include "llvm/Support/JSON.h"
16 
17 #define DEBUG_TYPE "dwarfdump"
18 using namespace llvm;
19 using namespace llvm::dwarfdump;
20 using namespace llvm::object;
21 
22 namespace {
23 /// This represents the number of categories of debug location coverage being
24 /// calculated. The first category is the number of variables with 0% location
25 /// coverage, but the last category is the number of variables with 100%
26 /// location coverage.
27 constexpr int NumOfCoverageCategories = 12;
28 
29 /// This is used for zero location coverage bucket.
30 constexpr unsigned ZeroCoverageBucket = 0;
31 
32 /// This represents variables DIE offsets.
33 using InlinedVarsTy = llvm::SmallVector<uint64_t>;
34 /// This maps function DIE offset to its variables.
35 using InlinedVarsTyMap = llvm::DenseMap<uint64_t, InlinedVarsTy>;
36 /// This represents inlined_subroutine DIE offsets.
37 using InlinedFnInstacesTy = llvm::SmallVector<uint64_t>;
38 
39 /// Holds statistics for one function (or other entity that has a PC range and
40 /// contains variables, such as a compile unit).
41 struct PerFunctionStats {
42   /// Number of inlined instances of this function.
43   unsigned NumFnInlined = 0;
44   /// Number of out-of-line instances of this function.
45   unsigned NumFnOutOfLine = 0;
46   /// Number of inlined instances that have abstract origins.
47   unsigned NumAbstractOrigins = 0;
48   /// Number of variables and parameters with location across all inlined
49   /// instances.
50   unsigned TotalVarWithLoc = 0;
51   /// Number of constants with location across all inlined instances.
52   unsigned ConstantMembers = 0;
53   /// Number of arificial variables, parameters or members across all instances.
54   unsigned NumArtificial = 0;
55   /// List of all Variables and parameters in this function.
56   StringSet<> VarsInFunction;
57   /// Compile units also cover a PC range, but have this flag set to false.
58   bool IsFunction = false;
59   /// Function has source location information.
60   bool HasSourceLocation = false;
61   /// Number of function parameters.
62   unsigned NumParams = 0;
63   /// Number of function parameters with source location.
64   unsigned NumParamSourceLocations = 0;
65   /// Number of function parameters with type.
66   unsigned NumParamTypes = 0;
67   /// Number of function parameters with a DW_AT_location.
68   unsigned NumParamLocations = 0;
69   /// Number of local variables.
70   unsigned NumLocalVars = 0;
71   /// Number of local variables with source location.
72   unsigned NumLocalVarSourceLocations = 0;
73   /// Number of local variables with type.
74   unsigned NumLocalVarTypes = 0;
75   /// Number of local variables with DW_AT_location.
76   unsigned NumLocalVarLocations = 0;
77 };
78 
79 /// Holds accumulated global statistics about DIEs.
80 struct GlobalStats {
81   /// Total number of PC range bytes covered by DW_AT_locations.
82   unsigned TotalBytesCovered = 0;
83   /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
84   unsigned ScopeBytesCovered = 0;
85   /// Total number of PC range bytes in each variable's enclosing scope.
86   unsigned ScopeBytes = 0;
87   /// Total number of PC range bytes covered by DW_AT_locations with
88   /// the debug entry values (DW_OP_entry_value).
89   unsigned ScopeEntryValueBytesCovered = 0;
90   /// Total number of PC range bytes covered by DW_AT_locations of
91   /// formal parameters.
92   unsigned ParamScopeBytesCovered = 0;
93   /// Total number of PC range bytes in each parameter's enclosing scope.
94   unsigned ParamScopeBytes = 0;
95   /// Total number of PC range bytes covered by DW_AT_locations with
96   /// the debug entry values (DW_OP_entry_value) (only for parameters).
97   unsigned ParamScopeEntryValueBytesCovered = 0;
98   /// Total number of PC range bytes covered by DW_AT_locations (only for local
99   /// variables).
100   unsigned LocalVarScopeBytesCovered = 0;
101   /// Total number of PC range bytes in each local variable's enclosing scope.
102   unsigned LocalVarScopeBytes = 0;
103   /// Total number of PC range bytes covered by DW_AT_locations with
104   /// the debug entry values (DW_OP_entry_value) (only for local variables).
105   unsigned LocalVarScopeEntryValueBytesCovered = 0;
106   /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
107   unsigned CallSiteEntries = 0;
108   /// Total number of call site DIEs (DW_TAG_call_site).
109   unsigned CallSiteDIEs = 0;
110   /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
111   unsigned CallSiteParamDIEs = 0;
112   /// Total byte size of concrete functions. This byte size includes
113   /// inline functions contained in the concrete functions.
114   unsigned FunctionSize = 0;
115   /// Total byte size of inlined functions. This is the total number of bytes
116   /// for the top inline functions within concrete functions. This can help
117   /// tune the inline settings when compiling to match user expectations.
118   unsigned InlineFunctionSize = 0;
119 };
120 
121 /// Holds accumulated debug location statistics about local variables and
122 /// formal parameters.
123 struct LocationStats {
124   /// Map the scope coverage decile to the number of variables in the decile.
125   /// The first element of the array (at the index zero) represents the number
126   /// of variables with the no debug location at all, but the last element
127   /// in the vector represents the number of fully covered variables within
128   /// its scope.
129   std::vector<unsigned> VarParamLocStats{
130       std::vector<unsigned>(NumOfCoverageCategories, 0)};
131   /// Map non debug entry values coverage.
132   std::vector<unsigned> VarParamNonEntryValLocStats{
133       std::vector<unsigned>(NumOfCoverageCategories, 0)};
134   /// The debug location statistics for formal parameters.
135   std::vector<unsigned> ParamLocStats{
136       std::vector<unsigned>(NumOfCoverageCategories, 0)};
137   /// Map non debug entry values coverage for formal parameters.
138   std::vector<unsigned> ParamNonEntryValLocStats{
139       std::vector<unsigned>(NumOfCoverageCategories, 0)};
140   /// The debug location statistics for local variables.
141   std::vector<unsigned> LocalVarLocStats{
142       std::vector<unsigned>(NumOfCoverageCategories, 0)};
143   /// Map non debug entry values coverage for local variables.
144   std::vector<unsigned> LocalVarNonEntryValLocStats{
145       std::vector<unsigned>(NumOfCoverageCategories, 0)};
146   /// Total number of local variables and function parameters processed.
147   unsigned NumVarParam = 0;
148   /// Total number of formal parameters processed.
149   unsigned NumParam = 0;
150   /// Total number of local variables processed.
151   unsigned NumVar = 0;
152 };
153 } // namespace
154 
155 /// Collect debug location statistics for one DIE.
156 static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
157                             std::vector<unsigned> &VarParamLocStats,
158                             std::vector<unsigned> &ParamLocStats,
159                             std::vector<unsigned> &LocalVarLocStats,
160                             bool IsParam, bool IsLocalVar) {
161   auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
162     // No debug location at all for the variable.
163     if (ScopeBytesCovered == 0)
164       return 0;
165     // Fully covered variable within its scope.
166     if (ScopeBytesCovered >= BytesInScope)
167       return NumOfCoverageCategories - 1;
168     // Get covered range (e.g. 20%-29%).
169     unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
170     LocBucket /= 10;
171     return LocBucket + 1;
172   };
173 
174   unsigned CoverageBucket = getCoverageBucket();
175 
176   VarParamLocStats[CoverageBucket]++;
177   if (IsParam)
178     ParamLocStats[CoverageBucket]++;
179   else if (IsLocalVar)
180     LocalVarLocStats[CoverageBucket]++;
181 }
182 
183 /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
184 /// and DeclLine. The identifier aims to be unique for any unique entities,
185 /// but keeping the same among different instances of the same entity.
186 static std::string constructDieID(DWARFDie Die,
187                                   StringRef Prefix = StringRef()) {
188   std::string IDStr;
189   llvm::raw_string_ostream ID(IDStr);
190   ID << Prefix
191      << Die.getName(DINameKind::LinkageName);
192 
193   // Prefix + Name is enough for local variables and parameters.
194   if (!Prefix.empty() && !Prefix.equals("g"))
195     return ID.str();
196 
197   auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
198   std::string File;
199   if (DeclFile) {
200     DWARFUnit *U = Die.getDwarfUnit();
201     if (const auto *LT = U->getContext().getLineTableForUnit(U))
202       if (LT->getFileNameByIndex(
203               dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
204               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
205         File = std::string(sys::path::filename(File));
206   }
207   ID << ":" << (File.empty() ? "/" : File);
208   ID << ":"
209      << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
210   return ID.str();
211 }
212 
213 /// Return the number of bytes in the overlap of ranges A and B.
214 static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
215   uint64_t Lower = std::max(A.LowPC, B.LowPC);
216   uint64_t Upper = std::min(A.HighPC, B.HighPC);
217   if (Lower >= Upper)
218     return 0;
219   return Upper - Lower;
220 }
221 
222 /// Collect debug info quality metrics for one DIE.
223 static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
224                                std::string VarPrefix, uint64_t BytesInScope,
225                                uint32_t InlineDepth,
226                                StringMap<PerFunctionStats> &FnStatMap,
227                                GlobalStats &GlobalStats,
228                                LocationStats &LocStats,
229                                InlinedVarsTy *InlinedVariables) {
230   const dwarf::Tag Tag = Die.getTag();
231   // Skip CU node.
232   if (Tag == dwarf::DW_TAG_compile_unit)
233     return;
234 
235   bool HasLoc = false;
236   bool HasSrcLoc = false;
237   bool HasType = false;
238   uint64_t TotalBytesCovered = 0;
239   uint64_t ScopeBytesCovered = 0;
240   uint64_t BytesEntryValuesCovered = 0;
241   auto &FnStats = FnStatMap[FnPrefix];
242   bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
243   bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
244   bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
245                           Die.find(dwarf::DW_AT_const_value);
246 
247   // For zero covered inlined variables the locstats will be
248   // calculated later.
249   bool DeferLocStats = false;
250 
251   if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
252     GlobalStats.CallSiteDIEs++;
253     return;
254   }
255 
256   if (Tag == dwarf::DW_TAG_call_site_parameter ||
257       Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
258     GlobalStats.CallSiteParamDIEs++;
259     return;
260   }
261 
262   if (!IsParam && !IsLocalVar && !IsConstantMember) {
263     // Not a variable or constant member.
264     return;
265   }
266 
267   // Ignore declarations of global variables.
268   if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
269     return;
270 
271   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
272       Die.findRecursively(dwarf::DW_AT_decl_line))
273     HasSrcLoc = true;
274 
275   if (Die.findRecursively(dwarf::DW_AT_type))
276     HasType = true;
277 
278   // Check if it is an inlined variable.
279   if (Die.find(dwarf::DW_AT_abstract_origin)) {
280       if (Die.find(dwarf::DW_AT_location) ||
281           Die.find(dwarf::DW_AT_const_value)) {
282         if (InlinedVariables) {
283           auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
284           // Do not track this inlined var any more, since it has location
285           // coverage.
286           llvm::erase_value(*InlinedVariables, (*Offset).getRawUValue());
287         }
288       } else {
289         // The locstats will be handled at the end of
290         // the collectStatsRecursive().
291         DeferLocStats = true;
292       }
293   }
294 
295   auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
296     DWARFUnit *U = Die.getDwarfUnit();
297     DataExtractor Data(toStringRef(D),
298                        Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
299     DWARFExpression Expression(Data, U->getAddressByteSize(),
300                                U->getFormParams().Format);
301     // Consider the expression containing the DW_OP_entry_value as
302     // an entry value.
303     return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) {
304       return Op.getCode() == dwarf::DW_OP_entry_value ||
305              Op.getCode() == dwarf::DW_OP_GNU_entry_value;
306     });
307   };
308 
309   if (Die.find(dwarf::DW_AT_const_value)) {
310     // This catches constant members *and* variables.
311     HasLoc = true;
312     ScopeBytesCovered = BytesInScope;
313     TotalBytesCovered = BytesInScope;
314   } else {
315     // Handle variables and function arguments.
316     Expected<std::vector<DWARFLocationExpression>> Loc =
317         Die.getLocations(dwarf::DW_AT_location);
318     if (!Loc) {
319       consumeError(Loc.takeError());
320     } else {
321       HasLoc = true;
322       // Get PC coverage.
323       auto Default = find_if(
324           *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
325       if (Default != Loc->end()) {
326         // Assume the entire range is covered by a single location.
327         ScopeBytesCovered = BytesInScope;
328         TotalBytesCovered = BytesInScope;
329       } else {
330         // Caller checks this Expected result already, it cannot fail.
331         auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
332         for (auto Entry : *Loc) {
333           TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
334           uint64_t ScopeBytesCoveredByEntry = 0;
335           // Calculate how many bytes of the parent scope this entry covers.
336           // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
337           // address ranges defined by the bounded location descriptions of a
338           // location list may overlap". So in theory a variable can have
339           // multiple simultaneous locations, which would make this calculation
340           // misleading because we will count the overlapped areas
341           // twice. However, clang does not currently emit DWARF like this.
342           for (DWARFAddressRange R : ScopeRanges) {
343             ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
344           }
345           ScopeBytesCovered += ScopeBytesCoveredByEntry;
346           if (IsEntryValue(Entry.Expr))
347             BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
348         }
349       }
350     }
351   }
352 
353   // Calculate the debug location statistics.
354   if (BytesInScope && !DeferLocStats) {
355     LocStats.NumVarParam++;
356     if (IsParam)
357       LocStats.NumParam++;
358     else if (IsLocalVar)
359       LocStats.NumVar++;
360 
361     collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
362                     LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
363                     IsLocalVar);
364     // Non debug entry values coverage statistics.
365     collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
366                     LocStats.VarParamNonEntryValLocStats,
367                     LocStats.ParamNonEntryValLocStats,
368                     LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
369   }
370 
371   // Collect PC range coverage data.
372   if (DWARFDie D =
373           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
374     Die = D;
375 
376   std::string VarID = constructDieID(Die, VarPrefix);
377   FnStats.VarsInFunction.insert(VarID);
378 
379   GlobalStats.TotalBytesCovered += TotalBytesCovered;
380   if (BytesInScope) {
381     GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
382     GlobalStats.ScopeBytes += BytesInScope;
383     GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
384     if (IsParam) {
385       GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
386       GlobalStats.ParamScopeBytes += BytesInScope;
387       GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
388     } else if (IsLocalVar) {
389       GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
390       GlobalStats.LocalVarScopeBytes += BytesInScope;
391       GlobalStats.LocalVarScopeEntryValueBytesCovered +=
392           BytesEntryValuesCovered;
393     }
394     assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes);
395   }
396 
397   if (IsConstantMember) {
398     FnStats.ConstantMembers++;
399     return;
400   }
401 
402   FnStats.TotalVarWithLoc += (unsigned)HasLoc;
403 
404   if (Die.find(dwarf::DW_AT_artificial)) {
405     FnStats.NumArtificial++;
406     return;
407   }
408 
409   if (IsParam) {
410     FnStats.NumParams++;
411     if (HasType)
412       FnStats.NumParamTypes++;
413     if (HasSrcLoc)
414       FnStats.NumParamSourceLocations++;
415     if (HasLoc)
416       FnStats.NumParamLocations++;
417   } else if (IsLocalVar) {
418     FnStats.NumLocalVars++;
419     if (HasType)
420       FnStats.NumLocalVarTypes++;
421     if (HasSrcLoc)
422       FnStats.NumLocalVarSourceLocations++;
423     if (HasLoc)
424       FnStats.NumLocalVarLocations++;
425   }
426 }
427 
428 /// Recursively collect variables from subprogram with
429 /// DW_AT_inline attribute.
430 static void collectInlinedFnInfo(DWARFDie Die,
431                                  uint64_t SPOffset,
432                                  InlinedVarsTyMap &GlobalInlinedFnInfo) {
433   DWARFDie Child = Die.getFirstChild();
434   while (Child) {
435     const dwarf::Tag ChildTag = Child.getTag();
436     if (ChildTag == dwarf::DW_TAG_formal_parameter ||
437         ChildTag == dwarf::DW_TAG_variable)
438       GlobalInlinedFnInfo[SPOffset].push_back(Child.getOffset());
439     else if (ChildTag == dwarf::DW_TAG_lexical_block)
440       collectInlinedFnInfo(Child, SPOffset, GlobalInlinedFnInfo);
441     Child = Child.getSibling();
442   }
443 }
444 
445 /// Recursively collect debug info quality metrics.
446 static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
447                                   std::string VarPrefix, uint64_t BytesInScope,
448                                   uint32_t InlineDepth,
449                                   StringMap<PerFunctionStats> &FnStatMap,
450                                   GlobalStats &GlobalStats,
451                                   LocationStats &LocStats,
452                                   InlinedVarsTyMap &GlobalInlinedFnInfo,
453                                   InlinedFnInstacesTy &InlinedFnsToBeProcessed,
454                                   InlinedVarsTy *InlinedVarsPtr = nullptr) {
455   // Skip NULL nodes.
456   if (Die.isNULL())
457     return;
458 
459   const dwarf::Tag Tag = Die.getTag();
460   // Skip function types.
461   if (Tag == dwarf::DW_TAG_subroutine_type)
462     return;
463 
464   // Handle any kind of lexical scope.
465   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
466   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
467   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
468   InlinedVarsTy InlinedVars;
469   // Get the vars of the inlined fn, so the locstats
470   // reports the missing vars (with coverage 0%).
471   if (IsInlinedFunction) {
472     auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
473     if (OffsetFn) {
474       uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
475       if (GlobalInlinedFnInfo.count(OffsetOfInlineFnCopy)) {
476         InlinedVars = GlobalInlinedFnInfo[OffsetOfInlineFnCopy];
477         InlinedVarsPtr = &InlinedVars;
478       } else {
479         // This means that the DW_AT_inline fn copy is out of order,
480         // so this inlined instance will be processed later.
481         InlinedFnsToBeProcessed.push_back(Die.getOffset());
482         InlinedVarsPtr = nullptr;
483       }
484     }
485   }
486 
487   if (IsFunction || IsInlinedFunction || IsBlock) {
488     // Reset VarPrefix when entering a new function.
489     if (IsFunction || IsInlinedFunction)
490       VarPrefix = "v";
491 
492     // Ignore forward declarations.
493     if (Die.find(dwarf::DW_AT_declaration))
494       return;
495 
496     // Check for call sites.
497     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
498       GlobalStats.CallSiteEntries++;
499 
500     // PC Ranges.
501     auto RangesOrError = Die.getAddressRanges();
502     if (!RangesOrError) {
503       llvm::consumeError(RangesOrError.takeError());
504       return;
505     }
506 
507     auto Ranges = RangesOrError.get();
508     uint64_t BytesInThisScope = 0;
509     for (auto Range : Ranges)
510       BytesInThisScope += Range.HighPC - Range.LowPC;
511 
512     // Count the function.
513     if (!IsBlock) {
514       // Skip over abstract origins, but collect variables
515       // from it so it can be used for location statistics
516       // for inlined instancies.
517       if (Die.find(dwarf::DW_AT_inline)) {
518         uint64_t SPOffset = Die.getOffset();
519         collectInlinedFnInfo(Die, SPOffset, GlobalInlinedFnInfo);
520         return;
521       }
522 
523       std::string FnID = constructDieID(Die);
524       // We've seen an instance of this function.
525       auto &FnStats = FnStatMap[FnID];
526       FnStats.IsFunction = true;
527       if (IsInlinedFunction) {
528         FnStats.NumFnInlined++;
529         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
530           FnStats.NumAbstractOrigins++;
531       } else {
532         FnStats.NumFnOutOfLine++;
533       }
534       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
535           Die.findRecursively(dwarf::DW_AT_decl_line))
536         FnStats.HasSourceLocation = true;
537       // Update function prefix.
538       FnPrefix = FnID;
539     }
540 
541     if (BytesInThisScope) {
542       BytesInScope = BytesInThisScope;
543       if (IsFunction)
544         GlobalStats.FunctionSize += BytesInThisScope;
545       else if (IsInlinedFunction && InlineDepth == 0)
546         GlobalStats.InlineFunctionSize += BytesInThisScope;
547     }
548   } else {
549     // Not a scope, visit the Die itself. It could be a variable.
550     collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
551                        FnStatMap, GlobalStats, LocStats, InlinedVarsPtr);
552   }
553 
554   // Set InlineDepth correctly for child recursion
555   if (IsFunction)
556     InlineDepth = 0;
557   else if (IsInlinedFunction)
558     ++InlineDepth;
559 
560   // Traverse children.
561   unsigned LexicalBlockIndex = 0;
562   unsigned FormalParameterIndex = 0;
563   DWARFDie Child = Die.getFirstChild();
564   while (Child) {
565     std::string ChildVarPrefix = VarPrefix;
566     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
567       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
568     if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
569       ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
570 
571     collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope,
572                           InlineDepth, FnStatMap, GlobalStats, LocStats,
573                           GlobalInlinedFnInfo, InlinedFnsToBeProcessed,
574                           InlinedVarsPtr);
575     Child = Child.getSibling();
576   }
577 
578   if (!IsInlinedFunction)
579     return;
580 
581   // After we have processed all vars of the inlined function,
582   // we want to know how many variables have no location.
583   for (auto Offset : InlinedVars) {
584     LocStats.NumVarParam++;
585     LocStats.VarParamLocStats[ZeroCoverageBucket]++;
586     auto InlineDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
587     if (!InlineDie)
588       continue;
589     auto Tag = InlineDie.getTag();
590     if (Tag == dwarf::DW_TAG_formal_parameter) {
591       LocStats.NumParam++;
592       LocStats.ParamLocStats[ZeroCoverageBucket]++;
593     } else if (Tag == dwarf::DW_TAG_variable) {
594       LocStats.NumVar++;
595       LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
596     }
597   }
598 }
599 
600 /// Print human-readable output.
601 /// \{
602 static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
603   J.attribute(Key, Value);
604   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
605 }
606 
607 static void printLocationStats(json::OStream &J, const char *Key,
608                                std::vector<unsigned> &LocationStats) {
609   J.attribute(
610       (Twine(Key) + " with 0% of parent scope covered by DW_AT_location").str(),
611       LocationStats[0]);
612   LLVM_DEBUG(
613       llvm::dbgs() << Key
614                    << " with 0% of parent scope covered by DW_AT_location: \\"
615                    << LocationStats[0] << '\n');
616   J.attribute(
617       (Twine(Key) + " with (0%,10%) of parent scope covered by DW_AT_location")
618           .str(),
619       LocationStats[1]);
620   LLVM_DEBUG(llvm::dbgs()
621              << Key
622              << " with (0%,10%) of parent scope covered by DW_AT_location: "
623              << LocationStats[1] << '\n');
624   for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
625     J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
626                  Twine(i * 10) + "%) of parent scope covered by DW_AT_location")
627                     .str(),
628                 LocationStats[i]);
629     LLVM_DEBUG(llvm::dbgs()
630                << Key << " with [" << (i - 1) * 10 << "%," << i * 10
631                << "%) of parent scope covered by DW_AT_location: "
632                << LocationStats[i]);
633   }
634   J.attribute(
635       (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
636           .str(),
637       LocationStats[NumOfCoverageCategories - 1]);
638   LLVM_DEBUG(
639       llvm::dbgs() << Key
640                    << " with 100% of parent scope covered by DW_AT_location: "
641                    << LocationStats[NumOfCoverageCategories - 1]);
642 }
643 
644 static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
645   for (const auto &It : Sizes.DebugSectionSizes)
646     J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
647 }
648 
649 /// Stop tracking inlined variables with a location.
650 /// This is used for out-of-order DW_AT_inline subprograms only.
651 static void updateInlinedVarsCovInfo(DWARFDie InlinedFnDie,
652                                      InlinedVarsTy &InlinedVars) {
653   DWARFDie Child = InlinedFnDie.getFirstChild();
654   while (Child) {
655     const dwarf::Tag ChildTag = Child.getTag();
656     if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
657          ChildTag == dwarf::DW_TAG_variable) &&
658         (Child.find(dwarf::DW_AT_location) ||
659          Child.find(dwarf::DW_AT_const_value))) {
660       auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
661       if (OffsetVar)
662         llvm::erase_value(InlinedVars, (*OffsetVar).getRawUValue());
663     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
664       updateInlinedVarsCovInfo(Child, InlinedVars);
665     Child = Child.getSibling();
666   }
667 }
668 
669 /// Collect zero location coverage for inlined variables which refer to
670 /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
671 static void
672 collectZeroCovInlinedVars(DWARFUnit *DwUnit, GlobalStats &GlobalStats,
673                           LocationStats &LocStats,
674                           InlinedVarsTyMap &GlobalInlinedFnInfo,
675                           InlinedFnInstacesTy &InlinedFnsToBeProcessed) {
676   for (auto FnOffset : InlinedFnsToBeProcessed) {
677     DWARFDie InlinedFnDie = DwUnit->getDIEForOffset(FnOffset);
678     auto InlinedCopy = InlinedFnDie.find(dwarf::DW_AT_abstract_origin);
679     InlinedVarsTy InlinedVars;
680     if (!InlinedCopy)
681       continue;
682 
683     InlinedVars = GlobalInlinedFnInfo[(*InlinedCopy).getRawUValue()];
684     updateInlinedVarsCovInfo(InlinedFnDie, InlinedVars);
685 
686     for (auto Offset : InlinedVars) {
687       LocStats.NumVarParam++;
688       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
689       auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
690       if (Tag == dwarf::DW_TAG_formal_parameter) {
691         LocStats.NumParam++;
692         LocStats.ParamLocStats[ZeroCoverageBucket]++;
693       } else if (Tag == dwarf::DW_TAG_variable) {
694         LocStats.NumVar++;
695         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
696       }
697     }
698   }
699 }
700 
701 /// \}
702 
703 /// Collect debug info quality metrics for an entire DIContext.
704 ///
705 /// Do the impossible and reduce the quality of the debug info down to a few
706 /// numbers. The idea is to condense the data into numbers that can be tracked
707 /// over time to identify trends in newer compiler versions and gauge the effect
708 /// of particular optimizations. The raw numbers themselves are not particularly
709 /// useful, only the delta between compiling the same program with different
710 /// compilers is.
711 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
712                                           const Twine &Filename,
713                                           raw_ostream &OS) {
714   StringRef FormatName = Obj.getFileFormatName();
715   GlobalStats GlobalStats;
716   LocationStats LocStats;
717   InlinedVarsTyMap GlobalInlinedFnInfo;
718   InlinedFnInstacesTy InlinedFnsToBeProcessed;
719   StringMap<PerFunctionStats> Statistics;
720   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
721     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
722       collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats,
723                             LocStats, GlobalInlinedFnInfo,
724                             InlinedFnsToBeProcessed);
725 
726       collectZeroCovInlinedVars(CUDie.getDwarfUnit(), GlobalStats, LocStats,
727                                 GlobalInlinedFnInfo, InlinedFnsToBeProcessed);
728     }
729   }
730 
731   /// Collect the sizes of debug sections.
732   SectionSizes Sizes;
733   calculateSectionSizes(Obj, Sizes, Filename);
734 
735   /// The version number should be increased every time the algorithm is changed
736   /// (including bug fixes). New metrics may be added without increasing the
737   /// version.
738   unsigned Version = 7;
739   unsigned VarParamTotal = 0;
740   unsigned VarParamUnique = 0;
741   unsigned VarParamWithLoc = 0;
742   unsigned NumFunctions = 0;
743   unsigned NumInlinedFunctions = 0;
744   unsigned NumFuncsWithSrcLoc = 0;
745   unsigned NumAbstractOrigins = 0;
746   unsigned ParamTotal = 0;
747   unsigned ParamWithType = 0;
748   unsigned ParamWithLoc = 0;
749   unsigned ParamWithSrcLoc = 0;
750   unsigned LocalVarTotal = 0;
751   unsigned LocalVarWithType = 0;
752   unsigned LocalVarWithSrcLoc = 0;
753   unsigned LocalVarWithLoc = 0;
754   for (auto &Entry : Statistics) {
755     PerFunctionStats &Stats = Entry.getValue();
756     unsigned TotalVars = Stats.VarsInFunction.size() *
757                          (Stats.NumFnInlined + Stats.NumFnOutOfLine);
758     // Count variables in global scope.
759     if (!Stats.IsFunction)
760       TotalVars =
761           Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
762     unsigned Constants = Stats.ConstantMembers;
763     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
764     VarParamTotal += TotalVars;
765     VarParamUnique += Stats.VarsInFunction.size();
766     LLVM_DEBUG(for (auto &V
767                     : Stats.VarsInFunction) llvm::dbgs()
768                << Entry.getKey() << ": " << V.getKey() << "\n");
769     NumFunctions += Stats.IsFunction;
770     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
771     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
772     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
773     ParamTotal += Stats.NumParams;
774     ParamWithType += Stats.NumParamTypes;
775     ParamWithLoc += Stats.NumParamLocations;
776     ParamWithSrcLoc += Stats.NumParamSourceLocations;
777     LocalVarTotal += Stats.NumLocalVars;
778     LocalVarWithType += Stats.NumLocalVarTypes;
779     LocalVarWithLoc += Stats.NumLocalVarLocations;
780     LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
781   }
782 
783   // Print summary.
784   OS.SetBufferSize(1024);
785   json::OStream J(OS, 2);
786   J.objectBegin();
787   J.attribute("version", Version);
788   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
789              llvm::dbgs() << "---------------------------------\n");
790 
791   printDatum(J, "file", Filename.str());
792   printDatum(J, "format", FormatName);
793 
794   printDatum(J, "#functions", NumFunctions);
795   printDatum(J, "#functions with location", NumFuncsWithSrcLoc);
796   printDatum(J, "#inlined functions", NumInlinedFunctions);
797   printDatum(J, "#inlined functions with abstract origins", NumAbstractOrigins);
798 
799   // This includes local variables and formal parameters.
800   printDatum(J, "#unique source variables", VarParamUnique);
801   printDatum(J, "#source variables", VarParamTotal);
802   printDatum(J, "#source variables with location", VarParamWithLoc);
803 
804   printDatum(J, "#call site entries", GlobalStats.CallSiteEntries);
805   printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs);
806   printDatum(J, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
807 
808   printDatum(J, "sum_all_variables(#bytes in parent scope)",
809              GlobalStats.ScopeBytes);
810   printDatum(J,
811              "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
812              GlobalStats.TotalBytesCovered);
813   printDatum(J,
814              "sum_all_variables(#bytes in parent scope covered by "
815              "DW_AT_location)",
816              GlobalStats.ScopeBytesCovered);
817   printDatum(J,
818              "sum_all_variables(#bytes in parent scope covered by "
819              "DW_OP_entry_value)",
820              GlobalStats.ScopeEntryValueBytesCovered);
821 
822   printDatum(J, "sum_all_params(#bytes in parent scope)",
823              GlobalStats.ParamScopeBytes);
824   printDatum(J,
825              "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
826              GlobalStats.ParamScopeBytesCovered);
827   printDatum(J,
828              "sum_all_params(#bytes in parent scope covered by "
829              "DW_OP_entry_value)",
830              GlobalStats.ParamScopeEntryValueBytesCovered);
831 
832   printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
833              GlobalStats.LocalVarScopeBytes);
834   printDatum(J,
835              "sum_all_local_vars(#bytes in parent scope covered by "
836              "DW_AT_location)",
837              GlobalStats.LocalVarScopeBytesCovered);
838   printDatum(J,
839              "sum_all_local_vars(#bytes in parent scope covered by "
840              "DW_OP_entry_value)",
841              GlobalStats.LocalVarScopeEntryValueBytesCovered);
842 
843   printDatum(J, "#bytes within functions", GlobalStats.FunctionSize);
844   printDatum(J, "#bytes within inlined functions",
845              GlobalStats.InlineFunctionSize);
846 
847   // Print the summary for formal parameters.
848   printDatum(J, "#params", ParamTotal);
849   printDatum(J, "#params with source location", ParamWithSrcLoc);
850   printDatum(J, "#params with type", ParamWithType);
851   printDatum(J, "#params with binary location", ParamWithLoc);
852 
853   // Print the summary for local variables.
854   printDatum(J, "#local vars", LocalVarTotal);
855   printDatum(J, "#local vars with source location", LocalVarWithSrcLoc);
856   printDatum(J, "#local vars with type", LocalVarWithType);
857   printDatum(J, "#local vars with binary location", LocalVarWithLoc);
858 
859   // Print the debug section sizes.
860   printSectionSizes(J, Sizes);
861 
862   // Print the location statistics for variables (includes local variables
863   // and formal parameters).
864   printDatum(J, "#variables processed by location statistics",
865              LocStats.NumVarParam);
866   printLocationStats(J, "#variables", LocStats.VarParamLocStats);
867   printLocationStats(J, "#variables - entry values",
868                      LocStats.VarParamNonEntryValLocStats);
869 
870   // Print the location statistics for formal parameters.
871   printDatum(J, "#params processed by location statistics", LocStats.NumParam);
872   printLocationStats(J, "#params", LocStats.ParamLocStats);
873   printLocationStats(J, "#params - entry values",
874                      LocStats.ParamNonEntryValLocStats);
875 
876   // Print the location statistics for local variables.
877   printDatum(J, "#local vars processed by location statistics",
878              LocStats.NumVar);
879   printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
880   printLocationStats(J, "#local vars - entry values",
881                      LocStats.LocalVarNonEntryValLocStats);
882   J.objectEnd();
883   OS << '\n';
884   LLVM_DEBUG(
885       llvm::dbgs() << "Total Availability: "
886                    << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal)
887                    << "%\n";
888       llvm::dbgs() << "PC Ranges covered: "
889                    << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
890                                       GlobalStats.ScopeBytes)
891                    << "%\n");
892   return true;
893 }
894