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

source code of llvm/tools/llvm-dwarfdump/Statistics.cpp