xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
15ffd83dbSDimitry Andric //===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "llvm-dwarfdump.h"
100b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
110b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h"
120b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h"
130b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
14*81ad6265SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
150b57cec5SDimitry Andric #include "llvm/Object/ObjectFile.h"
168bcb0991SDimitry Andric #include "llvm/Support/JSON.h"
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfdump"
190b57cec5SDimitry Andric using namespace llvm;
205ffd83dbSDimitry Andric using namespace llvm::dwarfdump;
215ffd83dbSDimitry Andric using namespace llvm::object;
220b57cec5SDimitry Andric 
23fe6060f1SDimitry Andric namespace {
248bcb0991SDimitry Andric /// This represents the number of categories of debug location coverage being
258bcb0991SDimitry Andric /// calculated. The first category is the number of variables with 0% location
268bcb0991SDimitry Andric /// coverage, but the last category is the number of variables with 100%
278bcb0991SDimitry Andric /// location coverage.
288bcb0991SDimitry Andric constexpr int NumOfCoverageCategories = 12;
298bcb0991SDimitry Andric 
30fe6060f1SDimitry Andric /// This is used for zero location coverage bucket.
31fe6060f1SDimitry Andric constexpr unsigned ZeroCoverageBucket = 0;
32fe6060f1SDimitry Andric 
33349cc55cSDimitry Andric /// The UINT64_MAX is used as an indication of the overflow.
34349cc55cSDimitry Andric constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
35349cc55cSDimitry Andric 
36fe6060f1SDimitry Andric /// This represents variables DIE offsets.
37fe6060f1SDimitry Andric using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
38fe6060f1SDimitry Andric /// This maps function DIE offset to its variables.
39fe6060f1SDimitry Andric using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
40fe6060f1SDimitry Andric /// This represents function DIE offsets containing an abstract_origin.
41fe6060f1SDimitry Andric using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
42fe6060f1SDimitry Andric 
43349cc55cSDimitry Andric /// This represents a data type for the stats and it helps us to
44349cc55cSDimitry Andric /// detect an overflow.
45349cc55cSDimitry Andric /// NOTE: This can be implemented as a template if there is an another type
46349cc55cSDimitry Andric /// needing this.
47349cc55cSDimitry Andric struct SaturatingUINT64 {
48349cc55cSDimitry Andric   /// Number that represents the stats.
49349cc55cSDimitry Andric   uint64_t Value;
50349cc55cSDimitry Andric 
51349cc55cSDimitry Andric   SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
52349cc55cSDimitry Andric 
53349cc55cSDimitry Andric   void operator++(int) { return *this += 1; }
54349cc55cSDimitry Andric   void operator+=(uint64_t Value_) {
55349cc55cSDimitry Andric     if (Value != OverflowValue) {
56349cc55cSDimitry Andric       if (Value < OverflowValue - Value_)
57349cc55cSDimitry Andric         Value += Value_;
58349cc55cSDimitry Andric       else
59349cc55cSDimitry Andric         Value = OverflowValue;
60349cc55cSDimitry Andric     }
61349cc55cSDimitry Andric   }
62349cc55cSDimitry Andric };
63349cc55cSDimitry Andric 
644824e7fdSDimitry Andric /// Utility struct to store the full location of a DIE - its CU and offset.
654824e7fdSDimitry Andric struct DIELocation {
664824e7fdSDimitry Andric   DWARFUnit *DwUnit;
674824e7fdSDimitry Andric   uint64_t DIEOffset;
684824e7fdSDimitry Andric   DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
694824e7fdSDimitry Andric       : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
704824e7fdSDimitry Andric };
714824e7fdSDimitry Andric /// This represents DWARF locations of CrossCU referencing DIEs.
724824e7fdSDimitry Andric using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
734824e7fdSDimitry Andric 
744824e7fdSDimitry Andric /// This maps function DIE offset to its DWARF CU.
754824e7fdSDimitry Andric using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
764824e7fdSDimitry Andric 
770b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and
780b57cec5SDimitry Andric /// contains variables, such as a compile unit).
790b57cec5SDimitry Andric struct PerFunctionStats {
800b57cec5SDimitry Andric   /// Number of inlined instances of this function.
81349cc55cSDimitry Andric   uint64_t NumFnInlined = 0;
825ffd83dbSDimitry Andric   /// Number of out-of-line instances of this function.
83349cc55cSDimitry Andric   uint64_t NumFnOutOfLine = 0;
840b57cec5SDimitry Andric   /// Number of inlined instances that have abstract origins.
85349cc55cSDimitry Andric   uint64_t NumAbstractOrigins = 0;
860b57cec5SDimitry Andric   /// Number of variables and parameters with location across all inlined
870b57cec5SDimitry Andric   /// instances.
88349cc55cSDimitry Andric   uint64_t TotalVarWithLoc = 0;
890b57cec5SDimitry Andric   /// Number of constants with location across all inlined instances.
90349cc55cSDimitry Andric   uint64_t ConstantMembers = 0;
915ffd83dbSDimitry Andric   /// Number of arificial variables, parameters or members across all instances.
92349cc55cSDimitry Andric   uint64_t NumArtificial = 0;
930b57cec5SDimitry Andric   /// List of all Variables and parameters in this function.
940b57cec5SDimitry Andric   StringSet<> VarsInFunction;
950b57cec5SDimitry Andric   /// Compile units also cover a PC range, but have this flag set to false.
960b57cec5SDimitry Andric   bool IsFunction = false;
970b57cec5SDimitry Andric   /// Function has source location information.
980b57cec5SDimitry Andric   bool HasSourceLocation = false;
990b57cec5SDimitry Andric   /// Number of function parameters.
100349cc55cSDimitry Andric   uint64_t NumParams = 0;
1010b57cec5SDimitry Andric   /// Number of function parameters with source location.
102349cc55cSDimitry Andric   uint64_t NumParamSourceLocations = 0;
1030b57cec5SDimitry Andric   /// Number of function parameters with type.
104349cc55cSDimitry Andric   uint64_t NumParamTypes = 0;
1050b57cec5SDimitry Andric   /// Number of function parameters with a DW_AT_location.
106349cc55cSDimitry Andric   uint64_t NumParamLocations = 0;
1075ffd83dbSDimitry Andric   /// Number of local variables.
108349cc55cSDimitry Andric   uint64_t NumLocalVars = 0;
1095ffd83dbSDimitry Andric   /// Number of local variables with source location.
110349cc55cSDimitry Andric   uint64_t NumLocalVarSourceLocations = 0;
1115ffd83dbSDimitry Andric   /// Number of local variables with type.
112349cc55cSDimitry Andric   uint64_t NumLocalVarTypes = 0;
1135ffd83dbSDimitry Andric   /// Number of local variables with DW_AT_location.
114349cc55cSDimitry Andric   uint64_t NumLocalVarLocations = 0;
1150b57cec5SDimitry Andric };
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs.
1180b57cec5SDimitry Andric struct GlobalStats {
1190b57cec5SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations.
120349cc55cSDimitry Andric   SaturatingUINT64 TotalBytesCovered = 0;
121e8d8bef9SDimitry Andric   /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
122349cc55cSDimitry Andric   SaturatingUINT64 ScopeBytesCovered = 0;
123480093f4SDimitry Andric   /// Total number of PC range bytes in each variable's enclosing scope.
124349cc55cSDimitry Andric   SaturatingUINT64 ScopeBytes = 0;
1258bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1268bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value).
127349cc55cSDimitry Andric   SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
1288bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations of
1298bcb0991SDimitry Andric   /// formal parameters.
130349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeBytesCovered = 0;
1315ffd83dbSDimitry Andric   /// Total number of PC range bytes in each parameter's enclosing scope.
132349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeBytes = 0;
1338bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1348bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value) (only for parameters).
135349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
1368bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations (only for local
1378bcb0991SDimitry Andric   /// variables).
138349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeBytesCovered = 0;
1395ffd83dbSDimitry Andric   /// Total number of PC range bytes in each local variable's enclosing scope.
140349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeBytes = 0;
1418bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1428bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value) (only for local variables).
143349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
1448bcb0991SDimitry Andric   /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
145349cc55cSDimitry Andric   SaturatingUINT64 CallSiteEntries = 0;
1468bcb0991SDimitry Andric   /// Total number of call site DIEs (DW_TAG_call_site).
147349cc55cSDimitry Andric   SaturatingUINT64 CallSiteDIEs = 0;
1488bcb0991SDimitry Andric   /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
149349cc55cSDimitry Andric   SaturatingUINT64 CallSiteParamDIEs = 0;
1500b57cec5SDimitry Andric   /// Total byte size of concrete functions. This byte size includes
1510b57cec5SDimitry Andric   /// inline functions contained in the concrete functions.
152349cc55cSDimitry Andric   SaturatingUINT64 FunctionSize = 0;
1530b57cec5SDimitry Andric   /// Total byte size of inlined functions. This is the total number of bytes
1540b57cec5SDimitry Andric   /// for the top inline functions within concrete functions. This can help
1550b57cec5SDimitry Andric   /// tune the inline settings when compiling to match user expectations.
156349cc55cSDimitry Andric   SaturatingUINT64 InlineFunctionSize = 0;
1578bcb0991SDimitry Andric };
1588bcb0991SDimitry Andric 
1598bcb0991SDimitry Andric /// Holds accumulated debug location statistics about local variables and
1608bcb0991SDimitry Andric /// formal parameters.
1618bcb0991SDimitry Andric struct LocationStats {
1628bcb0991SDimitry Andric   /// Map the scope coverage decile to the number of variables in the decile.
1638bcb0991SDimitry Andric   /// The first element of the array (at the index zero) represents the number
1648bcb0991SDimitry Andric   /// of variables with the no debug location at all, but the last element
1658bcb0991SDimitry Andric   /// in the vector represents the number of fully covered variables within
1668bcb0991SDimitry Andric   /// its scope.
167349cc55cSDimitry Andric   std::vector<SaturatingUINT64> VarParamLocStats{
168349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1698bcb0991SDimitry Andric   /// Map non debug entry values coverage.
170349cc55cSDimitry Andric   std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
171349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1728bcb0991SDimitry Andric   /// The debug location statistics for formal parameters.
173349cc55cSDimitry Andric   std::vector<SaturatingUINT64> ParamLocStats{
174349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1758bcb0991SDimitry Andric   /// Map non debug entry values coverage for formal parameters.
176349cc55cSDimitry Andric   std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
177349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1788bcb0991SDimitry Andric   /// The debug location statistics for local variables.
179349cc55cSDimitry Andric   std::vector<SaturatingUINT64> LocalVarLocStats{
180349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1818bcb0991SDimitry Andric   /// Map non debug entry values coverage for local variables.
182349cc55cSDimitry Andric   std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
183349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1848bcb0991SDimitry Andric   /// Total number of local variables and function parameters processed.
185349cc55cSDimitry Andric   SaturatingUINT64 NumVarParam = 0;
1868bcb0991SDimitry Andric   /// Total number of formal parameters processed.
187349cc55cSDimitry Andric   SaturatingUINT64 NumParam = 0;
1888bcb0991SDimitry Andric   /// Total number of local variables processed.
189349cc55cSDimitry Andric   SaturatingUINT64 NumVar = 0;
1900b57cec5SDimitry Andric };
1915ffd83dbSDimitry Andric } // namespace
1920b57cec5SDimitry Andric 
1938bcb0991SDimitry Andric /// Collect debug location statistics for one DIE.
194e8d8bef9SDimitry Andric static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
195349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &VarParamLocStats,
196349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &ParamLocStats,
197349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &LocalVarLocStats,
1985ffd83dbSDimitry Andric                             bool IsParam, bool IsLocalVar) {
199e8d8bef9SDimitry Andric   auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
2008bcb0991SDimitry Andric     // No debug location at all for the variable.
201e8d8bef9SDimitry Andric     if (ScopeBytesCovered == 0)
2028bcb0991SDimitry Andric       return 0;
2038bcb0991SDimitry Andric     // Fully covered variable within its scope.
204e8d8bef9SDimitry Andric     if (ScopeBytesCovered >= BytesInScope)
2058bcb0991SDimitry Andric       return NumOfCoverageCategories - 1;
2068bcb0991SDimitry Andric     // Get covered range (e.g. 20%-29%).
207e8d8bef9SDimitry Andric     unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
2088bcb0991SDimitry Andric     LocBucket /= 10;
2098bcb0991SDimitry Andric     return LocBucket + 1;
2108bcb0991SDimitry Andric   };
2118bcb0991SDimitry Andric 
2128bcb0991SDimitry Andric   unsigned CoverageBucket = getCoverageBucket();
213fe6060f1SDimitry Andric 
214349cc55cSDimitry Andric   VarParamLocStats[CoverageBucket].Value++;
2158bcb0991SDimitry Andric   if (IsParam)
216349cc55cSDimitry Andric     ParamLocStats[CoverageBucket].Value++;
2178bcb0991SDimitry Andric   else if (IsLocalVar)
218349cc55cSDimitry Andric     LocalVarLocStats[CoverageBucket].Value++;
2195ffd83dbSDimitry Andric }
220fe6060f1SDimitry Andric 
2215ffd83dbSDimitry Andric /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
2225ffd83dbSDimitry Andric /// and DeclLine. The identifier aims to be unique for any unique entities,
2235ffd83dbSDimitry Andric /// but keeping the same among different instances of the same entity.
2245ffd83dbSDimitry Andric static std::string constructDieID(DWARFDie Die,
2255ffd83dbSDimitry Andric                                   StringRef Prefix = StringRef()) {
2265ffd83dbSDimitry Andric   std::string IDStr;
2275ffd83dbSDimitry Andric   llvm::raw_string_ostream ID(IDStr);
2285ffd83dbSDimitry Andric   ID << Prefix
2295ffd83dbSDimitry Andric      << Die.getName(DINameKind::LinkageName);
2305ffd83dbSDimitry Andric 
2315ffd83dbSDimitry Andric   // Prefix + Name is enough for local variables and parameters.
2325ffd83dbSDimitry Andric   if (!Prefix.empty() && !Prefix.equals("g"))
2335ffd83dbSDimitry Andric     return ID.str();
2345ffd83dbSDimitry Andric 
2355ffd83dbSDimitry Andric   auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
2365ffd83dbSDimitry Andric   std::string File;
2375ffd83dbSDimitry Andric   if (DeclFile) {
2385ffd83dbSDimitry Andric     DWARFUnit *U = Die.getDwarfUnit();
2395ffd83dbSDimitry Andric     if (const auto *LT = U->getContext().getLineTableForUnit(U))
2405ffd83dbSDimitry Andric       if (LT->getFileNameByIndex(
2415ffd83dbSDimitry Andric               dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
2425ffd83dbSDimitry Andric               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
2435ffd83dbSDimitry Andric         File = std::string(sys::path::filename(File));
2445ffd83dbSDimitry Andric   }
2455ffd83dbSDimitry Andric   ID << ":" << (File.empty() ? "/" : File);
2465ffd83dbSDimitry Andric   ID << ":"
2475ffd83dbSDimitry Andric      << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
2485ffd83dbSDimitry Andric   return ID.str();
2498bcb0991SDimitry Andric }
2508bcb0991SDimitry Andric 
251e8d8bef9SDimitry Andric /// Return the number of bytes in the overlap of ranges A and B.
252e8d8bef9SDimitry Andric static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
253e8d8bef9SDimitry Andric   uint64_t Lower = std::max(A.LowPC, B.LowPC);
254e8d8bef9SDimitry Andric   uint64_t Upper = std::min(A.HighPC, B.HighPC);
255e8d8bef9SDimitry Andric   if (Lower >= Upper)
256e8d8bef9SDimitry Andric     return 0;
257e8d8bef9SDimitry Andric   return Upper - Lower;
258e8d8bef9SDimitry Andric }
259e8d8bef9SDimitry Andric 
2600b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE.
261fe6060f1SDimitry Andric static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
262fe6060f1SDimitry Andric                                const std::string &VarPrefix,
263fe6060f1SDimitry Andric                                uint64_t BytesInScope, uint32_t InlineDepth,
2640b57cec5SDimitry Andric                                StringMap<PerFunctionStats> &FnStatMap,
2658bcb0991SDimitry Andric                                GlobalStats &GlobalStats,
266fe6060f1SDimitry Andric                                LocationStats &LocStats,
267fe6060f1SDimitry Andric                                AbstractOriginVarsTy *AbstractOriginVariables) {
268fe6060f1SDimitry Andric   const dwarf::Tag Tag = Die.getTag();
269fe6060f1SDimitry Andric   // Skip CU node.
270fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_compile_unit)
271fe6060f1SDimitry Andric     return;
272fe6060f1SDimitry Andric 
2730b57cec5SDimitry Andric   bool HasLoc = false;
2740b57cec5SDimitry Andric   bool HasSrcLoc = false;
2750b57cec5SDimitry Andric   bool HasType = false;
276e8d8bef9SDimitry Andric   uint64_t TotalBytesCovered = 0;
277e8d8bef9SDimitry Andric   uint64_t ScopeBytesCovered = 0;
2788bcb0991SDimitry Andric   uint64_t BytesEntryValuesCovered = 0;
2798bcb0991SDimitry Andric   auto &FnStats = FnStatMap[FnPrefix];
280fe6060f1SDimitry Andric   bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
281fe6060f1SDimitry Andric   bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
282fe6060f1SDimitry Andric   bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
2835ffd83dbSDimitry Andric                           Die.find(dwarf::DW_AT_const_value);
2840b57cec5SDimitry Andric 
285fe6060f1SDimitry Andric   // For zero covered inlined variables the locstats will be
286fe6060f1SDimitry Andric   // calculated later.
287fe6060f1SDimitry Andric   bool DeferLocStats = false;
288fe6060f1SDimitry Andric 
289fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
2908bcb0991SDimitry Andric     GlobalStats.CallSiteDIEs++;
2910b57cec5SDimitry Andric     return;
2920b57cec5SDimitry Andric   }
2930b57cec5SDimitry Andric 
294fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_call_site_parameter ||
295fe6060f1SDimitry Andric       Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
2968bcb0991SDimitry Andric     GlobalStats.CallSiteParamDIEs++;
2978bcb0991SDimitry Andric     return;
2988bcb0991SDimitry Andric   }
2998bcb0991SDimitry Andric 
3005ffd83dbSDimitry Andric   if (!IsParam && !IsLocalVar && !IsConstantMember) {
3010b57cec5SDimitry Andric     // Not a variable or constant member.
3020b57cec5SDimitry Andric     return;
3030b57cec5SDimitry Andric   }
3040b57cec5SDimitry Andric 
3055ffd83dbSDimitry Andric   // Ignore declarations of global variables.
3065ffd83dbSDimitry Andric   if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
3075ffd83dbSDimitry Andric     return;
3085ffd83dbSDimitry Andric 
3090b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
3100b57cec5SDimitry Andric       Die.findRecursively(dwarf::DW_AT_decl_line))
3110b57cec5SDimitry Andric     HasSrcLoc = true;
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_type))
3140b57cec5SDimitry Andric     HasType = true;
3150b57cec5SDimitry Andric 
316fe6060f1SDimitry Andric   if (Die.find(dwarf::DW_AT_abstract_origin)) {
317fe6060f1SDimitry Andric     if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
318fe6060f1SDimitry Andric       if (AbstractOriginVariables) {
319fe6060f1SDimitry Andric         auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
320fe6060f1SDimitry Andric         // Do not track this variable any more, since it has location
321fe6060f1SDimitry Andric         // coverage.
322fe6060f1SDimitry Andric         llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue());
323fe6060f1SDimitry Andric       }
324fe6060f1SDimitry Andric     } else {
325fe6060f1SDimitry Andric       // The locstats will be handled at the end of
326fe6060f1SDimitry Andric       // the collectStatsRecursive().
327fe6060f1SDimitry Andric       DeferLocStats = true;
328fe6060f1SDimitry Andric     }
329fe6060f1SDimitry Andric   }
330fe6060f1SDimitry Andric 
3318bcb0991SDimitry Andric   auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
3328bcb0991SDimitry Andric     DWARFUnit *U = Die.getDwarfUnit();
3338bcb0991SDimitry Andric     DataExtractor Data(toStringRef(D),
3348bcb0991SDimitry Andric                        Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
3355ffd83dbSDimitry Andric     DWARFExpression Expression(Data, U->getAddressByteSize(),
3365ffd83dbSDimitry Andric                                U->getFormParams().Format);
3378bcb0991SDimitry Andric     // Consider the expression containing the DW_OP_entry_value as
3388bcb0991SDimitry Andric     // an entry value.
339349cc55cSDimitry Andric     return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
3408bcb0991SDimitry Andric       return Op.getCode() == dwarf::DW_OP_entry_value ||
3418bcb0991SDimitry Andric              Op.getCode() == dwarf::DW_OP_GNU_entry_value;
3428bcb0991SDimitry Andric     });
3438bcb0991SDimitry Andric   };
3448bcb0991SDimitry Andric 
3450b57cec5SDimitry Andric   if (Die.find(dwarf::DW_AT_const_value)) {
3460b57cec5SDimitry Andric     // This catches constant members *and* variables.
3470b57cec5SDimitry Andric     HasLoc = true;
348e8d8bef9SDimitry Andric     ScopeBytesCovered = BytesInScope;
349e8d8bef9SDimitry Andric     TotalBytesCovered = BytesInScope;
3500b57cec5SDimitry Andric   } else {
3510b57cec5SDimitry Andric     // Handle variables and function arguments.
352480093f4SDimitry Andric     Expected<std::vector<DWARFLocationExpression>> Loc =
353480093f4SDimitry Andric         Die.getLocations(dwarf::DW_AT_location);
354480093f4SDimitry Andric     if (!Loc) {
355480093f4SDimitry Andric       consumeError(Loc.takeError());
3560b57cec5SDimitry Andric     } else {
357480093f4SDimitry Andric       HasLoc = true;
358480093f4SDimitry Andric       // Get PC coverage.
359480093f4SDimitry Andric       auto Default = find_if(
360480093f4SDimitry Andric           *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
361480093f4SDimitry Andric       if (Default != Loc->end()) {
3620b57cec5SDimitry Andric         // Assume the entire range is covered by a single location.
363e8d8bef9SDimitry Andric         ScopeBytesCovered = BytesInScope;
364e8d8bef9SDimitry Andric         TotalBytesCovered = BytesInScope;
365480093f4SDimitry Andric       } else {
366e8d8bef9SDimitry Andric         // Caller checks this Expected result already, it cannot fail.
367e8d8bef9SDimitry Andric         auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
368480093f4SDimitry Andric         for (auto Entry : *Loc) {
369e8d8bef9SDimitry Andric           TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
370e8d8bef9SDimitry Andric           uint64_t ScopeBytesCoveredByEntry = 0;
371e8d8bef9SDimitry Andric           // Calculate how many bytes of the parent scope this entry covers.
372e8d8bef9SDimitry Andric           // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
373e8d8bef9SDimitry Andric           // address ranges defined by the bounded location descriptions of a
374e8d8bef9SDimitry Andric           // location list may overlap". So in theory a variable can have
375e8d8bef9SDimitry Andric           // multiple simultaneous locations, which would make this calculation
376e8d8bef9SDimitry Andric           // misleading because we will count the overlapped areas
377e8d8bef9SDimitry Andric           // twice. However, clang does not currently emit DWARF like this.
378e8d8bef9SDimitry Andric           for (DWARFAddressRange R : ScopeRanges) {
379e8d8bef9SDimitry Andric             ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
380e8d8bef9SDimitry Andric           }
381e8d8bef9SDimitry Andric           ScopeBytesCovered += ScopeBytesCoveredByEntry;
382480093f4SDimitry Andric           if (IsEntryValue(Entry.Expr))
383e8d8bef9SDimitry Andric             BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
384480093f4SDimitry Andric         }
3850b57cec5SDimitry Andric       }
3860b57cec5SDimitry Andric     }
3870b57cec5SDimitry Andric   }
3880b57cec5SDimitry Andric 
3898bcb0991SDimitry Andric   // Calculate the debug location statistics.
390fe6060f1SDimitry Andric   if (BytesInScope && !DeferLocStats) {
391349cc55cSDimitry Andric     LocStats.NumVarParam.Value++;
3928bcb0991SDimitry Andric     if (IsParam)
393349cc55cSDimitry Andric       LocStats.NumParam.Value++;
3948bcb0991SDimitry Andric     else if (IsLocalVar)
395349cc55cSDimitry Andric       LocStats.NumVar.Value++;
3968bcb0991SDimitry Andric 
397e8d8bef9SDimitry Andric     collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
3985ffd83dbSDimitry Andric                     LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
3998bcb0991SDimitry Andric                     IsLocalVar);
4008bcb0991SDimitry Andric     // Non debug entry values coverage statistics.
401e8d8bef9SDimitry Andric     collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
4028bcb0991SDimitry Andric                     LocStats.VarParamNonEntryValLocStats,
4038bcb0991SDimitry Andric                     LocStats.ParamNonEntryValLocStats,
4045ffd83dbSDimitry Andric                     LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
4058bcb0991SDimitry Andric   }
4068bcb0991SDimitry Andric 
4070b57cec5SDimitry Andric   // Collect PC range coverage data.
4080b57cec5SDimitry Andric   if (DWARFDie D =
4090b57cec5SDimitry Andric           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
4100b57cec5SDimitry Andric     Die = D;
4115ffd83dbSDimitry Andric 
4125ffd83dbSDimitry Andric   std::string VarID = constructDieID(Die, VarPrefix);
4135ffd83dbSDimitry Andric   FnStats.VarsInFunction.insert(VarID);
4145ffd83dbSDimitry Andric 
415e8d8bef9SDimitry Andric   GlobalStats.TotalBytesCovered += TotalBytesCovered;
4160b57cec5SDimitry Andric   if (BytesInScope) {
417e8d8bef9SDimitry Andric     GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
418480093f4SDimitry Andric     GlobalStats.ScopeBytes += BytesInScope;
4198bcb0991SDimitry Andric     GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
4208bcb0991SDimitry Andric     if (IsParam) {
421e8d8bef9SDimitry Andric       GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
422480093f4SDimitry Andric       GlobalStats.ParamScopeBytes += BytesInScope;
4238bcb0991SDimitry Andric       GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
4248bcb0991SDimitry Andric     } else if (IsLocalVar) {
425e8d8bef9SDimitry Andric       GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
4265ffd83dbSDimitry Andric       GlobalStats.LocalVarScopeBytes += BytesInScope;
4275ffd83dbSDimitry Andric       GlobalStats.LocalVarScopeEntryValueBytesCovered +=
4285ffd83dbSDimitry Andric           BytesEntryValuesCovered;
4298bcb0991SDimitry Andric     }
430349cc55cSDimitry Andric     assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
4310b57cec5SDimitry Andric   }
4325ffd83dbSDimitry Andric 
4335ffd83dbSDimitry Andric   if (IsConstantMember) {
4345ffd83dbSDimitry Andric     FnStats.ConstantMembers++;
4355ffd83dbSDimitry Andric     return;
4365ffd83dbSDimitry Andric   }
4375ffd83dbSDimitry Andric 
4385ffd83dbSDimitry Andric   FnStats.TotalVarWithLoc += (unsigned)HasLoc;
4395ffd83dbSDimitry Andric 
4405ffd83dbSDimitry Andric   if (Die.find(dwarf::DW_AT_artificial)) {
4415ffd83dbSDimitry Andric     FnStats.NumArtificial++;
4425ffd83dbSDimitry Andric     return;
4435ffd83dbSDimitry Andric   }
4445ffd83dbSDimitry Andric 
4458bcb0991SDimitry Andric   if (IsParam) {
4460b57cec5SDimitry Andric     FnStats.NumParams++;
4470b57cec5SDimitry Andric     if (HasType)
4480b57cec5SDimitry Andric       FnStats.NumParamTypes++;
4490b57cec5SDimitry Andric     if (HasSrcLoc)
4500b57cec5SDimitry Andric       FnStats.NumParamSourceLocations++;
4510b57cec5SDimitry Andric     if (HasLoc)
4520b57cec5SDimitry Andric       FnStats.NumParamLocations++;
4538bcb0991SDimitry Andric   } else if (IsLocalVar) {
4545ffd83dbSDimitry Andric     FnStats.NumLocalVars++;
4550b57cec5SDimitry Andric     if (HasType)
4565ffd83dbSDimitry Andric       FnStats.NumLocalVarTypes++;
4570b57cec5SDimitry Andric     if (HasSrcLoc)
4585ffd83dbSDimitry Andric       FnStats.NumLocalVarSourceLocations++;
4590b57cec5SDimitry Andric     if (HasLoc)
4605ffd83dbSDimitry Andric       FnStats.NumLocalVarLocations++;
4610b57cec5SDimitry Andric   }
4620b57cec5SDimitry Andric }
4630b57cec5SDimitry Andric 
464fe6060f1SDimitry Andric /// Recursively collect variables from subprogram with DW_AT_inline attribute.
465fe6060f1SDimitry Andric static void collectAbstractOriginFnInfo(
466fe6060f1SDimitry Andric     DWARFDie Die, uint64_t SPOffset,
4674824e7fdSDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
4684824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
469fe6060f1SDimitry Andric   DWARFDie Child = Die.getFirstChild();
470fe6060f1SDimitry Andric   while (Child) {
471fe6060f1SDimitry Andric     const dwarf::Tag ChildTag = Child.getTag();
472fe6060f1SDimitry Andric     if (ChildTag == dwarf::DW_TAG_formal_parameter ||
4734824e7fdSDimitry Andric         ChildTag == dwarf::DW_TAG_variable) {
474fe6060f1SDimitry Andric       GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
4754824e7fdSDimitry Andric       LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
4764824e7fdSDimitry Andric     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
4774824e7fdSDimitry Andric       collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
4784824e7fdSDimitry Andric                                   LocalAbstractOriginFnInfo);
479fe6060f1SDimitry Andric     Child = Child.getSibling();
480fe6060f1SDimitry Andric   }
481fe6060f1SDimitry Andric }
482fe6060f1SDimitry Andric 
4830b57cec5SDimitry Andric /// Recursively collect debug info quality metrics.
484fe6060f1SDimitry Andric static void collectStatsRecursive(
485fe6060f1SDimitry Andric     DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
486fe6060f1SDimitry Andric     uint64_t BytesInScope, uint32_t InlineDepth,
487fe6060f1SDimitry Andric     StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
4884824e7fdSDimitry Andric     LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
489fe6060f1SDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
4904824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
491fe6060f1SDimitry Andric     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
492fe6060f1SDimitry Andric     AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
493fe6060f1SDimitry Andric   // Skip NULL nodes.
494fe6060f1SDimitry Andric   if (Die.isNULL())
495fe6060f1SDimitry Andric     return;
496fe6060f1SDimitry Andric 
4970b57cec5SDimitry Andric   const dwarf::Tag Tag = Die.getTag();
4985ffd83dbSDimitry Andric   // Skip function types.
4995ffd83dbSDimitry Andric   if (Tag == dwarf::DW_TAG_subroutine_type)
5005ffd83dbSDimitry Andric     return;
5015ffd83dbSDimitry Andric 
5025ffd83dbSDimitry Andric   // Handle any kind of lexical scope.
503fe6060f1SDimitry Andric   const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None;
5040b57cec5SDimitry Andric   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
5050b57cec5SDimitry Andric   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
5060b57cec5SDimitry Andric   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
507fe6060f1SDimitry Andric   // We want to know how many variables (with abstract_origin) don't have
508fe6060f1SDimitry Andric   // location info.
509fe6060f1SDimitry Andric   const bool IsCandidateForZeroLocCovTracking =
510fe6060f1SDimitry Andric       (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
5110b57cec5SDimitry Andric 
512fe6060f1SDimitry Andric   AbstractOriginVarsTy AbstractOriginVars;
513fe6060f1SDimitry Andric 
514fe6060f1SDimitry Andric   // Get the vars of the inlined fn, so the locstats
515fe6060f1SDimitry Andric   // reports the missing vars (with coverage 0%).
516fe6060f1SDimitry Andric   if (IsCandidateForZeroLocCovTracking) {
517fe6060f1SDimitry Andric     auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
518fe6060f1SDimitry Andric     if (OffsetFn) {
519fe6060f1SDimitry Andric       uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
5204824e7fdSDimitry Andric       if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
5214824e7fdSDimitry Andric         AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
522fe6060f1SDimitry Andric         AbstractOriginVarsPtr = &AbstractOriginVars;
523fe6060f1SDimitry Andric       } else {
5244824e7fdSDimitry Andric         // This means that the DW_AT_inline fn copy is out of order
5254824e7fdSDimitry Andric         // or that the abstract_origin references another CU,
526fe6060f1SDimitry Andric         // so this abstract origin instance will be processed later.
527fe6060f1SDimitry Andric         FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
528fe6060f1SDimitry Andric         AbstractOriginVarsPtr = nullptr;
529fe6060f1SDimitry Andric       }
530fe6060f1SDimitry Andric     }
531fe6060f1SDimitry Andric   }
532fe6060f1SDimitry Andric 
533fe6060f1SDimitry Andric   if (IsFunction || IsInlinedFunction || IsBlock) {
5340b57cec5SDimitry Andric     // Reset VarPrefix when entering a new function.
535fe6060f1SDimitry Andric     if (IsFunction || IsInlinedFunction)
5360b57cec5SDimitry Andric       VarPrefix = "v";
5370b57cec5SDimitry Andric 
5380b57cec5SDimitry Andric     // Ignore forward declarations.
5390b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_declaration))
5400b57cec5SDimitry Andric       return;
5410b57cec5SDimitry Andric 
5420b57cec5SDimitry Andric     // Check for call sites.
5430b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
5440b57cec5SDimitry Andric       GlobalStats.CallSiteEntries++;
5450b57cec5SDimitry Andric 
5460b57cec5SDimitry Andric     // PC Ranges.
5470b57cec5SDimitry Andric     auto RangesOrError = Die.getAddressRanges();
5480b57cec5SDimitry Andric     if (!RangesOrError) {
5490b57cec5SDimitry Andric       llvm::consumeError(RangesOrError.takeError());
5500b57cec5SDimitry Andric       return;
5510b57cec5SDimitry Andric     }
5520b57cec5SDimitry Andric 
5530b57cec5SDimitry Andric     auto Ranges = RangesOrError.get();
5540b57cec5SDimitry Andric     uint64_t BytesInThisScope = 0;
5550b57cec5SDimitry Andric     for (auto Range : Ranges)
5560b57cec5SDimitry Andric       BytesInThisScope += Range.HighPC - Range.LowPC;
5570b57cec5SDimitry Andric 
5580b57cec5SDimitry Andric     // Count the function.
5590b57cec5SDimitry Andric     if (!IsBlock) {
560fe6060f1SDimitry Andric       // Skip over abstract origins, but collect variables
561fe6060f1SDimitry Andric       // from it so it can be used for location statistics
562fe6060f1SDimitry Andric       // for inlined instancies.
563fe6060f1SDimitry Andric       if (Die.find(dwarf::DW_AT_inline)) {
564fe6060f1SDimitry Andric         uint64_t SPOffset = Die.getOffset();
5654824e7fdSDimitry Andric         AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
5664824e7fdSDimitry Andric         collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
5674824e7fdSDimitry Andric                                     LocalAbstractOriginFnInfo);
5680b57cec5SDimitry Andric         return;
569fe6060f1SDimitry Andric       }
570fe6060f1SDimitry Andric 
5715ffd83dbSDimitry Andric       std::string FnID = constructDieID(Die);
5725ffd83dbSDimitry Andric       // We've seen an instance of this function.
5735ffd83dbSDimitry Andric       auto &FnStats = FnStatMap[FnID];
5745ffd83dbSDimitry Andric       FnStats.IsFunction = true;
5750b57cec5SDimitry Andric       if (IsInlinedFunction) {
5760b57cec5SDimitry Andric         FnStats.NumFnInlined++;
5770b57cec5SDimitry Andric         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
5780b57cec5SDimitry Andric           FnStats.NumAbstractOrigins++;
5795ffd83dbSDimitry Andric       } else {
5805ffd83dbSDimitry Andric         FnStats.NumFnOutOfLine++;
5810b57cec5SDimitry Andric       }
5820b57cec5SDimitry Andric       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
5830b57cec5SDimitry Andric           Die.findRecursively(dwarf::DW_AT_decl_line))
5840b57cec5SDimitry Andric         FnStats.HasSourceLocation = true;
5855ffd83dbSDimitry Andric       // Update function prefix.
5865ffd83dbSDimitry Andric       FnPrefix = FnID;
5870b57cec5SDimitry Andric     }
5880b57cec5SDimitry Andric 
5890b57cec5SDimitry Andric     if (BytesInThisScope) {
5900b57cec5SDimitry Andric       BytesInScope = BytesInThisScope;
5910b57cec5SDimitry Andric       if (IsFunction)
5920b57cec5SDimitry Andric         GlobalStats.FunctionSize += BytesInThisScope;
5930b57cec5SDimitry Andric       else if (IsInlinedFunction && InlineDepth == 0)
5940b57cec5SDimitry Andric         GlobalStats.InlineFunctionSize += BytesInThisScope;
5950b57cec5SDimitry Andric     }
5960b57cec5SDimitry Andric   } else {
5970b57cec5SDimitry Andric     // Not a scope, visit the Die itself. It could be a variable.
598480093f4SDimitry Andric     collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
599fe6060f1SDimitry Andric                        FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
6000b57cec5SDimitry Andric   }
6010b57cec5SDimitry Andric 
6020b57cec5SDimitry Andric   // Set InlineDepth correctly for child recursion
6030b57cec5SDimitry Andric   if (IsFunction)
6040b57cec5SDimitry Andric     InlineDepth = 0;
6050b57cec5SDimitry Andric   else if (IsInlinedFunction)
6060b57cec5SDimitry Andric     ++InlineDepth;
6070b57cec5SDimitry Andric 
6080b57cec5SDimitry Andric   // Traverse children.
6090b57cec5SDimitry Andric   unsigned LexicalBlockIndex = 0;
6105ffd83dbSDimitry Andric   unsigned FormalParameterIndex = 0;
6110b57cec5SDimitry Andric   DWARFDie Child = Die.getFirstChild();
6120b57cec5SDimitry Andric   while (Child) {
6130b57cec5SDimitry Andric     std::string ChildVarPrefix = VarPrefix;
6140b57cec5SDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
6150b57cec5SDimitry Andric       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
6165ffd83dbSDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
6175ffd83dbSDimitry Andric       ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
6180b57cec5SDimitry Andric 
619fe6060f1SDimitry Andric     collectStatsRecursive(
620fe6060f1SDimitry Andric         Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
6214824e7fdSDimitry Andric         GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
6224824e7fdSDimitry Andric         LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
6234824e7fdSDimitry Andric         AbstractOriginVarsPtr);
6240b57cec5SDimitry Andric     Child = Child.getSibling();
6250b57cec5SDimitry Andric   }
626fe6060f1SDimitry Andric 
627fe6060f1SDimitry Andric   if (!IsCandidateForZeroLocCovTracking)
628fe6060f1SDimitry Andric     return;
629fe6060f1SDimitry Andric 
630fe6060f1SDimitry Andric   // After we have processed all vars of the inlined function (or function with
631fe6060f1SDimitry Andric   // an abstract_origin), we want to know how many variables have no location.
632fe6060f1SDimitry Andric   for (auto Offset : AbstractOriginVars) {
633fe6060f1SDimitry Andric     LocStats.NumVarParam++;
634fe6060f1SDimitry Andric     LocStats.VarParamLocStats[ZeroCoverageBucket]++;
635fe6060f1SDimitry Andric     auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
636fe6060f1SDimitry Andric     if (!FnDie)
637fe6060f1SDimitry Andric       continue;
638fe6060f1SDimitry Andric     auto Tag = FnDie.getTag();
639fe6060f1SDimitry Andric     if (Tag == dwarf::DW_TAG_formal_parameter) {
640fe6060f1SDimitry Andric       LocStats.NumParam++;
641fe6060f1SDimitry Andric       LocStats.ParamLocStats[ZeroCoverageBucket]++;
642fe6060f1SDimitry Andric     } else if (Tag == dwarf::DW_TAG_variable) {
643fe6060f1SDimitry Andric       LocStats.NumVar++;
644fe6060f1SDimitry Andric       LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
645fe6060f1SDimitry Andric     }
646fe6060f1SDimitry Andric   }
6470b57cec5SDimitry Andric }
6480b57cec5SDimitry Andric 
649e8d8bef9SDimitry Andric /// Print human-readable output.
6500b57cec5SDimitry Andric /// \{
651e8d8bef9SDimitry Andric static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
652349cc55cSDimitry Andric   if (Value == OverflowValue)
653349cc55cSDimitry Andric     J.attribute(Key, "overflowed");
654349cc55cSDimitry Andric   else
655e8d8bef9SDimitry Andric     J.attribute(Key, Value);
656349cc55cSDimitry Andric 
6570b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
6580b57cec5SDimitry Andric }
6595ffd83dbSDimitry Andric 
660e8d8bef9SDimitry Andric static void printLocationStats(json::OStream &J, const char *Key,
661349cc55cSDimitry Andric                                std::vector<SaturatingUINT64> &LocationStats) {
662349cc55cSDimitry Andric   if (LocationStats[0].Value == OverflowValue)
663349cc55cSDimitry Andric     J.attribute((Twine(Key) +
664349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
665349cc55cSDimitry Andric                     .str(),
666349cc55cSDimitry Andric                 "overflowed");
667349cc55cSDimitry Andric   else
668e8d8bef9SDimitry Andric     J.attribute(
669349cc55cSDimitry Andric         (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
670349cc55cSDimitry Andric             .str(),
671349cc55cSDimitry Andric         LocationStats[0].Value);
6725ffd83dbSDimitry Andric   LLVM_DEBUG(
6735ffd83dbSDimitry Andric       llvm::dbgs() << Key
6745ffd83dbSDimitry Andric                    << " with 0% of parent scope covered by DW_AT_location: \\"
675349cc55cSDimitry Andric                    << LocationStats[0].Value << '\n');
676349cc55cSDimitry Andric 
677349cc55cSDimitry Andric   if (LocationStats[1].Value == OverflowValue)
678349cc55cSDimitry Andric     J.attribute((Twine(Key) +
679349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
680e8d8bef9SDimitry Andric                     .str(),
681349cc55cSDimitry Andric                 "overflowed");
682349cc55cSDimitry Andric   else
683349cc55cSDimitry Andric     J.attribute((Twine(Key) +
684349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
685349cc55cSDimitry Andric                     .str(),
686349cc55cSDimitry Andric                 LocationStats[1].Value);
6875ffd83dbSDimitry Andric   LLVM_DEBUG(llvm::dbgs()
6885ffd83dbSDimitry Andric              << Key
6895ffd83dbSDimitry Andric              << " with (0%,10%) of parent scope covered by DW_AT_location: "
690349cc55cSDimitry Andric              << LocationStats[1].Value << '\n');
691349cc55cSDimitry Andric 
6928bcb0991SDimitry Andric   for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
693349cc55cSDimitry Andric     if (LocationStats[i].Value == OverflowValue)
694e8d8bef9SDimitry Andric       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
695349cc55cSDimitry Andric                    Twine(i * 10) +
696349cc55cSDimitry Andric                    "%) of parent scope covered by DW_AT_location")
697e8d8bef9SDimitry Andric                       .str(),
698349cc55cSDimitry Andric                   "overflowed");
699349cc55cSDimitry Andric     else
700349cc55cSDimitry Andric       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
701349cc55cSDimitry Andric                    Twine(i * 10) +
702349cc55cSDimitry Andric                    "%) of parent scope covered by DW_AT_location")
703349cc55cSDimitry Andric                       .str(),
704349cc55cSDimitry Andric                   LocationStats[i].Value);
7058bcb0991SDimitry Andric     LLVM_DEBUG(llvm::dbgs()
706480093f4SDimitry Andric                << Key << " with [" << (i - 1) * 10 << "%," << i * 10
7075ffd83dbSDimitry Andric                << "%) of parent scope covered by DW_AT_location: "
708349cc55cSDimitry Andric                << LocationStats[i].Value);
7098bcb0991SDimitry Andric   }
710349cc55cSDimitry Andric   if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
711e8d8bef9SDimitry Andric     J.attribute(
712e8d8bef9SDimitry Andric         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
713e8d8bef9SDimitry Andric             .str(),
714349cc55cSDimitry Andric         "overflowed");
715349cc55cSDimitry Andric   else
716349cc55cSDimitry Andric     J.attribute(
717349cc55cSDimitry Andric         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
718349cc55cSDimitry Andric             .str(),
719349cc55cSDimitry Andric         LocationStats[NumOfCoverageCategories - 1].Value);
7205ffd83dbSDimitry Andric   LLVM_DEBUG(
7215ffd83dbSDimitry Andric       llvm::dbgs() << Key
7225ffd83dbSDimitry Andric                    << " with 100% of parent scope covered by DW_AT_location: "
723349cc55cSDimitry Andric                    << LocationStats[NumOfCoverageCategories - 1].Value);
7248bcb0991SDimitry Andric }
7255ffd83dbSDimitry Andric 
726e8d8bef9SDimitry Andric static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
727fe6060f1SDimitry Andric   for (const auto &It : Sizes.DebugSectionSizes)
728fe6060f1SDimitry Andric     J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
729fe6060f1SDimitry Andric }
730fe6060f1SDimitry Andric 
731fe6060f1SDimitry Andric /// Stop tracking variables that contain abstract_origin with a location.
732fe6060f1SDimitry Andric /// This is used for out-of-order DW_AT_inline subprograms only.
733fe6060f1SDimitry Andric static void updateVarsWithAbstractOriginLocCovInfo(
734fe6060f1SDimitry Andric     DWARFDie FnDieWithAbstractOrigin,
735fe6060f1SDimitry Andric     AbstractOriginVarsTy &AbstractOriginVars) {
736fe6060f1SDimitry Andric   DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
737fe6060f1SDimitry Andric   while (Child) {
738fe6060f1SDimitry Andric     const dwarf::Tag ChildTag = Child.getTag();
739fe6060f1SDimitry Andric     if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
740fe6060f1SDimitry Andric          ChildTag == dwarf::DW_TAG_variable) &&
741fe6060f1SDimitry Andric         (Child.find(dwarf::DW_AT_location) ||
742fe6060f1SDimitry Andric          Child.find(dwarf::DW_AT_const_value))) {
743fe6060f1SDimitry Andric       auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
744fe6060f1SDimitry Andric       if (OffsetVar)
745fe6060f1SDimitry Andric         llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue());
746fe6060f1SDimitry Andric     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
747fe6060f1SDimitry Andric       updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
748fe6060f1SDimitry Andric     Child = Child.getSibling();
749fe6060f1SDimitry Andric   }
750fe6060f1SDimitry Andric }
751fe6060f1SDimitry Andric 
752fe6060f1SDimitry Andric /// Collect zero location coverage for inlined variables which refer to
753fe6060f1SDimitry Andric /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
754fe6060f1SDimitry Andric /// Also cover the variables of a concrete function (represented with
755fe6060f1SDimitry Andric /// the DW_TAG_subprogram) with an abstract_origin attribute.
756fe6060f1SDimitry Andric static void collectZeroLocCovForVarsWithAbstractOrigin(
757fe6060f1SDimitry Andric     DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
7584824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
759fe6060f1SDimitry Andric     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
7604824e7fdSDimitry Andric   // The next variable is used to filter out functions that have been processed,
7614824e7fdSDimitry Andric   // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
7624824e7fdSDimitry Andric   FunctionsWithAbstractOriginTy ProcessedFns;
763fe6060f1SDimitry Andric   for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
764fe6060f1SDimitry Andric     DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
765fe6060f1SDimitry Andric     auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
766fe6060f1SDimitry Andric     AbstractOriginVarsTy AbstractOriginVars;
767fe6060f1SDimitry Andric     if (!FnCopy)
768fe6060f1SDimitry Andric       continue;
7694824e7fdSDimitry Andric     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
7704824e7fdSDimitry Andric     // If there is no entry within LocalAbstractOriginFnInfo for the given
7714824e7fdSDimitry Andric     // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
7724824e7fdSDimitry Andric     // CrossCU referencing.
7734824e7fdSDimitry Andric     if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
7744824e7fdSDimitry Andric       continue;
7754824e7fdSDimitry Andric     AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
776fe6060f1SDimitry Andric     updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
777fe6060f1SDimitry Andric                                            AbstractOriginVars);
778fe6060f1SDimitry Andric 
779fe6060f1SDimitry Andric     for (auto Offset : AbstractOriginVars) {
780fe6060f1SDimitry Andric       LocStats.NumVarParam++;
781fe6060f1SDimitry Andric       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
782fe6060f1SDimitry Andric       auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
783fe6060f1SDimitry Andric       if (Tag == dwarf::DW_TAG_formal_parameter) {
784fe6060f1SDimitry Andric         LocStats.NumParam++;
785fe6060f1SDimitry Andric         LocStats.ParamLocStats[ZeroCoverageBucket]++;
786fe6060f1SDimitry Andric       } else if (Tag == dwarf::DW_TAG_variable) {
787fe6060f1SDimitry Andric         LocStats.NumVar++;
788fe6060f1SDimitry Andric         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
789fe6060f1SDimitry Andric       }
790fe6060f1SDimitry Andric     }
7914824e7fdSDimitry Andric     ProcessedFns.push_back(FnOffset);
7924824e7fdSDimitry Andric   }
7934824e7fdSDimitry Andric   for (auto ProcessedFn : ProcessedFns)
7944824e7fdSDimitry Andric     llvm::erase_value(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
7954824e7fdSDimitry Andric }
7964824e7fdSDimitry Andric 
7974824e7fdSDimitry Andric /// Collect zero location coverage for inlined variables which refer to
7984824e7fdSDimitry Andric /// a DW_AT_inline copy of subprogram that is in a different CU.
7994824e7fdSDimitry Andric static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
8004824e7fdSDimitry Andric     LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
8014824e7fdSDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
8024824e7fdSDimitry Andric     CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
8034824e7fdSDimitry Andric   for (const auto &CrossCUReferenceToBeResolved :
8044824e7fdSDimitry Andric        CrossCUReferencesToBeResolved) {
8054824e7fdSDimitry Andric     DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
8064824e7fdSDimitry Andric     DWARFDie FnDIEWithCrossCUReferencing =
8074824e7fdSDimitry Andric         DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
8084824e7fdSDimitry Andric     auto FnCopy =
8094824e7fdSDimitry Andric         FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
8104824e7fdSDimitry Andric     if (!FnCopy)
8114824e7fdSDimitry Andric       continue;
8124824e7fdSDimitry Andric     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
8134824e7fdSDimitry Andric     AbstractOriginVarsTy AbstractOriginVars =
8144824e7fdSDimitry Andric         GlobalAbstractOriginFnInfo[FnCopyRawUValue];
8154824e7fdSDimitry Andric     updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
8164824e7fdSDimitry Andric                                            AbstractOriginVars);
8174824e7fdSDimitry Andric     for (auto Offset : AbstractOriginVars) {
8184824e7fdSDimitry Andric       LocStats.NumVarParam++;
8194824e7fdSDimitry Andric       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
8204824e7fdSDimitry Andric       auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
8214824e7fdSDimitry Andric                      ->getDIEForOffset(Offset)
8224824e7fdSDimitry Andric                      .getTag();
8234824e7fdSDimitry Andric       if (Tag == dwarf::DW_TAG_formal_parameter) {
8244824e7fdSDimitry Andric         LocStats.NumParam++;
8254824e7fdSDimitry Andric         LocStats.ParamLocStats[ZeroCoverageBucket]++;
8264824e7fdSDimitry Andric       } else if (Tag == dwarf::DW_TAG_variable) {
8274824e7fdSDimitry Andric         LocStats.NumVar++;
8284824e7fdSDimitry Andric         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
8294824e7fdSDimitry Andric       }
8304824e7fdSDimitry Andric     }
831fe6060f1SDimitry Andric   }
8325ffd83dbSDimitry Andric }
8335ffd83dbSDimitry Andric 
8340b57cec5SDimitry Andric /// \}
8350b57cec5SDimitry Andric 
8360b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext.
8370b57cec5SDimitry Andric ///
8380b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few
8390b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked
8400b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect
8410b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly
8420b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different
8430b57cec5SDimitry Andric /// compilers is.
8445ffd83dbSDimitry Andric bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
8455ffd83dbSDimitry Andric                                           const Twine &Filename,
8465ffd83dbSDimitry Andric                                           raw_ostream &OS) {
8470b57cec5SDimitry Andric   StringRef FormatName = Obj.getFileFormatName();
8480b57cec5SDimitry Andric   GlobalStats GlobalStats;
8498bcb0991SDimitry Andric   LocationStats LocStats;
8500b57cec5SDimitry Andric   StringMap<PerFunctionStats> Statistics;
8514824e7fdSDimitry Andric   // This variable holds variable information for functions with
8524824e7fdSDimitry Andric   // abstract_origin globally, across all CUs.
8534824e7fdSDimitry Andric   AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
8544824e7fdSDimitry Andric   // This variable holds information about the CU of a function with
8554824e7fdSDimitry Andric   // abstract_origin.
8564824e7fdSDimitry Andric   FunctionDIECUTyMap AbstractOriginFnCUs;
8574824e7fdSDimitry Andric   CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
858fe6060f1SDimitry Andric   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
859fe6060f1SDimitry Andric     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
8604824e7fdSDimitry Andric       // This variable holds variable information for functions with
8614824e7fdSDimitry Andric       // abstract_origin, but just for the current CU.
8624824e7fdSDimitry Andric       AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
863fe6060f1SDimitry Andric       FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
864fe6060f1SDimitry Andric 
8654824e7fdSDimitry Andric       collectStatsRecursive(
8664824e7fdSDimitry Andric           CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
8674824e7fdSDimitry Andric           AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
8684824e7fdSDimitry Andric           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
869fe6060f1SDimitry Andric 
8704824e7fdSDimitry Andric       // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
8714824e7fdSDimitry Andric       // out-of-order DWARF functions that have been processed within it,
8724824e7fdSDimitry Andric       // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
8734824e7fdSDimitry Andric       // references.
874fe6060f1SDimitry Andric       collectZeroLocCovForVarsWithAbstractOrigin(
875fe6060f1SDimitry Andric           CUDie.getDwarfUnit(), GlobalStats, LocStats,
8764824e7fdSDimitry Andric           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
8774824e7fdSDimitry Andric 
8784824e7fdSDimitry Andric       // Collect all CrossCU references into CrossCUReferencesToBeResolved.
8794824e7fdSDimitry Andric       for (auto CrossCUReferencingDIEOffset :
8804824e7fdSDimitry Andric            FnsWithAbstractOriginToBeProcessed)
8814824e7fdSDimitry Andric         CrossCUReferencesToBeResolved.push_back(
8824824e7fdSDimitry Andric             DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
883fe6060f1SDimitry Andric     }
884fe6060f1SDimitry Andric   }
8850b57cec5SDimitry Andric 
8864824e7fdSDimitry Andric   /// Resolve CrossCU references.
8874824e7fdSDimitry Andric   collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
8884824e7fdSDimitry Andric       LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
8894824e7fdSDimitry Andric       CrossCUReferencesToBeResolved);
8904824e7fdSDimitry Andric 
8915ffd83dbSDimitry Andric   /// Collect the sizes of debug sections.
8925ffd83dbSDimitry Andric   SectionSizes Sizes;
8935ffd83dbSDimitry Andric   calculateSectionSizes(Obj, Sizes, Filename);
8945ffd83dbSDimitry Andric 
8950b57cec5SDimitry Andric   /// The version number should be increased every time the algorithm is changed
8960b57cec5SDimitry Andric   /// (including bug fixes). New metrics may be added without increasing the
8970b57cec5SDimitry Andric   /// version.
898349cc55cSDimitry Andric   unsigned Version = 9;
899349cc55cSDimitry Andric   SaturatingUINT64 VarParamTotal = 0;
900349cc55cSDimitry Andric   SaturatingUINT64 VarParamUnique = 0;
901349cc55cSDimitry Andric   SaturatingUINT64 VarParamWithLoc = 0;
902349cc55cSDimitry Andric   SaturatingUINT64 NumFunctions = 0;
903349cc55cSDimitry Andric   SaturatingUINT64 NumInlinedFunctions = 0;
904349cc55cSDimitry Andric   SaturatingUINT64 NumFuncsWithSrcLoc = 0;
905349cc55cSDimitry Andric   SaturatingUINT64 NumAbstractOrigins = 0;
906349cc55cSDimitry Andric   SaturatingUINT64 ParamTotal = 0;
907349cc55cSDimitry Andric   SaturatingUINT64 ParamWithType = 0;
908349cc55cSDimitry Andric   SaturatingUINT64 ParamWithLoc = 0;
909349cc55cSDimitry Andric   SaturatingUINT64 ParamWithSrcLoc = 0;
910349cc55cSDimitry Andric   SaturatingUINT64 LocalVarTotal = 0;
911349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithType = 0;
912349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithSrcLoc = 0;
913349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithLoc = 0;
9140b57cec5SDimitry Andric   for (auto &Entry : Statistics) {
9150b57cec5SDimitry Andric     PerFunctionStats &Stats = Entry.getValue();
916349cc55cSDimitry Andric     uint64_t TotalVars = Stats.VarsInFunction.size() *
9175ffd83dbSDimitry Andric                          (Stats.NumFnInlined + Stats.NumFnOutOfLine);
9185ffd83dbSDimitry Andric     // Count variables in global scope.
9195ffd83dbSDimitry Andric     if (!Stats.IsFunction)
9205ffd83dbSDimitry Andric       TotalVars =
9215ffd83dbSDimitry Andric           Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
922349cc55cSDimitry Andric     uint64_t Constants = Stats.ConstantMembers;
9230b57cec5SDimitry Andric     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
9240b57cec5SDimitry Andric     VarParamTotal += TotalVars;
9250b57cec5SDimitry Andric     VarParamUnique += Stats.VarsInFunction.size();
9260b57cec5SDimitry Andric     LLVM_DEBUG(for (auto &V
9270b57cec5SDimitry Andric                     : Stats.VarsInFunction) llvm::dbgs()
9280b57cec5SDimitry Andric                << Entry.getKey() << ": " << V.getKey() << "\n");
9290b57cec5SDimitry Andric     NumFunctions += Stats.IsFunction;
9300b57cec5SDimitry Andric     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
9310b57cec5SDimitry Andric     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
9320b57cec5SDimitry Andric     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
9330b57cec5SDimitry Andric     ParamTotal += Stats.NumParams;
9340b57cec5SDimitry Andric     ParamWithType += Stats.NumParamTypes;
9350b57cec5SDimitry Andric     ParamWithLoc += Stats.NumParamLocations;
9360b57cec5SDimitry Andric     ParamWithSrcLoc += Stats.NumParamSourceLocations;
9375ffd83dbSDimitry Andric     LocalVarTotal += Stats.NumLocalVars;
9385ffd83dbSDimitry Andric     LocalVarWithType += Stats.NumLocalVarTypes;
9395ffd83dbSDimitry Andric     LocalVarWithLoc += Stats.NumLocalVarLocations;
9405ffd83dbSDimitry Andric     LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
9410b57cec5SDimitry Andric   }
9420b57cec5SDimitry Andric 
9430b57cec5SDimitry Andric   // Print summary.
9440b57cec5SDimitry Andric   OS.SetBufferSize(1024);
945e8d8bef9SDimitry Andric   json::OStream J(OS, 2);
946e8d8bef9SDimitry Andric   J.objectBegin();
947e8d8bef9SDimitry Andric   J.attribute("version", Version);
9480b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
9490b57cec5SDimitry Andric              llvm::dbgs() << "---------------------------------\n");
9505ffd83dbSDimitry Andric 
951e8d8bef9SDimitry Andric   printDatum(J, "file", Filename.str());
952e8d8bef9SDimitry Andric   printDatum(J, "format", FormatName);
9535ffd83dbSDimitry Andric 
954349cc55cSDimitry Andric   printDatum(J, "#functions", NumFunctions.Value);
955349cc55cSDimitry Andric   printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
956349cc55cSDimitry Andric   printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
957349cc55cSDimitry Andric   printDatum(J, "#inlined functions with abstract origins",
958349cc55cSDimitry Andric              NumAbstractOrigins.Value);
9595ffd83dbSDimitry Andric 
9605ffd83dbSDimitry Andric   // This includes local variables and formal parameters.
961349cc55cSDimitry Andric   printDatum(J, "#unique source variables", VarParamUnique.Value);
962349cc55cSDimitry Andric   printDatum(J, "#source variables", VarParamTotal.Value);
963349cc55cSDimitry Andric   printDatum(J, "#source variables with location", VarParamWithLoc.Value);
9645ffd83dbSDimitry Andric 
965349cc55cSDimitry Andric   printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
966349cc55cSDimitry Andric   printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
967349cc55cSDimitry Andric   printDatum(J, "#call site parameter DIEs",
968349cc55cSDimitry Andric              GlobalStats.CallSiteParamDIEs.Value);
9695ffd83dbSDimitry Andric 
970e8d8bef9SDimitry Andric   printDatum(J, "sum_all_variables(#bytes in parent scope)",
971349cc55cSDimitry Andric              GlobalStats.ScopeBytes.Value);
972e8d8bef9SDimitry Andric   printDatum(J,
973e8d8bef9SDimitry Andric              "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
974349cc55cSDimitry Andric              GlobalStats.TotalBytesCovered.Value);
975e8d8bef9SDimitry Andric   printDatum(J,
9765ffd83dbSDimitry Andric              "sum_all_variables(#bytes in parent scope covered by "
9775ffd83dbSDimitry Andric              "DW_AT_location)",
978349cc55cSDimitry Andric              GlobalStats.ScopeBytesCovered.Value);
979e8d8bef9SDimitry Andric   printDatum(J,
9805ffd83dbSDimitry Andric              "sum_all_variables(#bytes in parent scope covered by "
9815ffd83dbSDimitry Andric              "DW_OP_entry_value)",
982349cc55cSDimitry Andric              GlobalStats.ScopeEntryValueBytesCovered.Value);
9835ffd83dbSDimitry Andric 
984e8d8bef9SDimitry Andric   printDatum(J, "sum_all_params(#bytes in parent scope)",
985349cc55cSDimitry Andric              GlobalStats.ParamScopeBytes.Value);
986e8d8bef9SDimitry Andric   printDatum(J,
9875ffd83dbSDimitry Andric              "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
988349cc55cSDimitry Andric              GlobalStats.ParamScopeBytesCovered.Value);
989e8d8bef9SDimitry Andric   printDatum(J,
9905ffd83dbSDimitry Andric              "sum_all_params(#bytes in parent scope covered by "
9915ffd83dbSDimitry Andric              "DW_OP_entry_value)",
992349cc55cSDimitry Andric              GlobalStats.ParamScopeEntryValueBytesCovered.Value);
9935ffd83dbSDimitry Andric 
994e8d8bef9SDimitry Andric   printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
995349cc55cSDimitry Andric              GlobalStats.LocalVarScopeBytes.Value);
996e8d8bef9SDimitry Andric   printDatum(J,
9975ffd83dbSDimitry Andric              "sum_all_local_vars(#bytes in parent scope covered by "
9985ffd83dbSDimitry Andric              "DW_AT_location)",
999349cc55cSDimitry Andric              GlobalStats.LocalVarScopeBytesCovered.Value);
1000e8d8bef9SDimitry Andric   printDatum(J,
10015ffd83dbSDimitry Andric              "sum_all_local_vars(#bytes in parent scope covered by "
10025ffd83dbSDimitry Andric              "DW_OP_entry_value)",
1003349cc55cSDimitry Andric              GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
10045ffd83dbSDimitry Andric 
1005349cc55cSDimitry Andric   printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
1006e8d8bef9SDimitry Andric   printDatum(J, "#bytes within inlined functions",
1007349cc55cSDimitry Andric              GlobalStats.InlineFunctionSize.Value);
10085ffd83dbSDimitry Andric 
10095ffd83dbSDimitry Andric   // Print the summary for formal parameters.
1010349cc55cSDimitry Andric   printDatum(J, "#params", ParamTotal.Value);
1011349cc55cSDimitry Andric   printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
1012349cc55cSDimitry Andric   printDatum(J, "#params with type", ParamWithType.Value);
1013349cc55cSDimitry Andric   printDatum(J, "#params with binary location", ParamWithLoc.Value);
10145ffd83dbSDimitry Andric 
10155ffd83dbSDimitry Andric   // Print the summary for local variables.
1016349cc55cSDimitry Andric   printDatum(J, "#local vars", LocalVarTotal.Value);
1017349cc55cSDimitry Andric   printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
1018349cc55cSDimitry Andric   printDatum(J, "#local vars with type", LocalVarWithType.Value);
1019349cc55cSDimitry Andric   printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
10205ffd83dbSDimitry Andric 
10215ffd83dbSDimitry Andric   // Print the debug section sizes.
1022e8d8bef9SDimitry Andric   printSectionSizes(J, Sizes);
10235ffd83dbSDimitry Andric 
10245ffd83dbSDimitry Andric   // Print the location statistics for variables (includes local variables
10255ffd83dbSDimitry Andric   // and formal parameters).
1026e8d8bef9SDimitry Andric   printDatum(J, "#variables processed by location statistics",
1027349cc55cSDimitry Andric              LocStats.NumVarParam.Value);
1028e8d8bef9SDimitry Andric   printLocationStats(J, "#variables", LocStats.VarParamLocStats);
1029e8d8bef9SDimitry Andric   printLocationStats(J, "#variables - entry values",
10308bcb0991SDimitry Andric                      LocStats.VarParamNonEntryValLocStats);
10315ffd83dbSDimitry Andric 
10325ffd83dbSDimitry Andric   // Print the location statistics for formal parameters.
1033349cc55cSDimitry Andric   printDatum(J, "#params processed by location statistics",
1034349cc55cSDimitry Andric              LocStats.NumParam.Value);
1035e8d8bef9SDimitry Andric   printLocationStats(J, "#params", LocStats.ParamLocStats);
1036e8d8bef9SDimitry Andric   printLocationStats(J, "#params - entry values",
10378bcb0991SDimitry Andric                      LocStats.ParamNonEntryValLocStats);
10385ffd83dbSDimitry Andric 
10395ffd83dbSDimitry Andric   // Print the location statistics for local variables.
1040e8d8bef9SDimitry Andric   printDatum(J, "#local vars processed by location statistics",
1041349cc55cSDimitry Andric              LocStats.NumVar.Value);
1042e8d8bef9SDimitry Andric   printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
1043e8d8bef9SDimitry Andric   printLocationStats(J, "#local vars - entry values",
10445ffd83dbSDimitry Andric                      LocStats.LocalVarNonEntryValLocStats);
1045e8d8bef9SDimitry Andric   J.objectEnd();
1046e8d8bef9SDimitry Andric   OS << '\n';
1047*81ad6265SDimitry Andric   LLVM_DEBUG(
1048*81ad6265SDimitry Andric       llvm::dbgs() << "Total Availability: "
1049*81ad6265SDimitry Andric                    << (VarParamTotal.Value
1050*81ad6265SDimitry Andric                            ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1051349cc55cSDimitry Andric                                              VarParamTotal.Value)
1052*81ad6265SDimitry Andric                            : 0)
10530b57cec5SDimitry Andric                    << "%\n";
10540b57cec5SDimitry Andric       llvm::dbgs() << "PC Ranges covered: "
1055*81ad6265SDimitry Andric                    << (GlobalStats.ScopeBytes.Value
1056*81ad6265SDimitry Andric                            ? (int)std::round(
1057349cc55cSDimitry Andric                                  (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1058349cc55cSDimitry Andric                                  GlobalStats.ScopeBytes.Value)
1059*81ad6265SDimitry Andric                            : 0)
10600b57cec5SDimitry Andric                    << "%\n");
10610b57cec5SDimitry Andric   return true;
10620b57cec5SDimitry Andric }
1063