xref: /openbsd-src/gnu/llvm/llvm/tools/llvm-dwarfdump/Statistics.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
1097a140dSpatrick //===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
2097a140dSpatrick //
3097a140dSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4097a140dSpatrick // See https://llvm.org/LICENSE.txt for license information.
5097a140dSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6097a140dSpatrick //
7097a140dSpatrick //===----------------------------------------------------------------------===//
8097a140dSpatrick 
9097a140dSpatrick #include "llvm-dwarfdump.h"
1009467b48Spatrick #include "llvm/ADT/DenseMap.h"
1109467b48Spatrick #include "llvm/ADT/StringSet.h"
1209467b48Spatrick #include "llvm/DebugInfo/DWARF/DWARFContext.h"
1309467b48Spatrick #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
14*d415bd75Srobert #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
1509467b48Spatrick #include "llvm/Object/ObjectFile.h"
1609467b48Spatrick #include "llvm/Support/JSON.h"
1709467b48Spatrick 
1809467b48Spatrick #define DEBUG_TYPE "dwarfdump"
1909467b48Spatrick using namespace llvm;
20097a140dSpatrick using namespace llvm::dwarfdump;
21097a140dSpatrick using namespace llvm::object;
2209467b48Spatrick 
2373471bf0Spatrick namespace {
2409467b48Spatrick /// This represents the number of categories of debug location coverage being
2509467b48Spatrick /// calculated. The first category is the number of variables with 0% location
2609467b48Spatrick /// coverage, but the last category is the number of variables with 100%
2709467b48Spatrick /// location coverage.
2809467b48Spatrick constexpr int NumOfCoverageCategories = 12;
2909467b48Spatrick 
3073471bf0Spatrick /// This is used for zero location coverage bucket.
3173471bf0Spatrick constexpr unsigned ZeroCoverageBucket = 0;
3273471bf0Spatrick 
33*d415bd75Srobert /// The UINT64_MAX is used as an indication of the overflow.
34*d415bd75Srobert constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
35*d415bd75Srobert 
3673471bf0Spatrick /// This represents variables DIE offsets.
3773471bf0Spatrick using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
3873471bf0Spatrick /// This maps function DIE offset to its variables.
3973471bf0Spatrick using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
4073471bf0Spatrick /// This represents function DIE offsets containing an abstract_origin.
4173471bf0Spatrick using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
4273471bf0Spatrick 
43*d415bd75Srobert /// This represents a data type for the stats and it helps us to
44*d415bd75Srobert /// detect an overflow.
45*d415bd75Srobert /// NOTE: This can be implemented as a template if there is an another type
46*d415bd75Srobert /// needing this.
47*d415bd75Srobert struct SaturatingUINT64 {
48*d415bd75Srobert   /// Number that represents the stats.
49*d415bd75Srobert   uint64_t Value;
50*d415bd75Srobert 
SaturatingUINT64__anon696fdd2d0111::SaturatingUINT6451*d415bd75Srobert   SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
52*d415bd75Srobert 
operator ++__anon696fdd2d0111::SaturatingUINT6453*d415bd75Srobert   void operator++(int) { return *this += 1; }
operator +=__anon696fdd2d0111::SaturatingUINT6454*d415bd75Srobert   void operator+=(uint64_t Value_) {
55*d415bd75Srobert     if (Value != OverflowValue) {
56*d415bd75Srobert       if (Value < OverflowValue - Value_)
57*d415bd75Srobert         Value += Value_;
58*d415bd75Srobert       else
59*d415bd75Srobert         Value = OverflowValue;
60*d415bd75Srobert     }
61*d415bd75Srobert   }
62*d415bd75Srobert };
63*d415bd75Srobert 
64*d415bd75Srobert /// Utility struct to store the full location of a DIE - its CU and offset.
65*d415bd75Srobert struct DIELocation {
66*d415bd75Srobert   DWARFUnit *DwUnit;
67*d415bd75Srobert   uint64_t DIEOffset;
DIELocation__anon696fdd2d0111::DIELocation68*d415bd75Srobert   DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
69*d415bd75Srobert       : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
70*d415bd75Srobert };
71*d415bd75Srobert /// This represents DWARF locations of CrossCU referencing DIEs.
72*d415bd75Srobert using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
73*d415bd75Srobert 
74*d415bd75Srobert /// This maps function DIE offset to its DWARF CU.
75*d415bd75Srobert using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
76*d415bd75Srobert 
7709467b48Spatrick /// Holds statistics for one function (or other entity that has a PC range and
7809467b48Spatrick /// contains variables, such as a compile unit).
7909467b48Spatrick struct PerFunctionStats {
8009467b48Spatrick   /// Number of inlined instances of this function.
81*d415bd75Srobert   uint64_t NumFnInlined = 0;
82097a140dSpatrick   /// Number of out-of-line instances of this function.
83*d415bd75Srobert   uint64_t NumFnOutOfLine = 0;
8409467b48Spatrick   /// Number of inlined instances that have abstract origins.
85*d415bd75Srobert   uint64_t NumAbstractOrigins = 0;
8609467b48Spatrick   /// Number of variables and parameters with location across all inlined
8709467b48Spatrick   /// instances.
88*d415bd75Srobert   uint64_t TotalVarWithLoc = 0;
8909467b48Spatrick   /// Number of constants with location across all inlined instances.
90*d415bd75Srobert   uint64_t ConstantMembers = 0;
91097a140dSpatrick   /// Number of arificial variables, parameters or members across all instances.
92*d415bd75Srobert   uint64_t NumArtificial = 0;
9309467b48Spatrick   /// List of all Variables and parameters in this function.
9409467b48Spatrick   StringSet<> VarsInFunction;
9509467b48Spatrick   /// Compile units also cover a PC range, but have this flag set to false.
9609467b48Spatrick   bool IsFunction = false;
9709467b48Spatrick   /// Function has source location information.
9809467b48Spatrick   bool HasSourceLocation = false;
9909467b48Spatrick   /// Number of function parameters.
100*d415bd75Srobert   uint64_t NumParams = 0;
10109467b48Spatrick   /// Number of function parameters with source location.
102*d415bd75Srobert   uint64_t NumParamSourceLocations = 0;
10309467b48Spatrick   /// Number of function parameters with type.
104*d415bd75Srobert   uint64_t NumParamTypes = 0;
10509467b48Spatrick   /// Number of function parameters with a DW_AT_location.
106*d415bd75Srobert   uint64_t NumParamLocations = 0;
107097a140dSpatrick   /// Number of local variables.
108*d415bd75Srobert   uint64_t NumLocalVars = 0;
109097a140dSpatrick   /// Number of local variables with source location.
110*d415bd75Srobert   uint64_t NumLocalVarSourceLocations = 0;
111097a140dSpatrick   /// Number of local variables with type.
112*d415bd75Srobert   uint64_t NumLocalVarTypes = 0;
113097a140dSpatrick   /// Number of local variables with DW_AT_location.
114*d415bd75Srobert   uint64_t NumLocalVarLocations = 0;
11509467b48Spatrick };
11609467b48Spatrick 
11709467b48Spatrick /// Holds accumulated global statistics about DIEs.
11809467b48Spatrick struct GlobalStats {
11909467b48Spatrick   /// Total number of PC range bytes covered by DW_AT_locations.
120*d415bd75Srobert   SaturatingUINT64 TotalBytesCovered = 0;
12173471bf0Spatrick   /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
122*d415bd75Srobert   SaturatingUINT64 ScopeBytesCovered = 0;
12309467b48Spatrick   /// Total number of PC range bytes in each variable's enclosing scope.
124*d415bd75Srobert   SaturatingUINT64 ScopeBytes = 0;
12509467b48Spatrick   /// Total number of PC range bytes covered by DW_AT_locations with
12609467b48Spatrick   /// the debug entry values (DW_OP_entry_value).
127*d415bd75Srobert   SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
12809467b48Spatrick   /// Total number of PC range bytes covered by DW_AT_locations of
12909467b48Spatrick   /// formal parameters.
130*d415bd75Srobert   SaturatingUINT64 ParamScopeBytesCovered = 0;
131097a140dSpatrick   /// Total number of PC range bytes in each parameter's enclosing scope.
132*d415bd75Srobert   SaturatingUINT64 ParamScopeBytes = 0;
13309467b48Spatrick   /// Total number of PC range bytes covered by DW_AT_locations with
13409467b48Spatrick   /// the debug entry values (DW_OP_entry_value) (only for parameters).
135*d415bd75Srobert   SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
13609467b48Spatrick   /// Total number of PC range bytes covered by DW_AT_locations (only for local
13709467b48Spatrick   /// variables).
138*d415bd75Srobert   SaturatingUINT64 LocalVarScopeBytesCovered = 0;
139097a140dSpatrick   /// Total number of PC range bytes in each local variable's enclosing scope.
140*d415bd75Srobert   SaturatingUINT64 LocalVarScopeBytes = 0;
14109467b48Spatrick   /// Total number of PC range bytes covered by DW_AT_locations with
14209467b48Spatrick   /// the debug entry values (DW_OP_entry_value) (only for local variables).
143*d415bd75Srobert   SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
14409467b48Spatrick   /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
145*d415bd75Srobert   SaturatingUINT64 CallSiteEntries = 0;
14609467b48Spatrick   /// Total number of call site DIEs (DW_TAG_call_site).
147*d415bd75Srobert   SaturatingUINT64 CallSiteDIEs = 0;
14809467b48Spatrick   /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
149*d415bd75Srobert   SaturatingUINT64 CallSiteParamDIEs = 0;
15009467b48Spatrick   /// Total byte size of concrete functions. This byte size includes
15109467b48Spatrick   /// inline functions contained in the concrete functions.
152*d415bd75Srobert   SaturatingUINT64 FunctionSize = 0;
15309467b48Spatrick   /// Total byte size of inlined functions. This is the total number of bytes
15409467b48Spatrick   /// for the top inline functions within concrete functions. This can help
15509467b48Spatrick   /// tune the inline settings when compiling to match user expectations.
156*d415bd75Srobert   SaturatingUINT64 InlineFunctionSize = 0;
15709467b48Spatrick };
15809467b48Spatrick 
15909467b48Spatrick /// Holds accumulated debug location statistics about local variables and
16009467b48Spatrick /// formal parameters.
16109467b48Spatrick struct LocationStats {
16209467b48Spatrick   /// Map the scope coverage decile to the number of variables in the decile.
16309467b48Spatrick   /// The first element of the array (at the index zero) represents the number
16409467b48Spatrick   /// of variables with the no debug location at all, but the last element
16509467b48Spatrick   /// in the vector represents the number of fully covered variables within
16609467b48Spatrick   /// its scope.
167*d415bd75Srobert   std::vector<SaturatingUINT64> VarParamLocStats{
168*d415bd75Srobert       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
16909467b48Spatrick   /// Map non debug entry values coverage.
170*d415bd75Srobert   std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
171*d415bd75Srobert       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
17209467b48Spatrick   /// The debug location statistics for formal parameters.
173*d415bd75Srobert   std::vector<SaturatingUINT64> ParamLocStats{
174*d415bd75Srobert       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
17509467b48Spatrick   /// Map non debug entry values coverage for formal parameters.
176*d415bd75Srobert   std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
177*d415bd75Srobert       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
17809467b48Spatrick   /// The debug location statistics for local variables.
179*d415bd75Srobert   std::vector<SaturatingUINT64> LocalVarLocStats{
180*d415bd75Srobert       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
18109467b48Spatrick   /// Map non debug entry values coverage for local variables.
182*d415bd75Srobert   std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
183*d415bd75Srobert       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
18409467b48Spatrick   /// Total number of local variables and function parameters processed.
185*d415bd75Srobert   SaturatingUINT64 NumVarParam = 0;
18609467b48Spatrick   /// Total number of formal parameters processed.
187*d415bd75Srobert   SaturatingUINT64 NumParam = 0;
18809467b48Spatrick   /// Total number of local variables processed.
189*d415bd75Srobert   SaturatingUINT64 NumVar = 0;
19009467b48Spatrick };
191097a140dSpatrick } // namespace
19209467b48Spatrick 
19309467b48Spatrick /// 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)19473471bf0Spatrick static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
195*d415bd75Srobert                             std::vector<SaturatingUINT64> &VarParamLocStats,
196*d415bd75Srobert                             std::vector<SaturatingUINT64> &ParamLocStats,
197*d415bd75Srobert                             std::vector<SaturatingUINT64> &LocalVarLocStats,
198097a140dSpatrick                             bool IsParam, bool IsLocalVar) {
19973471bf0Spatrick   auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
20009467b48Spatrick     // No debug location at all for the variable.
20173471bf0Spatrick     if (ScopeBytesCovered == 0)
20209467b48Spatrick       return 0;
20309467b48Spatrick     // Fully covered variable within its scope.
20473471bf0Spatrick     if (ScopeBytesCovered >= BytesInScope)
20509467b48Spatrick       return NumOfCoverageCategories - 1;
20609467b48Spatrick     // Get covered range (e.g. 20%-29%).
20773471bf0Spatrick     unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
20809467b48Spatrick     LocBucket /= 10;
20909467b48Spatrick     return LocBucket + 1;
21009467b48Spatrick   };
21109467b48Spatrick 
21209467b48Spatrick   unsigned CoverageBucket = getCoverageBucket();
21373471bf0Spatrick 
214*d415bd75Srobert   VarParamLocStats[CoverageBucket].Value++;
21509467b48Spatrick   if (IsParam)
216*d415bd75Srobert     ParamLocStats[CoverageBucket].Value++;
21709467b48Spatrick   else if (IsLocalVar)
218*d415bd75Srobert     LocalVarLocStats[CoverageBucket].Value++;
219097a140dSpatrick }
22073471bf0Spatrick 
221097a140dSpatrick /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
222097a140dSpatrick /// and DeclLine. The identifier aims to be unique for any unique entities,
223097a140dSpatrick /// but keeping the same among different instances of the same entity.
constructDieID(DWARFDie Die,StringRef Prefix=StringRef ())224097a140dSpatrick static std::string constructDieID(DWARFDie Die,
225097a140dSpatrick                                   StringRef Prefix = StringRef()) {
226097a140dSpatrick   std::string IDStr;
227097a140dSpatrick   llvm::raw_string_ostream ID(IDStr);
228097a140dSpatrick   ID << Prefix
229097a140dSpatrick      << Die.getName(DINameKind::LinkageName);
230097a140dSpatrick 
231097a140dSpatrick   // Prefix + Name is enough for local variables and parameters.
232097a140dSpatrick   if (!Prefix.empty() && !Prefix.equals("g"))
233097a140dSpatrick     return ID.str();
234097a140dSpatrick 
235097a140dSpatrick   auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
236097a140dSpatrick   std::string File;
237097a140dSpatrick   if (DeclFile) {
238097a140dSpatrick     DWARFUnit *U = Die.getDwarfUnit();
239097a140dSpatrick     if (const auto *LT = U->getContext().getLineTableForUnit(U))
240097a140dSpatrick       if (LT->getFileNameByIndex(
241097a140dSpatrick               dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
242097a140dSpatrick               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
243097a140dSpatrick         File = std::string(sys::path::filename(File));
244097a140dSpatrick   }
245097a140dSpatrick   ID << ":" << (File.empty() ? "/" : File);
246097a140dSpatrick   ID << ":"
247097a140dSpatrick      << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
248097a140dSpatrick   return ID.str();
24909467b48Spatrick }
25009467b48Spatrick 
25173471bf0Spatrick /// Return the number of bytes in the overlap of ranges A and B.
calculateOverlap(DWARFAddressRange A,DWARFAddressRange B)25273471bf0Spatrick static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
25373471bf0Spatrick   uint64_t Lower = std::max(A.LowPC, B.LowPC);
25473471bf0Spatrick   uint64_t Upper = std::min(A.HighPC, B.HighPC);
25573471bf0Spatrick   if (Lower >= Upper)
25673471bf0Spatrick     return 0;
25773471bf0Spatrick   return Upper - Lower;
25873471bf0Spatrick }
25973471bf0Spatrick 
26009467b48Spatrick /// 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)26173471bf0Spatrick static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
26273471bf0Spatrick                                const std::string &VarPrefix,
26373471bf0Spatrick                                uint64_t BytesInScope, uint32_t InlineDepth,
26409467b48Spatrick                                StringMap<PerFunctionStats> &FnStatMap,
26509467b48Spatrick                                GlobalStats &GlobalStats,
26673471bf0Spatrick                                LocationStats &LocStats,
26773471bf0Spatrick                                AbstractOriginVarsTy *AbstractOriginVariables) {
26873471bf0Spatrick   const dwarf::Tag Tag = Die.getTag();
26973471bf0Spatrick   // Skip CU node.
27073471bf0Spatrick   if (Tag == dwarf::DW_TAG_compile_unit)
27173471bf0Spatrick     return;
27273471bf0Spatrick 
27309467b48Spatrick   bool HasLoc = false;
27409467b48Spatrick   bool HasSrcLoc = false;
27509467b48Spatrick   bool HasType = false;
27673471bf0Spatrick   uint64_t TotalBytesCovered = 0;
27773471bf0Spatrick   uint64_t ScopeBytesCovered = 0;
27809467b48Spatrick   uint64_t BytesEntryValuesCovered = 0;
27909467b48Spatrick   auto &FnStats = FnStatMap[FnPrefix];
28073471bf0Spatrick   bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
28173471bf0Spatrick   bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
28273471bf0Spatrick   bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
283097a140dSpatrick                           Die.find(dwarf::DW_AT_const_value);
28409467b48Spatrick 
28573471bf0Spatrick   // For zero covered inlined variables the locstats will be
28673471bf0Spatrick   // calculated later.
28773471bf0Spatrick   bool DeferLocStats = false;
28873471bf0Spatrick 
28973471bf0Spatrick   if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
29009467b48Spatrick     GlobalStats.CallSiteDIEs++;
29109467b48Spatrick     return;
29209467b48Spatrick   }
29309467b48Spatrick 
29473471bf0Spatrick   if (Tag == dwarf::DW_TAG_call_site_parameter ||
29573471bf0Spatrick       Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
29609467b48Spatrick     GlobalStats.CallSiteParamDIEs++;
29709467b48Spatrick     return;
29809467b48Spatrick   }
29909467b48Spatrick 
300097a140dSpatrick   if (!IsParam && !IsLocalVar && !IsConstantMember) {
30109467b48Spatrick     // Not a variable or constant member.
30209467b48Spatrick     return;
30309467b48Spatrick   }
30409467b48Spatrick 
305097a140dSpatrick   // Ignore declarations of global variables.
306097a140dSpatrick   if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
307097a140dSpatrick     return;
308097a140dSpatrick 
30909467b48Spatrick   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
31009467b48Spatrick       Die.findRecursively(dwarf::DW_AT_decl_line))
31109467b48Spatrick     HasSrcLoc = true;
31209467b48Spatrick 
31309467b48Spatrick   if (Die.findRecursively(dwarf::DW_AT_type))
31409467b48Spatrick     HasType = true;
31509467b48Spatrick 
31673471bf0Spatrick   if (Die.find(dwarf::DW_AT_abstract_origin)) {
31773471bf0Spatrick     if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
31873471bf0Spatrick       if (AbstractOriginVariables) {
31973471bf0Spatrick         auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
32073471bf0Spatrick         // Do not track this variable any more, since it has location
32173471bf0Spatrick         // coverage.
32273471bf0Spatrick         llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue());
32373471bf0Spatrick       }
32473471bf0Spatrick     } else {
32573471bf0Spatrick       // The locstats will be handled at the end of
32673471bf0Spatrick       // the collectStatsRecursive().
32773471bf0Spatrick       DeferLocStats = true;
32873471bf0Spatrick     }
32973471bf0Spatrick   }
33073471bf0Spatrick 
33109467b48Spatrick   auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
33209467b48Spatrick     DWARFUnit *U = Die.getDwarfUnit();
33309467b48Spatrick     DataExtractor Data(toStringRef(D),
33409467b48Spatrick                        Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
335097a140dSpatrick     DWARFExpression Expression(Data, U->getAddressByteSize(),
336097a140dSpatrick                                U->getFormParams().Format);
33709467b48Spatrick     // Consider the expression containing the DW_OP_entry_value as
33809467b48Spatrick     // an entry value.
339*d415bd75Srobert     return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
34009467b48Spatrick       return Op.getCode() == dwarf::DW_OP_entry_value ||
34109467b48Spatrick              Op.getCode() == dwarf::DW_OP_GNU_entry_value;
34209467b48Spatrick     });
34309467b48Spatrick   };
34409467b48Spatrick 
34509467b48Spatrick   if (Die.find(dwarf::DW_AT_const_value)) {
34609467b48Spatrick     // This catches constant members *and* variables.
34709467b48Spatrick     HasLoc = true;
34873471bf0Spatrick     ScopeBytesCovered = BytesInScope;
34973471bf0Spatrick     TotalBytesCovered = BytesInScope;
35009467b48Spatrick   } else {
35109467b48Spatrick     // Handle variables and function arguments.
35209467b48Spatrick     Expected<std::vector<DWARFLocationExpression>> Loc =
35309467b48Spatrick         Die.getLocations(dwarf::DW_AT_location);
35409467b48Spatrick     if (!Loc) {
35509467b48Spatrick       consumeError(Loc.takeError());
35609467b48Spatrick     } else {
35709467b48Spatrick       HasLoc = true;
35809467b48Spatrick       // Get PC coverage.
35909467b48Spatrick       auto Default = find_if(
36009467b48Spatrick           *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
36109467b48Spatrick       if (Default != Loc->end()) {
36209467b48Spatrick         // Assume the entire range is covered by a single location.
36373471bf0Spatrick         ScopeBytesCovered = BytesInScope;
36473471bf0Spatrick         TotalBytesCovered = BytesInScope;
36509467b48Spatrick       } else {
36673471bf0Spatrick         // Caller checks this Expected result already, it cannot fail.
36773471bf0Spatrick         auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
36809467b48Spatrick         for (auto Entry : *Loc) {
36973471bf0Spatrick           TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
37073471bf0Spatrick           uint64_t ScopeBytesCoveredByEntry = 0;
37173471bf0Spatrick           // Calculate how many bytes of the parent scope this entry covers.
37273471bf0Spatrick           // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
37373471bf0Spatrick           // address ranges defined by the bounded location descriptions of a
37473471bf0Spatrick           // location list may overlap". So in theory a variable can have
37573471bf0Spatrick           // multiple simultaneous locations, which would make this calculation
37673471bf0Spatrick           // misleading because we will count the overlapped areas
37773471bf0Spatrick           // twice. However, clang does not currently emit DWARF like this.
37873471bf0Spatrick           for (DWARFAddressRange R : ScopeRanges) {
37973471bf0Spatrick             ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
38073471bf0Spatrick           }
38173471bf0Spatrick           ScopeBytesCovered += ScopeBytesCoveredByEntry;
38209467b48Spatrick           if (IsEntryValue(Entry.Expr))
38373471bf0Spatrick             BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
38409467b48Spatrick         }
38509467b48Spatrick       }
38609467b48Spatrick     }
38709467b48Spatrick   }
38809467b48Spatrick 
38909467b48Spatrick   // Calculate the debug location statistics.
39073471bf0Spatrick   if (BytesInScope && !DeferLocStats) {
391*d415bd75Srobert     LocStats.NumVarParam.Value++;
39209467b48Spatrick     if (IsParam)
393*d415bd75Srobert       LocStats.NumParam.Value++;
39409467b48Spatrick     else if (IsLocalVar)
395*d415bd75Srobert       LocStats.NumVar.Value++;
39609467b48Spatrick 
39773471bf0Spatrick     collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
398097a140dSpatrick                     LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
39909467b48Spatrick                     IsLocalVar);
40009467b48Spatrick     // Non debug entry values coverage statistics.
40173471bf0Spatrick     collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
40209467b48Spatrick                     LocStats.VarParamNonEntryValLocStats,
40309467b48Spatrick                     LocStats.ParamNonEntryValLocStats,
404097a140dSpatrick                     LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
40509467b48Spatrick   }
40609467b48Spatrick 
40709467b48Spatrick   // Collect PC range coverage data.
40809467b48Spatrick   if (DWARFDie D =
40909467b48Spatrick           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
41009467b48Spatrick     Die = D;
411097a140dSpatrick 
412097a140dSpatrick   std::string VarID = constructDieID(Die, VarPrefix);
413097a140dSpatrick   FnStats.VarsInFunction.insert(VarID);
414097a140dSpatrick 
41573471bf0Spatrick   GlobalStats.TotalBytesCovered += TotalBytesCovered;
41609467b48Spatrick   if (BytesInScope) {
41773471bf0Spatrick     GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
41809467b48Spatrick     GlobalStats.ScopeBytes += BytesInScope;
41909467b48Spatrick     GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
42009467b48Spatrick     if (IsParam) {
42173471bf0Spatrick       GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
42209467b48Spatrick       GlobalStats.ParamScopeBytes += BytesInScope;
42309467b48Spatrick       GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
42409467b48Spatrick     } else if (IsLocalVar) {
42573471bf0Spatrick       GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
426097a140dSpatrick       GlobalStats.LocalVarScopeBytes += BytesInScope;
427097a140dSpatrick       GlobalStats.LocalVarScopeEntryValueBytesCovered +=
428097a140dSpatrick           BytesEntryValuesCovered;
42909467b48Spatrick     }
430*d415bd75Srobert     assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
43109467b48Spatrick   }
432097a140dSpatrick 
433097a140dSpatrick   if (IsConstantMember) {
434097a140dSpatrick     FnStats.ConstantMembers++;
435097a140dSpatrick     return;
436097a140dSpatrick   }
437097a140dSpatrick 
438097a140dSpatrick   FnStats.TotalVarWithLoc += (unsigned)HasLoc;
439097a140dSpatrick 
440097a140dSpatrick   if (Die.find(dwarf::DW_AT_artificial)) {
441097a140dSpatrick     FnStats.NumArtificial++;
442097a140dSpatrick     return;
443097a140dSpatrick   }
444097a140dSpatrick 
44509467b48Spatrick   if (IsParam) {
44609467b48Spatrick     FnStats.NumParams++;
44709467b48Spatrick     if (HasType)
44809467b48Spatrick       FnStats.NumParamTypes++;
44909467b48Spatrick     if (HasSrcLoc)
45009467b48Spatrick       FnStats.NumParamSourceLocations++;
45109467b48Spatrick     if (HasLoc)
45209467b48Spatrick       FnStats.NumParamLocations++;
45309467b48Spatrick   } else if (IsLocalVar) {
454097a140dSpatrick     FnStats.NumLocalVars++;
45509467b48Spatrick     if (HasType)
456097a140dSpatrick       FnStats.NumLocalVarTypes++;
45709467b48Spatrick     if (HasSrcLoc)
458097a140dSpatrick       FnStats.NumLocalVarSourceLocations++;
45909467b48Spatrick     if (HasLoc)
460097a140dSpatrick       FnStats.NumLocalVarLocations++;
46109467b48Spatrick   }
46209467b48Spatrick }
46309467b48Spatrick 
46473471bf0Spatrick /// Recursively collect variables from subprogram with DW_AT_inline attribute.
collectAbstractOriginFnInfo(DWARFDie Die,uint64_t SPOffset,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo)46573471bf0Spatrick static void collectAbstractOriginFnInfo(
46673471bf0Spatrick     DWARFDie Die, uint64_t SPOffset,
467*d415bd75Srobert     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
468*d415bd75Srobert     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
46973471bf0Spatrick   DWARFDie Child = Die.getFirstChild();
47073471bf0Spatrick   while (Child) {
47173471bf0Spatrick     const dwarf::Tag ChildTag = Child.getTag();
47273471bf0Spatrick     if (ChildTag == dwarf::DW_TAG_formal_parameter ||
473*d415bd75Srobert         ChildTag == dwarf::DW_TAG_variable) {
47473471bf0Spatrick       GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
475*d415bd75Srobert       LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
476*d415bd75Srobert     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
477*d415bd75Srobert       collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
478*d415bd75Srobert                                   LocalAbstractOriginFnInfo);
47973471bf0Spatrick     Child = Child.getSibling();
48073471bf0Spatrick   }
48173471bf0Spatrick }
48273471bf0Spatrick 
48309467b48Spatrick /// 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)48473471bf0Spatrick static void collectStatsRecursive(
48573471bf0Spatrick     DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
48673471bf0Spatrick     uint64_t BytesInScope, uint32_t InlineDepth,
48773471bf0Spatrick     StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
488*d415bd75Srobert     LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
48973471bf0Spatrick     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
490*d415bd75Srobert     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
49173471bf0Spatrick     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
49273471bf0Spatrick     AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
49373471bf0Spatrick   // Skip NULL nodes.
49473471bf0Spatrick   if (Die.isNULL())
49573471bf0Spatrick     return;
49673471bf0Spatrick 
49709467b48Spatrick   const dwarf::Tag Tag = Die.getTag();
498097a140dSpatrick   // Skip function types.
499097a140dSpatrick   if (Tag == dwarf::DW_TAG_subroutine_type)
500097a140dSpatrick     return;
501097a140dSpatrick 
502097a140dSpatrick   // Handle any kind of lexical scope.
503*d415bd75Srobert   const bool HasAbstractOrigin =
504*d415bd75Srobert       Die.find(dwarf::DW_AT_abstract_origin) != std::nullopt;
50509467b48Spatrick   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
50609467b48Spatrick   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
50709467b48Spatrick   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
50873471bf0Spatrick   // We want to know how many variables (with abstract_origin) don't have
50973471bf0Spatrick   // location info.
51073471bf0Spatrick   const bool IsCandidateForZeroLocCovTracking =
51173471bf0Spatrick       (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
51209467b48Spatrick 
51373471bf0Spatrick   AbstractOriginVarsTy AbstractOriginVars;
51473471bf0Spatrick 
51573471bf0Spatrick   // Get the vars of the inlined fn, so the locstats
51673471bf0Spatrick   // reports the missing vars (with coverage 0%).
51773471bf0Spatrick   if (IsCandidateForZeroLocCovTracking) {
51873471bf0Spatrick     auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
51973471bf0Spatrick     if (OffsetFn) {
52073471bf0Spatrick       uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
521*d415bd75Srobert       if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
522*d415bd75Srobert         AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
52373471bf0Spatrick         AbstractOriginVarsPtr = &AbstractOriginVars;
52473471bf0Spatrick       } else {
525*d415bd75Srobert         // This means that the DW_AT_inline fn copy is out of order
526*d415bd75Srobert         // or that the abstract_origin references another CU,
52773471bf0Spatrick         // so this abstract origin instance will be processed later.
52873471bf0Spatrick         FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
52973471bf0Spatrick         AbstractOriginVarsPtr = nullptr;
53073471bf0Spatrick       }
53173471bf0Spatrick     }
53273471bf0Spatrick   }
53373471bf0Spatrick 
53473471bf0Spatrick   if (IsFunction || IsInlinedFunction || IsBlock) {
53509467b48Spatrick     // Reset VarPrefix when entering a new function.
53673471bf0Spatrick     if (IsFunction || IsInlinedFunction)
53709467b48Spatrick       VarPrefix = "v";
53809467b48Spatrick 
53909467b48Spatrick     // Ignore forward declarations.
54009467b48Spatrick     if (Die.find(dwarf::DW_AT_declaration))
54109467b48Spatrick       return;
54209467b48Spatrick 
54309467b48Spatrick     // Check for call sites.
54409467b48Spatrick     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
54509467b48Spatrick       GlobalStats.CallSiteEntries++;
54609467b48Spatrick 
54709467b48Spatrick     // PC Ranges.
54809467b48Spatrick     auto RangesOrError = Die.getAddressRanges();
54909467b48Spatrick     if (!RangesOrError) {
55009467b48Spatrick       llvm::consumeError(RangesOrError.takeError());
55109467b48Spatrick       return;
55209467b48Spatrick     }
55309467b48Spatrick 
55409467b48Spatrick     auto Ranges = RangesOrError.get();
55509467b48Spatrick     uint64_t BytesInThisScope = 0;
55609467b48Spatrick     for (auto Range : Ranges)
55709467b48Spatrick       BytesInThisScope += Range.HighPC - Range.LowPC;
55809467b48Spatrick 
55909467b48Spatrick     // Count the function.
56009467b48Spatrick     if (!IsBlock) {
56173471bf0Spatrick       // Skip over abstract origins, but collect variables
56273471bf0Spatrick       // from it so it can be used for location statistics
56373471bf0Spatrick       // for inlined instancies.
56473471bf0Spatrick       if (Die.find(dwarf::DW_AT_inline)) {
56573471bf0Spatrick         uint64_t SPOffset = Die.getOffset();
566*d415bd75Srobert         AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
567*d415bd75Srobert         collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
568*d415bd75Srobert                                     LocalAbstractOriginFnInfo);
56909467b48Spatrick         return;
57073471bf0Spatrick       }
57173471bf0Spatrick 
572097a140dSpatrick       std::string FnID = constructDieID(Die);
573097a140dSpatrick       // We've seen an instance of this function.
574097a140dSpatrick       auto &FnStats = FnStatMap[FnID];
575097a140dSpatrick       FnStats.IsFunction = true;
57609467b48Spatrick       if (IsInlinedFunction) {
57709467b48Spatrick         FnStats.NumFnInlined++;
57809467b48Spatrick         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
57909467b48Spatrick           FnStats.NumAbstractOrigins++;
580097a140dSpatrick       } else {
581097a140dSpatrick         FnStats.NumFnOutOfLine++;
58209467b48Spatrick       }
58309467b48Spatrick       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
58409467b48Spatrick           Die.findRecursively(dwarf::DW_AT_decl_line))
58509467b48Spatrick         FnStats.HasSourceLocation = true;
586097a140dSpatrick       // Update function prefix.
587097a140dSpatrick       FnPrefix = FnID;
58809467b48Spatrick     }
58909467b48Spatrick 
59009467b48Spatrick     if (BytesInThisScope) {
59109467b48Spatrick       BytesInScope = BytesInThisScope;
59209467b48Spatrick       if (IsFunction)
59309467b48Spatrick         GlobalStats.FunctionSize += BytesInThisScope;
59409467b48Spatrick       else if (IsInlinedFunction && InlineDepth == 0)
59509467b48Spatrick         GlobalStats.InlineFunctionSize += BytesInThisScope;
59609467b48Spatrick     }
59709467b48Spatrick   } else {
59809467b48Spatrick     // Not a scope, visit the Die itself. It could be a variable.
59909467b48Spatrick     collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
60073471bf0Spatrick                        FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
60109467b48Spatrick   }
60209467b48Spatrick 
60309467b48Spatrick   // Set InlineDepth correctly for child recursion
60409467b48Spatrick   if (IsFunction)
60509467b48Spatrick     InlineDepth = 0;
60609467b48Spatrick   else if (IsInlinedFunction)
60709467b48Spatrick     ++InlineDepth;
60809467b48Spatrick 
60909467b48Spatrick   // Traverse children.
61009467b48Spatrick   unsigned LexicalBlockIndex = 0;
611097a140dSpatrick   unsigned FormalParameterIndex = 0;
61209467b48Spatrick   DWARFDie Child = Die.getFirstChild();
61309467b48Spatrick   while (Child) {
61409467b48Spatrick     std::string ChildVarPrefix = VarPrefix;
61509467b48Spatrick     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
61609467b48Spatrick       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
617097a140dSpatrick     if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
618097a140dSpatrick       ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
61909467b48Spatrick 
62073471bf0Spatrick     collectStatsRecursive(
62173471bf0Spatrick         Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
622*d415bd75Srobert         GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
623*d415bd75Srobert         LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
624*d415bd75Srobert         AbstractOriginVarsPtr);
62509467b48Spatrick     Child = Child.getSibling();
62609467b48Spatrick   }
62773471bf0Spatrick 
62873471bf0Spatrick   if (!IsCandidateForZeroLocCovTracking)
62973471bf0Spatrick     return;
63073471bf0Spatrick 
63173471bf0Spatrick   // After we have processed all vars of the inlined function (or function with
63273471bf0Spatrick   // an abstract_origin), we want to know how many variables have no location.
63373471bf0Spatrick   for (auto Offset : AbstractOriginVars) {
63473471bf0Spatrick     LocStats.NumVarParam++;
63573471bf0Spatrick     LocStats.VarParamLocStats[ZeroCoverageBucket]++;
63673471bf0Spatrick     auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
63773471bf0Spatrick     if (!FnDie)
63873471bf0Spatrick       continue;
63973471bf0Spatrick     auto Tag = FnDie.getTag();
64073471bf0Spatrick     if (Tag == dwarf::DW_TAG_formal_parameter) {
64173471bf0Spatrick       LocStats.NumParam++;
64273471bf0Spatrick       LocStats.ParamLocStats[ZeroCoverageBucket]++;
64373471bf0Spatrick     } else if (Tag == dwarf::DW_TAG_variable) {
64473471bf0Spatrick       LocStats.NumVar++;
64573471bf0Spatrick       LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
64673471bf0Spatrick     }
64773471bf0Spatrick   }
64809467b48Spatrick }
64909467b48Spatrick 
65073471bf0Spatrick /// Print human-readable output.
65109467b48Spatrick /// \{
printDatum(json::OStream & J,const char * Key,json::Value Value)65273471bf0Spatrick static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
653*d415bd75Srobert   if (Value == OverflowValue)
654*d415bd75Srobert     J.attribute(Key, "overflowed");
655*d415bd75Srobert   else
65673471bf0Spatrick     J.attribute(Key, Value);
657*d415bd75Srobert 
65809467b48Spatrick   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
65909467b48Spatrick }
660097a140dSpatrick 
printLocationStats(json::OStream & J,const char * Key,std::vector<SaturatingUINT64> & LocationStats)66173471bf0Spatrick static void printLocationStats(json::OStream &J, const char *Key,
662*d415bd75Srobert                                std::vector<SaturatingUINT64> &LocationStats) {
663*d415bd75Srobert   if (LocationStats[0].Value == OverflowValue)
664*d415bd75Srobert     J.attribute((Twine(Key) +
665*d415bd75Srobert                  " with (0%,10%) of parent scope covered by DW_AT_location")
666*d415bd75Srobert                     .str(),
667*d415bd75Srobert                 "overflowed");
668*d415bd75Srobert   else
66973471bf0Spatrick     J.attribute(
670*d415bd75Srobert         (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
671*d415bd75Srobert             .str(),
672*d415bd75Srobert         LocationStats[0].Value);
673097a140dSpatrick   LLVM_DEBUG(
674097a140dSpatrick       llvm::dbgs() << Key
675097a140dSpatrick                    << " with 0% of parent scope covered by DW_AT_location: \\"
676*d415bd75Srobert                    << LocationStats[0].Value << '\n');
677*d415bd75Srobert 
678*d415bd75Srobert   if (LocationStats[1].Value == OverflowValue)
679*d415bd75Srobert     J.attribute((Twine(Key) +
680*d415bd75Srobert                  " with (0%,10%) of parent scope covered by DW_AT_location")
68173471bf0Spatrick                     .str(),
682*d415bd75Srobert                 "overflowed");
683*d415bd75Srobert   else
684*d415bd75Srobert     J.attribute((Twine(Key) +
685*d415bd75Srobert                  " with (0%,10%) of parent scope covered by DW_AT_location")
686*d415bd75Srobert                     .str(),
687*d415bd75Srobert                 LocationStats[1].Value);
688097a140dSpatrick   LLVM_DEBUG(llvm::dbgs()
689097a140dSpatrick              << Key
690097a140dSpatrick              << " with (0%,10%) of parent scope covered by DW_AT_location: "
691*d415bd75Srobert              << LocationStats[1].Value << '\n');
692*d415bd75Srobert 
69309467b48Spatrick   for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
694*d415bd75Srobert     if (LocationStats[i].Value == OverflowValue)
69573471bf0Spatrick       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
696*d415bd75Srobert                    Twine(i * 10) +
697*d415bd75Srobert                    "%) of parent scope covered by DW_AT_location")
69873471bf0Spatrick                       .str(),
699*d415bd75Srobert                   "overflowed");
700*d415bd75Srobert     else
701*d415bd75Srobert       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
702*d415bd75Srobert                    Twine(i * 10) +
703*d415bd75Srobert                    "%) of parent scope covered by DW_AT_location")
704*d415bd75Srobert                       .str(),
705*d415bd75Srobert                   LocationStats[i].Value);
70609467b48Spatrick     LLVM_DEBUG(llvm::dbgs()
70709467b48Spatrick                << Key << " with [" << (i - 1) * 10 << "%," << i * 10
708097a140dSpatrick                << "%) of parent scope covered by DW_AT_location: "
709*d415bd75Srobert                << LocationStats[i].Value);
71009467b48Spatrick   }
711*d415bd75Srobert   if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
71273471bf0Spatrick     J.attribute(
71373471bf0Spatrick         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
71473471bf0Spatrick             .str(),
715*d415bd75Srobert         "overflowed");
716*d415bd75Srobert   else
717*d415bd75Srobert     J.attribute(
718*d415bd75Srobert         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
719*d415bd75Srobert             .str(),
720*d415bd75Srobert         LocationStats[NumOfCoverageCategories - 1].Value);
721097a140dSpatrick   LLVM_DEBUG(
722097a140dSpatrick       llvm::dbgs() << Key
723097a140dSpatrick                    << " with 100% of parent scope covered by DW_AT_location: "
724*d415bd75Srobert                    << LocationStats[NumOfCoverageCategories - 1].Value);
72509467b48Spatrick }
726097a140dSpatrick 
printSectionSizes(json::OStream & J,const SectionSizes & Sizes)72773471bf0Spatrick static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
72873471bf0Spatrick   for (const auto &It : Sizes.DebugSectionSizes)
72973471bf0Spatrick     J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
73073471bf0Spatrick }
73173471bf0Spatrick 
73273471bf0Spatrick /// Stop tracking variables that contain abstract_origin with a location.
73373471bf0Spatrick /// This is used for out-of-order DW_AT_inline subprograms only.
updateVarsWithAbstractOriginLocCovInfo(DWARFDie FnDieWithAbstractOrigin,AbstractOriginVarsTy & AbstractOriginVars)73473471bf0Spatrick static void updateVarsWithAbstractOriginLocCovInfo(
73573471bf0Spatrick     DWARFDie FnDieWithAbstractOrigin,
73673471bf0Spatrick     AbstractOriginVarsTy &AbstractOriginVars) {
73773471bf0Spatrick   DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
73873471bf0Spatrick   while (Child) {
73973471bf0Spatrick     const dwarf::Tag ChildTag = Child.getTag();
74073471bf0Spatrick     if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
74173471bf0Spatrick          ChildTag == dwarf::DW_TAG_variable) &&
74273471bf0Spatrick         (Child.find(dwarf::DW_AT_location) ||
74373471bf0Spatrick          Child.find(dwarf::DW_AT_const_value))) {
74473471bf0Spatrick       auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
74573471bf0Spatrick       if (OffsetVar)
74673471bf0Spatrick         llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue());
74773471bf0Spatrick     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
74873471bf0Spatrick       updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
74973471bf0Spatrick     Child = Child.getSibling();
75073471bf0Spatrick   }
75173471bf0Spatrick }
75273471bf0Spatrick 
75373471bf0Spatrick /// Collect zero location coverage for inlined variables which refer to
75473471bf0Spatrick /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
75573471bf0Spatrick /// Also cover the variables of a concrete function (represented with
75673471bf0Spatrick /// the DW_TAG_subprogram) with an abstract_origin attribute.
collectZeroLocCovForVarsWithAbstractOrigin(DWARFUnit * DwUnit,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed)75773471bf0Spatrick static void collectZeroLocCovForVarsWithAbstractOrigin(
75873471bf0Spatrick     DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
759*d415bd75Srobert     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
76073471bf0Spatrick     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
761*d415bd75Srobert   // The next variable is used to filter out functions that have been processed,
762*d415bd75Srobert   // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
763*d415bd75Srobert   FunctionsWithAbstractOriginTy ProcessedFns;
76473471bf0Spatrick   for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
76573471bf0Spatrick     DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
76673471bf0Spatrick     auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
76773471bf0Spatrick     AbstractOriginVarsTy AbstractOriginVars;
76873471bf0Spatrick     if (!FnCopy)
76973471bf0Spatrick       continue;
770*d415bd75Srobert     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
771*d415bd75Srobert     // If there is no entry within LocalAbstractOriginFnInfo for the given
772*d415bd75Srobert     // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
773*d415bd75Srobert     // CrossCU referencing.
774*d415bd75Srobert     if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
775*d415bd75Srobert       continue;
776*d415bd75Srobert     AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
77773471bf0Spatrick     updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
77873471bf0Spatrick                                            AbstractOriginVars);
77973471bf0Spatrick 
78073471bf0Spatrick     for (auto Offset : AbstractOriginVars) {
78173471bf0Spatrick       LocStats.NumVarParam++;
78273471bf0Spatrick       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
78373471bf0Spatrick       auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
78473471bf0Spatrick       if (Tag == dwarf::DW_TAG_formal_parameter) {
78573471bf0Spatrick         LocStats.NumParam++;
78673471bf0Spatrick         LocStats.ParamLocStats[ZeroCoverageBucket]++;
78773471bf0Spatrick       } else if (Tag == dwarf::DW_TAG_variable) {
78873471bf0Spatrick         LocStats.NumVar++;
78973471bf0Spatrick         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
79073471bf0Spatrick       }
79173471bf0Spatrick     }
792*d415bd75Srobert     ProcessedFns.push_back(FnOffset);
793*d415bd75Srobert   }
794*d415bd75Srobert   for (auto ProcessedFn : ProcessedFns)
795*d415bd75Srobert     llvm::erase_value(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
796*d415bd75Srobert }
797*d415bd75Srobert 
798*d415bd75Srobert /// Collect zero location coverage for inlined variables which refer to
799*d415bd75Srobert /// a DW_AT_inline copy of subprogram that is in a different CU.
collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(LocationStats & LocStats,FunctionDIECUTyMap AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,CrossCUReferencingDIELocationTy & CrossCUReferencesToBeResolved)800*d415bd75Srobert static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
801*d415bd75Srobert     LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
802*d415bd75Srobert     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
803*d415bd75Srobert     CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
804*d415bd75Srobert   for (const auto &CrossCUReferenceToBeResolved :
805*d415bd75Srobert        CrossCUReferencesToBeResolved) {
806*d415bd75Srobert     DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
807*d415bd75Srobert     DWARFDie FnDIEWithCrossCUReferencing =
808*d415bd75Srobert         DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
809*d415bd75Srobert     auto FnCopy =
810*d415bd75Srobert         FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
811*d415bd75Srobert     if (!FnCopy)
812*d415bd75Srobert       continue;
813*d415bd75Srobert     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
814*d415bd75Srobert     AbstractOriginVarsTy AbstractOriginVars =
815*d415bd75Srobert         GlobalAbstractOriginFnInfo[FnCopyRawUValue];
816*d415bd75Srobert     updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
817*d415bd75Srobert                                            AbstractOriginVars);
818*d415bd75Srobert     for (auto Offset : AbstractOriginVars) {
819*d415bd75Srobert       LocStats.NumVarParam++;
820*d415bd75Srobert       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
821*d415bd75Srobert       auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
822*d415bd75Srobert                      ->getDIEForOffset(Offset)
823*d415bd75Srobert                      .getTag();
824*d415bd75Srobert       if (Tag == dwarf::DW_TAG_formal_parameter) {
825*d415bd75Srobert         LocStats.NumParam++;
826*d415bd75Srobert         LocStats.ParamLocStats[ZeroCoverageBucket]++;
827*d415bd75Srobert       } else if (Tag == dwarf::DW_TAG_variable) {
828*d415bd75Srobert         LocStats.NumVar++;
829*d415bd75Srobert         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
830*d415bd75Srobert       }
831*d415bd75Srobert     }
83273471bf0Spatrick   }
833097a140dSpatrick }
834097a140dSpatrick 
83509467b48Spatrick /// \}
83609467b48Spatrick 
83709467b48Spatrick /// Collect debug info quality metrics for an entire DIContext.
83809467b48Spatrick ///
83909467b48Spatrick /// Do the impossible and reduce the quality of the debug info down to a few
84009467b48Spatrick /// numbers. The idea is to condense the data into numbers that can be tracked
84109467b48Spatrick /// over time to identify trends in newer compiler versions and gauge the effect
84209467b48Spatrick /// of particular optimizations. The raw numbers themselves are not particularly
84309467b48Spatrick /// useful, only the delta between compiling the same program with different
84409467b48Spatrick /// compilers is.
collectStatsForObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)845097a140dSpatrick bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
846097a140dSpatrick                                           const Twine &Filename,
847097a140dSpatrick                                           raw_ostream &OS) {
84809467b48Spatrick   StringRef FormatName = Obj.getFileFormatName();
84909467b48Spatrick   GlobalStats GlobalStats;
85009467b48Spatrick   LocationStats LocStats;
85109467b48Spatrick   StringMap<PerFunctionStats> Statistics;
852*d415bd75Srobert   // This variable holds variable information for functions with
853*d415bd75Srobert   // abstract_origin globally, across all CUs.
854*d415bd75Srobert   AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
855*d415bd75Srobert   // This variable holds information about the CU of a function with
856*d415bd75Srobert   // abstract_origin.
857*d415bd75Srobert   FunctionDIECUTyMap AbstractOriginFnCUs;
858*d415bd75Srobert   CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
85973471bf0Spatrick   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
86073471bf0Spatrick     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
861*d415bd75Srobert       // This variable holds variable information for functions with
862*d415bd75Srobert       // abstract_origin, but just for the current CU.
863*d415bd75Srobert       AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
86473471bf0Spatrick       FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
86573471bf0Spatrick 
866*d415bd75Srobert       collectStatsRecursive(
867*d415bd75Srobert           CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
868*d415bd75Srobert           AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
869*d415bd75Srobert           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
87073471bf0Spatrick 
871*d415bd75Srobert       // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
872*d415bd75Srobert       // out-of-order DWARF functions that have been processed within it,
873*d415bd75Srobert       // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
874*d415bd75Srobert       // references.
87573471bf0Spatrick       collectZeroLocCovForVarsWithAbstractOrigin(
87673471bf0Spatrick           CUDie.getDwarfUnit(), GlobalStats, LocStats,
877*d415bd75Srobert           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
878*d415bd75Srobert 
879*d415bd75Srobert       // Collect all CrossCU references into CrossCUReferencesToBeResolved.
880*d415bd75Srobert       for (auto CrossCUReferencingDIEOffset :
881*d415bd75Srobert            FnsWithAbstractOriginToBeProcessed)
882*d415bd75Srobert         CrossCUReferencesToBeResolved.push_back(
883*d415bd75Srobert             DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
88473471bf0Spatrick     }
88573471bf0Spatrick   }
88609467b48Spatrick 
887*d415bd75Srobert   /// Resolve CrossCU references.
888*d415bd75Srobert   collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
889*d415bd75Srobert       LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
890*d415bd75Srobert       CrossCUReferencesToBeResolved);
891*d415bd75Srobert 
892097a140dSpatrick   /// Collect the sizes of debug sections.
893097a140dSpatrick   SectionSizes Sizes;
894097a140dSpatrick   calculateSectionSizes(Obj, Sizes, Filename);
895097a140dSpatrick 
89609467b48Spatrick   /// The version number should be increased every time the algorithm is changed
89709467b48Spatrick   /// (including bug fixes). New metrics may be added without increasing the
89809467b48Spatrick   /// version.
899*d415bd75Srobert   unsigned Version = 9;
900*d415bd75Srobert   SaturatingUINT64 VarParamTotal = 0;
901*d415bd75Srobert   SaturatingUINT64 VarParamUnique = 0;
902*d415bd75Srobert   SaturatingUINT64 VarParamWithLoc = 0;
903*d415bd75Srobert   SaturatingUINT64 NumFunctions = 0;
904*d415bd75Srobert   SaturatingUINT64 NumInlinedFunctions = 0;
905*d415bd75Srobert   SaturatingUINT64 NumFuncsWithSrcLoc = 0;
906*d415bd75Srobert   SaturatingUINT64 NumAbstractOrigins = 0;
907*d415bd75Srobert   SaturatingUINT64 ParamTotal = 0;
908*d415bd75Srobert   SaturatingUINT64 ParamWithType = 0;
909*d415bd75Srobert   SaturatingUINT64 ParamWithLoc = 0;
910*d415bd75Srobert   SaturatingUINT64 ParamWithSrcLoc = 0;
911*d415bd75Srobert   SaturatingUINT64 LocalVarTotal = 0;
912*d415bd75Srobert   SaturatingUINT64 LocalVarWithType = 0;
913*d415bd75Srobert   SaturatingUINT64 LocalVarWithSrcLoc = 0;
914*d415bd75Srobert   SaturatingUINT64 LocalVarWithLoc = 0;
91509467b48Spatrick   for (auto &Entry : Statistics) {
91609467b48Spatrick     PerFunctionStats &Stats = Entry.getValue();
917*d415bd75Srobert     uint64_t TotalVars = Stats.VarsInFunction.size() *
918097a140dSpatrick                          (Stats.NumFnInlined + Stats.NumFnOutOfLine);
919097a140dSpatrick     // Count variables in global scope.
920097a140dSpatrick     if (!Stats.IsFunction)
921097a140dSpatrick       TotalVars =
922097a140dSpatrick           Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
923*d415bd75Srobert     uint64_t Constants = Stats.ConstantMembers;
92409467b48Spatrick     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
92509467b48Spatrick     VarParamTotal += TotalVars;
92609467b48Spatrick     VarParamUnique += Stats.VarsInFunction.size();
92709467b48Spatrick     LLVM_DEBUG(for (auto &V
92809467b48Spatrick                     : Stats.VarsInFunction) llvm::dbgs()
92909467b48Spatrick                << Entry.getKey() << ": " << V.getKey() << "\n");
93009467b48Spatrick     NumFunctions += Stats.IsFunction;
93109467b48Spatrick     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
93209467b48Spatrick     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
93309467b48Spatrick     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
93409467b48Spatrick     ParamTotal += Stats.NumParams;
93509467b48Spatrick     ParamWithType += Stats.NumParamTypes;
93609467b48Spatrick     ParamWithLoc += Stats.NumParamLocations;
93709467b48Spatrick     ParamWithSrcLoc += Stats.NumParamSourceLocations;
938097a140dSpatrick     LocalVarTotal += Stats.NumLocalVars;
939097a140dSpatrick     LocalVarWithType += Stats.NumLocalVarTypes;
940097a140dSpatrick     LocalVarWithLoc += Stats.NumLocalVarLocations;
941097a140dSpatrick     LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
94209467b48Spatrick   }
94309467b48Spatrick 
94409467b48Spatrick   // Print summary.
94509467b48Spatrick   OS.SetBufferSize(1024);
94673471bf0Spatrick   json::OStream J(OS, 2);
94773471bf0Spatrick   J.objectBegin();
94873471bf0Spatrick   J.attribute("version", Version);
94909467b48Spatrick   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
95009467b48Spatrick              llvm::dbgs() << "---------------------------------\n");
951097a140dSpatrick 
95273471bf0Spatrick   printDatum(J, "file", Filename.str());
95373471bf0Spatrick   printDatum(J, "format", FormatName);
954097a140dSpatrick 
955*d415bd75Srobert   printDatum(J, "#functions", NumFunctions.Value);
956*d415bd75Srobert   printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
957*d415bd75Srobert   printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
958*d415bd75Srobert   printDatum(J, "#inlined functions with abstract origins",
959*d415bd75Srobert              NumAbstractOrigins.Value);
960097a140dSpatrick 
961097a140dSpatrick   // This includes local variables and formal parameters.
962*d415bd75Srobert   printDatum(J, "#unique source variables", VarParamUnique.Value);
963*d415bd75Srobert   printDatum(J, "#source variables", VarParamTotal.Value);
964*d415bd75Srobert   printDatum(J, "#source variables with location", VarParamWithLoc.Value);
965097a140dSpatrick 
966*d415bd75Srobert   printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
967*d415bd75Srobert   printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
968*d415bd75Srobert   printDatum(J, "#call site parameter DIEs",
969*d415bd75Srobert              GlobalStats.CallSiteParamDIEs.Value);
970097a140dSpatrick 
97173471bf0Spatrick   printDatum(J, "sum_all_variables(#bytes in parent scope)",
972*d415bd75Srobert              GlobalStats.ScopeBytes.Value);
97373471bf0Spatrick   printDatum(J,
97473471bf0Spatrick              "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
975*d415bd75Srobert              GlobalStats.TotalBytesCovered.Value);
97673471bf0Spatrick   printDatum(J,
977097a140dSpatrick              "sum_all_variables(#bytes in parent scope covered by "
978097a140dSpatrick              "DW_AT_location)",
979*d415bd75Srobert              GlobalStats.ScopeBytesCovered.Value);
98073471bf0Spatrick   printDatum(J,
981097a140dSpatrick              "sum_all_variables(#bytes in parent scope covered by "
982097a140dSpatrick              "DW_OP_entry_value)",
983*d415bd75Srobert              GlobalStats.ScopeEntryValueBytesCovered.Value);
984097a140dSpatrick 
98573471bf0Spatrick   printDatum(J, "sum_all_params(#bytes in parent scope)",
986*d415bd75Srobert              GlobalStats.ParamScopeBytes.Value);
98773471bf0Spatrick   printDatum(J,
988097a140dSpatrick              "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
989*d415bd75Srobert              GlobalStats.ParamScopeBytesCovered.Value);
99073471bf0Spatrick   printDatum(J,
991097a140dSpatrick              "sum_all_params(#bytes in parent scope covered by "
992097a140dSpatrick              "DW_OP_entry_value)",
993*d415bd75Srobert              GlobalStats.ParamScopeEntryValueBytesCovered.Value);
994097a140dSpatrick 
99573471bf0Spatrick   printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
996*d415bd75Srobert              GlobalStats.LocalVarScopeBytes.Value);
99773471bf0Spatrick   printDatum(J,
998097a140dSpatrick              "sum_all_local_vars(#bytes in parent scope covered by "
999097a140dSpatrick              "DW_AT_location)",
1000*d415bd75Srobert              GlobalStats.LocalVarScopeBytesCovered.Value);
100173471bf0Spatrick   printDatum(J,
1002097a140dSpatrick              "sum_all_local_vars(#bytes in parent scope covered by "
1003097a140dSpatrick              "DW_OP_entry_value)",
1004*d415bd75Srobert              GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
1005097a140dSpatrick 
1006*d415bd75Srobert   printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
100773471bf0Spatrick   printDatum(J, "#bytes within inlined functions",
1008*d415bd75Srobert              GlobalStats.InlineFunctionSize.Value);
1009097a140dSpatrick 
1010097a140dSpatrick   // Print the summary for formal parameters.
1011*d415bd75Srobert   printDatum(J, "#params", ParamTotal.Value);
1012*d415bd75Srobert   printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
1013*d415bd75Srobert   printDatum(J, "#params with type", ParamWithType.Value);
1014*d415bd75Srobert   printDatum(J, "#params with binary location", ParamWithLoc.Value);
1015097a140dSpatrick 
1016097a140dSpatrick   // Print the summary for local variables.
1017*d415bd75Srobert   printDatum(J, "#local vars", LocalVarTotal.Value);
1018*d415bd75Srobert   printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
1019*d415bd75Srobert   printDatum(J, "#local vars with type", LocalVarWithType.Value);
1020*d415bd75Srobert   printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
1021097a140dSpatrick 
1022097a140dSpatrick   // Print the debug section sizes.
102373471bf0Spatrick   printSectionSizes(J, Sizes);
1024097a140dSpatrick 
1025097a140dSpatrick   // Print the location statistics for variables (includes local variables
1026097a140dSpatrick   // and formal parameters).
102773471bf0Spatrick   printDatum(J, "#variables processed by location statistics",
1028*d415bd75Srobert              LocStats.NumVarParam.Value);
102973471bf0Spatrick   printLocationStats(J, "#variables", LocStats.VarParamLocStats);
103073471bf0Spatrick   printLocationStats(J, "#variables - entry values",
103109467b48Spatrick                      LocStats.VarParamNonEntryValLocStats);
1032097a140dSpatrick 
1033097a140dSpatrick   // Print the location statistics for formal parameters.
1034*d415bd75Srobert   printDatum(J, "#params processed by location statistics",
1035*d415bd75Srobert              LocStats.NumParam.Value);
103673471bf0Spatrick   printLocationStats(J, "#params", LocStats.ParamLocStats);
103773471bf0Spatrick   printLocationStats(J, "#params - entry values",
103809467b48Spatrick                      LocStats.ParamNonEntryValLocStats);
1039097a140dSpatrick 
1040097a140dSpatrick   // Print the location statistics for local variables.
104173471bf0Spatrick   printDatum(J, "#local vars processed by location statistics",
1042*d415bd75Srobert              LocStats.NumVar.Value);
104373471bf0Spatrick   printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
104473471bf0Spatrick   printLocationStats(J, "#local vars - entry values",
1045097a140dSpatrick                      LocStats.LocalVarNonEntryValLocStats);
104673471bf0Spatrick   J.objectEnd();
104773471bf0Spatrick   OS << '\n';
104809467b48Spatrick   LLVM_DEBUG(
104909467b48Spatrick       llvm::dbgs() << "Total Availability: "
1050*d415bd75Srobert                    << (VarParamTotal.Value
1051*d415bd75Srobert                            ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1052*d415bd75Srobert                                              VarParamTotal.Value)
1053*d415bd75Srobert                            : 0)
105409467b48Spatrick                    << "%\n";
105509467b48Spatrick       llvm::dbgs() << "PC Ranges covered: "
1056*d415bd75Srobert                    << (GlobalStats.ScopeBytes.Value
1057*d415bd75Srobert                            ? (int)std::round(
1058*d415bd75Srobert                                  (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1059*d415bd75Srobert                                  GlobalStats.ScopeBytes.Value)
1060*d415bd75Srobert                            : 0)
106109467b48Spatrick                    << "%\n");
106209467b48Spatrick   return true;
106309467b48Spatrick }
1064