1 //===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm-dwarfdump.h"
10 #include "llvm/ADT/DenseMap.h"
11 #include "llvm/ADT/DenseSet.h"
12 #include "llvm/ADT/StringSet.h"
13 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
14 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
15 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
16 #include "llvm/Object/ObjectFile.h"
17 #include "llvm/Support/JSON.h"
18
19 #define DEBUG_TYPE "dwarfdump"
20 using namespace llvm;
21 using namespace llvm::dwarfdump;
22 using namespace llvm::object;
23
24 namespace {
25 /// This represents the number of categories of debug location coverage being
26 /// calculated. The first category is the number of variables with 0% location
27 /// coverage, but the last category is the number of variables with 100%
28 /// location coverage.
29 constexpr int NumOfCoverageCategories = 12;
30
31 /// This is used for zero location coverage bucket.
32 constexpr unsigned ZeroCoverageBucket = 0;
33
34 /// The UINT64_MAX is used as an indication of the overflow.
35 constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
36
37 /// This represents variables DIE offsets.
38 using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
39 /// This maps function DIE offset to its variables.
40 using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
41 /// This represents function DIE offsets containing an abstract_origin.
42 using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
43
44 /// This represents a data type for the stats and it helps us to
45 /// detect an overflow.
46 /// NOTE: This can be implemented as a template if there is an another type
47 /// needing this.
48 struct SaturatingUINT64 {
49 /// Number that represents the stats.
50 uint64_t Value;
51
SaturatingUINT64__anond5eb1a760111::SaturatingUINT6452 SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
53
operator ++__anond5eb1a760111::SaturatingUINT6454 void operator++(int) { return *this += 1; }
operator +=__anond5eb1a760111::SaturatingUINT6455 void operator+=(uint64_t Value_) {
56 if (Value != OverflowValue) {
57 if (Value < OverflowValue - Value_)
58 Value += Value_;
59 else
60 Value = OverflowValue;
61 }
62 }
63 };
64
65 /// Utility struct to store the full location of a DIE - its CU and offset.
66 struct DIELocation {
67 DWARFUnit *DwUnit;
68 uint64_t DIEOffset;
DIELocation__anond5eb1a760111::DIELocation69 DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
70 : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
71 };
72 /// This represents DWARF locations of CrossCU referencing DIEs.
73 using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
74
75 /// This maps function DIE offset to its DWARF CU.
76 using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
77
78 /// Holds statistics for one function (or other entity that has a PC range and
79 /// contains variables, such as a compile unit).
80 struct PerFunctionStats {
81 /// Number of inlined instances of this function.
82 uint64_t NumFnInlined = 0;
83 /// Number of out-of-line instances of this function.
84 uint64_t NumFnOutOfLine = 0;
85 /// Number of inlined instances that have abstract origins.
86 uint64_t NumAbstractOrigins = 0;
87 /// Number of variables and parameters with location across all inlined
88 /// instances.
89 uint64_t TotalVarWithLoc = 0;
90 /// Number of constants with location across all inlined instances.
91 uint64_t ConstantMembers = 0;
92 /// Number of arificial variables, parameters or members across all instances.
93 uint64_t NumArtificial = 0;
94 /// List of all Variables and parameters in this function.
95 StringSet<> VarsInFunction;
96 /// Compile units also cover a PC range, but have this flag set to false.
97 bool IsFunction = false;
98 /// Function has source location information.
99 bool HasSourceLocation = false;
100 /// Number of function parameters.
101 uint64_t NumParams = 0;
102 /// Number of function parameters with source location.
103 uint64_t NumParamSourceLocations = 0;
104 /// Number of function parameters with type.
105 uint64_t NumParamTypes = 0;
106 /// Number of function parameters with a DW_AT_location.
107 uint64_t NumParamLocations = 0;
108 /// Number of local variables.
109 uint64_t NumLocalVars = 0;
110 /// Number of local variables with source location.
111 uint64_t NumLocalVarSourceLocations = 0;
112 /// Number of local variables with type.
113 uint64_t NumLocalVarTypes = 0;
114 /// Number of local variables with DW_AT_location.
115 uint64_t NumLocalVarLocations = 0;
116 };
117
118 /// Holds accumulated global statistics about DIEs.
119 struct GlobalStats {
120 /// Total number of PC range bytes covered by DW_AT_locations.
121 SaturatingUINT64 TotalBytesCovered = 0;
122 /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
123 SaturatingUINT64 ScopeBytesCovered = 0;
124 /// Total number of PC range bytes in each variable's enclosing scope.
125 SaturatingUINT64 ScopeBytes = 0;
126 /// Total number of PC range bytes covered by DW_AT_locations with
127 /// the debug entry values (DW_OP_entry_value).
128 SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
129 /// Total number of PC range bytes covered by DW_AT_locations of
130 /// formal parameters.
131 SaturatingUINT64 ParamScopeBytesCovered = 0;
132 /// Total number of PC range bytes in each parameter's enclosing scope.
133 SaturatingUINT64 ParamScopeBytes = 0;
134 /// Total number of PC range bytes covered by DW_AT_locations with
135 /// the debug entry values (DW_OP_entry_value) (only for parameters).
136 SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
137 /// Total number of PC range bytes covered by DW_AT_locations (only for local
138 /// variables).
139 SaturatingUINT64 LocalVarScopeBytesCovered = 0;
140 /// Total number of PC range bytes in each local variable's enclosing scope.
141 SaturatingUINT64 LocalVarScopeBytes = 0;
142 /// Total number of PC range bytes covered by DW_AT_locations with
143 /// the debug entry values (DW_OP_entry_value) (only for local variables).
144 SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
145 /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
146 SaturatingUINT64 CallSiteEntries = 0;
147 /// Total number of call site DIEs (DW_TAG_call_site).
148 SaturatingUINT64 CallSiteDIEs = 0;
149 /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
150 SaturatingUINT64 CallSiteParamDIEs = 0;
151 /// Total byte size of concrete functions. This byte size includes
152 /// inline functions contained in the concrete functions.
153 SaturatingUINT64 FunctionSize = 0;
154 /// Total byte size of inlined functions. This is the total number of bytes
155 /// for the top inline functions within concrete functions. This can help
156 /// tune the inline settings when compiling to match user expectations.
157 SaturatingUINT64 InlineFunctionSize = 0;
158 };
159
160 /// Holds accumulated debug location statistics about local variables and
161 /// formal parameters.
162 struct LocationStats {
163 /// Map the scope coverage decile to the number of variables in the decile.
164 /// The first element of the array (at the index zero) represents the number
165 /// of variables with the no debug location at all, but the last element
166 /// in the vector represents the number of fully covered variables within
167 /// its scope.
168 std::vector<SaturatingUINT64> VarParamLocStats{
169 std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
170 /// Map non debug entry values coverage.
171 std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
172 std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
173 /// The debug location statistics for formal parameters.
174 std::vector<SaturatingUINT64> ParamLocStats{
175 std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
176 /// Map non debug entry values coverage for formal parameters.
177 std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
178 std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
179 /// The debug location statistics for local variables.
180 std::vector<SaturatingUINT64> LocalVarLocStats{
181 std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
182 /// Map non debug entry values coverage for local variables.
183 std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
184 std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
185 /// Total number of local variables and function parameters processed.
186 SaturatingUINT64 NumVarParam = 0;
187 /// Total number of formal parameters processed.
188 SaturatingUINT64 NumParam = 0;
189 /// Total number of local variables processed.
190 SaturatingUINT64 NumVar = 0;
191 };
192
193 /// Holds accumulated debug line statistics across all CUs.
194 struct LineStats {
195 SaturatingUINT64 NumBytes = 0;
196 SaturatingUINT64 NumLineZeroBytes = 0;
197 SaturatingUINT64 NumEntries = 0;
198 SaturatingUINT64 NumIsStmtEntries = 0;
199 SaturatingUINT64 NumUniqueEntries = 0;
200 SaturatingUINT64 NumUniqueNonZeroEntries = 0;
201 };
202 } // namespace
203
204 /// Collect debug location statistics for one DIE.
collectLocStats(uint64_t ScopeBytesCovered,uint64_t BytesInScope,std::vector<SaturatingUINT64> & VarParamLocStats,std::vector<SaturatingUINT64> & ParamLocStats,std::vector<SaturatingUINT64> & LocalVarLocStats,bool IsParam,bool IsLocalVar)205 static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
206 std::vector<SaturatingUINT64> &VarParamLocStats,
207 std::vector<SaturatingUINT64> &ParamLocStats,
208 std::vector<SaturatingUINT64> &LocalVarLocStats,
209 bool IsParam, bool IsLocalVar) {
210 auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
211 // No debug location at all for the variable.
212 if (ScopeBytesCovered == 0)
213 return 0;
214 // Fully covered variable within its scope.
215 if (ScopeBytesCovered >= BytesInScope)
216 return NumOfCoverageCategories - 1;
217 // Get covered range (e.g. 20%-29%).
218 unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
219 LocBucket /= 10;
220 return LocBucket + 1;
221 };
222
223 unsigned CoverageBucket = getCoverageBucket();
224
225 VarParamLocStats[CoverageBucket].Value++;
226 if (IsParam)
227 ParamLocStats[CoverageBucket].Value++;
228 else if (IsLocalVar)
229 LocalVarLocStats[CoverageBucket].Value++;
230 }
231
232 /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
233 /// and DeclLine. The identifier aims to be unique for any unique entities,
234 /// but keeping the same among different instances of the same entity.
constructDieID(DWARFDie Die,StringRef Prefix=StringRef ())235 static std::string constructDieID(DWARFDie Die,
236 StringRef Prefix = StringRef()) {
237 std::string IDStr;
238 llvm::raw_string_ostream ID(IDStr);
239 ID << Prefix
240 << Die.getName(DINameKind::LinkageName);
241
242 // Prefix + Name is enough for local variables and parameters.
243 if (!Prefix.empty() && Prefix != "g")
244 return ID.str();
245
246 auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
247 std::string File;
248 if (DeclFile) {
249 DWARFUnit *U = Die.getDwarfUnit();
250 if (const auto *LT = U->getContext().getLineTableForUnit(U))
251 if (LT->getFileNameByIndex(
252 dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
253 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
254 File = std::string(sys::path::filename(File));
255 }
256 ID << ":" << (File.empty() ? "/" : File);
257 ID << ":"
258 << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
259 return ID.str();
260 }
261
262 /// Return the number of bytes in the overlap of ranges A and B.
calculateOverlap(DWARFAddressRange A,DWARFAddressRange B)263 static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
264 uint64_t Lower = std::max(A.LowPC, B.LowPC);
265 uint64_t Upper = std::min(A.HighPC, B.HighPC);
266 if (Lower >= Upper)
267 return 0;
268 return Upper - Lower;
269 }
270
271 /// Collect debug info quality metrics for one DIE.
collectStatsForDie(DWARFDie Die,const std::string & FnPrefix,const std::string & VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTy * AbstractOriginVariables)272 static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
273 const std::string &VarPrefix,
274 uint64_t BytesInScope, uint32_t InlineDepth,
275 StringMap<PerFunctionStats> &FnStatMap,
276 GlobalStats &GlobalStats,
277 LocationStats &LocStats,
278 AbstractOriginVarsTy *AbstractOriginVariables) {
279 const dwarf::Tag Tag = Die.getTag();
280 // Skip CU node.
281 if (Tag == dwarf::DW_TAG_compile_unit)
282 return;
283
284 bool HasLoc = false;
285 bool HasSrcLoc = false;
286 bool HasType = false;
287 uint64_t TotalBytesCovered = 0;
288 uint64_t ScopeBytesCovered = 0;
289 uint64_t BytesEntryValuesCovered = 0;
290 auto &FnStats = FnStatMap[FnPrefix];
291 bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
292 bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
293 bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
294 Die.find(dwarf::DW_AT_const_value);
295
296 // For zero covered inlined variables the locstats will be
297 // calculated later.
298 bool DeferLocStats = false;
299
300 if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
301 GlobalStats.CallSiteDIEs++;
302 return;
303 }
304
305 if (Tag == dwarf::DW_TAG_call_site_parameter ||
306 Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
307 GlobalStats.CallSiteParamDIEs++;
308 return;
309 }
310
311 if (!IsParam && !IsLocalVar && !IsConstantMember) {
312 // Not a variable or constant member.
313 return;
314 }
315
316 // Ignore declarations of global variables.
317 if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
318 return;
319
320 if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
321 Die.findRecursively(dwarf::DW_AT_decl_line))
322 HasSrcLoc = true;
323
324 if (Die.findRecursively(dwarf::DW_AT_type))
325 HasType = true;
326
327 if (Die.find(dwarf::DW_AT_abstract_origin)) {
328 if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
329 if (AbstractOriginVariables) {
330 auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
331 // Do not track this variable any more, since it has location
332 // coverage.
333 llvm::erase(*AbstractOriginVariables, (*Offset).getRawUValue());
334 }
335 } else {
336 // The locstats will be handled at the end of
337 // the collectStatsRecursive().
338 DeferLocStats = true;
339 }
340 }
341
342 auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
343 DWARFUnit *U = Die.getDwarfUnit();
344 DataExtractor Data(toStringRef(D),
345 Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
346 DWARFExpression Expression(Data, U->getAddressByteSize(),
347 U->getFormParams().Format);
348 // Consider the expression containing the DW_OP_entry_value as
349 // an entry value.
350 return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
351 return Op.getCode() == dwarf::DW_OP_entry_value ||
352 Op.getCode() == dwarf::DW_OP_GNU_entry_value;
353 });
354 };
355
356 if (Die.find(dwarf::DW_AT_const_value)) {
357 // This catches constant members *and* variables.
358 HasLoc = true;
359 ScopeBytesCovered = BytesInScope;
360 TotalBytesCovered = BytesInScope;
361 } else {
362 // Handle variables and function arguments.
363 Expected<std::vector<DWARFLocationExpression>> Loc =
364 Die.getLocations(dwarf::DW_AT_location);
365 if (!Loc) {
366 consumeError(Loc.takeError());
367 } else {
368 HasLoc = true;
369 // Get PC coverage.
370 auto Default = find_if(
371 *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
372 if (Default != Loc->end()) {
373 // Assume the entire range is covered by a single location.
374 ScopeBytesCovered = BytesInScope;
375 TotalBytesCovered = BytesInScope;
376 } else {
377 // Caller checks this Expected result already, it cannot fail.
378 auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
379 for (auto Entry : *Loc) {
380 TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
381 uint64_t ScopeBytesCoveredByEntry = 0;
382 // Calculate how many bytes of the parent scope this entry covers.
383 // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
384 // address ranges defined by the bounded location descriptions of a
385 // location list may overlap". So in theory a variable can have
386 // multiple simultaneous locations, which would make this calculation
387 // misleading because we will count the overlapped areas
388 // twice. However, clang does not currently emit DWARF like this.
389 for (DWARFAddressRange R : ScopeRanges) {
390 ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
391 }
392 ScopeBytesCovered += ScopeBytesCoveredByEntry;
393 if (IsEntryValue(Entry.Expr))
394 BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
395 }
396 }
397 }
398 }
399
400 // Calculate the debug location statistics.
401 if (BytesInScope && !DeferLocStats) {
402 LocStats.NumVarParam.Value++;
403 if (IsParam)
404 LocStats.NumParam.Value++;
405 else if (IsLocalVar)
406 LocStats.NumVar.Value++;
407
408 collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
409 LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
410 IsLocalVar);
411 // Non debug entry values coverage statistics.
412 collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
413 LocStats.VarParamNonEntryValLocStats,
414 LocStats.ParamNonEntryValLocStats,
415 LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
416 }
417
418 // Collect PC range coverage data.
419 if (DWARFDie D =
420 Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
421 Die = D;
422
423 std::string VarID = constructDieID(Die, VarPrefix);
424 FnStats.VarsInFunction.insert(VarID);
425
426 GlobalStats.TotalBytesCovered += TotalBytesCovered;
427 if (BytesInScope) {
428 GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
429 GlobalStats.ScopeBytes += BytesInScope;
430 GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
431 if (IsParam) {
432 GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
433 GlobalStats.ParamScopeBytes += BytesInScope;
434 GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
435 } else if (IsLocalVar) {
436 GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
437 GlobalStats.LocalVarScopeBytes += BytesInScope;
438 GlobalStats.LocalVarScopeEntryValueBytesCovered +=
439 BytesEntryValuesCovered;
440 }
441 assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
442 }
443
444 if (IsConstantMember) {
445 FnStats.ConstantMembers++;
446 return;
447 }
448
449 FnStats.TotalVarWithLoc += (unsigned)HasLoc;
450
451 if (Die.find(dwarf::DW_AT_artificial)) {
452 FnStats.NumArtificial++;
453 return;
454 }
455
456 if (IsParam) {
457 FnStats.NumParams++;
458 if (HasType)
459 FnStats.NumParamTypes++;
460 if (HasSrcLoc)
461 FnStats.NumParamSourceLocations++;
462 if (HasLoc)
463 FnStats.NumParamLocations++;
464 } else if (IsLocalVar) {
465 FnStats.NumLocalVars++;
466 if (HasType)
467 FnStats.NumLocalVarTypes++;
468 if (HasSrcLoc)
469 FnStats.NumLocalVarSourceLocations++;
470 if (HasLoc)
471 FnStats.NumLocalVarLocations++;
472 }
473 }
474
475 /// Recursively collect variables from subprogram with DW_AT_inline attribute.
collectAbstractOriginFnInfo(DWARFDie Die,uint64_t SPOffset,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo)476 static void collectAbstractOriginFnInfo(
477 DWARFDie Die, uint64_t SPOffset,
478 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
479 AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
480 DWARFDie Child = Die.getFirstChild();
481 while (Child) {
482 const dwarf::Tag ChildTag = Child.getTag();
483 if (ChildTag == dwarf::DW_TAG_formal_parameter ||
484 ChildTag == dwarf::DW_TAG_variable) {
485 GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
486 LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
487 } else if (ChildTag == dwarf::DW_TAG_lexical_block)
488 collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
489 LocalAbstractOriginFnInfo);
490 Child = Child.getSibling();
491 }
492 }
493
494 /// Recursively collect debug info quality metrics.
collectStatsRecursive(DWARFDie Die,std::string FnPrefix,std::string VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats,FunctionDIECUTyMap & AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed,AbstractOriginVarsTy * AbstractOriginVarsPtr=nullptr)495 static void collectStatsRecursive(
496 DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
497 uint64_t BytesInScope, uint32_t InlineDepth,
498 StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
499 LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
500 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
501 AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
502 FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
503 AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
504 // Skip NULL nodes.
505 if (Die.isNULL())
506 return;
507
508 const dwarf::Tag Tag = Die.getTag();
509 // Skip function types.
510 if (Tag == dwarf::DW_TAG_subroutine_type)
511 return;
512
513 // Handle any kind of lexical scope.
514 const bool HasAbstractOrigin =
515 Die.find(dwarf::DW_AT_abstract_origin) != std::nullopt;
516 const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
517 const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
518 const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
519 // We want to know how many variables (with abstract_origin) don't have
520 // location info.
521 const bool IsCandidateForZeroLocCovTracking =
522 (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
523
524 AbstractOriginVarsTy AbstractOriginVars;
525
526 // Get the vars of the inlined fn, so the locstats
527 // reports the missing vars (with coverage 0%).
528 if (IsCandidateForZeroLocCovTracking) {
529 auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
530 if (OffsetFn) {
531 uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
532 if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
533 AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
534 AbstractOriginVarsPtr = &AbstractOriginVars;
535 } else {
536 // This means that the DW_AT_inline fn copy is out of order
537 // or that the abstract_origin references another CU,
538 // so this abstract origin instance will be processed later.
539 FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
540 AbstractOriginVarsPtr = nullptr;
541 }
542 }
543 }
544
545 if (IsFunction || IsInlinedFunction || IsBlock) {
546 // Reset VarPrefix when entering a new function.
547 if (IsFunction || IsInlinedFunction)
548 VarPrefix = "v";
549
550 // Ignore forward declarations.
551 if (Die.find(dwarf::DW_AT_declaration))
552 return;
553
554 // Check for call sites.
555 if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
556 GlobalStats.CallSiteEntries++;
557
558 // PC Ranges.
559 auto RangesOrError = Die.getAddressRanges();
560 if (!RangesOrError) {
561 llvm::consumeError(RangesOrError.takeError());
562 return;
563 }
564
565 auto Ranges = RangesOrError.get();
566 uint64_t BytesInThisScope = 0;
567 for (auto Range : Ranges)
568 BytesInThisScope += Range.HighPC - Range.LowPC;
569
570 // Count the function.
571 if (!IsBlock) {
572 // Skip over abstract origins, but collect variables
573 // from it so it can be used for location statistics
574 // for inlined instancies.
575 if (Die.find(dwarf::DW_AT_inline)) {
576 uint64_t SPOffset = Die.getOffset();
577 AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
578 collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
579 LocalAbstractOriginFnInfo);
580 return;
581 }
582
583 std::string FnID = constructDieID(Die);
584 // We've seen an instance of this function.
585 auto &FnStats = FnStatMap[FnID];
586 FnStats.IsFunction = true;
587 if (IsInlinedFunction) {
588 FnStats.NumFnInlined++;
589 if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
590 FnStats.NumAbstractOrigins++;
591 } else {
592 FnStats.NumFnOutOfLine++;
593 }
594 if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
595 Die.findRecursively(dwarf::DW_AT_decl_line))
596 FnStats.HasSourceLocation = true;
597 // Update function prefix.
598 FnPrefix = FnID;
599 }
600
601 if (BytesInThisScope) {
602 BytesInScope = BytesInThisScope;
603 if (IsFunction)
604 GlobalStats.FunctionSize += BytesInThisScope;
605 else if (IsInlinedFunction && InlineDepth == 0)
606 GlobalStats.InlineFunctionSize += BytesInThisScope;
607 }
608 } else {
609 // Not a scope, visit the Die itself. It could be a variable.
610 collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
611 FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
612 }
613
614 // Set InlineDepth correctly for child recursion
615 if (IsFunction)
616 InlineDepth = 0;
617 else if (IsInlinedFunction)
618 ++InlineDepth;
619
620 // Traverse children.
621 unsigned LexicalBlockIndex = 0;
622 unsigned FormalParameterIndex = 0;
623 DWARFDie Child = Die.getFirstChild();
624 while (Child) {
625 std::string ChildVarPrefix = VarPrefix;
626 if (Child.getTag() == dwarf::DW_TAG_lexical_block)
627 ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
628 if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
629 ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
630
631 collectStatsRecursive(
632 Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
633 GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
634 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
635 AbstractOriginVarsPtr);
636 Child = Child.getSibling();
637 }
638
639 if (!IsCandidateForZeroLocCovTracking)
640 return;
641
642 // After we have processed all vars of the inlined function (or function with
643 // an abstract_origin), we want to know how many variables have no location.
644 for (auto Offset : AbstractOriginVars) {
645 LocStats.NumVarParam++;
646 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
647 auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
648 if (!FnDie)
649 continue;
650 auto Tag = FnDie.getTag();
651 if (Tag == dwarf::DW_TAG_formal_parameter) {
652 LocStats.NumParam++;
653 LocStats.ParamLocStats[ZeroCoverageBucket]++;
654 } else if (Tag == dwarf::DW_TAG_variable) {
655 LocStats.NumVar++;
656 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
657 }
658 }
659 }
660
661 /// Print human-readable output.
662 /// \{
printDatum(json::OStream & J,const char * Key,json::Value Value)663 static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
664 if (Value == OverflowValue)
665 J.attribute(Key, "overflowed");
666 else
667 J.attribute(Key, Value);
668
669 LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
670 }
671
printLocationStats(json::OStream & J,const char * Key,std::vector<SaturatingUINT64> & LocationStats)672 static void printLocationStats(json::OStream &J, const char *Key,
673 std::vector<SaturatingUINT64> &LocationStats) {
674 if (LocationStats[0].Value == OverflowValue)
675 J.attribute((Twine(Key) +
676 " with (0%,10%) of parent scope covered by DW_AT_location")
677 .str(),
678 "overflowed");
679 else
680 J.attribute(
681 (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
682 .str(),
683 LocationStats[0].Value);
684 LLVM_DEBUG(
685 llvm::dbgs() << Key
686 << " with 0% of parent scope covered by DW_AT_location: \\"
687 << LocationStats[0].Value << '\n');
688
689 if (LocationStats[1].Value == OverflowValue)
690 J.attribute((Twine(Key) +
691 " with (0%,10%) of parent scope covered by DW_AT_location")
692 .str(),
693 "overflowed");
694 else
695 J.attribute((Twine(Key) +
696 " with (0%,10%) of parent scope covered by DW_AT_location")
697 .str(),
698 LocationStats[1].Value);
699 LLVM_DEBUG(llvm::dbgs()
700 << Key
701 << " with (0%,10%) of parent scope covered by DW_AT_location: "
702 << LocationStats[1].Value << '\n');
703
704 for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
705 if (LocationStats[i].Value == OverflowValue)
706 J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
707 Twine(i * 10) +
708 "%) of parent scope covered by DW_AT_location")
709 .str(),
710 "overflowed");
711 else
712 J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
713 Twine(i * 10) +
714 "%) of parent scope covered by DW_AT_location")
715 .str(),
716 LocationStats[i].Value);
717 LLVM_DEBUG(llvm::dbgs()
718 << Key << " with [" << (i - 1) * 10 << "%," << i * 10
719 << "%) of parent scope covered by DW_AT_location: "
720 << LocationStats[i].Value);
721 }
722 if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
723 J.attribute(
724 (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
725 .str(),
726 "overflowed");
727 else
728 J.attribute(
729 (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
730 .str(),
731 LocationStats[NumOfCoverageCategories - 1].Value);
732 LLVM_DEBUG(
733 llvm::dbgs() << Key
734 << " with 100% of parent scope covered by DW_AT_location: "
735 << LocationStats[NumOfCoverageCategories - 1].Value);
736 }
737
printSectionSizes(json::OStream & J,const SectionSizes & Sizes)738 static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
739 for (const auto &It : Sizes.DebugSectionSizes)
740 J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
741 }
742
743 /// Stop tracking variables that contain abstract_origin with a location.
744 /// This is used for out-of-order DW_AT_inline subprograms only.
updateVarsWithAbstractOriginLocCovInfo(DWARFDie FnDieWithAbstractOrigin,AbstractOriginVarsTy & AbstractOriginVars)745 static void updateVarsWithAbstractOriginLocCovInfo(
746 DWARFDie FnDieWithAbstractOrigin,
747 AbstractOriginVarsTy &AbstractOriginVars) {
748 DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
749 while (Child) {
750 const dwarf::Tag ChildTag = Child.getTag();
751 if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
752 ChildTag == dwarf::DW_TAG_variable) &&
753 (Child.find(dwarf::DW_AT_location) ||
754 Child.find(dwarf::DW_AT_const_value))) {
755 auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
756 if (OffsetVar)
757 llvm::erase(AbstractOriginVars, (*OffsetVar).getRawUValue());
758 } else if (ChildTag == dwarf::DW_TAG_lexical_block)
759 updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
760 Child = Child.getSibling();
761 }
762 }
763
764 /// Collect zero location coverage for inlined variables which refer to
765 /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
766 /// Also cover the variables of a concrete function (represented with
767 /// the DW_TAG_subprogram) with an abstract_origin attribute.
collectZeroLocCovForVarsWithAbstractOrigin(DWARFUnit * DwUnit,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed)768 static void collectZeroLocCovForVarsWithAbstractOrigin(
769 DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
770 AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
771 FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
772 // The next variable is used to filter out functions that have been processed,
773 // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
774 FunctionsWithAbstractOriginTy ProcessedFns;
775 for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
776 DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
777 auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
778 AbstractOriginVarsTy AbstractOriginVars;
779 if (!FnCopy)
780 continue;
781 uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
782 // If there is no entry within LocalAbstractOriginFnInfo for the given
783 // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
784 // CrossCU referencing.
785 if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
786 continue;
787 AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
788 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
789 AbstractOriginVars);
790
791 for (auto Offset : AbstractOriginVars) {
792 LocStats.NumVarParam++;
793 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
794 auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
795 if (Tag == dwarf::DW_TAG_formal_parameter) {
796 LocStats.NumParam++;
797 LocStats.ParamLocStats[ZeroCoverageBucket]++;
798 } else if (Tag == dwarf::DW_TAG_variable) {
799 LocStats.NumVar++;
800 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
801 }
802 }
803 ProcessedFns.push_back(FnOffset);
804 }
805 for (auto ProcessedFn : ProcessedFns)
806 llvm::erase(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
807 }
808
809 /// Collect zero location coverage for inlined variables which refer to
810 /// a DW_AT_inline copy of subprogram that is in a different CU.
collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(LocationStats & LocStats,FunctionDIECUTyMap AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,CrossCUReferencingDIELocationTy & CrossCUReferencesToBeResolved)811 static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
812 LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
813 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
814 CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
815 for (const auto &CrossCUReferenceToBeResolved :
816 CrossCUReferencesToBeResolved) {
817 DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
818 DWARFDie FnDIEWithCrossCUReferencing =
819 DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
820 auto FnCopy =
821 FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
822 if (!FnCopy)
823 continue;
824 uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
825 AbstractOriginVarsTy AbstractOriginVars =
826 GlobalAbstractOriginFnInfo[FnCopyRawUValue];
827 updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
828 AbstractOriginVars);
829 for (auto Offset : AbstractOriginVars) {
830 LocStats.NumVarParam++;
831 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
832 auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
833 ->getDIEForOffset(Offset)
834 .getTag();
835 if (Tag == dwarf::DW_TAG_formal_parameter) {
836 LocStats.NumParam++;
837 LocStats.ParamLocStats[ZeroCoverageBucket]++;
838 } else if (Tag == dwarf::DW_TAG_variable) {
839 LocStats.NumVar++;
840 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
841 }
842 }
843 }
844 }
845
846 /// \}
847
848 /// Collect debug info quality metrics for an entire DIContext.
849 ///
850 /// Do the impossible and reduce the quality of the debug info down to a few
851 /// numbers. The idea is to condense the data into numbers that can be tracked
852 /// over time to identify trends in newer compiler versions and gauge the effect
853 /// of particular optimizations. The raw numbers themselves are not particularly
854 /// useful, only the delta between compiling the same program with different
855 /// compilers is.
collectStatsForObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)856 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
857 const Twine &Filename,
858 raw_ostream &OS) {
859 StringRef FormatName = Obj.getFileFormatName();
860 GlobalStats GlobalStats;
861 LocationStats LocStats;
862 LineStats LnStats;
863 StringMap<PerFunctionStats> Statistics;
864 // This variable holds variable information for functions with
865 // abstract_origin globally, across all CUs.
866 AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
867 // This variable holds information about the CU of a function with
868 // abstract_origin.
869 FunctionDIECUTyMap AbstractOriginFnCUs;
870 CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
871 // Tuple representing a single source code position in the line table. Fields
872 // are respectively: Line, Col, File, where 'File' is an index into the Files
873 // vector below.
874 using LineTuple = std::tuple<uint32_t, uint16_t, uint16_t>;
875 SmallVector<std::string> Files;
876 DenseSet<LineTuple> UniqueLines;
877 DenseSet<LineTuple> UniqueNonZeroLines;
878
879 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
880 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
881 // This variable holds variable information for functions with
882 // abstract_origin, but just for the current CU.
883 AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
884 FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
885
886 collectStatsRecursive(
887 CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
888 AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
889 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
890
891 // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
892 // out-of-order DWARF functions that have been processed within it,
893 // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
894 // references.
895 collectZeroLocCovForVarsWithAbstractOrigin(
896 CUDie.getDwarfUnit(), GlobalStats, LocStats,
897 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
898
899 // Collect all CrossCU references into CrossCUReferencesToBeResolved.
900 for (auto CrossCUReferencingDIEOffset :
901 FnsWithAbstractOriginToBeProcessed)
902 CrossCUReferencesToBeResolved.push_back(
903 DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
904 }
905 const auto *LineTable = DICtx.getLineTableForUnit(CU.get());
906 std::optional<uint64_t> LastFileIdxOpt;
907 if (LineTable)
908 LastFileIdxOpt = LineTable->getLastValidFileIndex();
909 if (LastFileIdxOpt) {
910 // Each CU has its own file index; in order to track unique line entries
911 // across CUs, we therefore need to map each CU file index to a global
912 // file index, which we store here.
913 DenseMap<uint64_t, uint16_t> CUFileMapping;
914 for (uint64_t FileIdx = 0; FileIdx <= *LastFileIdxOpt; ++FileIdx) {
915 std::string File;
916 if (LineTable->getFileNameByIndex(
917 FileIdx, CU->getCompilationDir(),
918 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
919 File)) {
920 auto ExistingFile = llvm::find(Files, File);
921 if (ExistingFile != Files.end()) {
922 CUFileMapping[FileIdx] = std::distance(Files.begin(), ExistingFile);
923 } else {
924 CUFileMapping[FileIdx] = Files.size();
925 Files.push_back(File);
926 }
927 }
928 }
929 for (const auto &Seq : LineTable->Sequences) {
930 LnStats.NumBytes += Seq.HighPC - Seq.LowPC;
931 // Ignore the `end_sequence` entry, since it's not interesting for us.
932 LnStats.NumEntries += Seq.LastRowIndex - Seq.FirstRowIndex - 1;
933 for (size_t RowIdx = Seq.FirstRowIndex; RowIdx < Seq.LastRowIndex - 1;
934 ++RowIdx) {
935 auto Entry = LineTable->Rows[RowIdx];
936 if (Entry.IsStmt)
937 LnStats.NumIsStmtEntries += 1;
938 assert(CUFileMapping.contains(Entry.File) &&
939 "Should have been collected earlier!");
940 uint16_t MappedFile = CUFileMapping[Entry.File];
941 UniqueLines.insert({Entry.Line, Entry.Column, MappedFile});
942 if (Entry.Line != 0) {
943 UniqueNonZeroLines.insert({Entry.Line, Entry.Column, MappedFile});
944 } else {
945 auto EntryStartAddress = Entry.Address.Address;
946 auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address;
947 LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress;
948 }
949 }
950 }
951 }
952 }
953
954 LnStats.NumUniqueEntries = UniqueLines.size();
955 LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size();
956
957 /// Resolve CrossCU references.
958 collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
959 LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
960 CrossCUReferencesToBeResolved);
961
962 /// Collect the sizes of debug sections.
963 SectionSizes Sizes;
964 calculateSectionSizes(Obj, Sizes, Filename);
965
966 /// The version number should be increased every time the algorithm is changed
967 /// (including bug fixes). New metrics may be added without increasing the
968 /// version.
969 unsigned Version = 9;
970 SaturatingUINT64 VarParamTotal = 0;
971 SaturatingUINT64 VarParamUnique = 0;
972 SaturatingUINT64 VarParamWithLoc = 0;
973 SaturatingUINT64 NumFunctions = 0;
974 SaturatingUINT64 NumInlinedFunctions = 0;
975 SaturatingUINT64 NumFuncsWithSrcLoc = 0;
976 SaturatingUINT64 NumAbstractOrigins = 0;
977 SaturatingUINT64 ParamTotal = 0;
978 SaturatingUINT64 ParamWithType = 0;
979 SaturatingUINT64 ParamWithLoc = 0;
980 SaturatingUINT64 ParamWithSrcLoc = 0;
981 SaturatingUINT64 LocalVarTotal = 0;
982 SaturatingUINT64 LocalVarWithType = 0;
983 SaturatingUINT64 LocalVarWithSrcLoc = 0;
984 SaturatingUINT64 LocalVarWithLoc = 0;
985 for (auto &Entry : Statistics) {
986 PerFunctionStats &Stats = Entry.getValue();
987 uint64_t TotalVars = Stats.VarsInFunction.size() *
988 (Stats.NumFnInlined + Stats.NumFnOutOfLine);
989 // Count variables in global scope.
990 if (!Stats.IsFunction)
991 TotalVars =
992 Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
993 uint64_t Constants = Stats.ConstantMembers;
994 VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
995 VarParamTotal += TotalVars;
996 VarParamUnique += Stats.VarsInFunction.size();
997 LLVM_DEBUG(for (auto &V
998 : Stats.VarsInFunction) llvm::dbgs()
999 << Entry.getKey() << ": " << V.getKey() << "\n");
1000 NumFunctions += Stats.IsFunction;
1001 NumFuncsWithSrcLoc += Stats.HasSourceLocation;
1002 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
1003 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
1004 ParamTotal += Stats.NumParams;
1005 ParamWithType += Stats.NumParamTypes;
1006 ParamWithLoc += Stats.NumParamLocations;
1007 ParamWithSrcLoc += Stats.NumParamSourceLocations;
1008 LocalVarTotal += Stats.NumLocalVars;
1009 LocalVarWithType += Stats.NumLocalVarTypes;
1010 LocalVarWithLoc += Stats.NumLocalVarLocations;
1011 LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
1012 }
1013
1014 // Print summary.
1015 OS.SetBufferSize(1024);
1016 json::OStream J(OS, 2);
1017 J.objectBegin();
1018 J.attribute("version", Version);
1019 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
1020 llvm::dbgs() << "---------------------------------\n");
1021
1022 printDatum(J, "file", Filename.str());
1023 printDatum(J, "format", FormatName);
1024
1025 printDatum(J, "#functions", NumFunctions.Value);
1026 printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
1027 printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
1028 printDatum(J, "#inlined functions with abstract origins",
1029 NumAbstractOrigins.Value);
1030
1031 // This includes local variables and formal parameters.
1032 printDatum(J, "#unique source variables", VarParamUnique.Value);
1033 printDatum(J, "#source variables", VarParamTotal.Value);
1034 printDatum(J, "#source variables with location", VarParamWithLoc.Value);
1035
1036 printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
1037 printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
1038 printDatum(J, "#call site parameter DIEs",
1039 GlobalStats.CallSiteParamDIEs.Value);
1040
1041 printDatum(J, "sum_all_variables(#bytes in parent scope)",
1042 GlobalStats.ScopeBytes.Value);
1043 printDatum(J,
1044 "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
1045 GlobalStats.TotalBytesCovered.Value);
1046 printDatum(J,
1047 "sum_all_variables(#bytes in parent scope covered by "
1048 "DW_AT_location)",
1049 GlobalStats.ScopeBytesCovered.Value);
1050 printDatum(J,
1051 "sum_all_variables(#bytes in parent scope covered by "
1052 "DW_OP_entry_value)",
1053 GlobalStats.ScopeEntryValueBytesCovered.Value);
1054
1055 printDatum(J, "sum_all_params(#bytes in parent scope)",
1056 GlobalStats.ParamScopeBytes.Value);
1057 printDatum(J,
1058 "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
1059 GlobalStats.ParamScopeBytesCovered.Value);
1060 printDatum(J,
1061 "sum_all_params(#bytes in parent scope covered by "
1062 "DW_OP_entry_value)",
1063 GlobalStats.ParamScopeEntryValueBytesCovered.Value);
1064
1065 printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
1066 GlobalStats.LocalVarScopeBytes.Value);
1067 printDatum(J,
1068 "sum_all_local_vars(#bytes in parent scope covered by "
1069 "DW_AT_location)",
1070 GlobalStats.LocalVarScopeBytesCovered.Value);
1071 printDatum(J,
1072 "sum_all_local_vars(#bytes in parent scope covered by "
1073 "DW_OP_entry_value)",
1074 GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
1075
1076 printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
1077 printDatum(J, "#bytes within inlined functions",
1078 GlobalStats.InlineFunctionSize.Value);
1079
1080 // Print the summary for formal parameters.
1081 printDatum(J, "#params", ParamTotal.Value);
1082 printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
1083 printDatum(J, "#params with type", ParamWithType.Value);
1084 printDatum(J, "#params with binary location", ParamWithLoc.Value);
1085
1086 // Print the summary for local variables.
1087 printDatum(J, "#local vars", LocalVarTotal.Value);
1088 printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
1089 printDatum(J, "#local vars with type", LocalVarWithType.Value);
1090 printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
1091
1092 // Print the debug section sizes.
1093 printSectionSizes(J, Sizes);
1094
1095 // Print the location statistics for variables (includes local variables
1096 // and formal parameters).
1097 printDatum(J, "#variables processed by location statistics",
1098 LocStats.NumVarParam.Value);
1099 printLocationStats(J, "#variables", LocStats.VarParamLocStats);
1100 printLocationStats(J, "#variables - entry values",
1101 LocStats.VarParamNonEntryValLocStats);
1102
1103 // Print the location statistics for formal parameters.
1104 printDatum(J, "#params processed by location statistics",
1105 LocStats.NumParam.Value);
1106 printLocationStats(J, "#params", LocStats.ParamLocStats);
1107 printLocationStats(J, "#params - entry values",
1108 LocStats.ParamNonEntryValLocStats);
1109
1110 // Print the location statistics for local variables.
1111 printDatum(J, "#local vars processed by location statistics",
1112 LocStats.NumVar.Value);
1113 printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
1114 printLocationStats(J, "#local vars - entry values",
1115 LocStats.LocalVarNonEntryValLocStats);
1116
1117 // Print line statistics for the object file.
1118 printDatum(J, "#bytes with line information", LnStats.NumBytes.Value);
1119 printDatum(J, "#bytes with line-0 locations", LnStats.NumLineZeroBytes.Value);
1120 printDatum(J, "#line entries", LnStats.NumEntries.Value);
1121 printDatum(J, "#line entries (is_stmt)", LnStats.NumIsStmtEntries.Value);
1122 printDatum(J, "#line entries (unique)", LnStats.NumUniqueEntries.Value);
1123 printDatum(J, "#line entries (unique non-0)",
1124 LnStats.NumUniqueNonZeroEntries.Value);
1125
1126 J.objectEnd();
1127 OS << '\n';
1128 LLVM_DEBUG(
1129 llvm::dbgs() << "Total Availability: "
1130 << (VarParamTotal.Value
1131 ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1132 VarParamTotal.Value)
1133 : 0)
1134 << "%\n";
1135 llvm::dbgs() << "PC Ranges covered: "
1136 << (GlobalStats.ScopeBytes.Value
1137 ? (int)std::round(
1138 (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1139 GlobalStats.ScopeBytes.Value)
1140 : 0)
1141 << "%\n");
1142 return true;
1143 }
1144