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(toStringRef(Input: 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(Range&: Expression, P: [](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(Attr: 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(Attr: dwarf::DW_AT_location);
365 if (!Loc) {
366 consumeError(Err: Loc.takeError());
367 } else {
368 HasLoc = true;
369 // Get PC coverage.
370 auto Default = find_if(
371 Range&: *Loc, P: [](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(ValOrErr: 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(A: *Entry.Range, B: 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, VarParamLocStats&: LocStats.VarParamLocStats,
409 ParamLocStats&: LocStats.ParamLocStats, LocalVarLocStats&: LocStats.LocalVarLocStats, IsParam,
410 IsLocalVar);
411 // Non debug entry values coverage statistics.
412 collectLocStats(ScopeBytesCovered: ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
413 VarParamLocStats&: LocStats.VarParamNonEntryValLocStats,
414 ParamLocStats&: LocStats.ParamNonEntryValLocStats,
415 LocalVarLocStats&: LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
416 }
417
418 // Collect PC range coverage data.
419 if (DWARFDie D =
420 Die.getAttributeValueAsReferencedDie(Attr: dwarf::DW_AT_abstract_origin))
421 Die = D;
422
423 std::string VarID = constructDieID(Die, Prefix: VarPrefix);
424 FnStats.VarsInFunction.insert(key: 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(Attr: 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.
476static 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(Elt: Child.getOffset());
486 LocalAbstractOriginFnInfo[SPOffset].push_back(Elt: Child.getOffset());
487 } else if (ChildTag == dwarf::DW_TAG_lexical_block)
488 collectAbstractOriginFnInfo(Die: Child, SPOffset, GlobalAbstractOriginFnInfo,
489 LocalAbstractOriginFnInfo);
490 Child = Child.getSibling();
491 }
492}
493
494/// Recursively collect debug info quality metrics.
495static 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(Attr: 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(Attr: dwarf::DW_AT_abstract_origin);
530 if (OffsetFn) {
531 uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
532 if (auto It = LocalAbstractOriginFnInfo.find(Val: OffsetOfInlineFnCopy);
533 It != LocalAbstractOriginFnInfo.end()) {
534 AbstractOriginVars = It->second;
535 AbstractOriginVarsPtr = &AbstractOriginVars;
536 } else {
537 // This means that the DW_AT_inline fn copy is out of order
538 // or that the abstract_origin references another CU,
539 // so this abstract origin instance will be processed later.
540 FnsWithAbstractOriginToBeProcessed.push_back(Elt: Die.getOffset());
541 AbstractOriginVarsPtr = nullptr;
542 }
543 }
544 }
545
546 if (IsFunction || IsInlinedFunction || IsBlock) {
547 // Reset VarPrefix when entering a new function.
548 if (IsFunction || IsInlinedFunction)
549 VarPrefix = "v";
550
551 // Ignore forward declarations.
552 if (Die.find(Attr: dwarf::DW_AT_declaration))
553 return;
554
555 // Check for call sites.
556 if (Die.find(Attr: dwarf::DW_AT_call_file) && Die.find(Attr: dwarf::DW_AT_call_line))
557 GlobalStats.CallSiteEntries++;
558
559 // PC Ranges.
560 auto RangesOrError = Die.getAddressRanges();
561 if (!RangesOrError) {
562 llvm::consumeError(Err: RangesOrError.takeError());
563 return;
564 }
565
566 auto Ranges = RangesOrError.get();
567 uint64_t BytesInThisScope = 0;
568 for (auto Range : Ranges)
569 BytesInThisScope += Range.HighPC - Range.LowPC;
570
571 // Count the function.
572 if (!IsBlock) {
573 // Skip over abstract origins, but collect variables
574 // from it so it can be used for location statistics
575 // for inlined instancies.
576 if (Die.find(Attr: dwarf::DW_AT_inline)) {
577 uint64_t SPOffset = Die.getOffset();
578 AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
579 collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
580 LocalAbstractOriginFnInfo);
581 return;
582 }
583
584 std::string FnID = constructDieID(Die);
585 // We've seen an instance of this function.
586 auto &FnStats = FnStatMap[FnID];
587 FnStats.IsFunction = true;
588 if (IsInlinedFunction) {
589 FnStats.NumFnInlined++;
590 if (Die.findRecursively(Attrs: dwarf::DW_AT_abstract_origin))
591 FnStats.NumAbstractOrigins++;
592 } else {
593 FnStats.NumFnOutOfLine++;
594 }
595 if (Die.findRecursively(Attrs: dwarf::DW_AT_decl_file) &&
596 Die.findRecursively(Attrs: dwarf::DW_AT_decl_line))
597 FnStats.HasSourceLocation = true;
598 // Update function prefix.
599 FnPrefix = FnID;
600 }
601
602 if (BytesInThisScope) {
603 BytesInScope = BytesInThisScope;
604 if (IsFunction)
605 GlobalStats.FunctionSize += BytesInThisScope;
606 else if (IsInlinedFunction && InlineDepth == 0)
607 GlobalStats.InlineFunctionSize += BytesInThisScope;
608 }
609 } else {
610 // Not a scope, visit the Die itself. It could be a variable.
611 collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
612 FnStatMap, GlobalStats, LocStats, AbstractOriginVariables: AbstractOriginVarsPtr);
613 }
614
615 // Set InlineDepth correctly for child recursion
616 if (IsFunction)
617 InlineDepth = 0;
618 else if (IsInlinedFunction)
619 ++InlineDepth;
620
621 // Traverse children.
622 unsigned LexicalBlockIndex = 0;
623 unsigned FormalParameterIndex = 0;
624 DWARFDie Child = Die.getFirstChild();
625 while (Child) {
626 std::string ChildVarPrefix = VarPrefix;
627 if (Child.getTag() == dwarf::DW_TAG_lexical_block)
628 ChildVarPrefix += toHex(Input: LexicalBlockIndex++) + '.';
629 if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
630 ChildVarPrefix += 'p' + toHex(Input: FormalParameterIndex++) + '.';
631
632 collectStatsRecursive(
633 Die: Child, FnPrefix, VarPrefix: ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
634 GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
635 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
636 AbstractOriginVarsPtr);
637 Child = Child.getSibling();
638 }
639
640 if (!IsCandidateForZeroLocCovTracking)
641 return;
642
643 // After we have processed all vars of the inlined function (or function with
644 // an abstract_origin), we want to know how many variables have no location.
645 for (auto Offset : AbstractOriginVars) {
646 LocStats.NumVarParam++;
647 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
648 auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
649 if (!FnDie)
650 continue;
651 auto Tag = FnDie.getTag();
652 if (Tag == dwarf::DW_TAG_formal_parameter) {
653 LocStats.NumParam++;
654 LocStats.ParamLocStats[ZeroCoverageBucket]++;
655 } else if (Tag == dwarf::DW_TAG_variable) {
656 LocStats.NumVar++;
657 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
658 }
659 }
660}
661
662/// Print human-readable output.
663/// \{
664static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
665 if (Value == OverflowValue)
666 J.attribute(Key, Contents: "overflowed");
667 else
668 J.attribute(Key, Contents: Value);
669
670 LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
671}
672
673static void printLocationStats(json::OStream &J, const char *Key,
674 std::vector<SaturatingUINT64> &LocationStats) {
675 if (LocationStats[0].Value == OverflowValue)
676 J.attribute(Key: (Twine(Key) +
677 " with (0%,10%) of parent scope covered by DW_AT_location")
678 .str(),
679 Contents: "overflowed");
680 else
681 J.attribute(
682 Key: (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
683 .str(),
684 Contents: LocationStats[0].Value);
685 LLVM_DEBUG(
686 llvm::dbgs() << Key
687 << " with 0% of parent scope covered by DW_AT_location: \\"
688 << LocationStats[0].Value << '\n');
689
690 if (LocationStats[1].Value == OverflowValue)
691 J.attribute(Key: (Twine(Key) +
692 " with (0%,10%) of parent scope covered by DW_AT_location")
693 .str(),
694 Contents: "overflowed");
695 else
696 J.attribute(Key: (Twine(Key) +
697 " with (0%,10%) of parent scope covered by DW_AT_location")
698 .str(),
699 Contents: LocationStats[1].Value);
700 LLVM_DEBUG(llvm::dbgs()
701 << Key
702 << " with (0%,10%) of parent scope covered by DW_AT_location: "
703 << LocationStats[1].Value << '\n');
704
705 for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
706 if (LocationStats[i].Value == OverflowValue)
707 J.attribute(Key: (Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
708 Twine(i * 10) +
709 "%) of parent scope covered by DW_AT_location")
710 .str(),
711 Contents: "overflowed");
712 else
713 J.attribute(Key: (Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
714 Twine(i * 10) +
715 "%) of parent scope covered by DW_AT_location")
716 .str(),
717 Contents: LocationStats[i].Value);
718 LLVM_DEBUG(llvm::dbgs()
719 << Key << " with [" << (i - 1) * 10 << "%," << i * 10
720 << "%) of parent scope covered by DW_AT_location: "
721 << LocationStats[i].Value);
722 }
723 if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
724 J.attribute(
725 Key: (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
726 .str(),
727 Contents: "overflowed");
728 else
729 J.attribute(
730 Key: (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
731 .str(),
732 Contents: LocationStats[NumOfCoverageCategories - 1].Value);
733 LLVM_DEBUG(
734 llvm::dbgs() << Key
735 << " with 100% of parent scope covered by DW_AT_location: "
736 << LocationStats[NumOfCoverageCategories - 1].Value);
737}
738
739static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
740 for (const auto &It : Sizes.DebugSectionSizes)
741 J.attribute(Key: (Twine("#bytes in ") + It.first).str(), Contents: int64_t(It.second));
742}
743
744/// Stop tracking variables that contain abstract_origin with a location.
745/// This is used for out-of-order DW_AT_inline subprograms only.
746static void updateVarsWithAbstractOriginLocCovInfo(
747 DWARFDie FnDieWithAbstractOrigin,
748 AbstractOriginVarsTy &AbstractOriginVars) {
749 DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
750 while (Child) {
751 const dwarf::Tag ChildTag = Child.getTag();
752 if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
753 ChildTag == dwarf::DW_TAG_variable) &&
754 (Child.find(Attr: dwarf::DW_AT_location) ||
755 Child.find(Attr: dwarf::DW_AT_const_value))) {
756 auto OffsetVar = Child.find(Attr: dwarf::DW_AT_abstract_origin);
757 if (OffsetVar)
758 llvm::erase(C&: AbstractOriginVars, V: (*OffsetVar).getRawUValue());
759 } else if (ChildTag == dwarf::DW_TAG_lexical_block)
760 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin: Child, AbstractOriginVars);
761 Child = Child.getSibling();
762 }
763}
764
765/// Collect zero location coverage for inlined variables which refer to
766/// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
767/// Also cover the variables of a concrete function (represented with
768/// the DW_TAG_subprogram) with an abstract_origin attribute.
769static void collectZeroLocCovForVarsWithAbstractOrigin(
770 DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
771 AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
772 FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
773 // The next variable is used to filter out functions that have been processed,
774 // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
775 FunctionsWithAbstractOriginTy ProcessedFns;
776 for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
777 DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(Offset: FnOffset);
778 auto FnCopy = FnDieWithAbstractOrigin.find(Attr: dwarf::DW_AT_abstract_origin);
779 AbstractOriginVarsTy AbstractOriginVars;
780 if (!FnCopy)
781 continue;
782 uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
783 // If there is no entry within LocalAbstractOriginFnInfo for the given
784 // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
785 // CrossCU referencing.
786 auto It = LocalAbstractOriginFnInfo.find(Val: FnCopyRawUValue);
787 if (It == LocalAbstractOriginFnInfo.end())
788 continue;
789 AbstractOriginVars = It->second;
790 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
791 AbstractOriginVars);
792
793 for (auto Offset : AbstractOriginVars) {
794 LocStats.NumVarParam++;
795 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
796 auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
797 if (Tag == dwarf::DW_TAG_formal_parameter) {
798 LocStats.NumParam++;
799 LocStats.ParamLocStats[ZeroCoverageBucket]++;
800 } else if (Tag == dwarf::DW_TAG_variable) {
801 LocStats.NumVar++;
802 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
803 }
804 }
805 ProcessedFns.push_back(Elt: FnOffset);
806 }
807 for (auto ProcessedFn : ProcessedFns)
808 llvm::erase(C&: FnsWithAbstractOriginToBeProcessed, V: ProcessedFn);
809}
810
811/// Collect zero location coverage for inlined variables which refer to
812/// a DW_AT_inline copy of subprogram that is in a different CU.
813static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
814 LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
815 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
816 CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
817 for (const auto &CrossCUReferenceToBeResolved :
818 CrossCUReferencesToBeResolved) {
819 DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
820 DWARFDie FnDIEWithCrossCUReferencing =
821 DwUnit->getDIEForOffset(Offset: CrossCUReferenceToBeResolved.DIEOffset);
822 auto FnCopy =
823 FnDIEWithCrossCUReferencing.find(Attr: dwarf::DW_AT_abstract_origin);
824 if (!FnCopy)
825 continue;
826 uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
827 AbstractOriginVarsTy AbstractOriginVars =
828 GlobalAbstractOriginFnInfo[FnCopyRawUValue];
829 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin: FnDIEWithCrossCUReferencing,
830 AbstractOriginVars);
831 for (auto Offset : AbstractOriginVars) {
832 LocStats.NumVarParam++;
833 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
834 auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
835 ->getDIEForOffset(Offset)
836 .getTag();
837 if (Tag == dwarf::DW_TAG_formal_parameter) {
838 LocStats.NumParam++;
839 LocStats.ParamLocStats[ZeroCoverageBucket]++;
840 } else if (Tag == dwarf::DW_TAG_variable) {
841 LocStats.NumVar++;
842 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
843 }
844 }
845 }
846}
847
848/// \}
849
850/// Collect debug info quality metrics for an entire DIContext.
851///
852/// Do the impossible and reduce the quality of the debug info down to a few
853/// numbers. The idea is to condense the data into numbers that can be tracked
854/// over time to identify trends in newer compiler versions and gauge the effect
855/// of particular optimizations. The raw numbers themselves are not particularly
856/// useful, only the delta between compiling the same program with different
857/// compilers is.
858bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
859 const Twine &Filename,
860 raw_ostream &OS) {
861 StringRef FormatName = Obj.getFileFormatName();
862 GlobalStats GlobalStats;
863 LocationStats LocStats;
864 LineStats LnStats;
865 StringMap<PerFunctionStats> Statistics;
866 // This variable holds variable information for functions with
867 // abstract_origin globally, across all CUs.
868 AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
869 // This variable holds information about the CU of a function with
870 // abstract_origin.
871 FunctionDIECUTyMap AbstractOriginFnCUs;
872 CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
873 // Tuple representing a single source code position in the line table. Fields
874 // are respectively: Line, Col, File, where 'File' is an index into the Files
875 // vector below.
876 using LineTuple = std::tuple<uint32_t, uint16_t, uint16_t>;
877 SmallVector<std::string> Files;
878 DenseSet<LineTuple> UniqueLines;
879 DenseSet<LineTuple> UniqueNonZeroLines;
880
881 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
882 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(ExtractUnitDIEOnly: false)) {
883 // This variable holds variable information for functions with
884 // abstract_origin, but just for the current CU.
885 AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
886 FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
887
888 collectStatsRecursive(
889 Die: CUDie, FnPrefix: "/", VarPrefix: "g", BytesInScope: 0, InlineDepth: 0, FnStatMap&: Statistics, GlobalStats, LocStats,
890 AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
891 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
892
893 // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
894 // out-of-order DWARF functions that have been processed within it,
895 // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
896 // references.
897 collectZeroLocCovForVarsWithAbstractOrigin(
898 DwUnit: CUDie.getDwarfUnit(), GlobalStats, LocStats,
899 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
900
901 // Collect all CrossCU references into CrossCUReferencesToBeResolved.
902 for (auto CrossCUReferencingDIEOffset :
903 FnsWithAbstractOriginToBeProcessed)
904 CrossCUReferencesToBeResolved.push_back(
905 Elt: DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
906 }
907 const auto *LineTable = DICtx.getLineTableForUnit(U: CU.get());
908 std::optional<uint64_t> LastFileIdxOpt;
909 if (LineTable)
910 LastFileIdxOpt = LineTable->getLastValidFileIndex();
911 if (LastFileIdxOpt) {
912 // Each CU has its own file index; in order to track unique line entries
913 // across CUs, we therefore need to map each CU file index to a global
914 // file index, which we store here.
915 DenseMap<uint64_t, uint16_t> CUFileMapping;
916 for (uint64_t FileIdx = 0; FileIdx <= *LastFileIdxOpt; ++FileIdx) {
917 std::string File;
918 if (LineTable->getFileNameByIndex(
919 FileIndex: FileIdx, CompDir: CU->getCompilationDir(),
920 Kind: DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
921 Result&: File)) {
922 auto ExistingFile = llvm::find(Range&: Files, Val: File);
923 if (ExistingFile != Files.end()) {
924 CUFileMapping[FileIdx] = std::distance(first: Files.begin(), last: ExistingFile);
925 } else {
926 CUFileMapping[FileIdx] = Files.size();
927 Files.push_back(Elt: File);
928 }
929 }
930 }
931 for (const auto &Seq : LineTable->Sequences) {
932 LnStats.NumBytes += Seq.HighPC - Seq.LowPC;
933 // Ignore the `end_sequence` entry, since it's not interesting for us.
934 LnStats.NumEntries += Seq.LastRowIndex - Seq.FirstRowIndex - 1;
935 for (size_t RowIdx = Seq.FirstRowIndex; RowIdx < Seq.LastRowIndex - 1;
936 ++RowIdx) {
937 auto Entry = LineTable->Rows[RowIdx];
938 if (Entry.IsStmt)
939 LnStats.NumIsStmtEntries += 1;
940 assert(CUFileMapping.contains(Entry.File) &&
941 "Should have been collected earlier!");
942 uint16_t MappedFile = CUFileMapping[Entry.File];
943 UniqueLines.insert(V: {Entry.Line, Entry.Column, MappedFile});
944 if (Entry.Line != 0) {
945 UniqueNonZeroLines.insert(V: {Entry.Line, Entry.Column, MappedFile});
946 } else {
947 auto EntryStartAddress = Entry.Address.Address;
948 auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address;
949 LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress;
950 }
951 }
952 }
953 }
954 }
955
956 LnStats.NumUniqueEntries = UniqueLines.size();
957 LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size();
958
959 /// Resolve CrossCU references.
960 collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
961 LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
962 CrossCUReferencesToBeResolved);
963
964 /// Collect the sizes of debug sections.
965 SectionSizes Sizes;
966 calculateSectionSizes(Obj, Sizes, Filename);
967
968 /// The version number should be increased every time the algorithm is changed
969 /// (including bug fixes). New metrics may be added without increasing the
970 /// version.
971 unsigned Version = 9;
972 SaturatingUINT64 VarParamTotal = 0;
973 SaturatingUINT64 VarParamUnique = 0;
974 SaturatingUINT64 VarParamWithLoc = 0;
975 SaturatingUINT64 NumFunctions = 0;
976 SaturatingUINT64 NumOutOfLineFunctions = 0;
977 SaturatingUINT64 NumInlinedFunctions = 0;
978 SaturatingUINT64 NumFuncsWithSrcLoc = 0;
979 SaturatingUINT64 NumAbstractOrigins = 0;
980 SaturatingUINT64 ParamTotal = 0;
981 SaturatingUINT64 ParamWithType = 0;
982 SaturatingUINT64 ParamWithLoc = 0;
983 SaturatingUINT64 ParamWithSrcLoc = 0;
984 SaturatingUINT64 LocalVarTotal = 0;
985 SaturatingUINT64 LocalVarWithType = 0;
986 SaturatingUINT64 LocalVarWithSrcLoc = 0;
987 SaturatingUINT64 LocalVarWithLoc = 0;
988 for (auto &Entry : Statistics) {
989 PerFunctionStats &Stats = Entry.getValue();
990 uint64_t TotalVars = Stats.VarsInFunction.size() *
991 (Stats.NumFnInlined + Stats.NumFnOutOfLine);
992 // Count variables in global scope.
993 if (!Stats.IsFunction)
994 TotalVars =
995 Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
996 uint64_t Constants = Stats.ConstantMembers;
997 VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
998 VarParamTotal += TotalVars;
999 VarParamUnique += Stats.VarsInFunction.size();
1000 LLVM_DEBUG(for (auto &V
1001 : Stats.VarsInFunction) llvm::dbgs()
1002 << Entry.getKey() << ": " << V.getKey() << "\n");
1003 NumFunctions += Stats.IsFunction;
1004 NumFuncsWithSrcLoc += Stats.HasSourceLocation;
1005 NumOutOfLineFunctions += Stats.IsFunction * Stats.NumFnOutOfLine;
1006 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
1007 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
1008 ParamTotal += Stats.NumParams;
1009 ParamWithType += Stats.NumParamTypes;
1010 ParamWithLoc += Stats.NumParamLocations;
1011 ParamWithSrcLoc += Stats.NumParamSourceLocations;
1012 LocalVarTotal += Stats.NumLocalVars;
1013 LocalVarWithType += Stats.NumLocalVarTypes;
1014 LocalVarWithLoc += Stats.NumLocalVarLocations;
1015 LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
1016 }
1017
1018 // Print summary.
1019 OS.SetBufferSize(1024);
1020 json::OStream J(OS, 2);
1021 J.objectBegin();
1022 J.attribute(Key: "version", Contents: Version);
1023 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
1024 llvm::dbgs() << "---------------------------------\n");
1025
1026 printDatum(J, Key: "file", Value: Filename.str());
1027 printDatum(J, Key: "format", Value: FormatName);
1028
1029 printDatum(J, Key: "#functions", Value: NumFunctions.Value);
1030 printDatum(J, Key: "#functions with location", Value: NumFuncsWithSrcLoc.Value);
1031 printDatum(J, Key: "#out-of-line functions", Value: NumOutOfLineFunctions.Value);
1032 printDatum(J, Key: "#inlined functions", Value: NumInlinedFunctions.Value);
1033 printDatum(J, Key: "#inlined functions with abstract origins",
1034 Value: NumAbstractOrigins.Value);
1035
1036 // This includes local variables and formal parameters.
1037 printDatum(J, Key: "#unique source variables", Value: VarParamUnique.Value);
1038 printDatum(J, Key: "#source variables", Value: VarParamTotal.Value);
1039 printDatum(J, Key: "#source variables with location", Value: VarParamWithLoc.Value);
1040
1041 printDatum(J, Key: "#call site entries", Value: GlobalStats.CallSiteEntries.Value);
1042 printDatum(J, Key: "#call site DIEs", Value: GlobalStats.CallSiteDIEs.Value);
1043 printDatum(J, Key: "#call site parameter DIEs",
1044 Value: GlobalStats.CallSiteParamDIEs.Value);
1045
1046 printDatum(J, Key: "sum_all_variables(#bytes in parent scope)",
1047 Value: GlobalStats.ScopeBytes.Value);
1048 printDatum(J,
1049 Key: "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
1050 Value: GlobalStats.TotalBytesCovered.Value);
1051 printDatum(J,
1052 Key: "sum_all_variables(#bytes in parent scope covered by "
1053 "DW_AT_location)",
1054 Value: GlobalStats.ScopeBytesCovered.Value);
1055 printDatum(J,
1056 Key: "sum_all_variables(#bytes in parent scope covered by "
1057 "DW_OP_entry_value)",
1058 Value: GlobalStats.ScopeEntryValueBytesCovered.Value);
1059
1060 printDatum(J, Key: "sum_all_params(#bytes in parent scope)",
1061 Value: GlobalStats.ParamScopeBytes.Value);
1062 printDatum(J,
1063 Key: "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
1064 Value: GlobalStats.ParamScopeBytesCovered.Value);
1065 printDatum(J,
1066 Key: "sum_all_params(#bytes in parent scope covered by "
1067 "DW_OP_entry_value)",
1068 Value: GlobalStats.ParamScopeEntryValueBytesCovered.Value);
1069
1070 printDatum(J, Key: "sum_all_local_vars(#bytes in parent scope)",
1071 Value: GlobalStats.LocalVarScopeBytes.Value);
1072 printDatum(J,
1073 Key: "sum_all_local_vars(#bytes in parent scope covered by "
1074 "DW_AT_location)",
1075 Value: GlobalStats.LocalVarScopeBytesCovered.Value);
1076 printDatum(J,
1077 Key: "sum_all_local_vars(#bytes in parent scope covered by "
1078 "DW_OP_entry_value)",
1079 Value: GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
1080
1081 printDatum(J, Key: "#bytes within functions", Value: GlobalStats.FunctionSize.Value);
1082 printDatum(J, Key: "#bytes within inlined functions",
1083 Value: GlobalStats.InlineFunctionSize.Value);
1084
1085 // Print the summary for formal parameters.
1086 printDatum(J, Key: "#params", Value: ParamTotal.Value);
1087 printDatum(J, Key: "#params with source location", Value: ParamWithSrcLoc.Value);
1088 printDatum(J, Key: "#params with type", Value: ParamWithType.Value);
1089 printDatum(J, Key: "#params with binary location", Value: ParamWithLoc.Value);
1090
1091 // Print the summary for local variables.
1092 printDatum(J, Key: "#local vars", Value: LocalVarTotal.Value);
1093 printDatum(J, Key: "#local vars with source location", Value: LocalVarWithSrcLoc.Value);
1094 printDatum(J, Key: "#local vars with type", Value: LocalVarWithType.Value);
1095 printDatum(J, Key: "#local vars with binary location", Value: LocalVarWithLoc.Value);
1096
1097 // Print the debug section sizes.
1098 printSectionSizes(J, Sizes);
1099
1100 // Print the location statistics for variables (includes local variables
1101 // and formal parameters).
1102 printDatum(J, Key: "#variables processed by location statistics",
1103 Value: LocStats.NumVarParam.Value);
1104 printLocationStats(J, Key: "#variables", LocationStats&: LocStats.VarParamLocStats);
1105 printLocationStats(J, Key: "#variables - entry values",
1106 LocationStats&: LocStats.VarParamNonEntryValLocStats);
1107
1108 // Print the location statistics for formal parameters.
1109 printDatum(J, Key: "#params processed by location statistics",
1110 Value: LocStats.NumParam.Value);
1111 printLocationStats(J, Key: "#params", LocationStats&: LocStats.ParamLocStats);
1112 printLocationStats(J, Key: "#params - entry values",
1113 LocationStats&: LocStats.ParamNonEntryValLocStats);
1114
1115 // Print the location statistics for local variables.
1116 printDatum(J, Key: "#local vars processed by location statistics",
1117 Value: LocStats.NumVar.Value);
1118 printLocationStats(J, Key: "#local vars", LocationStats&: LocStats.LocalVarLocStats);
1119 printLocationStats(J, Key: "#local vars - entry values",
1120 LocationStats&: LocStats.LocalVarNonEntryValLocStats);
1121
1122 // Print line statistics for the object file.
1123 printDatum(J, Key: "#bytes with line information", Value: LnStats.NumBytes.Value);
1124 printDatum(J, Key: "#bytes with line-0 locations", Value: LnStats.NumLineZeroBytes.Value);
1125 printDatum(J, Key: "#line entries", Value: LnStats.NumEntries.Value);
1126 printDatum(J, Key: "#line entries (is_stmt)", Value: LnStats.NumIsStmtEntries.Value);
1127 printDatum(J, Key: "#line entries (unique)", Value: LnStats.NumUniqueEntries.Value);
1128 printDatum(J, Key: "#line entries (unique non-0)",
1129 Value: LnStats.NumUniqueNonZeroEntries.Value);
1130
1131 J.objectEnd();
1132 OS << '\n';
1133 LLVM_DEBUG(
1134 llvm::dbgs() << "Total Availability: "
1135 << (VarParamTotal.Value
1136 ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1137 VarParamTotal.Value)
1138 : 0)
1139 << "%\n";
1140 llvm::dbgs() << "PC Ranges covered: "
1141 << (GlobalStats.ScopeBytes.Value
1142 ? (int)std::round(
1143 (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1144 GlobalStats.ScopeBytes.Value)
1145 : 0)
1146 << "%\n");
1147 return true;
1148}
1149