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