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/LowLevel/DWARFExpression.h"
16#include "llvm/Object/ObjectFile.h"
17#include "llvm/Support/JSON.h"
18
19#define DEBUG_TYPE "dwarfdump"
20using namespace llvm;
21using namespace llvm::dwarfdump;
22using namespace llvm::object;
23
24namespace {
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.
29constexpr int NumOfCoverageCategories = 12;
30
31/// This is used for zero location coverage bucket.
32constexpr unsigned ZeroCoverageBucket = 0;
33
34/// The UINT64_MAX is used as an indication of the overflow.
35constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
36
37/// This represents variables DIE offsets.
38using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
39/// This maps function DIE offset to its variables.
40using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
41/// This represents function DIE offsets containing an abstract_origin.
42using 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.
48struct SaturatingUINT64 {
49 /// Number that represents the stats.
50 uint64_t Value;
51
52 SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
53
54 void operator++(int) { return *this += 1; }
55 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.
66struct DIELocation {
67 DWARFUnit *DwUnit;
68 uint64_t DIEOffset;
69 DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
70 : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
71};
72/// This represents DWARF locations of CrossCU referencing DIEs.
73using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
74
75/// This maps function DIE offset to its DWARF CU.
76using 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).
80struct 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.
119struct 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.
162struct 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.
194struct 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.
205static 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.
235static 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(Kind: DINameKind::LinkageName);
241
242 // Prefix + Name is enough for local variables and parameters.
243 if (!Prefix.empty() && Prefix != "g")
244 return IDStr;
245
246 auto DeclFile = Die.findRecursively(Attrs: 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 FileIndex: dwarf::toUnsigned(V: DeclFile, Default: 0), CompDir: U->getCompilationDir(),
253 Kind: DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Result&: File))
254 File = std::string(sys::path::filename(path: File));
255 }
256 ID << ":" << (File.empty() ? "/" : File);
257 ID << ":"
258 << dwarf::toUnsigned(V: Die.findRecursively(Attrs: dwarf::DW_AT_decl_line), Default: 0);
259 return IDStr;
260}
261
262/// Return the number of bytes in the overlap of ranges A and B.
263static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
264 uint64_t Lower = std::max(a: A.LowPC, b: B.LowPC);
265 uint64_t Upper = std::min(a: A.HighPC, b: B.HighPC);
266 if (Lower >= Upper)
267 return 0;
268 return Upper - Lower;
269}
270
271/// Collect debug info quality metrics for one DIE.
272static 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(Attr: 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(Attr: dwarf::DW_AT_declaration))
318 return;
319
320 if (Die.findRecursively(Attrs: dwarf::DW_AT_decl_file) &&
321 Die.findRecursively(Attrs: dwarf::DW_AT_decl_line))
322 HasSrcLoc = true;
323
324 if (Die.findRecursively(Attrs: dwarf::DW_AT_type))
325 HasType = true;
326
327 if (Die.find(Attr: dwarf::DW_AT_abstract_origin)) {
328 if (Die.find(Attr: dwarf::DW_AT_location) || Die.find(Attr: dwarf::DW_AT_const_value)) {
329 if (AbstractOriginVariables) {
330 auto Offset = Die.find(Attr: dwarf::DW_AT_abstract_origin);
331 // Do not track this variable any more, since it has location
332 // coverage.
333 llvm::erase(C&: *AbstractOriginVariables, V: (*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(D, Die.getDwarfUnit()->getContext().isLittleEndian());
345 DWARFExpression Expression(Data, U->getAddressByteSize(),
346 U->getFormParams().Format);
347 // Consider the expression containing the DW_OP_entry_value as
348 // an entry value.
349 return llvm::any_of(Range&: Expression, P: [](const DWARFExpression::Operation &Op) {
350 return Op.getCode() == dwarf::DW_OP_entry_value ||
351 Op.getCode() == dwarf::DW_OP_GNU_entry_value;
352 });
353 };
354
355 if (Die.find(Attr: dwarf::DW_AT_const_value)) {
356 // This catches constant members *and* variables.
357 HasLoc = true;
358 ScopeBytesCovered = BytesInScope;
359 TotalBytesCovered = BytesInScope;
360 } else {
361 // Handle variables and function arguments.
362 Expected<std::vector<DWARFLocationExpression>> Loc =
363 Die.getLocations(Attr: dwarf::DW_AT_location);
364 if (!Loc) {
365 consumeError(Err: Loc.takeError());
366 } else {
367 HasLoc = true;
368 // Get PC coverage.
369 auto Default = find_if(
370 Range&: *Loc, P: [](const DWARFLocationExpression &L) { return !L.Range; });
371 if (Default != Loc->end()) {
372 // Assume the entire range is covered by a single location.
373 ScopeBytesCovered = BytesInScope;
374 TotalBytesCovered = BytesInScope;
375 } else {
376 // Caller checks this Expected result already, it cannot fail.
377 auto ScopeRanges = cantFail(ValOrErr: Die.getParent().getAddressRanges());
378 for (auto Entry : *Loc) {
379 TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
380 uint64_t ScopeBytesCoveredByEntry = 0;
381 // Calculate how many bytes of the parent scope this entry covers.
382 // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
383 // address ranges defined by the bounded location descriptions of a
384 // location list may overlap". So in theory a variable can have
385 // multiple simultaneous locations, which would make this calculation
386 // misleading because we will count the overlapped areas
387 // twice. However, clang does not currently emit DWARF like this.
388 for (DWARFAddressRange R : ScopeRanges) {
389 ScopeBytesCoveredByEntry += calculateOverlap(A: *Entry.Range, B: R);
390 }
391 ScopeBytesCovered += ScopeBytesCoveredByEntry;
392 if (IsEntryValue(Entry.Expr))
393 BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
394 }
395 }
396 }
397 }
398
399 // Calculate the debug location statistics.
400 if (BytesInScope && !DeferLocStats) {
401 LocStats.NumVarParam.Value++;
402 if (IsParam)
403 LocStats.NumParam.Value++;
404 else if (IsLocalVar)
405 LocStats.NumVar.Value++;
406
407 collectLocStats(ScopeBytesCovered, BytesInScope, VarParamLocStats&: LocStats.VarParamLocStats,
408 ParamLocStats&: LocStats.ParamLocStats, LocalVarLocStats&: LocStats.LocalVarLocStats, IsParam,
409 IsLocalVar);
410 // Non debug entry values coverage statistics.
411 collectLocStats(ScopeBytesCovered: ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
412 VarParamLocStats&: LocStats.VarParamNonEntryValLocStats,
413 ParamLocStats&: LocStats.ParamNonEntryValLocStats,
414 LocalVarLocStats&: LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
415 }
416
417 // Collect PC range coverage data.
418 if (DWARFDie D =
419 Die.getAttributeValueAsReferencedDie(Attr: dwarf::DW_AT_abstract_origin))
420 Die = D;
421
422 std::string VarID = constructDieID(Die, Prefix: VarPrefix);
423 FnStats.VarsInFunction.insert(key: VarID);
424
425 GlobalStats.TotalBytesCovered += TotalBytesCovered;
426 if (BytesInScope) {
427 GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
428 GlobalStats.ScopeBytes += BytesInScope;
429 GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
430 if (IsParam) {
431 GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
432 GlobalStats.ParamScopeBytes += BytesInScope;
433 GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
434 } else if (IsLocalVar) {
435 GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
436 GlobalStats.LocalVarScopeBytes += BytesInScope;
437 GlobalStats.LocalVarScopeEntryValueBytesCovered +=
438 BytesEntryValuesCovered;
439 }
440 assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
441 }
442
443 if (IsConstantMember) {
444 FnStats.ConstantMembers++;
445 return;
446 }
447
448 FnStats.TotalVarWithLoc += (unsigned)HasLoc;
449
450 if (Die.find(Attr: dwarf::DW_AT_artificial)) {
451 FnStats.NumArtificial++;
452 return;
453 }
454
455 if (IsParam) {
456 FnStats.NumParams++;
457 if (HasType)
458 FnStats.NumParamTypes++;
459 if (HasSrcLoc)
460 FnStats.NumParamSourceLocations++;
461 if (HasLoc)
462 FnStats.NumParamLocations++;
463 } else if (IsLocalVar) {
464 FnStats.NumLocalVars++;
465 if (HasType)
466 FnStats.NumLocalVarTypes++;
467 if (HasSrcLoc)
468 FnStats.NumLocalVarSourceLocations++;
469 if (HasLoc)
470 FnStats.NumLocalVarLocations++;
471 }
472}
473
474/// Recursively collect variables from subprogram with DW_AT_inline attribute.
475static void collectAbstractOriginFnInfo(
476 DWARFDie Die, uint64_t SPOffset,
477 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
478 AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
479 DWARFDie Child = Die.getFirstChild();
480 while (Child) {
481 const dwarf::Tag ChildTag = Child.getTag();
482 if (ChildTag == dwarf::DW_TAG_formal_parameter ||
483 ChildTag == dwarf::DW_TAG_variable) {
484 GlobalAbstractOriginFnInfo[SPOffset].push_back(Elt: Child.getOffset());
485 LocalAbstractOriginFnInfo[SPOffset].push_back(Elt: Child.getOffset());
486 } else if (ChildTag == dwarf::DW_TAG_lexical_block)
487 collectAbstractOriginFnInfo(Die: Child, SPOffset, GlobalAbstractOriginFnInfo,
488 LocalAbstractOriginFnInfo);
489 Child = Child.getSibling();
490 }
491}
492
493/// Recursively collect debug info quality metrics.
494static void collectStatsRecursive(
495 DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
496 uint64_t BytesInScope, uint32_t InlineDepth,
497 StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
498 LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
499 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
500 AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
501 FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
502 AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
503 // Skip NULL nodes.
504 if (Die.isNULL())
505 return;
506
507 const dwarf::Tag Tag = Die.getTag();
508 // Skip function types.
509 if (Tag == dwarf::DW_TAG_subroutine_type)
510 return;
511
512 // Handle any kind of lexical scope.
513 const bool HasAbstractOrigin =
514 Die.find(Attr: dwarf::DW_AT_abstract_origin) != std::nullopt;
515 const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
516 const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
517 const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
518 // We want to know how many variables (with abstract_origin) don't have
519 // location info.
520 const bool IsCandidateForZeroLocCovTracking =
521 (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
522
523 AbstractOriginVarsTy AbstractOriginVars;
524
525 // Get the vars of the inlined fn, so the locstats
526 // reports the missing vars (with coverage 0%).
527 if (IsCandidateForZeroLocCovTracking) {
528 auto OffsetFn = Die.find(Attr: dwarf::DW_AT_abstract_origin);
529 if (OffsetFn) {
530 uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
531 if (auto It = LocalAbstractOriginFnInfo.find(Val: OffsetOfInlineFnCopy);
532 It != LocalAbstractOriginFnInfo.end()) {
533 AbstractOriginVars = It->second;
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(Elt: 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(Attr: dwarf::DW_AT_declaration))
552 return;
553
554 // Check for call sites.
555 if (Die.find(Attr: dwarf::DW_AT_call_file) && Die.find(Attr: dwarf::DW_AT_call_line))
556 GlobalStats.CallSiteEntries++;
557
558 // PC Ranges.
559 auto RangesOrError = Die.getAddressRanges();
560 if (!RangesOrError) {
561 llvm::consumeError(Err: 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(Attr: 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(Attrs: dwarf::DW_AT_abstract_origin))
590 FnStats.NumAbstractOrigins++;
591 } else {
592 FnStats.NumFnOutOfLine++;
593 }
594 if (Die.findRecursively(Attrs: dwarf::DW_AT_decl_file) &&
595 Die.findRecursively(Attrs: 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, AbstractOriginVariables: 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(Input: LexicalBlockIndex++) + '.';
628 if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
629 ChildVarPrefix += 'p' + toHex(Input: FormalParameterIndex++) + '.';
630
631 collectStatsRecursive(
632 Die: Child, FnPrefix, VarPrefix: 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/// \{
663static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
664 if (Value == OverflowValue)
665 J.attribute(Key, Contents: "overflowed");
666 else
667 J.attribute(Key, Contents: Value);
668
669 LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
670}
671
672static void printLocationStats(json::OStream &J, const char *Key,
673 std::vector<SaturatingUINT64> &LocationStats) {
674 if (LocationStats[0].Value == OverflowValue)
675 J.attribute(Key: (Twine(Key) +
676 " with (0%,10%) of parent scope covered by DW_AT_location")
677 .str(),
678 Contents: "overflowed");
679 else
680 J.attribute(
681 Key: (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
682 .str(),
683 Contents: 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(Key: (Twine(Key) +
691 " with (0%,10%) of parent scope covered by DW_AT_location")
692 .str(),
693 Contents: "overflowed");
694 else
695 J.attribute(Key: (Twine(Key) +
696 " with (0%,10%) of parent scope covered by DW_AT_location")
697 .str(),
698 Contents: 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(Key: (Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
707 Twine(i * 10) +
708 "%) of parent scope covered by DW_AT_location")
709 .str(),
710 Contents: "overflowed");
711 else
712 J.attribute(Key: (Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
713 Twine(i * 10) +
714 "%) of parent scope covered by DW_AT_location")
715 .str(),
716 Contents: 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 Key: (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
725 .str(),
726 Contents: "overflowed");
727 else
728 J.attribute(
729 Key: (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
730 .str(),
731 Contents: 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
738static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
739 for (const auto &It : Sizes.DebugSectionSizes)
740 J.attribute(Key: (Twine("#bytes in ") + It.first).str(), Contents: 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.
745static 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(Attr: dwarf::DW_AT_location) ||
754 Child.find(Attr: dwarf::DW_AT_const_value))) {
755 auto OffsetVar = Child.find(Attr: dwarf::DW_AT_abstract_origin);
756 if (OffsetVar)
757 llvm::erase(C&: AbstractOriginVars, V: (*OffsetVar).getRawUValue());
758 } else if (ChildTag == dwarf::DW_TAG_lexical_block)
759 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin: 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.
768static 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(Offset: FnOffset);
777 auto FnCopy = FnDieWithAbstractOrigin.find(Attr: 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 auto It = LocalAbstractOriginFnInfo.find(Val: FnCopyRawUValue);
786 if (It == LocalAbstractOriginFnInfo.end())
787 continue;
788 AbstractOriginVars = It->second;
789 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
790 AbstractOriginVars);
791
792 for (auto Offset : AbstractOriginVars) {
793 LocStats.NumVarParam++;
794 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
795 auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
796 if (Tag == dwarf::DW_TAG_formal_parameter) {
797 LocStats.NumParam++;
798 LocStats.ParamLocStats[ZeroCoverageBucket]++;
799 } else if (Tag == dwarf::DW_TAG_variable) {
800 LocStats.NumVar++;
801 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
802 }
803 }
804 ProcessedFns.push_back(Elt: FnOffset);
805 }
806 for (auto ProcessedFn : ProcessedFns)
807 llvm::erase(C&: FnsWithAbstractOriginToBeProcessed, V: ProcessedFn);
808}
809
810/// Collect zero location coverage for inlined variables which refer to
811/// a DW_AT_inline copy of subprogram that is in a different CU.
812static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
813 LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
814 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
815 CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
816 for (const auto &CrossCUReferenceToBeResolved :
817 CrossCUReferencesToBeResolved) {
818 DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
819 DWARFDie FnDIEWithCrossCUReferencing =
820 DwUnit->getDIEForOffset(Offset: CrossCUReferenceToBeResolved.DIEOffset);
821 auto FnCopy =
822 FnDIEWithCrossCUReferencing.find(Attr: dwarf::DW_AT_abstract_origin);
823 if (!FnCopy)
824 continue;
825 uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
826 AbstractOriginVarsTy AbstractOriginVars =
827 GlobalAbstractOriginFnInfo[FnCopyRawUValue];
828 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin: FnDIEWithCrossCUReferencing,
829 AbstractOriginVars);
830 for (auto Offset : AbstractOriginVars) {
831 LocStats.NumVarParam++;
832 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
833 auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
834 ->getDIEForOffset(Offset)
835 .getTag();
836 if (Tag == dwarf::DW_TAG_formal_parameter) {
837 LocStats.NumParam++;
838 LocStats.ParamLocStats[ZeroCoverageBucket]++;
839 } else if (Tag == dwarf::DW_TAG_variable) {
840 LocStats.NumVar++;
841 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
842 }
843 }
844 }
845}
846
847/// \}
848
849/// Collect debug info quality metrics for an entire DIContext.
850///
851/// Do the impossible and reduce the quality of the debug info down to a few
852/// numbers. The idea is to condense the data into numbers that can be tracked
853/// over time to identify trends in newer compiler versions and gauge the effect
854/// of particular optimizations. The raw numbers themselves are not particularly
855/// useful, only the delta between compiling the same program with different
856/// compilers is.
857bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
858 const Twine &Filename,
859 raw_ostream &OS) {
860 StringRef FormatName = Obj.getFileFormatName();
861 GlobalStats GlobalStats;
862 LocationStats LocStats;
863 LineStats LnStats;
864 StringMap<PerFunctionStats> Statistics;
865 // This variable holds variable information for functions with
866 // abstract_origin globally, across all CUs.
867 AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
868 // This variable holds information about the CU of a function with
869 // abstract_origin.
870 FunctionDIECUTyMap AbstractOriginFnCUs;
871 CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
872 // Tuple representing a single source code position in the line table. Fields
873 // are respectively: Line, Col, File, where 'File' is an index into the Files
874 // vector below.
875 using LineTuple = std::tuple<uint32_t, uint16_t, uint16_t>;
876 SmallVector<std::string> Files;
877 DenseSet<LineTuple> UniqueLines;
878 DenseSet<LineTuple> UniqueNonZeroLines;
879
880 for (const auto &CU : DICtx.compile_units()) {
881 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(ExtractUnitDIEOnly: false)) {
882 // This variable holds variable information for functions with
883 // abstract_origin, but just for the current CU.
884 AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
885 FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
886
887 collectStatsRecursive(
888 Die: CUDie, FnPrefix: "/", VarPrefix: "g", BytesInScope: 0, InlineDepth: 0, FnStatMap&: Statistics, GlobalStats, LocStats,
889 AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
890 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
891
892 // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
893 // out-of-order DWARF functions that have been processed within it,
894 // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
895 // references.
896 collectZeroLocCovForVarsWithAbstractOrigin(
897 DwUnit: CUDie.getDwarfUnit(), GlobalStats, LocStats,
898 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
899
900 // Collect all CrossCU references into CrossCUReferencesToBeResolved.
901 for (auto CrossCUReferencingDIEOffset :
902 FnsWithAbstractOriginToBeProcessed)
903 CrossCUReferencesToBeResolved.push_back(
904 Elt: DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
905 }
906 const auto *LineTable = DICtx.getLineTableForUnit(U: CU.get());
907 std::optional<uint64_t> LastFileIdxOpt;
908 if (LineTable)
909 LastFileIdxOpt = LineTable->getLastValidFileIndex();
910 if (LastFileIdxOpt) {
911 // Each CU has its own file index; in order to track unique line entries
912 // across CUs, we therefore need to map each CU file index to a global
913 // file index, which we store here.
914 DenseMap<uint64_t, uint16_t> CUFileMapping;
915 for (uint64_t FileIdx = 0; FileIdx <= *LastFileIdxOpt; ++FileIdx) {
916 std::string File;
917 if (LineTable->getFileNameByIndex(
918 FileIndex: FileIdx, CompDir: CU->getCompilationDir(),
919 Kind: DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
920 Result&: File)) {
921 auto ExistingFile = llvm::find(Range&: Files, Val: File);
922 if (ExistingFile != Files.end()) {
923 CUFileMapping[FileIdx] = std::distance(first: Files.begin(), last: ExistingFile);
924 } else {
925 CUFileMapping[FileIdx] = Files.size();
926 Files.push_back(Elt: File);
927 }
928 }
929 }
930 for (const auto &Seq : LineTable->Sequences) {
931 LnStats.NumBytes += Seq.HighPC - Seq.LowPC;
932 // Ignore the `end_sequence` entry, since it's not interesting for us.
933 LnStats.NumEntries += Seq.LastRowIndex - Seq.FirstRowIndex - 1;
934 for (size_t RowIdx = Seq.FirstRowIndex; RowIdx < Seq.LastRowIndex - 1;
935 ++RowIdx) {
936 auto Entry = LineTable->Rows[RowIdx];
937 if (Entry.IsStmt)
938 LnStats.NumIsStmtEntries += 1;
939 assert(CUFileMapping.contains(Entry.File) &&
940 "Should have been collected earlier!");
941 uint16_t MappedFile = CUFileMapping[Entry.File];
942 UniqueLines.insert(V: {Entry.Line, Entry.Column, MappedFile});
943 if (Entry.Line != 0) {
944 UniqueNonZeroLines.insert(V: {Entry.Line, Entry.Column, MappedFile});
945 } else {
946 auto EntryStartAddress = Entry.Address.Address;
947 auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address;
948 LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress;
949 }
950 }
951 }
952 }
953 }
954
955 LnStats.NumUniqueEntries = UniqueLines.size();
956 LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size();
957
958 /// Resolve CrossCU references.
959 collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
960 LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
961 CrossCUReferencesToBeResolved);
962
963 /// Collect the sizes of debug sections.
964 SectionSizes Sizes;
965 calculateSectionSizes(Obj, Sizes, Filename);
966
967 /// The version number should be increased every time the algorithm is changed
968 /// (including bug fixes). New metrics may be added without increasing the
969 /// version.
970 unsigned Version = 9;
971 SaturatingUINT64 VarParamTotal = 0;
972 SaturatingUINT64 VarParamUnique = 0;
973 SaturatingUINT64 VarParamWithLoc = 0;
974 SaturatingUINT64 NumFunctions = 0;
975 SaturatingUINT64 NumOutOfLineFunctions = 0;
976 SaturatingUINT64 NumInlinedFunctions = 0;
977 SaturatingUINT64 NumFuncsWithSrcLoc = 0;
978 SaturatingUINT64 NumAbstractOrigins = 0;
979 SaturatingUINT64 ParamTotal = 0;
980 SaturatingUINT64 ParamWithType = 0;
981 SaturatingUINT64 ParamWithLoc = 0;
982 SaturatingUINT64 ParamWithSrcLoc = 0;
983 SaturatingUINT64 LocalVarTotal = 0;
984 SaturatingUINT64 LocalVarWithType = 0;
985 SaturatingUINT64 LocalVarWithSrcLoc = 0;
986 SaturatingUINT64 LocalVarWithLoc = 0;
987 for (auto &Entry : Statistics) {
988 PerFunctionStats &Stats = Entry.getValue();
989 uint64_t TotalVars = Stats.VarsInFunction.size() *
990 (Stats.NumFnInlined + Stats.NumFnOutOfLine);
991 // Count variables in global scope.
992 if (!Stats.IsFunction)
993 TotalVars =
994 Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
995 uint64_t Constants = Stats.ConstantMembers;
996 VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
997 VarParamTotal += TotalVars;
998 VarParamUnique += Stats.VarsInFunction.size();
999 LLVM_DEBUG(for (auto &V
1000 : Stats.VarsInFunction) llvm::dbgs()
1001 << Entry.getKey() << ": " << V.getKey() << "\n");
1002 NumFunctions += Stats.IsFunction;
1003 NumFuncsWithSrcLoc += Stats.HasSourceLocation;
1004 NumOutOfLineFunctions += Stats.IsFunction * Stats.NumFnOutOfLine;
1005 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
1006 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
1007 ParamTotal += Stats.NumParams;
1008 ParamWithType += Stats.NumParamTypes;
1009 ParamWithLoc += Stats.NumParamLocations;
1010 ParamWithSrcLoc += Stats.NumParamSourceLocations;
1011 LocalVarTotal += Stats.NumLocalVars;
1012 LocalVarWithType += Stats.NumLocalVarTypes;
1013 LocalVarWithLoc += Stats.NumLocalVarLocations;
1014 LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
1015 }
1016
1017 // Print summary.
1018 OS.SetBufferSize(1024);
1019 json::OStream J(OS, 2);
1020 J.objectBegin();
1021 J.attribute(Key: "version", Contents: Version);
1022 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
1023 llvm::dbgs() << "---------------------------------\n");
1024
1025 printDatum(J, Key: "file", Value: Filename.str());
1026 printDatum(J, Key: "format", Value: FormatName);
1027
1028 printDatum(J, Key: "#functions", Value: NumFunctions.Value);
1029 printDatum(J, Key: "#functions with location", Value: NumFuncsWithSrcLoc.Value);
1030 printDatum(J, Key: "#out-of-line functions", Value: NumOutOfLineFunctions.Value);
1031 printDatum(J, Key: "#inlined functions", Value: NumInlinedFunctions.Value);
1032 printDatum(J, Key: "#inlined functions with abstract origins",
1033 Value: NumAbstractOrigins.Value);
1034
1035 // This includes local variables and formal parameters.
1036 printDatum(J, Key: "#unique source variables", Value: VarParamUnique.Value);
1037 printDatum(J, Key: "#source variables", Value: VarParamTotal.Value);
1038 printDatum(J, Key: "#source variables with location", Value: VarParamWithLoc.Value);
1039
1040 printDatum(J, Key: "#call site entries", Value: GlobalStats.CallSiteEntries.Value);
1041 printDatum(J, Key: "#call site DIEs", Value: GlobalStats.CallSiteDIEs.Value);
1042 printDatum(J, Key: "#call site parameter DIEs",
1043 Value: GlobalStats.CallSiteParamDIEs.Value);
1044
1045 printDatum(J, Key: "sum_all_variables(#bytes in parent scope)",
1046 Value: GlobalStats.ScopeBytes.Value);
1047 printDatum(J,
1048 Key: "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
1049 Value: GlobalStats.TotalBytesCovered.Value);
1050 printDatum(J,
1051 Key: "sum_all_variables(#bytes in parent scope covered by "
1052 "DW_AT_location)",
1053 Value: GlobalStats.ScopeBytesCovered.Value);
1054 printDatum(J,
1055 Key: "sum_all_variables(#bytes in parent scope covered by "
1056 "DW_OP_entry_value)",
1057 Value: GlobalStats.ScopeEntryValueBytesCovered.Value);
1058
1059 printDatum(J, Key: "sum_all_params(#bytes in parent scope)",
1060 Value: GlobalStats.ParamScopeBytes.Value);
1061 printDatum(J,
1062 Key: "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
1063 Value: GlobalStats.ParamScopeBytesCovered.Value);
1064 printDatum(J,
1065 Key: "sum_all_params(#bytes in parent scope covered by "
1066 "DW_OP_entry_value)",
1067 Value: GlobalStats.ParamScopeEntryValueBytesCovered.Value);
1068
1069 printDatum(J, Key: "sum_all_local_vars(#bytes in parent scope)",
1070 Value: GlobalStats.LocalVarScopeBytes.Value);
1071 printDatum(J,
1072 Key: "sum_all_local_vars(#bytes in parent scope covered by "
1073 "DW_AT_location)",
1074 Value: GlobalStats.LocalVarScopeBytesCovered.Value);
1075 printDatum(J,
1076 Key: "sum_all_local_vars(#bytes in parent scope covered by "
1077 "DW_OP_entry_value)",
1078 Value: GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
1079
1080 printDatum(J, Key: "#bytes within functions", Value: GlobalStats.FunctionSize.Value);
1081 printDatum(J, Key: "#bytes within inlined functions",
1082 Value: GlobalStats.InlineFunctionSize.Value);
1083
1084 // Print the summary for formal parameters.
1085 printDatum(J, Key: "#params", Value: ParamTotal.Value);
1086 printDatum(J, Key: "#params with source location", Value: ParamWithSrcLoc.Value);
1087 printDatum(J, Key: "#params with type", Value: ParamWithType.Value);
1088 printDatum(J, Key: "#params with binary location", Value: ParamWithLoc.Value);
1089
1090 // Print the summary for local variables.
1091 printDatum(J, Key: "#local vars", Value: LocalVarTotal.Value);
1092 printDatum(J, Key: "#local vars with source location", Value: LocalVarWithSrcLoc.Value);
1093 printDatum(J, Key: "#local vars with type", Value: LocalVarWithType.Value);
1094 printDatum(J, Key: "#local vars with binary location", Value: LocalVarWithLoc.Value);
1095
1096 // Print the debug section sizes.
1097 printSectionSizes(J, Sizes);
1098
1099 // Print the location statistics for variables (includes local variables
1100 // and formal parameters).
1101 printDatum(J, Key: "#variables processed by location statistics",
1102 Value: LocStats.NumVarParam.Value);
1103 printLocationStats(J, Key: "#variables", LocationStats&: LocStats.VarParamLocStats);
1104 printLocationStats(J, Key: "#variables - entry values",
1105 LocationStats&: LocStats.VarParamNonEntryValLocStats);
1106
1107 // Print the location statistics for formal parameters.
1108 printDatum(J, Key: "#params processed by location statistics",
1109 Value: LocStats.NumParam.Value);
1110 printLocationStats(J, Key: "#params", LocationStats&: LocStats.ParamLocStats);
1111 printLocationStats(J, Key: "#params - entry values",
1112 LocationStats&: LocStats.ParamNonEntryValLocStats);
1113
1114 // Print the location statistics for local variables.
1115 printDatum(J, Key: "#local vars processed by location statistics",
1116 Value: LocStats.NumVar.Value);
1117 printLocationStats(J, Key: "#local vars", LocationStats&: LocStats.LocalVarLocStats);
1118 printLocationStats(J, Key: "#local vars - entry values",
1119 LocationStats&: LocStats.LocalVarNonEntryValLocStats);
1120
1121 // Print line statistics for the object file.
1122 printDatum(J, Key: "#bytes with line information", Value: LnStats.NumBytes.Value);
1123 printDatum(J, Key: "#bytes with line-0 locations", Value: LnStats.NumLineZeroBytes.Value);
1124 printDatum(J, Key: "#line entries", Value: LnStats.NumEntries.Value);
1125 printDatum(J, Key: "#line entries (is_stmt)", Value: LnStats.NumIsStmtEntries.Value);
1126 printDatum(J, Key: "#line entries (unique)", Value: LnStats.NumUniqueEntries.Value);
1127 printDatum(J, Key: "#line entries (unique non-0)",
1128 Value: LnStats.NumUniqueNonZeroEntries.Value);
1129
1130 J.objectEnd();
1131 OS << '\n';
1132 LLVM_DEBUG(
1133 llvm::dbgs() << "Total Availability: "
1134 << (VarParamTotal.Value
1135 ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1136 VarParamTotal.Value)
1137 : 0)
1138 << "%\n";
1139 llvm::dbgs() << "PC Ranges covered: "
1140 << (GlobalStats.ScopeBytes.Value
1141 ? (int)std::round(
1142 (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1143 GlobalStats.ScopeBytes.Value)
1144 : 0)
1145 << "%\n");
1146 return true;
1147}
1148