xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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"
11*0fca6ea1SDimitry Andric #include "llvm/ADT/DenseSet.h"
120b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h"
130b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h"
140b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
1581ad6265SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
160b57cec5SDimitry Andric #include "llvm/Object/ObjectFile.h"
178bcb0991SDimitry Andric #include "llvm/Support/JSON.h"
180b57cec5SDimitry Andric 
190b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfdump"
200b57cec5SDimitry Andric using namespace llvm;
215ffd83dbSDimitry Andric using namespace llvm::dwarfdump;
225ffd83dbSDimitry Andric using namespace llvm::object;
230b57cec5SDimitry Andric 
24fe6060f1SDimitry Andric namespace {
258bcb0991SDimitry Andric /// This represents the number of categories of debug location coverage being
268bcb0991SDimitry Andric /// calculated. The first category is the number of variables with 0% location
278bcb0991SDimitry Andric /// coverage, but the last category is the number of variables with 100%
288bcb0991SDimitry Andric /// location coverage.
298bcb0991SDimitry Andric constexpr int NumOfCoverageCategories = 12;
308bcb0991SDimitry Andric 
31fe6060f1SDimitry Andric /// This is used for zero location coverage bucket.
32fe6060f1SDimitry Andric constexpr unsigned ZeroCoverageBucket = 0;
33fe6060f1SDimitry Andric 
34349cc55cSDimitry Andric /// The UINT64_MAX is used as an indication of the overflow.
35349cc55cSDimitry Andric constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
36349cc55cSDimitry Andric 
37fe6060f1SDimitry Andric /// This represents variables DIE offsets.
38fe6060f1SDimitry Andric using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
39fe6060f1SDimitry Andric /// This maps function DIE offset to its variables.
40fe6060f1SDimitry Andric using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
41fe6060f1SDimitry Andric /// This represents function DIE offsets containing an abstract_origin.
42fe6060f1SDimitry Andric using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
43fe6060f1SDimitry Andric 
44349cc55cSDimitry Andric /// This represents a data type for the stats and it helps us to
45349cc55cSDimitry Andric /// detect an overflow.
46349cc55cSDimitry Andric /// NOTE: This can be implemented as a template if there is an another type
47349cc55cSDimitry Andric /// needing this.
48349cc55cSDimitry Andric struct SaturatingUINT64 {
49349cc55cSDimitry Andric   /// Number that represents the stats.
50349cc55cSDimitry Andric   uint64_t Value;
51349cc55cSDimitry Andric 
52349cc55cSDimitry Andric   SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
53349cc55cSDimitry Andric 
54349cc55cSDimitry Andric   void operator++(int) { return *this += 1; }
55349cc55cSDimitry Andric   void operator+=(uint64_t Value_) {
56349cc55cSDimitry Andric     if (Value != OverflowValue) {
57349cc55cSDimitry Andric       if (Value < OverflowValue - Value_)
58349cc55cSDimitry Andric         Value += Value_;
59349cc55cSDimitry Andric       else
60349cc55cSDimitry Andric         Value = OverflowValue;
61349cc55cSDimitry Andric     }
62349cc55cSDimitry Andric   }
63349cc55cSDimitry Andric };
64349cc55cSDimitry Andric 
654824e7fdSDimitry Andric /// Utility struct to store the full location of a DIE - its CU and offset.
664824e7fdSDimitry Andric struct DIELocation {
674824e7fdSDimitry Andric   DWARFUnit *DwUnit;
684824e7fdSDimitry Andric   uint64_t DIEOffset;
694824e7fdSDimitry Andric   DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
704824e7fdSDimitry Andric       : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
714824e7fdSDimitry Andric };
724824e7fdSDimitry Andric /// This represents DWARF locations of CrossCU referencing DIEs.
734824e7fdSDimitry Andric using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
744824e7fdSDimitry Andric 
754824e7fdSDimitry Andric /// This maps function DIE offset to its DWARF CU.
764824e7fdSDimitry Andric using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
774824e7fdSDimitry Andric 
780b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and
790b57cec5SDimitry Andric /// contains variables, such as a compile unit).
800b57cec5SDimitry Andric struct PerFunctionStats {
810b57cec5SDimitry Andric   /// Number of inlined instances of this function.
82349cc55cSDimitry Andric   uint64_t NumFnInlined = 0;
835ffd83dbSDimitry Andric   /// Number of out-of-line instances of this function.
84349cc55cSDimitry Andric   uint64_t NumFnOutOfLine = 0;
850b57cec5SDimitry Andric   /// Number of inlined instances that have abstract origins.
86349cc55cSDimitry Andric   uint64_t NumAbstractOrigins = 0;
870b57cec5SDimitry Andric   /// Number of variables and parameters with location across all inlined
880b57cec5SDimitry Andric   /// instances.
89349cc55cSDimitry Andric   uint64_t TotalVarWithLoc = 0;
900b57cec5SDimitry Andric   /// Number of constants with location across all inlined instances.
91349cc55cSDimitry Andric   uint64_t ConstantMembers = 0;
925ffd83dbSDimitry Andric   /// Number of arificial variables, parameters or members across all instances.
93349cc55cSDimitry Andric   uint64_t NumArtificial = 0;
940b57cec5SDimitry Andric   /// List of all Variables and parameters in this function.
950b57cec5SDimitry Andric   StringSet<> VarsInFunction;
960b57cec5SDimitry Andric   /// Compile units also cover a PC range, but have this flag set to false.
970b57cec5SDimitry Andric   bool IsFunction = false;
980b57cec5SDimitry Andric   /// Function has source location information.
990b57cec5SDimitry Andric   bool HasSourceLocation = false;
1000b57cec5SDimitry Andric   /// Number of function parameters.
101349cc55cSDimitry Andric   uint64_t NumParams = 0;
1020b57cec5SDimitry Andric   /// Number of function parameters with source location.
103349cc55cSDimitry Andric   uint64_t NumParamSourceLocations = 0;
1040b57cec5SDimitry Andric   /// Number of function parameters with type.
105349cc55cSDimitry Andric   uint64_t NumParamTypes = 0;
1060b57cec5SDimitry Andric   /// Number of function parameters with a DW_AT_location.
107349cc55cSDimitry Andric   uint64_t NumParamLocations = 0;
1085ffd83dbSDimitry Andric   /// Number of local variables.
109349cc55cSDimitry Andric   uint64_t NumLocalVars = 0;
1105ffd83dbSDimitry Andric   /// Number of local variables with source location.
111349cc55cSDimitry Andric   uint64_t NumLocalVarSourceLocations = 0;
1125ffd83dbSDimitry Andric   /// Number of local variables with type.
113349cc55cSDimitry Andric   uint64_t NumLocalVarTypes = 0;
1145ffd83dbSDimitry Andric   /// Number of local variables with DW_AT_location.
115349cc55cSDimitry Andric   uint64_t NumLocalVarLocations = 0;
1160b57cec5SDimitry Andric };
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs.
1190b57cec5SDimitry Andric struct GlobalStats {
1200b57cec5SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations.
121349cc55cSDimitry Andric   SaturatingUINT64 TotalBytesCovered = 0;
122e8d8bef9SDimitry Andric   /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
123349cc55cSDimitry Andric   SaturatingUINT64 ScopeBytesCovered = 0;
124480093f4SDimitry Andric   /// Total number of PC range bytes in each variable's enclosing scope.
125349cc55cSDimitry Andric   SaturatingUINT64 ScopeBytes = 0;
1268bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1278bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value).
128349cc55cSDimitry Andric   SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
1298bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations of
1308bcb0991SDimitry Andric   /// formal parameters.
131349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeBytesCovered = 0;
1325ffd83dbSDimitry Andric   /// Total number of PC range bytes in each parameter's enclosing scope.
133349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeBytes = 0;
1348bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1358bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value) (only for parameters).
136349cc55cSDimitry Andric   SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
1378bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations (only for local
1388bcb0991SDimitry Andric   /// variables).
139349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeBytesCovered = 0;
1405ffd83dbSDimitry Andric   /// Total number of PC range bytes in each local variable's enclosing scope.
141349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeBytes = 0;
1428bcb0991SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations with
1438bcb0991SDimitry Andric   /// the debug entry values (DW_OP_entry_value) (only for local variables).
144349cc55cSDimitry Andric   SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
1458bcb0991SDimitry Andric   /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
146349cc55cSDimitry Andric   SaturatingUINT64 CallSiteEntries = 0;
1478bcb0991SDimitry Andric   /// Total number of call site DIEs (DW_TAG_call_site).
148349cc55cSDimitry Andric   SaturatingUINT64 CallSiteDIEs = 0;
1498bcb0991SDimitry Andric   /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
150349cc55cSDimitry Andric   SaturatingUINT64 CallSiteParamDIEs = 0;
1510b57cec5SDimitry Andric   /// Total byte size of concrete functions. This byte size includes
1520b57cec5SDimitry Andric   /// inline functions contained in the concrete functions.
153349cc55cSDimitry Andric   SaturatingUINT64 FunctionSize = 0;
1540b57cec5SDimitry Andric   /// Total byte size of inlined functions. This is the total number of bytes
1550b57cec5SDimitry Andric   /// for the top inline functions within concrete functions. This can help
1560b57cec5SDimitry Andric   /// tune the inline settings when compiling to match user expectations.
157349cc55cSDimitry Andric   SaturatingUINT64 InlineFunctionSize = 0;
1588bcb0991SDimitry Andric };
1598bcb0991SDimitry Andric 
1608bcb0991SDimitry Andric /// Holds accumulated debug location statistics about local variables and
1618bcb0991SDimitry Andric /// formal parameters.
1628bcb0991SDimitry Andric struct LocationStats {
1638bcb0991SDimitry Andric   /// Map the scope coverage decile to the number of variables in the decile.
1648bcb0991SDimitry Andric   /// The first element of the array (at the index zero) represents the number
1658bcb0991SDimitry Andric   /// of variables with the no debug location at all, but the last element
1668bcb0991SDimitry Andric   /// in the vector represents the number of fully covered variables within
1678bcb0991SDimitry Andric   /// its scope.
168349cc55cSDimitry Andric   std::vector<SaturatingUINT64> VarParamLocStats{
169349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1708bcb0991SDimitry Andric   /// Map non debug entry values coverage.
171349cc55cSDimitry Andric   std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
172349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1738bcb0991SDimitry Andric   /// The debug location statistics for formal parameters.
174349cc55cSDimitry Andric   std::vector<SaturatingUINT64> ParamLocStats{
175349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1768bcb0991SDimitry Andric   /// Map non debug entry values coverage for formal parameters.
177349cc55cSDimitry Andric   std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
178349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1798bcb0991SDimitry Andric   /// The debug location statistics for local variables.
180349cc55cSDimitry Andric   std::vector<SaturatingUINT64> LocalVarLocStats{
181349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1828bcb0991SDimitry Andric   /// Map non debug entry values coverage for local variables.
183349cc55cSDimitry Andric   std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
184349cc55cSDimitry Andric       std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
1858bcb0991SDimitry Andric   /// Total number of local variables and function parameters processed.
186349cc55cSDimitry Andric   SaturatingUINT64 NumVarParam = 0;
1878bcb0991SDimitry Andric   /// Total number of formal parameters processed.
188349cc55cSDimitry Andric   SaturatingUINT64 NumParam = 0;
1898bcb0991SDimitry Andric   /// Total number of local variables processed.
190349cc55cSDimitry Andric   SaturatingUINT64 NumVar = 0;
1910b57cec5SDimitry Andric };
192*0fca6ea1SDimitry Andric 
193*0fca6ea1SDimitry Andric /// Holds accumulated debug line statistics across all CUs.
194*0fca6ea1SDimitry Andric struct LineStats {
195*0fca6ea1SDimitry Andric   SaturatingUINT64 NumBytes = 0;
196*0fca6ea1SDimitry Andric   SaturatingUINT64 NumLineZeroBytes = 0;
197*0fca6ea1SDimitry Andric   SaturatingUINT64 NumEntries = 0;
198*0fca6ea1SDimitry Andric   SaturatingUINT64 NumIsStmtEntries = 0;
199*0fca6ea1SDimitry Andric   SaturatingUINT64 NumUniqueEntries = 0;
200*0fca6ea1SDimitry Andric   SaturatingUINT64 NumUniqueNonZeroEntries = 0;
201*0fca6ea1SDimitry Andric };
2025ffd83dbSDimitry Andric } // namespace
2030b57cec5SDimitry Andric 
2048bcb0991SDimitry Andric /// Collect debug location statistics for one DIE.
205e8d8bef9SDimitry Andric static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
206349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &VarParamLocStats,
207349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &ParamLocStats,
208349cc55cSDimitry Andric                             std::vector<SaturatingUINT64> &LocalVarLocStats,
2095ffd83dbSDimitry Andric                             bool IsParam, bool IsLocalVar) {
210e8d8bef9SDimitry Andric   auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
2118bcb0991SDimitry Andric     // No debug location at all for the variable.
212e8d8bef9SDimitry Andric     if (ScopeBytesCovered == 0)
2138bcb0991SDimitry Andric       return 0;
2148bcb0991SDimitry Andric     // Fully covered variable within its scope.
215e8d8bef9SDimitry Andric     if (ScopeBytesCovered >= BytesInScope)
2168bcb0991SDimitry Andric       return NumOfCoverageCategories - 1;
2178bcb0991SDimitry Andric     // Get covered range (e.g. 20%-29%).
218e8d8bef9SDimitry Andric     unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
2198bcb0991SDimitry Andric     LocBucket /= 10;
2208bcb0991SDimitry Andric     return LocBucket + 1;
2218bcb0991SDimitry Andric   };
2228bcb0991SDimitry Andric 
2238bcb0991SDimitry Andric   unsigned CoverageBucket = getCoverageBucket();
224fe6060f1SDimitry Andric 
225349cc55cSDimitry Andric   VarParamLocStats[CoverageBucket].Value++;
2268bcb0991SDimitry Andric   if (IsParam)
227349cc55cSDimitry Andric     ParamLocStats[CoverageBucket].Value++;
2288bcb0991SDimitry Andric   else if (IsLocalVar)
229349cc55cSDimitry Andric     LocalVarLocStats[CoverageBucket].Value++;
2305ffd83dbSDimitry Andric }
231fe6060f1SDimitry Andric 
2325ffd83dbSDimitry Andric /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
2335ffd83dbSDimitry Andric /// and DeclLine. The identifier aims to be unique for any unique entities,
2345ffd83dbSDimitry Andric /// but keeping the same among different instances of the same entity.
2355ffd83dbSDimitry Andric static std::string constructDieID(DWARFDie Die,
2365ffd83dbSDimitry Andric                                   StringRef Prefix = StringRef()) {
2375ffd83dbSDimitry Andric   std::string IDStr;
2385ffd83dbSDimitry Andric   llvm::raw_string_ostream ID(IDStr);
2395ffd83dbSDimitry Andric   ID << Prefix
2405ffd83dbSDimitry Andric      << Die.getName(DINameKind::LinkageName);
2415ffd83dbSDimitry Andric 
2425ffd83dbSDimitry Andric   // Prefix + Name is enough for local variables and parameters.
243*0fca6ea1SDimitry Andric   if (!Prefix.empty() && Prefix != "g")
2445ffd83dbSDimitry Andric     return ID.str();
2455ffd83dbSDimitry Andric 
2465ffd83dbSDimitry Andric   auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
2475ffd83dbSDimitry Andric   std::string File;
2485ffd83dbSDimitry Andric   if (DeclFile) {
2495ffd83dbSDimitry Andric     DWARFUnit *U = Die.getDwarfUnit();
2505ffd83dbSDimitry Andric     if (const auto *LT = U->getContext().getLineTableForUnit(U))
2515ffd83dbSDimitry Andric       if (LT->getFileNameByIndex(
2525ffd83dbSDimitry Andric               dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
2535ffd83dbSDimitry Andric               DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
2545ffd83dbSDimitry Andric         File = std::string(sys::path::filename(File));
2555ffd83dbSDimitry Andric   }
2565ffd83dbSDimitry Andric   ID << ":" << (File.empty() ? "/" : File);
2575ffd83dbSDimitry Andric   ID << ":"
2585ffd83dbSDimitry Andric      << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
2595ffd83dbSDimitry Andric   return ID.str();
2608bcb0991SDimitry Andric }
2618bcb0991SDimitry Andric 
262e8d8bef9SDimitry Andric /// Return the number of bytes in the overlap of ranges A and B.
263e8d8bef9SDimitry Andric static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
264e8d8bef9SDimitry Andric   uint64_t Lower = std::max(A.LowPC, B.LowPC);
265e8d8bef9SDimitry Andric   uint64_t Upper = std::min(A.HighPC, B.HighPC);
266e8d8bef9SDimitry Andric   if (Lower >= Upper)
267e8d8bef9SDimitry Andric     return 0;
268e8d8bef9SDimitry Andric   return Upper - Lower;
269e8d8bef9SDimitry Andric }
270e8d8bef9SDimitry Andric 
2710b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE.
272fe6060f1SDimitry Andric static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
273fe6060f1SDimitry Andric                                const std::string &VarPrefix,
274fe6060f1SDimitry Andric                                uint64_t BytesInScope, uint32_t InlineDepth,
2750b57cec5SDimitry Andric                                StringMap<PerFunctionStats> &FnStatMap,
2768bcb0991SDimitry Andric                                GlobalStats &GlobalStats,
277fe6060f1SDimitry Andric                                LocationStats &LocStats,
278fe6060f1SDimitry Andric                                AbstractOriginVarsTy *AbstractOriginVariables) {
279fe6060f1SDimitry Andric   const dwarf::Tag Tag = Die.getTag();
280fe6060f1SDimitry Andric   // Skip CU node.
281fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_compile_unit)
282fe6060f1SDimitry Andric     return;
283fe6060f1SDimitry Andric 
2840b57cec5SDimitry Andric   bool HasLoc = false;
2850b57cec5SDimitry Andric   bool HasSrcLoc = false;
2860b57cec5SDimitry Andric   bool HasType = false;
287e8d8bef9SDimitry Andric   uint64_t TotalBytesCovered = 0;
288e8d8bef9SDimitry Andric   uint64_t ScopeBytesCovered = 0;
2898bcb0991SDimitry Andric   uint64_t BytesEntryValuesCovered = 0;
2908bcb0991SDimitry Andric   auto &FnStats = FnStatMap[FnPrefix];
291fe6060f1SDimitry Andric   bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
292fe6060f1SDimitry Andric   bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
293fe6060f1SDimitry Andric   bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
2945ffd83dbSDimitry Andric                           Die.find(dwarf::DW_AT_const_value);
2950b57cec5SDimitry Andric 
296fe6060f1SDimitry Andric   // For zero covered inlined variables the locstats will be
297fe6060f1SDimitry Andric   // calculated later.
298fe6060f1SDimitry Andric   bool DeferLocStats = false;
299fe6060f1SDimitry Andric 
300fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
3018bcb0991SDimitry Andric     GlobalStats.CallSiteDIEs++;
3020b57cec5SDimitry Andric     return;
3030b57cec5SDimitry Andric   }
3040b57cec5SDimitry Andric 
305fe6060f1SDimitry Andric   if (Tag == dwarf::DW_TAG_call_site_parameter ||
306fe6060f1SDimitry Andric       Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
3078bcb0991SDimitry Andric     GlobalStats.CallSiteParamDIEs++;
3088bcb0991SDimitry Andric     return;
3098bcb0991SDimitry Andric   }
3108bcb0991SDimitry Andric 
3115ffd83dbSDimitry Andric   if (!IsParam && !IsLocalVar && !IsConstantMember) {
3120b57cec5SDimitry Andric     // Not a variable or constant member.
3130b57cec5SDimitry Andric     return;
3140b57cec5SDimitry Andric   }
3150b57cec5SDimitry Andric 
3165ffd83dbSDimitry Andric   // Ignore declarations of global variables.
3175ffd83dbSDimitry Andric   if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
3185ffd83dbSDimitry Andric     return;
3195ffd83dbSDimitry Andric 
3200b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
3210b57cec5SDimitry Andric       Die.findRecursively(dwarf::DW_AT_decl_line))
3220b57cec5SDimitry Andric     HasSrcLoc = true;
3230b57cec5SDimitry Andric 
3240b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_type))
3250b57cec5SDimitry Andric     HasType = true;
3260b57cec5SDimitry Andric 
327fe6060f1SDimitry Andric   if (Die.find(dwarf::DW_AT_abstract_origin)) {
328fe6060f1SDimitry Andric     if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
329fe6060f1SDimitry Andric       if (AbstractOriginVariables) {
330fe6060f1SDimitry Andric         auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
331fe6060f1SDimitry Andric         // Do not track this variable any more, since it has location
332fe6060f1SDimitry Andric         // coverage.
3335f757f3fSDimitry Andric         llvm::erase(*AbstractOriginVariables, (*Offset).getRawUValue());
334fe6060f1SDimitry Andric       }
335fe6060f1SDimitry Andric     } else {
336fe6060f1SDimitry Andric       // The locstats will be handled at the end of
337fe6060f1SDimitry Andric       // the collectStatsRecursive().
338fe6060f1SDimitry Andric       DeferLocStats = true;
339fe6060f1SDimitry Andric     }
340fe6060f1SDimitry Andric   }
341fe6060f1SDimitry Andric 
3428bcb0991SDimitry Andric   auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
3438bcb0991SDimitry Andric     DWARFUnit *U = Die.getDwarfUnit();
3448bcb0991SDimitry Andric     DataExtractor Data(toStringRef(D),
3458bcb0991SDimitry Andric                        Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
3465ffd83dbSDimitry Andric     DWARFExpression Expression(Data, U->getAddressByteSize(),
3475ffd83dbSDimitry Andric                                U->getFormParams().Format);
3488bcb0991SDimitry Andric     // Consider the expression containing the DW_OP_entry_value as
3498bcb0991SDimitry Andric     // an entry value.
350349cc55cSDimitry Andric     return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
3518bcb0991SDimitry Andric       return Op.getCode() == dwarf::DW_OP_entry_value ||
3528bcb0991SDimitry Andric              Op.getCode() == dwarf::DW_OP_GNU_entry_value;
3538bcb0991SDimitry Andric     });
3548bcb0991SDimitry Andric   };
3558bcb0991SDimitry Andric 
3560b57cec5SDimitry Andric   if (Die.find(dwarf::DW_AT_const_value)) {
3570b57cec5SDimitry Andric     // This catches constant members *and* variables.
3580b57cec5SDimitry Andric     HasLoc = true;
359e8d8bef9SDimitry Andric     ScopeBytesCovered = BytesInScope;
360e8d8bef9SDimitry Andric     TotalBytesCovered = BytesInScope;
3610b57cec5SDimitry Andric   } else {
3620b57cec5SDimitry Andric     // Handle variables and function arguments.
363480093f4SDimitry Andric     Expected<std::vector<DWARFLocationExpression>> Loc =
364480093f4SDimitry Andric         Die.getLocations(dwarf::DW_AT_location);
365480093f4SDimitry Andric     if (!Loc) {
366480093f4SDimitry Andric       consumeError(Loc.takeError());
3670b57cec5SDimitry Andric     } else {
368480093f4SDimitry Andric       HasLoc = true;
369480093f4SDimitry Andric       // Get PC coverage.
370480093f4SDimitry Andric       auto Default = find_if(
371480093f4SDimitry Andric           *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
372480093f4SDimitry Andric       if (Default != Loc->end()) {
3730b57cec5SDimitry Andric         // Assume the entire range is covered by a single location.
374e8d8bef9SDimitry Andric         ScopeBytesCovered = BytesInScope;
375e8d8bef9SDimitry Andric         TotalBytesCovered = BytesInScope;
376480093f4SDimitry Andric       } else {
377e8d8bef9SDimitry Andric         // Caller checks this Expected result already, it cannot fail.
378e8d8bef9SDimitry Andric         auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
379480093f4SDimitry Andric         for (auto Entry : *Loc) {
380e8d8bef9SDimitry Andric           TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
381e8d8bef9SDimitry Andric           uint64_t ScopeBytesCoveredByEntry = 0;
382e8d8bef9SDimitry Andric           // Calculate how many bytes of the parent scope this entry covers.
383e8d8bef9SDimitry Andric           // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
384e8d8bef9SDimitry Andric           // address ranges defined by the bounded location descriptions of a
385e8d8bef9SDimitry Andric           // location list may overlap". So in theory a variable can have
386e8d8bef9SDimitry Andric           // multiple simultaneous locations, which would make this calculation
387e8d8bef9SDimitry Andric           // misleading because we will count the overlapped areas
388e8d8bef9SDimitry Andric           // twice. However, clang does not currently emit DWARF like this.
389e8d8bef9SDimitry Andric           for (DWARFAddressRange R : ScopeRanges) {
390e8d8bef9SDimitry Andric             ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
391e8d8bef9SDimitry Andric           }
392e8d8bef9SDimitry Andric           ScopeBytesCovered += ScopeBytesCoveredByEntry;
393480093f4SDimitry Andric           if (IsEntryValue(Entry.Expr))
394e8d8bef9SDimitry Andric             BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
395480093f4SDimitry Andric         }
3960b57cec5SDimitry Andric       }
3970b57cec5SDimitry Andric     }
3980b57cec5SDimitry Andric   }
3990b57cec5SDimitry Andric 
4008bcb0991SDimitry Andric   // Calculate the debug location statistics.
401fe6060f1SDimitry Andric   if (BytesInScope && !DeferLocStats) {
402349cc55cSDimitry Andric     LocStats.NumVarParam.Value++;
4038bcb0991SDimitry Andric     if (IsParam)
404349cc55cSDimitry Andric       LocStats.NumParam.Value++;
4058bcb0991SDimitry Andric     else if (IsLocalVar)
406349cc55cSDimitry Andric       LocStats.NumVar.Value++;
4078bcb0991SDimitry Andric 
408e8d8bef9SDimitry Andric     collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
4095ffd83dbSDimitry Andric                     LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
4108bcb0991SDimitry Andric                     IsLocalVar);
4118bcb0991SDimitry Andric     // Non debug entry values coverage statistics.
412e8d8bef9SDimitry Andric     collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
4138bcb0991SDimitry Andric                     LocStats.VarParamNonEntryValLocStats,
4148bcb0991SDimitry Andric                     LocStats.ParamNonEntryValLocStats,
4155ffd83dbSDimitry Andric                     LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
4168bcb0991SDimitry Andric   }
4178bcb0991SDimitry Andric 
4180b57cec5SDimitry Andric   // Collect PC range coverage data.
4190b57cec5SDimitry Andric   if (DWARFDie D =
4200b57cec5SDimitry Andric           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
4210b57cec5SDimitry Andric     Die = D;
4225ffd83dbSDimitry Andric 
4235ffd83dbSDimitry Andric   std::string VarID = constructDieID(Die, VarPrefix);
4245ffd83dbSDimitry Andric   FnStats.VarsInFunction.insert(VarID);
4255ffd83dbSDimitry Andric 
426e8d8bef9SDimitry Andric   GlobalStats.TotalBytesCovered += TotalBytesCovered;
4270b57cec5SDimitry Andric   if (BytesInScope) {
428e8d8bef9SDimitry Andric     GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
429480093f4SDimitry Andric     GlobalStats.ScopeBytes += BytesInScope;
4308bcb0991SDimitry Andric     GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
4318bcb0991SDimitry Andric     if (IsParam) {
432e8d8bef9SDimitry Andric       GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
433480093f4SDimitry Andric       GlobalStats.ParamScopeBytes += BytesInScope;
4348bcb0991SDimitry Andric       GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
4358bcb0991SDimitry Andric     } else if (IsLocalVar) {
436e8d8bef9SDimitry Andric       GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
4375ffd83dbSDimitry Andric       GlobalStats.LocalVarScopeBytes += BytesInScope;
4385ffd83dbSDimitry Andric       GlobalStats.LocalVarScopeEntryValueBytesCovered +=
4395ffd83dbSDimitry Andric           BytesEntryValuesCovered;
4408bcb0991SDimitry Andric     }
441349cc55cSDimitry Andric     assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
4420b57cec5SDimitry Andric   }
4435ffd83dbSDimitry Andric 
4445ffd83dbSDimitry Andric   if (IsConstantMember) {
4455ffd83dbSDimitry Andric     FnStats.ConstantMembers++;
4465ffd83dbSDimitry Andric     return;
4475ffd83dbSDimitry Andric   }
4485ffd83dbSDimitry Andric 
4495ffd83dbSDimitry Andric   FnStats.TotalVarWithLoc += (unsigned)HasLoc;
4505ffd83dbSDimitry Andric 
4515ffd83dbSDimitry Andric   if (Die.find(dwarf::DW_AT_artificial)) {
4525ffd83dbSDimitry Andric     FnStats.NumArtificial++;
4535ffd83dbSDimitry Andric     return;
4545ffd83dbSDimitry Andric   }
4555ffd83dbSDimitry Andric 
4568bcb0991SDimitry Andric   if (IsParam) {
4570b57cec5SDimitry Andric     FnStats.NumParams++;
4580b57cec5SDimitry Andric     if (HasType)
4590b57cec5SDimitry Andric       FnStats.NumParamTypes++;
4600b57cec5SDimitry Andric     if (HasSrcLoc)
4610b57cec5SDimitry Andric       FnStats.NumParamSourceLocations++;
4620b57cec5SDimitry Andric     if (HasLoc)
4630b57cec5SDimitry Andric       FnStats.NumParamLocations++;
4648bcb0991SDimitry Andric   } else if (IsLocalVar) {
4655ffd83dbSDimitry Andric     FnStats.NumLocalVars++;
4660b57cec5SDimitry Andric     if (HasType)
4675ffd83dbSDimitry Andric       FnStats.NumLocalVarTypes++;
4680b57cec5SDimitry Andric     if (HasSrcLoc)
4695ffd83dbSDimitry Andric       FnStats.NumLocalVarSourceLocations++;
4700b57cec5SDimitry Andric     if (HasLoc)
4715ffd83dbSDimitry Andric       FnStats.NumLocalVarLocations++;
4720b57cec5SDimitry Andric   }
4730b57cec5SDimitry Andric }
4740b57cec5SDimitry Andric 
475fe6060f1SDimitry Andric /// Recursively collect variables from subprogram with DW_AT_inline attribute.
476fe6060f1SDimitry Andric static void collectAbstractOriginFnInfo(
477fe6060f1SDimitry Andric     DWARFDie Die, uint64_t SPOffset,
4784824e7fdSDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
4794824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
480fe6060f1SDimitry Andric   DWARFDie Child = Die.getFirstChild();
481fe6060f1SDimitry Andric   while (Child) {
482fe6060f1SDimitry Andric     const dwarf::Tag ChildTag = Child.getTag();
483fe6060f1SDimitry Andric     if (ChildTag == dwarf::DW_TAG_formal_parameter ||
4844824e7fdSDimitry Andric         ChildTag == dwarf::DW_TAG_variable) {
485fe6060f1SDimitry Andric       GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
4864824e7fdSDimitry Andric       LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
4874824e7fdSDimitry Andric     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
4884824e7fdSDimitry Andric       collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
4894824e7fdSDimitry Andric                                   LocalAbstractOriginFnInfo);
490fe6060f1SDimitry Andric     Child = Child.getSibling();
491fe6060f1SDimitry Andric   }
492fe6060f1SDimitry Andric }
493fe6060f1SDimitry Andric 
4940b57cec5SDimitry Andric /// Recursively collect debug info quality metrics.
495fe6060f1SDimitry Andric static void collectStatsRecursive(
496fe6060f1SDimitry Andric     DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
497fe6060f1SDimitry Andric     uint64_t BytesInScope, uint32_t InlineDepth,
498fe6060f1SDimitry Andric     StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
4994824e7fdSDimitry Andric     LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
500fe6060f1SDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
5014824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
502fe6060f1SDimitry Andric     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
503fe6060f1SDimitry Andric     AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
504fe6060f1SDimitry Andric   // Skip NULL nodes.
505fe6060f1SDimitry Andric   if (Die.isNULL())
506fe6060f1SDimitry Andric     return;
507fe6060f1SDimitry Andric 
5080b57cec5SDimitry Andric   const dwarf::Tag Tag = Die.getTag();
5095ffd83dbSDimitry Andric   // Skip function types.
5105ffd83dbSDimitry Andric   if (Tag == dwarf::DW_TAG_subroutine_type)
5115ffd83dbSDimitry Andric     return;
5125ffd83dbSDimitry Andric 
5135ffd83dbSDimitry Andric   // Handle any kind of lexical scope.
514bdd1243dSDimitry Andric   const bool HasAbstractOrigin =
515bdd1243dSDimitry Andric       Die.find(dwarf::DW_AT_abstract_origin) != std::nullopt;
5160b57cec5SDimitry Andric   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
5170b57cec5SDimitry Andric   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
5180b57cec5SDimitry Andric   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
519fe6060f1SDimitry Andric   // We want to know how many variables (with abstract_origin) don't have
520fe6060f1SDimitry Andric   // location info.
521fe6060f1SDimitry Andric   const bool IsCandidateForZeroLocCovTracking =
522fe6060f1SDimitry Andric       (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
5230b57cec5SDimitry Andric 
524fe6060f1SDimitry Andric   AbstractOriginVarsTy AbstractOriginVars;
525fe6060f1SDimitry Andric 
526fe6060f1SDimitry Andric   // Get the vars of the inlined fn, so the locstats
527fe6060f1SDimitry Andric   // reports the missing vars (with coverage 0%).
528fe6060f1SDimitry Andric   if (IsCandidateForZeroLocCovTracking) {
529fe6060f1SDimitry Andric     auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
530fe6060f1SDimitry Andric     if (OffsetFn) {
531fe6060f1SDimitry Andric       uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
5324824e7fdSDimitry Andric       if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
5334824e7fdSDimitry Andric         AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
534fe6060f1SDimitry Andric         AbstractOriginVarsPtr = &AbstractOriginVars;
535fe6060f1SDimitry Andric       } else {
5364824e7fdSDimitry Andric         // This means that the DW_AT_inline fn copy is out of order
5374824e7fdSDimitry Andric         // or that the abstract_origin references another CU,
538fe6060f1SDimitry Andric         // so this abstract origin instance will be processed later.
539fe6060f1SDimitry Andric         FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
540fe6060f1SDimitry Andric         AbstractOriginVarsPtr = nullptr;
541fe6060f1SDimitry Andric       }
542fe6060f1SDimitry Andric     }
543fe6060f1SDimitry Andric   }
544fe6060f1SDimitry Andric 
545fe6060f1SDimitry Andric   if (IsFunction || IsInlinedFunction || IsBlock) {
5460b57cec5SDimitry Andric     // Reset VarPrefix when entering a new function.
547fe6060f1SDimitry Andric     if (IsFunction || IsInlinedFunction)
5480b57cec5SDimitry Andric       VarPrefix = "v";
5490b57cec5SDimitry Andric 
5500b57cec5SDimitry Andric     // Ignore forward declarations.
5510b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_declaration))
5520b57cec5SDimitry Andric       return;
5530b57cec5SDimitry Andric 
5540b57cec5SDimitry Andric     // Check for call sites.
5550b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
5560b57cec5SDimitry Andric       GlobalStats.CallSiteEntries++;
5570b57cec5SDimitry Andric 
5580b57cec5SDimitry Andric     // PC Ranges.
5590b57cec5SDimitry Andric     auto RangesOrError = Die.getAddressRanges();
5600b57cec5SDimitry Andric     if (!RangesOrError) {
5610b57cec5SDimitry Andric       llvm::consumeError(RangesOrError.takeError());
5620b57cec5SDimitry Andric       return;
5630b57cec5SDimitry Andric     }
5640b57cec5SDimitry Andric 
5650b57cec5SDimitry Andric     auto Ranges = RangesOrError.get();
5660b57cec5SDimitry Andric     uint64_t BytesInThisScope = 0;
5670b57cec5SDimitry Andric     for (auto Range : Ranges)
5680b57cec5SDimitry Andric       BytesInThisScope += Range.HighPC - Range.LowPC;
5690b57cec5SDimitry Andric 
5700b57cec5SDimitry Andric     // Count the function.
5710b57cec5SDimitry Andric     if (!IsBlock) {
572fe6060f1SDimitry Andric       // Skip over abstract origins, but collect variables
573fe6060f1SDimitry Andric       // from it so it can be used for location statistics
574fe6060f1SDimitry Andric       // for inlined instancies.
575fe6060f1SDimitry Andric       if (Die.find(dwarf::DW_AT_inline)) {
576fe6060f1SDimitry Andric         uint64_t SPOffset = Die.getOffset();
5774824e7fdSDimitry Andric         AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
5784824e7fdSDimitry Andric         collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
5794824e7fdSDimitry Andric                                     LocalAbstractOriginFnInfo);
5800b57cec5SDimitry Andric         return;
581fe6060f1SDimitry Andric       }
582fe6060f1SDimitry Andric 
5835ffd83dbSDimitry Andric       std::string FnID = constructDieID(Die);
5845ffd83dbSDimitry Andric       // We've seen an instance of this function.
5855ffd83dbSDimitry Andric       auto &FnStats = FnStatMap[FnID];
5865ffd83dbSDimitry Andric       FnStats.IsFunction = true;
5870b57cec5SDimitry Andric       if (IsInlinedFunction) {
5880b57cec5SDimitry Andric         FnStats.NumFnInlined++;
5890b57cec5SDimitry Andric         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
5900b57cec5SDimitry Andric           FnStats.NumAbstractOrigins++;
5915ffd83dbSDimitry Andric       } else {
5925ffd83dbSDimitry Andric         FnStats.NumFnOutOfLine++;
5930b57cec5SDimitry Andric       }
5940b57cec5SDimitry Andric       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
5950b57cec5SDimitry Andric           Die.findRecursively(dwarf::DW_AT_decl_line))
5960b57cec5SDimitry Andric         FnStats.HasSourceLocation = true;
5975ffd83dbSDimitry Andric       // Update function prefix.
5985ffd83dbSDimitry Andric       FnPrefix = FnID;
5990b57cec5SDimitry Andric     }
6000b57cec5SDimitry Andric 
6010b57cec5SDimitry Andric     if (BytesInThisScope) {
6020b57cec5SDimitry Andric       BytesInScope = BytesInThisScope;
6030b57cec5SDimitry Andric       if (IsFunction)
6040b57cec5SDimitry Andric         GlobalStats.FunctionSize += BytesInThisScope;
6050b57cec5SDimitry Andric       else if (IsInlinedFunction && InlineDepth == 0)
6060b57cec5SDimitry Andric         GlobalStats.InlineFunctionSize += BytesInThisScope;
6070b57cec5SDimitry Andric     }
6080b57cec5SDimitry Andric   } else {
6090b57cec5SDimitry Andric     // Not a scope, visit the Die itself. It could be a variable.
610480093f4SDimitry Andric     collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
611fe6060f1SDimitry Andric                        FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
6120b57cec5SDimitry Andric   }
6130b57cec5SDimitry Andric 
6140b57cec5SDimitry Andric   // Set InlineDepth correctly for child recursion
6150b57cec5SDimitry Andric   if (IsFunction)
6160b57cec5SDimitry Andric     InlineDepth = 0;
6170b57cec5SDimitry Andric   else if (IsInlinedFunction)
6180b57cec5SDimitry Andric     ++InlineDepth;
6190b57cec5SDimitry Andric 
6200b57cec5SDimitry Andric   // Traverse children.
6210b57cec5SDimitry Andric   unsigned LexicalBlockIndex = 0;
6225ffd83dbSDimitry Andric   unsigned FormalParameterIndex = 0;
6230b57cec5SDimitry Andric   DWARFDie Child = Die.getFirstChild();
6240b57cec5SDimitry Andric   while (Child) {
6250b57cec5SDimitry Andric     std::string ChildVarPrefix = VarPrefix;
6260b57cec5SDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
6270b57cec5SDimitry Andric       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
6285ffd83dbSDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
6295ffd83dbSDimitry Andric       ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
6300b57cec5SDimitry Andric 
631fe6060f1SDimitry Andric     collectStatsRecursive(
632fe6060f1SDimitry Andric         Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
6334824e7fdSDimitry Andric         GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
6344824e7fdSDimitry Andric         LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
6354824e7fdSDimitry Andric         AbstractOriginVarsPtr);
6360b57cec5SDimitry Andric     Child = Child.getSibling();
6370b57cec5SDimitry Andric   }
638fe6060f1SDimitry Andric 
639fe6060f1SDimitry Andric   if (!IsCandidateForZeroLocCovTracking)
640fe6060f1SDimitry Andric     return;
641fe6060f1SDimitry Andric 
642fe6060f1SDimitry Andric   // After we have processed all vars of the inlined function (or function with
643fe6060f1SDimitry Andric   // an abstract_origin), we want to know how many variables have no location.
644fe6060f1SDimitry Andric   for (auto Offset : AbstractOriginVars) {
645fe6060f1SDimitry Andric     LocStats.NumVarParam++;
646fe6060f1SDimitry Andric     LocStats.VarParamLocStats[ZeroCoverageBucket]++;
647fe6060f1SDimitry Andric     auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
648fe6060f1SDimitry Andric     if (!FnDie)
649fe6060f1SDimitry Andric       continue;
650fe6060f1SDimitry Andric     auto Tag = FnDie.getTag();
651fe6060f1SDimitry Andric     if (Tag == dwarf::DW_TAG_formal_parameter) {
652fe6060f1SDimitry Andric       LocStats.NumParam++;
653fe6060f1SDimitry Andric       LocStats.ParamLocStats[ZeroCoverageBucket]++;
654fe6060f1SDimitry Andric     } else if (Tag == dwarf::DW_TAG_variable) {
655fe6060f1SDimitry Andric       LocStats.NumVar++;
656fe6060f1SDimitry Andric       LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
657fe6060f1SDimitry Andric     }
658fe6060f1SDimitry Andric   }
6590b57cec5SDimitry Andric }
6600b57cec5SDimitry Andric 
661e8d8bef9SDimitry Andric /// Print human-readable output.
6620b57cec5SDimitry Andric /// \{
663e8d8bef9SDimitry Andric static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
664349cc55cSDimitry Andric   if (Value == OverflowValue)
665349cc55cSDimitry Andric     J.attribute(Key, "overflowed");
666349cc55cSDimitry Andric   else
667e8d8bef9SDimitry Andric     J.attribute(Key, Value);
668349cc55cSDimitry Andric 
6690b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
6700b57cec5SDimitry Andric }
6715ffd83dbSDimitry Andric 
672e8d8bef9SDimitry Andric static void printLocationStats(json::OStream &J, const char *Key,
673349cc55cSDimitry Andric                                std::vector<SaturatingUINT64> &LocationStats) {
674349cc55cSDimitry Andric   if (LocationStats[0].Value == OverflowValue)
675349cc55cSDimitry Andric     J.attribute((Twine(Key) +
676349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
677349cc55cSDimitry Andric                     .str(),
678349cc55cSDimitry Andric                 "overflowed");
679349cc55cSDimitry Andric   else
680e8d8bef9SDimitry Andric     J.attribute(
681349cc55cSDimitry Andric         (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
682349cc55cSDimitry Andric             .str(),
683349cc55cSDimitry Andric         LocationStats[0].Value);
6845ffd83dbSDimitry Andric   LLVM_DEBUG(
6855ffd83dbSDimitry Andric       llvm::dbgs() << Key
6865ffd83dbSDimitry Andric                    << " with 0% of parent scope covered by DW_AT_location: \\"
687349cc55cSDimitry Andric                    << LocationStats[0].Value << '\n');
688349cc55cSDimitry Andric 
689349cc55cSDimitry Andric   if (LocationStats[1].Value == OverflowValue)
690349cc55cSDimitry Andric     J.attribute((Twine(Key) +
691349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
692e8d8bef9SDimitry Andric                     .str(),
693349cc55cSDimitry Andric                 "overflowed");
694349cc55cSDimitry Andric   else
695349cc55cSDimitry Andric     J.attribute((Twine(Key) +
696349cc55cSDimitry Andric                  " with (0%,10%) of parent scope covered by DW_AT_location")
697349cc55cSDimitry Andric                     .str(),
698349cc55cSDimitry Andric                 LocationStats[1].Value);
6995ffd83dbSDimitry Andric   LLVM_DEBUG(llvm::dbgs()
7005ffd83dbSDimitry Andric              << Key
7015ffd83dbSDimitry Andric              << " with (0%,10%) of parent scope covered by DW_AT_location: "
702349cc55cSDimitry Andric              << LocationStats[1].Value << '\n');
703349cc55cSDimitry Andric 
7048bcb0991SDimitry Andric   for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
705349cc55cSDimitry Andric     if (LocationStats[i].Value == OverflowValue)
706e8d8bef9SDimitry Andric       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
707349cc55cSDimitry Andric                    Twine(i * 10) +
708349cc55cSDimitry Andric                    "%) of parent scope covered by DW_AT_location")
709e8d8bef9SDimitry Andric                       .str(),
710349cc55cSDimitry Andric                   "overflowed");
711349cc55cSDimitry Andric     else
712349cc55cSDimitry Andric       J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
713349cc55cSDimitry Andric                    Twine(i * 10) +
714349cc55cSDimitry Andric                    "%) of parent scope covered by DW_AT_location")
715349cc55cSDimitry Andric                       .str(),
716349cc55cSDimitry Andric                   LocationStats[i].Value);
7178bcb0991SDimitry Andric     LLVM_DEBUG(llvm::dbgs()
718480093f4SDimitry Andric                << Key << " with [" << (i - 1) * 10 << "%," << i * 10
7195ffd83dbSDimitry Andric                << "%) of parent scope covered by DW_AT_location: "
720349cc55cSDimitry Andric                << LocationStats[i].Value);
7218bcb0991SDimitry Andric   }
722349cc55cSDimitry Andric   if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
723e8d8bef9SDimitry Andric     J.attribute(
724e8d8bef9SDimitry Andric         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
725e8d8bef9SDimitry Andric             .str(),
726349cc55cSDimitry Andric         "overflowed");
727349cc55cSDimitry Andric   else
728349cc55cSDimitry Andric     J.attribute(
729349cc55cSDimitry Andric         (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
730349cc55cSDimitry Andric             .str(),
731349cc55cSDimitry Andric         LocationStats[NumOfCoverageCategories - 1].Value);
7325ffd83dbSDimitry Andric   LLVM_DEBUG(
7335ffd83dbSDimitry Andric       llvm::dbgs() << Key
7345ffd83dbSDimitry Andric                    << " with 100% of parent scope covered by DW_AT_location: "
735349cc55cSDimitry Andric                    << LocationStats[NumOfCoverageCategories - 1].Value);
7368bcb0991SDimitry Andric }
7375ffd83dbSDimitry Andric 
738e8d8bef9SDimitry Andric static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
739fe6060f1SDimitry Andric   for (const auto &It : Sizes.DebugSectionSizes)
740fe6060f1SDimitry Andric     J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
741fe6060f1SDimitry Andric }
742fe6060f1SDimitry Andric 
743fe6060f1SDimitry Andric /// Stop tracking variables that contain abstract_origin with a location.
744fe6060f1SDimitry Andric /// This is used for out-of-order DW_AT_inline subprograms only.
745fe6060f1SDimitry Andric static void updateVarsWithAbstractOriginLocCovInfo(
746fe6060f1SDimitry Andric     DWARFDie FnDieWithAbstractOrigin,
747fe6060f1SDimitry Andric     AbstractOriginVarsTy &AbstractOriginVars) {
748fe6060f1SDimitry Andric   DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
749fe6060f1SDimitry Andric   while (Child) {
750fe6060f1SDimitry Andric     const dwarf::Tag ChildTag = Child.getTag();
751fe6060f1SDimitry Andric     if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
752fe6060f1SDimitry Andric          ChildTag == dwarf::DW_TAG_variable) &&
753fe6060f1SDimitry Andric         (Child.find(dwarf::DW_AT_location) ||
754fe6060f1SDimitry Andric          Child.find(dwarf::DW_AT_const_value))) {
755fe6060f1SDimitry Andric       auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
756fe6060f1SDimitry Andric       if (OffsetVar)
7575f757f3fSDimitry Andric         llvm::erase(AbstractOriginVars, (*OffsetVar).getRawUValue());
758fe6060f1SDimitry Andric     } else if (ChildTag == dwarf::DW_TAG_lexical_block)
759fe6060f1SDimitry Andric       updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
760fe6060f1SDimitry Andric     Child = Child.getSibling();
761fe6060f1SDimitry Andric   }
762fe6060f1SDimitry Andric }
763fe6060f1SDimitry Andric 
764fe6060f1SDimitry Andric /// Collect zero location coverage for inlined variables which refer to
765fe6060f1SDimitry Andric /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
766fe6060f1SDimitry Andric /// Also cover the variables of a concrete function (represented with
767fe6060f1SDimitry Andric /// the DW_TAG_subprogram) with an abstract_origin attribute.
768fe6060f1SDimitry Andric static void collectZeroLocCovForVarsWithAbstractOrigin(
769fe6060f1SDimitry Andric     DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
7704824e7fdSDimitry Andric     AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
771fe6060f1SDimitry Andric     FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
7724824e7fdSDimitry Andric   // The next variable is used to filter out functions that have been processed,
7734824e7fdSDimitry Andric   // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
7744824e7fdSDimitry Andric   FunctionsWithAbstractOriginTy ProcessedFns;
775fe6060f1SDimitry Andric   for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
776fe6060f1SDimitry Andric     DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
777fe6060f1SDimitry Andric     auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
778fe6060f1SDimitry Andric     AbstractOriginVarsTy AbstractOriginVars;
779fe6060f1SDimitry Andric     if (!FnCopy)
780fe6060f1SDimitry Andric       continue;
7814824e7fdSDimitry Andric     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
7824824e7fdSDimitry Andric     // If there is no entry within LocalAbstractOriginFnInfo for the given
7834824e7fdSDimitry Andric     // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
7844824e7fdSDimitry Andric     // CrossCU referencing.
7854824e7fdSDimitry Andric     if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
7864824e7fdSDimitry Andric       continue;
7874824e7fdSDimitry Andric     AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
788fe6060f1SDimitry Andric     updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
789fe6060f1SDimitry Andric                                            AbstractOriginVars);
790fe6060f1SDimitry Andric 
791fe6060f1SDimitry Andric     for (auto Offset : AbstractOriginVars) {
792fe6060f1SDimitry Andric       LocStats.NumVarParam++;
793fe6060f1SDimitry Andric       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
794fe6060f1SDimitry Andric       auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
795fe6060f1SDimitry Andric       if (Tag == dwarf::DW_TAG_formal_parameter) {
796fe6060f1SDimitry Andric         LocStats.NumParam++;
797fe6060f1SDimitry Andric         LocStats.ParamLocStats[ZeroCoverageBucket]++;
798fe6060f1SDimitry Andric       } else if (Tag == dwarf::DW_TAG_variable) {
799fe6060f1SDimitry Andric         LocStats.NumVar++;
800fe6060f1SDimitry Andric         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
801fe6060f1SDimitry Andric       }
802fe6060f1SDimitry Andric     }
8034824e7fdSDimitry Andric     ProcessedFns.push_back(FnOffset);
8044824e7fdSDimitry Andric   }
8054824e7fdSDimitry Andric   for (auto ProcessedFn : ProcessedFns)
8065f757f3fSDimitry Andric     llvm::erase(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
8074824e7fdSDimitry Andric }
8084824e7fdSDimitry Andric 
8094824e7fdSDimitry Andric /// Collect zero location coverage for inlined variables which refer to
8104824e7fdSDimitry Andric /// a DW_AT_inline copy of subprogram that is in a different CU.
8114824e7fdSDimitry Andric static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
8124824e7fdSDimitry Andric     LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
8134824e7fdSDimitry Andric     AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
8144824e7fdSDimitry Andric     CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
8154824e7fdSDimitry Andric   for (const auto &CrossCUReferenceToBeResolved :
8164824e7fdSDimitry Andric        CrossCUReferencesToBeResolved) {
8174824e7fdSDimitry Andric     DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
8184824e7fdSDimitry Andric     DWARFDie FnDIEWithCrossCUReferencing =
8194824e7fdSDimitry Andric         DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
8204824e7fdSDimitry Andric     auto FnCopy =
8214824e7fdSDimitry Andric         FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
8224824e7fdSDimitry Andric     if (!FnCopy)
8234824e7fdSDimitry Andric       continue;
8244824e7fdSDimitry Andric     uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
8254824e7fdSDimitry Andric     AbstractOriginVarsTy AbstractOriginVars =
8264824e7fdSDimitry Andric         GlobalAbstractOriginFnInfo[FnCopyRawUValue];
8274824e7fdSDimitry Andric     updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
8284824e7fdSDimitry Andric                                            AbstractOriginVars);
8294824e7fdSDimitry Andric     for (auto Offset : AbstractOriginVars) {
8304824e7fdSDimitry Andric       LocStats.NumVarParam++;
8314824e7fdSDimitry Andric       LocStats.VarParamLocStats[ZeroCoverageBucket]++;
8324824e7fdSDimitry Andric       auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
8334824e7fdSDimitry Andric                      ->getDIEForOffset(Offset)
8344824e7fdSDimitry Andric                      .getTag();
8354824e7fdSDimitry Andric       if (Tag == dwarf::DW_TAG_formal_parameter) {
8364824e7fdSDimitry Andric         LocStats.NumParam++;
8374824e7fdSDimitry Andric         LocStats.ParamLocStats[ZeroCoverageBucket]++;
8384824e7fdSDimitry Andric       } else if (Tag == dwarf::DW_TAG_variable) {
8394824e7fdSDimitry Andric         LocStats.NumVar++;
8404824e7fdSDimitry Andric         LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
8414824e7fdSDimitry Andric       }
8424824e7fdSDimitry Andric     }
843fe6060f1SDimitry Andric   }
8445ffd83dbSDimitry Andric }
8455ffd83dbSDimitry Andric 
8460b57cec5SDimitry Andric /// \}
8470b57cec5SDimitry Andric 
8480b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext.
8490b57cec5SDimitry Andric ///
8500b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few
8510b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked
8520b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect
8530b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly
8540b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different
8550b57cec5SDimitry Andric /// compilers is.
8565ffd83dbSDimitry Andric bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
8575ffd83dbSDimitry Andric                                           const Twine &Filename,
8585ffd83dbSDimitry Andric                                           raw_ostream &OS) {
8590b57cec5SDimitry Andric   StringRef FormatName = Obj.getFileFormatName();
8600b57cec5SDimitry Andric   GlobalStats GlobalStats;
8618bcb0991SDimitry Andric   LocationStats LocStats;
862*0fca6ea1SDimitry Andric   LineStats LnStats;
8630b57cec5SDimitry Andric   StringMap<PerFunctionStats> Statistics;
8644824e7fdSDimitry Andric   // This variable holds variable information for functions with
8654824e7fdSDimitry Andric   // abstract_origin globally, across all CUs.
8664824e7fdSDimitry Andric   AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
8674824e7fdSDimitry Andric   // This variable holds information about the CU of a function with
8684824e7fdSDimitry Andric   // abstract_origin.
8694824e7fdSDimitry Andric   FunctionDIECUTyMap AbstractOriginFnCUs;
8704824e7fdSDimitry Andric   CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
871*0fca6ea1SDimitry Andric   // Tuple representing a single source code position in the line table. Fields
872*0fca6ea1SDimitry Andric   // are respectively: Line, Col, File, where 'File' is an index into the Files
873*0fca6ea1SDimitry Andric   // vector below.
874*0fca6ea1SDimitry Andric   using LineTuple = std::tuple<uint32_t, uint16_t, uint16_t>;
875*0fca6ea1SDimitry Andric   SmallVector<std::string> Files;
876*0fca6ea1SDimitry Andric   DenseSet<LineTuple> UniqueLines;
877*0fca6ea1SDimitry Andric   DenseSet<LineTuple> UniqueNonZeroLines;
878*0fca6ea1SDimitry Andric 
879fe6060f1SDimitry Andric   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
880fe6060f1SDimitry Andric     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
8814824e7fdSDimitry Andric       // This variable holds variable information for functions with
8824824e7fdSDimitry Andric       // abstract_origin, but just for the current CU.
8834824e7fdSDimitry Andric       AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
884fe6060f1SDimitry Andric       FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
885fe6060f1SDimitry Andric 
8864824e7fdSDimitry Andric       collectStatsRecursive(
8874824e7fdSDimitry Andric           CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
8884824e7fdSDimitry Andric           AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
8894824e7fdSDimitry Andric           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
890fe6060f1SDimitry Andric 
8914824e7fdSDimitry Andric       // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
8924824e7fdSDimitry Andric       // out-of-order DWARF functions that have been processed within it,
8934824e7fdSDimitry Andric       // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
8944824e7fdSDimitry Andric       // references.
895fe6060f1SDimitry Andric       collectZeroLocCovForVarsWithAbstractOrigin(
896fe6060f1SDimitry Andric           CUDie.getDwarfUnit(), GlobalStats, LocStats,
8974824e7fdSDimitry Andric           LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
8984824e7fdSDimitry Andric 
8994824e7fdSDimitry Andric       // Collect all CrossCU references into CrossCUReferencesToBeResolved.
9004824e7fdSDimitry Andric       for (auto CrossCUReferencingDIEOffset :
9014824e7fdSDimitry Andric            FnsWithAbstractOriginToBeProcessed)
9024824e7fdSDimitry Andric         CrossCUReferencesToBeResolved.push_back(
9034824e7fdSDimitry Andric             DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
904fe6060f1SDimitry Andric     }
905*0fca6ea1SDimitry Andric     const auto *LineTable = DICtx.getLineTableForUnit(CU.get());
906*0fca6ea1SDimitry Andric     std::optional<uint64_t> LastFileIdxOpt;
907*0fca6ea1SDimitry Andric     if (LineTable)
908*0fca6ea1SDimitry Andric       LastFileIdxOpt = LineTable->getLastValidFileIndex();
909*0fca6ea1SDimitry Andric     if (LastFileIdxOpt) {
910*0fca6ea1SDimitry Andric       // Each CU has its own file index; in order to track unique line entries
911*0fca6ea1SDimitry Andric       // across CUs, we therefore need to map each CU file index to a global
912*0fca6ea1SDimitry Andric       // file index, which we store here.
913*0fca6ea1SDimitry Andric       DenseMap<uint64_t, uint16_t> CUFileMapping;
914*0fca6ea1SDimitry Andric       for (uint64_t FileIdx = 0; FileIdx <= *LastFileIdxOpt; ++FileIdx) {
915*0fca6ea1SDimitry Andric         std::string File;
916*0fca6ea1SDimitry Andric         if (LineTable->getFileNameByIndex(
917*0fca6ea1SDimitry Andric                 FileIdx, CU->getCompilationDir(),
918*0fca6ea1SDimitry Andric                 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
919*0fca6ea1SDimitry Andric                 File)) {
920*0fca6ea1SDimitry Andric           auto ExistingFile = llvm::find(Files, File);
921*0fca6ea1SDimitry Andric           if (ExistingFile != Files.end()) {
922*0fca6ea1SDimitry Andric             CUFileMapping[FileIdx] = std::distance(Files.begin(), ExistingFile);
923*0fca6ea1SDimitry Andric           } else {
924*0fca6ea1SDimitry Andric             CUFileMapping[FileIdx] = Files.size();
925*0fca6ea1SDimitry Andric             Files.push_back(File);
926fe6060f1SDimitry Andric           }
927*0fca6ea1SDimitry Andric         }
928*0fca6ea1SDimitry Andric       }
929*0fca6ea1SDimitry Andric       for (const auto &Seq : LineTable->Sequences) {
930*0fca6ea1SDimitry Andric         LnStats.NumBytes += Seq.HighPC - Seq.LowPC;
931*0fca6ea1SDimitry Andric         // Ignore the `end_sequence` entry, since it's not interesting for us.
932*0fca6ea1SDimitry Andric         LnStats.NumEntries += Seq.LastRowIndex - Seq.FirstRowIndex - 1;
933*0fca6ea1SDimitry Andric         for (size_t RowIdx = Seq.FirstRowIndex; RowIdx < Seq.LastRowIndex - 1;
934*0fca6ea1SDimitry Andric              ++RowIdx) {
935*0fca6ea1SDimitry Andric           auto Entry = LineTable->Rows[RowIdx];
936*0fca6ea1SDimitry Andric           if (Entry.IsStmt)
937*0fca6ea1SDimitry Andric             LnStats.NumIsStmtEntries += 1;
938*0fca6ea1SDimitry Andric           assert(CUFileMapping.contains(Entry.File) &&
939*0fca6ea1SDimitry Andric                  "Should have been collected earlier!");
940*0fca6ea1SDimitry Andric           uint16_t MappedFile = CUFileMapping[Entry.File];
941*0fca6ea1SDimitry Andric           UniqueLines.insert({Entry.Line, Entry.Column, MappedFile});
942*0fca6ea1SDimitry Andric           if (Entry.Line != 0) {
943*0fca6ea1SDimitry Andric             UniqueNonZeroLines.insert({Entry.Line, Entry.Column, MappedFile});
944*0fca6ea1SDimitry Andric           } else {
945*0fca6ea1SDimitry Andric             auto EntryStartAddress = Entry.Address.Address;
946*0fca6ea1SDimitry Andric             auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address;
947*0fca6ea1SDimitry Andric             LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress;
948*0fca6ea1SDimitry Andric           }
949*0fca6ea1SDimitry Andric         }
950*0fca6ea1SDimitry Andric       }
951*0fca6ea1SDimitry Andric     }
952*0fca6ea1SDimitry Andric   }
953*0fca6ea1SDimitry Andric 
954*0fca6ea1SDimitry Andric   LnStats.NumUniqueEntries = UniqueLines.size();
955*0fca6ea1SDimitry Andric   LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size();
9560b57cec5SDimitry Andric 
9574824e7fdSDimitry Andric   /// Resolve CrossCU references.
9584824e7fdSDimitry Andric   collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
9594824e7fdSDimitry Andric       LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
9604824e7fdSDimitry Andric       CrossCUReferencesToBeResolved);
9614824e7fdSDimitry Andric 
9625ffd83dbSDimitry Andric   /// Collect the sizes of debug sections.
9635ffd83dbSDimitry Andric   SectionSizes Sizes;
9645ffd83dbSDimitry Andric   calculateSectionSizes(Obj, Sizes, Filename);
9655ffd83dbSDimitry Andric 
9660b57cec5SDimitry Andric   /// The version number should be increased every time the algorithm is changed
9670b57cec5SDimitry Andric   /// (including bug fixes). New metrics may be added without increasing the
9680b57cec5SDimitry Andric   /// version.
969349cc55cSDimitry Andric   unsigned Version = 9;
970349cc55cSDimitry Andric   SaturatingUINT64 VarParamTotal = 0;
971349cc55cSDimitry Andric   SaturatingUINT64 VarParamUnique = 0;
972349cc55cSDimitry Andric   SaturatingUINT64 VarParamWithLoc = 0;
973349cc55cSDimitry Andric   SaturatingUINT64 NumFunctions = 0;
974349cc55cSDimitry Andric   SaturatingUINT64 NumInlinedFunctions = 0;
975349cc55cSDimitry Andric   SaturatingUINT64 NumFuncsWithSrcLoc = 0;
976349cc55cSDimitry Andric   SaturatingUINT64 NumAbstractOrigins = 0;
977349cc55cSDimitry Andric   SaturatingUINT64 ParamTotal = 0;
978349cc55cSDimitry Andric   SaturatingUINT64 ParamWithType = 0;
979349cc55cSDimitry Andric   SaturatingUINT64 ParamWithLoc = 0;
980349cc55cSDimitry Andric   SaturatingUINT64 ParamWithSrcLoc = 0;
981349cc55cSDimitry Andric   SaturatingUINT64 LocalVarTotal = 0;
982349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithType = 0;
983349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithSrcLoc = 0;
984349cc55cSDimitry Andric   SaturatingUINT64 LocalVarWithLoc = 0;
9850b57cec5SDimitry Andric   for (auto &Entry : Statistics) {
9860b57cec5SDimitry Andric     PerFunctionStats &Stats = Entry.getValue();
987349cc55cSDimitry Andric     uint64_t TotalVars = Stats.VarsInFunction.size() *
9885ffd83dbSDimitry Andric                          (Stats.NumFnInlined + Stats.NumFnOutOfLine);
9895ffd83dbSDimitry Andric     // Count variables in global scope.
9905ffd83dbSDimitry Andric     if (!Stats.IsFunction)
9915ffd83dbSDimitry Andric       TotalVars =
9925ffd83dbSDimitry Andric           Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
993349cc55cSDimitry Andric     uint64_t Constants = Stats.ConstantMembers;
9940b57cec5SDimitry Andric     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
9950b57cec5SDimitry Andric     VarParamTotal += TotalVars;
9960b57cec5SDimitry Andric     VarParamUnique += Stats.VarsInFunction.size();
9970b57cec5SDimitry Andric     LLVM_DEBUG(for (auto &V
9980b57cec5SDimitry Andric                     : Stats.VarsInFunction) llvm::dbgs()
9990b57cec5SDimitry Andric                << Entry.getKey() << ": " << V.getKey() << "\n");
10000b57cec5SDimitry Andric     NumFunctions += Stats.IsFunction;
10010b57cec5SDimitry Andric     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
10020b57cec5SDimitry Andric     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
10030b57cec5SDimitry Andric     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
10040b57cec5SDimitry Andric     ParamTotal += Stats.NumParams;
10050b57cec5SDimitry Andric     ParamWithType += Stats.NumParamTypes;
10060b57cec5SDimitry Andric     ParamWithLoc += Stats.NumParamLocations;
10070b57cec5SDimitry Andric     ParamWithSrcLoc += Stats.NumParamSourceLocations;
10085ffd83dbSDimitry Andric     LocalVarTotal += Stats.NumLocalVars;
10095ffd83dbSDimitry Andric     LocalVarWithType += Stats.NumLocalVarTypes;
10105ffd83dbSDimitry Andric     LocalVarWithLoc += Stats.NumLocalVarLocations;
10115ffd83dbSDimitry Andric     LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
10120b57cec5SDimitry Andric   }
10130b57cec5SDimitry Andric 
10140b57cec5SDimitry Andric   // Print summary.
10150b57cec5SDimitry Andric   OS.SetBufferSize(1024);
1016e8d8bef9SDimitry Andric   json::OStream J(OS, 2);
1017e8d8bef9SDimitry Andric   J.objectBegin();
1018e8d8bef9SDimitry Andric   J.attribute("version", Version);
10190b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
10200b57cec5SDimitry Andric              llvm::dbgs() << "---------------------------------\n");
10215ffd83dbSDimitry Andric 
1022e8d8bef9SDimitry Andric   printDatum(J, "file", Filename.str());
1023e8d8bef9SDimitry Andric   printDatum(J, "format", FormatName);
10245ffd83dbSDimitry Andric 
1025349cc55cSDimitry Andric   printDatum(J, "#functions", NumFunctions.Value);
1026349cc55cSDimitry Andric   printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
1027349cc55cSDimitry Andric   printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
1028349cc55cSDimitry Andric   printDatum(J, "#inlined functions with abstract origins",
1029349cc55cSDimitry Andric              NumAbstractOrigins.Value);
10305ffd83dbSDimitry Andric 
10315ffd83dbSDimitry Andric   // This includes local variables and formal parameters.
1032349cc55cSDimitry Andric   printDatum(J, "#unique source variables", VarParamUnique.Value);
1033349cc55cSDimitry Andric   printDatum(J, "#source variables", VarParamTotal.Value);
1034349cc55cSDimitry Andric   printDatum(J, "#source variables with location", VarParamWithLoc.Value);
10355ffd83dbSDimitry Andric 
1036349cc55cSDimitry Andric   printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
1037349cc55cSDimitry Andric   printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
1038349cc55cSDimitry Andric   printDatum(J, "#call site parameter DIEs",
1039349cc55cSDimitry Andric              GlobalStats.CallSiteParamDIEs.Value);
10405ffd83dbSDimitry Andric 
1041e8d8bef9SDimitry Andric   printDatum(J, "sum_all_variables(#bytes in parent scope)",
1042349cc55cSDimitry Andric              GlobalStats.ScopeBytes.Value);
1043e8d8bef9SDimitry Andric   printDatum(J,
1044e8d8bef9SDimitry Andric              "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
1045349cc55cSDimitry Andric              GlobalStats.TotalBytesCovered.Value);
1046e8d8bef9SDimitry Andric   printDatum(J,
10475ffd83dbSDimitry Andric              "sum_all_variables(#bytes in parent scope covered by "
10485ffd83dbSDimitry Andric              "DW_AT_location)",
1049349cc55cSDimitry Andric              GlobalStats.ScopeBytesCovered.Value);
1050e8d8bef9SDimitry Andric   printDatum(J,
10515ffd83dbSDimitry Andric              "sum_all_variables(#bytes in parent scope covered by "
10525ffd83dbSDimitry Andric              "DW_OP_entry_value)",
1053349cc55cSDimitry Andric              GlobalStats.ScopeEntryValueBytesCovered.Value);
10545ffd83dbSDimitry Andric 
1055e8d8bef9SDimitry Andric   printDatum(J, "sum_all_params(#bytes in parent scope)",
1056349cc55cSDimitry Andric              GlobalStats.ParamScopeBytes.Value);
1057e8d8bef9SDimitry Andric   printDatum(J,
10585ffd83dbSDimitry Andric              "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
1059349cc55cSDimitry Andric              GlobalStats.ParamScopeBytesCovered.Value);
1060e8d8bef9SDimitry Andric   printDatum(J,
10615ffd83dbSDimitry Andric              "sum_all_params(#bytes in parent scope covered by "
10625ffd83dbSDimitry Andric              "DW_OP_entry_value)",
1063349cc55cSDimitry Andric              GlobalStats.ParamScopeEntryValueBytesCovered.Value);
10645ffd83dbSDimitry Andric 
1065e8d8bef9SDimitry Andric   printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
1066349cc55cSDimitry Andric              GlobalStats.LocalVarScopeBytes.Value);
1067e8d8bef9SDimitry Andric   printDatum(J,
10685ffd83dbSDimitry Andric              "sum_all_local_vars(#bytes in parent scope covered by "
10695ffd83dbSDimitry Andric              "DW_AT_location)",
1070349cc55cSDimitry Andric              GlobalStats.LocalVarScopeBytesCovered.Value);
1071e8d8bef9SDimitry Andric   printDatum(J,
10725ffd83dbSDimitry Andric              "sum_all_local_vars(#bytes in parent scope covered by "
10735ffd83dbSDimitry Andric              "DW_OP_entry_value)",
1074349cc55cSDimitry Andric              GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
10755ffd83dbSDimitry Andric 
1076349cc55cSDimitry Andric   printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
1077e8d8bef9SDimitry Andric   printDatum(J, "#bytes within inlined functions",
1078349cc55cSDimitry Andric              GlobalStats.InlineFunctionSize.Value);
10795ffd83dbSDimitry Andric 
10805ffd83dbSDimitry Andric   // Print the summary for formal parameters.
1081349cc55cSDimitry Andric   printDatum(J, "#params", ParamTotal.Value);
1082349cc55cSDimitry Andric   printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
1083349cc55cSDimitry Andric   printDatum(J, "#params with type", ParamWithType.Value);
1084349cc55cSDimitry Andric   printDatum(J, "#params with binary location", ParamWithLoc.Value);
10855ffd83dbSDimitry Andric 
10865ffd83dbSDimitry Andric   // Print the summary for local variables.
1087349cc55cSDimitry Andric   printDatum(J, "#local vars", LocalVarTotal.Value);
1088349cc55cSDimitry Andric   printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
1089349cc55cSDimitry Andric   printDatum(J, "#local vars with type", LocalVarWithType.Value);
1090349cc55cSDimitry Andric   printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
10915ffd83dbSDimitry Andric 
10925ffd83dbSDimitry Andric   // Print the debug section sizes.
1093e8d8bef9SDimitry Andric   printSectionSizes(J, Sizes);
10945ffd83dbSDimitry Andric 
10955ffd83dbSDimitry Andric   // Print the location statistics for variables (includes local variables
10965ffd83dbSDimitry Andric   // and formal parameters).
1097e8d8bef9SDimitry Andric   printDatum(J, "#variables processed by location statistics",
1098349cc55cSDimitry Andric              LocStats.NumVarParam.Value);
1099e8d8bef9SDimitry Andric   printLocationStats(J, "#variables", LocStats.VarParamLocStats);
1100e8d8bef9SDimitry Andric   printLocationStats(J, "#variables - entry values",
11018bcb0991SDimitry Andric                      LocStats.VarParamNonEntryValLocStats);
11025ffd83dbSDimitry Andric 
11035ffd83dbSDimitry Andric   // Print the location statistics for formal parameters.
1104349cc55cSDimitry Andric   printDatum(J, "#params processed by location statistics",
1105349cc55cSDimitry Andric              LocStats.NumParam.Value);
1106e8d8bef9SDimitry Andric   printLocationStats(J, "#params", LocStats.ParamLocStats);
1107e8d8bef9SDimitry Andric   printLocationStats(J, "#params - entry values",
11088bcb0991SDimitry Andric                      LocStats.ParamNonEntryValLocStats);
11095ffd83dbSDimitry Andric 
11105ffd83dbSDimitry Andric   // Print the location statistics for local variables.
1111e8d8bef9SDimitry Andric   printDatum(J, "#local vars processed by location statistics",
1112349cc55cSDimitry Andric              LocStats.NumVar.Value);
1113e8d8bef9SDimitry Andric   printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
1114e8d8bef9SDimitry Andric   printLocationStats(J, "#local vars - entry values",
11155ffd83dbSDimitry Andric                      LocStats.LocalVarNonEntryValLocStats);
1116*0fca6ea1SDimitry Andric 
1117*0fca6ea1SDimitry Andric   // Print line statistics for the object file.
1118*0fca6ea1SDimitry Andric   printDatum(J, "#bytes with line information", LnStats.NumBytes.Value);
1119*0fca6ea1SDimitry Andric   printDatum(J, "#bytes with line-0 locations", LnStats.NumLineZeroBytes.Value);
1120*0fca6ea1SDimitry Andric   printDatum(J, "#line entries", LnStats.NumEntries.Value);
1121*0fca6ea1SDimitry Andric   printDatum(J, "#line entries (is_stmt)", LnStats.NumIsStmtEntries.Value);
1122*0fca6ea1SDimitry Andric   printDatum(J, "#line entries (unique)", LnStats.NumUniqueEntries.Value);
1123*0fca6ea1SDimitry Andric   printDatum(J, "#line entries (unique non-0)",
1124*0fca6ea1SDimitry Andric              LnStats.NumUniqueNonZeroEntries.Value);
1125*0fca6ea1SDimitry Andric 
1126e8d8bef9SDimitry Andric   J.objectEnd();
1127e8d8bef9SDimitry Andric   OS << '\n';
112881ad6265SDimitry Andric   LLVM_DEBUG(
112981ad6265SDimitry Andric       llvm::dbgs() << "Total Availability: "
113081ad6265SDimitry Andric                    << (VarParamTotal.Value
113181ad6265SDimitry Andric                            ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1132349cc55cSDimitry Andric                                              VarParamTotal.Value)
113381ad6265SDimitry Andric                            : 0)
11340b57cec5SDimitry Andric                    << "%\n";
11350b57cec5SDimitry Andric       llvm::dbgs() << "PC Ranges covered: "
113681ad6265SDimitry Andric                    << (GlobalStats.ScopeBytes.Value
113781ad6265SDimitry Andric                            ? (int)std::round(
1138349cc55cSDimitry Andric                                  (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1139349cc55cSDimitry Andric                                  GlobalStats.ScopeBytes.Value)
114081ad6265SDimitry Andric                            : 0)
11410b57cec5SDimitry Andric                    << "%\n");
11420b57cec5SDimitry Andric   return true;
11430b57cec5SDimitry Andric }
1144