1 //===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm-dwarfdump.h"
10 #include "llvm/ADT/DenseMap.h"
11 #include "llvm/ADT/DenseSet.h"
12 #include "llvm/ADT/StringSet.h"
13 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
14 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
15 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
16 #include "llvm/Object/ObjectFile.h"
17 #include "llvm/Support/JSON.h"
19 #define DEBUG_TYPE "dwarfdump"
21 using namespace llvm::dwarfdump
;
22 using namespace llvm::object
;
25 /// This represents the number of categories of debug location coverage being
26 /// calculated. The first category is the number of variables with 0% location
27 /// coverage, but the last category is the number of variables with 100%
28 /// location coverage.
29 constexpr int NumOfCoverageCategories
= 12;
31 /// This is used for zero location coverage bucket.
32 constexpr unsigned ZeroCoverageBucket
= 0;
34 /// The UINT64_MAX is used as an indication of the overflow.
35 constexpr uint64_t OverflowValue
= std::numeric_limits
<uint64_t>::max();
37 /// This represents variables DIE offsets.
38 using AbstractOriginVarsTy
= llvm::SmallVector
<uint64_t>;
39 /// This maps function DIE offset to its variables.
40 using AbstractOriginVarsTyMap
= llvm::DenseMap
<uint64_t, AbstractOriginVarsTy
>;
41 /// This represents function DIE offsets containing an abstract_origin.
42 using FunctionsWithAbstractOriginTy
= llvm::SmallVector
<uint64_t>;
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
48 struct SaturatingUINT64
{
49 /// Number that represents the stats.
52 SaturatingUINT64(uint64_t Value_
) : Value(Value_
) {}
54 void operator++(int) { return *this += 1; }
55 void operator+=(uint64_t Value_
) {
56 if (Value
!= OverflowValue
) {
57 if (Value
< OverflowValue
- Value_
)
60 Value
= OverflowValue
;
65 /// Utility struct to store the full location of a DIE - its CU and offset.
69 DIELocation(DWARFUnit
*_DwUnit
, uint64_t _DIEOffset
)
70 : DwUnit(_DwUnit
), DIEOffset(_DIEOffset
) {}
72 /// This represents DWARF locations of CrossCU referencing DIEs.
73 using CrossCUReferencingDIELocationTy
= llvm::SmallVector
<DIELocation
>;
75 /// This maps function DIE offset to its DWARF CU.
76 using FunctionDIECUTyMap
= llvm::DenseMap
<uint64_t, DWARFUnit
*>;
78 /// Holds statistics for one function (or other entity that has a PC range and
79 /// contains variables, such as a compile unit).
80 struct PerFunctionStats
{
81 /// Number of inlined instances of this function.
82 uint64_t NumFnInlined
= 0;
83 /// Number of out-of-line instances of this function.
84 uint64_t NumFnOutOfLine
= 0;
85 /// Number of inlined instances that have abstract origins.
86 uint64_t NumAbstractOrigins
= 0;
87 /// Number of variables and parameters with location across all inlined
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;
118 /// Holds accumulated global statistics about DIEs.
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
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;
160 /// Holds accumulated debug location statistics about local variables and
161 /// formal parameters.
162 struct LocationStats
{
163 /// Map the scope coverage decile to the number of variables in the decile.
164 /// The first element of the array (at the index zero) represents the number
165 /// of variables with the no debug location at all, but the last element
166 /// in the vector represents the number of fully covered variables within
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;
193 /// Holds accumulated debug line statistics across all CUs.
195 SaturatingUINT64 NumBytes
= 0;
196 SaturatingUINT64 NumLineZeroBytes
= 0;
197 SaturatingUINT64 NumEntries
= 0;
198 SaturatingUINT64 NumIsStmtEntries
= 0;
199 SaturatingUINT64 NumUniqueEntries
= 0;
200 SaturatingUINT64 NumUniqueNonZeroEntries
= 0;
204 /// Collect debug location statistics for one DIE.
205 static void collectLocStats(uint64_t ScopeBytesCovered
, uint64_t BytesInScope
,
206 std::vector
<SaturatingUINT64
> &VarParamLocStats
,
207 std::vector
<SaturatingUINT64
> &ParamLocStats
,
208 std::vector
<SaturatingUINT64
> &LocalVarLocStats
,
209 bool IsParam
, bool IsLocalVar
) {
210 auto getCoverageBucket
= [ScopeBytesCovered
, BytesInScope
]() -> unsigned {
211 // No debug location at all for the variable.
212 if (ScopeBytesCovered
== 0)
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
;
220 return LocBucket
+ 1;
223 unsigned CoverageBucket
= getCoverageBucket();
225 VarParamLocStats
[CoverageBucket
].Value
++;
227 ParamLocStats
[CoverageBucket
].Value
++;
229 LocalVarLocStats
[CoverageBucket
].Value
++;
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.
235 static std::string
constructDieID(DWARFDie Die
,
236 StringRef Prefix
= StringRef()) {
238 llvm::raw_string_ostream
ID(IDStr
);
240 << Die
.getName(DINameKind::LinkageName
);
242 // Prefix + Name is enough for local variables and parameters.
243 if (!Prefix
.empty() && Prefix
!= "g")
246 auto DeclFile
= Die
.findRecursively(dwarf::DW_AT_decl_file
);
249 DWARFUnit
*U
= Die
.getDwarfUnit();
250 if (const auto *LT
= U
->getContext().getLineTableForUnit(U
))
251 if (LT
->getFileNameByIndex(
252 dwarf::toUnsigned(DeclFile
, 0), U
->getCompilationDir(),
253 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath
, File
))
254 File
= std::string(sys::path::filename(File
));
256 ID
<< ":" << (File
.empty() ? "/" : File
);
258 << dwarf::toUnsigned(Die
.findRecursively(dwarf::DW_AT_decl_line
), 0);
262 /// Return the number of bytes in the overlap of ranges A and B.
263 static uint64_t calculateOverlap(DWARFAddressRange A
, DWARFAddressRange B
) {
264 uint64_t Lower
= std::max(A
.LowPC
, B
.LowPC
);
265 uint64_t Upper
= std::min(A
.HighPC
, B
.HighPC
);
268 return Upper
- Lower
;
271 /// Collect debug info quality metrics for one DIE.
272 static void collectStatsForDie(DWARFDie Die
, const std::string
&FnPrefix
,
273 const std::string
&VarPrefix
,
274 uint64_t BytesInScope
, uint32_t InlineDepth
,
275 StringMap
<PerFunctionStats
> &FnStatMap
,
276 GlobalStats
&GlobalStats
,
277 LocationStats
&LocStats
,
278 AbstractOriginVarsTy
*AbstractOriginVariables
) {
279 const dwarf::Tag Tag
= Die
.getTag();
281 if (Tag
== dwarf::DW_TAG_compile_unit
)
285 bool HasSrcLoc
= false;
286 bool HasType
= false;
287 uint64_t TotalBytesCovered
= 0;
288 uint64_t ScopeBytesCovered
= 0;
289 uint64_t BytesEntryValuesCovered
= 0;
290 auto &FnStats
= FnStatMap
[FnPrefix
];
291 bool IsParam
= Tag
== dwarf::DW_TAG_formal_parameter
;
292 bool IsLocalVar
= Tag
== dwarf::DW_TAG_variable
;
293 bool IsConstantMember
= Tag
== dwarf::DW_TAG_member
&&
294 Die
.find(dwarf::DW_AT_const_value
);
296 // For zero covered inlined variables the locstats will be
298 bool DeferLocStats
= false;
300 if (Tag
== dwarf::DW_TAG_call_site
|| Tag
== dwarf::DW_TAG_GNU_call_site
) {
301 GlobalStats
.CallSiteDIEs
++;
305 if (Tag
== dwarf::DW_TAG_call_site_parameter
||
306 Tag
== dwarf::DW_TAG_GNU_call_site_parameter
) {
307 GlobalStats
.CallSiteParamDIEs
++;
311 if (!IsParam
&& !IsLocalVar
&& !IsConstantMember
) {
312 // Not a variable or constant member.
316 // Ignore declarations of global variables.
317 if (IsLocalVar
&& Die
.find(dwarf::DW_AT_declaration
))
320 if (Die
.findRecursively(dwarf::DW_AT_decl_file
) &&
321 Die
.findRecursively(dwarf::DW_AT_decl_line
))
324 if (Die
.findRecursively(dwarf::DW_AT_type
))
327 if (Die
.find(dwarf::DW_AT_abstract_origin
)) {
328 if (Die
.find(dwarf::DW_AT_location
) || Die
.find(dwarf::DW_AT_const_value
)) {
329 if (AbstractOriginVariables
) {
330 auto Offset
= Die
.find(dwarf::DW_AT_abstract_origin
);
331 // Do not track this variable any more, since it has location
333 llvm::erase(*AbstractOriginVariables
, (*Offset
).getRawUValue());
336 // The locstats will be handled at the end of
337 // the collectStatsRecursive().
338 DeferLocStats
= true;
342 auto IsEntryValue
= [&](ArrayRef
<uint8_t> D
) -> bool {
343 DWARFUnit
*U
= Die
.getDwarfUnit();
344 DataExtractor
Data(toStringRef(D
),
345 Die
.getDwarfUnit()->getContext().isLittleEndian(), 0);
346 DWARFExpression
Expression(Data
, U
->getAddressByteSize(),
347 U
->getFormParams().Format
);
348 // Consider the expression containing the DW_OP_entry_value as
350 return llvm::any_of(Expression
, [](const DWARFExpression::Operation
&Op
) {
351 return Op
.getCode() == dwarf::DW_OP_entry_value
||
352 Op
.getCode() == dwarf::DW_OP_GNU_entry_value
;
356 if (Die
.find(dwarf::DW_AT_const_value
)) {
357 // This catches constant members *and* variables.
359 ScopeBytesCovered
= BytesInScope
;
360 TotalBytesCovered
= BytesInScope
;
362 // Handle variables and function arguments.
363 Expected
<std::vector
<DWARFLocationExpression
>> Loc
=
364 Die
.getLocations(dwarf::DW_AT_location
);
366 consumeError(Loc
.takeError());
370 auto Default
= find_if(
371 *Loc
, [](const DWARFLocationExpression
&L
) { return !L
.Range
; });
372 if (Default
!= Loc
->end()) {
373 // Assume the entire range is covered by a single location.
374 ScopeBytesCovered
= BytesInScope
;
375 TotalBytesCovered
= BytesInScope
;
377 // Caller checks this Expected result already, it cannot fail.
378 auto ScopeRanges
= cantFail(Die
.getParent().getAddressRanges());
379 for (auto Entry
: *Loc
) {
380 TotalBytesCovered
+= Entry
.Range
->HighPC
- Entry
.Range
->LowPC
;
381 uint64_t ScopeBytesCoveredByEntry
= 0;
382 // Calculate how many bytes of the parent scope this entry covers.
383 // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
384 // address ranges defined by the bounded location descriptions of a
385 // location list may overlap". So in theory a variable can have
386 // multiple simultaneous locations, which would make this calculation
387 // misleading because we will count the overlapped areas
388 // twice. However, clang does not currently emit DWARF like this.
389 for (DWARFAddressRange R
: ScopeRanges
) {
390 ScopeBytesCoveredByEntry
+= calculateOverlap(*Entry
.Range
, R
);
392 ScopeBytesCovered
+= ScopeBytesCoveredByEntry
;
393 if (IsEntryValue(Entry
.Expr
))
394 BytesEntryValuesCovered
+= ScopeBytesCoveredByEntry
;
400 // Calculate the debug location statistics.
401 if (BytesInScope
&& !DeferLocStats
) {
402 LocStats
.NumVarParam
.Value
++;
404 LocStats
.NumParam
.Value
++;
406 LocStats
.NumVar
.Value
++;
408 collectLocStats(ScopeBytesCovered
, BytesInScope
, LocStats
.VarParamLocStats
,
409 LocStats
.ParamLocStats
, LocStats
.LocalVarLocStats
, IsParam
,
411 // Non debug entry values coverage statistics.
412 collectLocStats(ScopeBytesCovered
- BytesEntryValuesCovered
, BytesInScope
,
413 LocStats
.VarParamNonEntryValLocStats
,
414 LocStats
.ParamNonEntryValLocStats
,
415 LocStats
.LocalVarNonEntryValLocStats
, IsParam
, IsLocalVar
);
418 // Collect PC range coverage data.
420 Die
.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin
))
423 std::string VarID
= constructDieID(Die
, VarPrefix
);
424 FnStats
.VarsInFunction
.insert(VarID
);
426 GlobalStats
.TotalBytesCovered
+= TotalBytesCovered
;
428 GlobalStats
.ScopeBytesCovered
+= ScopeBytesCovered
;
429 GlobalStats
.ScopeBytes
+= BytesInScope
;
430 GlobalStats
.ScopeEntryValueBytesCovered
+= BytesEntryValuesCovered
;
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
;
441 assert(GlobalStats
.ScopeBytesCovered
.Value
<= GlobalStats
.ScopeBytes
.Value
);
444 if (IsConstantMember
) {
445 FnStats
.ConstantMembers
++;
449 FnStats
.TotalVarWithLoc
+= (unsigned)HasLoc
;
451 if (Die
.find(dwarf::DW_AT_artificial
)) {
452 FnStats
.NumArtificial
++;
459 FnStats
.NumParamTypes
++;
461 FnStats
.NumParamSourceLocations
++;
463 FnStats
.NumParamLocations
++;
464 } else if (IsLocalVar
) {
465 FnStats
.NumLocalVars
++;
467 FnStats
.NumLocalVarTypes
++;
469 FnStats
.NumLocalVarSourceLocations
++;
471 FnStats
.NumLocalVarLocations
++;
475 /// Recursively collect variables from subprogram with DW_AT_inline attribute.
476 static void collectAbstractOriginFnInfo(
477 DWARFDie Die
, uint64_t SPOffset
,
478 AbstractOriginVarsTyMap
&GlobalAbstractOriginFnInfo
,
479 AbstractOriginVarsTyMap
&LocalAbstractOriginFnInfo
) {
480 DWARFDie Child
= Die
.getFirstChild();
482 const dwarf::Tag ChildTag
= Child
.getTag();
483 if (ChildTag
== dwarf::DW_TAG_formal_parameter
||
484 ChildTag
== dwarf::DW_TAG_variable
) {
485 GlobalAbstractOriginFnInfo
[SPOffset
].push_back(Child
.getOffset());
486 LocalAbstractOriginFnInfo
[SPOffset
].push_back(Child
.getOffset());
487 } else if (ChildTag
== dwarf::DW_TAG_lexical_block
)
488 collectAbstractOriginFnInfo(Child
, SPOffset
, GlobalAbstractOriginFnInfo
,
489 LocalAbstractOriginFnInfo
);
490 Child
= Child
.getSibling();
494 /// Recursively collect debug info quality metrics.
495 static void collectStatsRecursive(
496 DWARFDie Die
, std::string FnPrefix
, std::string VarPrefix
,
497 uint64_t BytesInScope
, uint32_t InlineDepth
,
498 StringMap
<PerFunctionStats
> &FnStatMap
, GlobalStats
&GlobalStats
,
499 LocationStats
&LocStats
, FunctionDIECUTyMap
&AbstractOriginFnCUs
,
500 AbstractOriginVarsTyMap
&GlobalAbstractOriginFnInfo
,
501 AbstractOriginVarsTyMap
&LocalAbstractOriginFnInfo
,
502 FunctionsWithAbstractOriginTy
&FnsWithAbstractOriginToBeProcessed
,
503 AbstractOriginVarsTy
*AbstractOriginVarsPtr
= nullptr) {
508 const dwarf::Tag Tag
= Die
.getTag();
509 // Skip function types.
510 if (Tag
== dwarf::DW_TAG_subroutine_type
)
513 // Handle any kind of lexical scope.
514 const bool HasAbstractOrigin
=
515 Die
.find(dwarf::DW_AT_abstract_origin
) != std::nullopt
;
516 const bool IsFunction
= Tag
== dwarf::DW_TAG_subprogram
;
517 const bool IsBlock
= Tag
== dwarf::DW_TAG_lexical_block
;
518 const bool IsInlinedFunction
= Tag
== dwarf::DW_TAG_inlined_subroutine
;
519 // We want to know how many variables (with abstract_origin) don't have
521 const bool IsCandidateForZeroLocCovTracking
=
522 (IsInlinedFunction
|| (IsFunction
&& HasAbstractOrigin
));
524 AbstractOriginVarsTy AbstractOriginVars
;
526 // Get the vars of the inlined fn, so the locstats
527 // reports the missing vars (with coverage 0%).
528 if (IsCandidateForZeroLocCovTracking
) {
529 auto OffsetFn
= Die
.find(dwarf::DW_AT_abstract_origin
);
531 uint64_t OffsetOfInlineFnCopy
= (*OffsetFn
).getRawUValue();
532 if (LocalAbstractOriginFnInfo
.count(OffsetOfInlineFnCopy
)) {
533 AbstractOriginVars
= LocalAbstractOriginFnInfo
[OffsetOfInlineFnCopy
];
534 AbstractOriginVarsPtr
= &AbstractOriginVars
;
536 // This means that the DW_AT_inline fn copy is out of order
537 // or that the abstract_origin references another CU,
538 // so this abstract origin instance will be processed later.
539 FnsWithAbstractOriginToBeProcessed
.push_back(Die
.getOffset());
540 AbstractOriginVarsPtr
= nullptr;
545 if (IsFunction
|| IsInlinedFunction
|| IsBlock
) {
546 // Reset VarPrefix when entering a new function.
547 if (IsFunction
|| IsInlinedFunction
)
550 // Ignore forward declarations.
551 if (Die
.find(dwarf::DW_AT_declaration
))
554 // Check for call sites.
555 if (Die
.find(dwarf::DW_AT_call_file
) && Die
.find(dwarf::DW_AT_call_line
))
556 GlobalStats
.CallSiteEntries
++;
559 auto RangesOrError
= Die
.getAddressRanges();
560 if (!RangesOrError
) {
561 llvm::consumeError(RangesOrError
.takeError());
565 auto Ranges
= RangesOrError
.get();
566 uint64_t BytesInThisScope
= 0;
567 for (auto Range
: Ranges
)
568 BytesInThisScope
+= Range
.HighPC
- Range
.LowPC
;
570 // Count the function.
572 // Skip over abstract origins, but collect variables
573 // from it so it can be used for location statistics
574 // for inlined instancies.
575 if (Die
.find(dwarf::DW_AT_inline
)) {
576 uint64_t SPOffset
= Die
.getOffset();
577 AbstractOriginFnCUs
[SPOffset
] = Die
.getDwarfUnit();
578 collectAbstractOriginFnInfo(Die
, SPOffset
, GlobalAbstractOriginFnInfo
,
579 LocalAbstractOriginFnInfo
);
583 std::string FnID
= constructDieID(Die
);
584 // We've seen an instance of this function.
585 auto &FnStats
= FnStatMap
[FnID
];
586 FnStats
.IsFunction
= true;
587 if (IsInlinedFunction
) {
588 FnStats
.NumFnInlined
++;
589 if (Die
.findRecursively(dwarf::DW_AT_abstract_origin
))
590 FnStats
.NumAbstractOrigins
++;
592 FnStats
.NumFnOutOfLine
++;
594 if (Die
.findRecursively(dwarf::DW_AT_decl_file
) &&
595 Die
.findRecursively(dwarf::DW_AT_decl_line
))
596 FnStats
.HasSourceLocation
= true;
597 // Update function prefix.
601 if (BytesInThisScope
) {
602 BytesInScope
= BytesInThisScope
;
604 GlobalStats
.FunctionSize
+= BytesInThisScope
;
605 else if (IsInlinedFunction
&& InlineDepth
== 0)
606 GlobalStats
.InlineFunctionSize
+= BytesInThisScope
;
609 // Not a scope, visit the Die itself. It could be a variable.
610 collectStatsForDie(Die
, FnPrefix
, VarPrefix
, BytesInScope
, InlineDepth
,
611 FnStatMap
, GlobalStats
, LocStats
, AbstractOriginVarsPtr
);
614 // Set InlineDepth correctly for child recursion
617 else if (IsInlinedFunction
)
620 // Traverse children.
621 unsigned LexicalBlockIndex
= 0;
622 unsigned FormalParameterIndex
= 0;
623 DWARFDie Child
= Die
.getFirstChild();
625 std::string ChildVarPrefix
= VarPrefix
;
626 if (Child
.getTag() == dwarf::DW_TAG_lexical_block
)
627 ChildVarPrefix
+= toHex(LexicalBlockIndex
++) + '.';
628 if (Child
.getTag() == dwarf::DW_TAG_formal_parameter
)
629 ChildVarPrefix
+= 'p' + toHex(FormalParameterIndex
++) + '.';
631 collectStatsRecursive(
632 Child
, FnPrefix
, ChildVarPrefix
, BytesInScope
, InlineDepth
, FnStatMap
,
633 GlobalStats
, LocStats
, AbstractOriginFnCUs
, GlobalAbstractOriginFnInfo
,
634 LocalAbstractOriginFnInfo
, FnsWithAbstractOriginToBeProcessed
,
635 AbstractOriginVarsPtr
);
636 Child
= Child
.getSibling();
639 if (!IsCandidateForZeroLocCovTracking
)
642 // After we have processed all vars of the inlined function (or function with
643 // an abstract_origin), we want to know how many variables have no location.
644 for (auto Offset
: AbstractOriginVars
) {
645 LocStats
.NumVarParam
++;
646 LocStats
.VarParamLocStats
[ZeroCoverageBucket
]++;
647 auto FnDie
= Die
.getDwarfUnit()->getDIEForOffset(Offset
);
650 auto Tag
= FnDie
.getTag();
651 if (Tag
== dwarf::DW_TAG_formal_parameter
) {
653 LocStats
.ParamLocStats
[ZeroCoverageBucket
]++;
654 } else if (Tag
== dwarf::DW_TAG_variable
) {
656 LocStats
.LocalVarLocStats
[ZeroCoverageBucket
]++;
661 /// Print human-readable output.
663 static void printDatum(json::OStream
&J
, const char *Key
, json::Value Value
) {
664 if (Value
== OverflowValue
)
665 J
.attribute(Key
, "overflowed");
667 J
.attribute(Key
, Value
);
669 LLVM_DEBUG(llvm::dbgs() << Key
<< ": " << Value
<< '\n');
672 static void printLocationStats(json::OStream
&J
, const char *Key
,
673 std::vector
<SaturatingUINT64
> &LocationStats
) {
674 if (LocationStats
[0].Value
== OverflowValue
)
675 J
.attribute((Twine(Key
) +
676 " with (0%,10%) of parent scope covered by DW_AT_location")
681 (Twine(Key
) + " with 0% of parent scope covered by DW_AT_location")
683 LocationStats
[0].Value
);
686 << " with 0% of parent scope covered by DW_AT_location: \\"
687 << LocationStats
[0].Value
<< '\n');
689 if (LocationStats
[1].Value
== OverflowValue
)
690 J
.attribute((Twine(Key
) +
691 " with (0%,10%) of parent scope covered by DW_AT_location")
695 J
.attribute((Twine(Key
) +
696 " with (0%,10%) of parent scope covered by DW_AT_location")
698 LocationStats
[1].Value
);
699 LLVM_DEBUG(llvm::dbgs()
701 << " with (0%,10%) of parent scope covered by DW_AT_location: "
702 << LocationStats
[1].Value
<< '\n');
704 for (unsigned i
= 2; i
< NumOfCoverageCategories
- 1; ++i
) {
705 if (LocationStats
[i
].Value
== OverflowValue
)
706 J
.attribute((Twine(Key
) + " with [" + Twine((i
- 1) * 10) + "%," +
708 "%) of parent scope covered by DW_AT_location")
712 J
.attribute((Twine(Key
) + " with [" + Twine((i
- 1) * 10) + "%," +
714 "%) of parent scope covered by DW_AT_location")
716 LocationStats
[i
].Value
);
717 LLVM_DEBUG(llvm::dbgs()
718 << Key
<< " with [" << (i
- 1) * 10 << "%," << i
* 10
719 << "%) of parent scope covered by DW_AT_location: "
720 << LocationStats
[i
].Value
);
722 if (LocationStats
[NumOfCoverageCategories
- 1].Value
== OverflowValue
)
724 (Twine(Key
) + " with 100% of parent scope covered by DW_AT_location")
729 (Twine(Key
) + " with 100% of parent scope covered by DW_AT_location")
731 LocationStats
[NumOfCoverageCategories
- 1].Value
);
734 << " with 100% of parent scope covered by DW_AT_location: "
735 << LocationStats
[NumOfCoverageCategories
- 1].Value
);
738 static void printSectionSizes(json::OStream
&J
, const SectionSizes
&Sizes
) {
739 for (const auto &It
: Sizes
.DebugSectionSizes
)
740 J
.attribute((Twine("#bytes in ") + It
.first
).str(), int64_t(It
.second
));
743 /// Stop tracking variables that contain abstract_origin with a location.
744 /// This is used for out-of-order DW_AT_inline subprograms only.
745 static void updateVarsWithAbstractOriginLocCovInfo(
746 DWARFDie FnDieWithAbstractOrigin
,
747 AbstractOriginVarsTy
&AbstractOriginVars
) {
748 DWARFDie Child
= FnDieWithAbstractOrigin
.getFirstChild();
750 const dwarf::Tag ChildTag
= Child
.getTag();
751 if ((ChildTag
== dwarf::DW_TAG_formal_parameter
||
752 ChildTag
== dwarf::DW_TAG_variable
) &&
753 (Child
.find(dwarf::DW_AT_location
) ||
754 Child
.find(dwarf::DW_AT_const_value
))) {
755 auto OffsetVar
= Child
.find(dwarf::DW_AT_abstract_origin
);
757 llvm::erase(AbstractOriginVars
, (*OffsetVar
).getRawUValue());
758 } else if (ChildTag
== dwarf::DW_TAG_lexical_block
)
759 updateVarsWithAbstractOriginLocCovInfo(Child
, AbstractOriginVars
);
760 Child
= Child
.getSibling();
764 /// Collect zero location coverage for inlined variables which refer to
765 /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
766 /// Also cover the variables of a concrete function (represented with
767 /// the DW_TAG_subprogram) with an abstract_origin attribute.
768 static void collectZeroLocCovForVarsWithAbstractOrigin(
769 DWARFUnit
*DwUnit
, GlobalStats
&GlobalStats
, LocationStats
&LocStats
,
770 AbstractOriginVarsTyMap
&LocalAbstractOriginFnInfo
,
771 FunctionsWithAbstractOriginTy
&FnsWithAbstractOriginToBeProcessed
) {
772 // The next variable is used to filter out functions that have been processed,
773 // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
774 FunctionsWithAbstractOriginTy ProcessedFns
;
775 for (auto FnOffset
: FnsWithAbstractOriginToBeProcessed
) {
776 DWARFDie FnDieWithAbstractOrigin
= DwUnit
->getDIEForOffset(FnOffset
);
777 auto FnCopy
= FnDieWithAbstractOrigin
.find(dwarf::DW_AT_abstract_origin
);
778 AbstractOriginVarsTy AbstractOriginVars
;
781 uint64_t FnCopyRawUValue
= (*FnCopy
).getRawUValue();
782 // If there is no entry within LocalAbstractOriginFnInfo for the given
783 // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
784 // CrossCU referencing.
785 if (!LocalAbstractOriginFnInfo
.count(FnCopyRawUValue
))
787 AbstractOriginVars
= LocalAbstractOriginFnInfo
[FnCopyRawUValue
];
788 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin
,
791 for (auto Offset
: AbstractOriginVars
) {
792 LocStats
.NumVarParam
++;
793 LocStats
.VarParamLocStats
[ZeroCoverageBucket
]++;
794 auto Tag
= DwUnit
->getDIEForOffset(Offset
).getTag();
795 if (Tag
== dwarf::DW_TAG_formal_parameter
) {
797 LocStats
.ParamLocStats
[ZeroCoverageBucket
]++;
798 } else if (Tag
== dwarf::DW_TAG_variable
) {
800 LocStats
.LocalVarLocStats
[ZeroCoverageBucket
]++;
803 ProcessedFns
.push_back(FnOffset
);
805 for (auto ProcessedFn
: ProcessedFns
)
806 llvm::erase(FnsWithAbstractOriginToBeProcessed
, ProcessedFn
);
809 /// Collect zero location coverage for inlined variables which refer to
810 /// a DW_AT_inline copy of subprogram that is in a different CU.
811 static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
812 LocationStats
&LocStats
, FunctionDIECUTyMap AbstractOriginFnCUs
,
813 AbstractOriginVarsTyMap
&GlobalAbstractOriginFnInfo
,
814 CrossCUReferencingDIELocationTy
&CrossCUReferencesToBeResolved
) {
815 for (const auto &CrossCUReferenceToBeResolved
:
816 CrossCUReferencesToBeResolved
) {
817 DWARFUnit
*DwUnit
= CrossCUReferenceToBeResolved
.DwUnit
;
818 DWARFDie FnDIEWithCrossCUReferencing
=
819 DwUnit
->getDIEForOffset(CrossCUReferenceToBeResolved
.DIEOffset
);
821 FnDIEWithCrossCUReferencing
.find(dwarf::DW_AT_abstract_origin
);
824 uint64_t FnCopyRawUValue
= (*FnCopy
).getRawUValue();
825 AbstractOriginVarsTy AbstractOriginVars
=
826 GlobalAbstractOriginFnInfo
[FnCopyRawUValue
];
827 updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing
,
829 for (auto Offset
: AbstractOriginVars
) {
830 LocStats
.NumVarParam
++;
831 LocStats
.VarParamLocStats
[ZeroCoverageBucket
]++;
832 auto Tag
= (AbstractOriginFnCUs
[FnCopyRawUValue
])
833 ->getDIEForOffset(Offset
)
835 if (Tag
== dwarf::DW_TAG_formal_parameter
) {
837 LocStats
.ParamLocStats
[ZeroCoverageBucket
]++;
838 } else if (Tag
== dwarf::DW_TAG_variable
) {
840 LocStats
.LocalVarLocStats
[ZeroCoverageBucket
]++;
848 /// Collect debug info quality metrics for an entire DIContext.
850 /// Do the impossible and reduce the quality of the debug info down to a few
851 /// numbers. The idea is to condense the data into numbers that can be tracked
852 /// over time to identify trends in newer compiler versions and gauge the effect
853 /// of particular optimizations. The raw numbers themselves are not particularly
854 /// useful, only the delta between compiling the same program with different
856 bool dwarfdump::collectStatsForObjectFile(ObjectFile
&Obj
, DWARFContext
&DICtx
,
857 const Twine
&Filename
,
859 StringRef FormatName
= Obj
.getFileFormatName();
860 GlobalStats GlobalStats
;
861 LocationStats LocStats
;
863 StringMap
<PerFunctionStats
> Statistics
;
864 // This variable holds variable information for functions with
865 // abstract_origin globally, across all CUs.
866 AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo
;
867 // This variable holds information about the CU of a function with
869 FunctionDIECUTyMap AbstractOriginFnCUs
;
870 CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved
;
871 // Tuple representing a single source code position in the line table. Fields
872 // are respectively: Line, Col, File, where 'File' is an index into the Files
874 using LineTuple
= std::tuple
<uint32_t, uint16_t, uint16_t>;
875 SmallVector
<std::string
> Files
;
876 DenseSet
<LineTuple
> UniqueLines
;
877 DenseSet
<LineTuple
> UniqueNonZeroLines
;
879 for (const auto &CU
: static_cast<DWARFContext
*>(&DICtx
)->compile_units()) {
880 if (DWARFDie CUDie
= CU
->getNonSkeletonUnitDIE(false)) {
881 // This variable holds variable information for functions with
882 // abstract_origin, but just for the current CU.
883 AbstractOriginVarsTyMap LocalAbstractOriginFnInfo
;
884 FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed
;
886 collectStatsRecursive(
887 CUDie
, "/", "g", 0, 0, Statistics
, GlobalStats
, LocStats
,
888 AbstractOriginFnCUs
, GlobalAbstractOriginFnInfo
,
889 LocalAbstractOriginFnInfo
, FnsWithAbstractOriginToBeProcessed
);
891 // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
892 // out-of-order DWARF functions that have been processed within it,
893 // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
895 collectZeroLocCovForVarsWithAbstractOrigin(
896 CUDie
.getDwarfUnit(), GlobalStats
, LocStats
,
897 LocalAbstractOriginFnInfo
, FnsWithAbstractOriginToBeProcessed
);
899 // Collect all CrossCU references into CrossCUReferencesToBeResolved.
900 for (auto CrossCUReferencingDIEOffset
:
901 FnsWithAbstractOriginToBeProcessed
)
902 CrossCUReferencesToBeResolved
.push_back(
903 DIELocation(CUDie
.getDwarfUnit(), CrossCUReferencingDIEOffset
));
905 const auto *LineTable
= DICtx
.getLineTableForUnit(CU
.get());
906 std::optional
<uint64_t> LastFileIdxOpt
;
908 LastFileIdxOpt
= LineTable
->getLastValidFileIndex();
909 if (LastFileIdxOpt
) {
910 // Each CU has its own file index; in order to track unique line entries
911 // across CUs, we therefore need to map each CU file index to a global
912 // file index, which we store here.
913 DenseMap
<uint64_t, uint16_t> CUFileMapping
;
914 for (uint64_t FileIdx
= 0; FileIdx
<= *LastFileIdxOpt
; ++FileIdx
) {
916 if (LineTable
->getFileNameByIndex(
917 FileIdx
, CU
->getCompilationDir(),
918 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath
,
920 auto ExistingFile
= llvm::find(Files
, File
);
921 if (ExistingFile
!= Files
.end()) {
922 CUFileMapping
[FileIdx
] = std::distance(Files
.begin(), ExistingFile
);
924 CUFileMapping
[FileIdx
] = Files
.size();
925 Files
.push_back(File
);
929 for (const auto &Seq
: LineTable
->Sequences
) {
930 LnStats
.NumBytes
+= Seq
.HighPC
- Seq
.LowPC
;
931 // Ignore the `end_sequence` entry, since it's not interesting for us.
932 LnStats
.NumEntries
+= Seq
.LastRowIndex
- Seq
.FirstRowIndex
- 1;
933 for (size_t RowIdx
= Seq
.FirstRowIndex
; RowIdx
< Seq
.LastRowIndex
- 1;
935 auto Entry
= LineTable
->Rows
[RowIdx
];
937 LnStats
.NumIsStmtEntries
+= 1;
938 assert(CUFileMapping
.contains(Entry
.File
) &&
939 "Should have been collected earlier!");
940 uint16_t MappedFile
= CUFileMapping
[Entry
.File
];
941 UniqueLines
.insert({Entry
.Line
, Entry
.Column
, MappedFile
});
942 if (Entry
.Line
!= 0) {
943 UniqueNonZeroLines
.insert({Entry
.Line
, Entry
.Column
, MappedFile
});
945 auto EntryStartAddress
= Entry
.Address
.Address
;
946 auto EntryEndAddress
= LineTable
->Rows
[RowIdx
+ 1].Address
.Address
;
947 LnStats
.NumLineZeroBytes
+= EntryEndAddress
- EntryStartAddress
;
954 LnStats
.NumUniqueEntries
= UniqueLines
.size();
955 LnStats
.NumUniqueNonZeroEntries
= UniqueNonZeroLines
.size();
957 /// Resolve CrossCU references.
958 collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
959 LocStats
, AbstractOriginFnCUs
, GlobalAbstractOriginFnInfo
,
960 CrossCUReferencesToBeResolved
);
962 /// Collect the sizes of debug sections.
964 calculateSectionSizes(Obj
, Sizes
, Filename
);
966 /// The version number should be increased every time the algorithm is changed
967 /// (including bug fixes). New metrics may be added without increasing the
969 unsigned Version
= 9;
970 SaturatingUINT64 VarParamTotal
= 0;
971 SaturatingUINT64 VarParamUnique
= 0;
972 SaturatingUINT64 VarParamWithLoc
= 0;
973 SaturatingUINT64 NumFunctions
= 0;
974 SaturatingUINT64 NumInlinedFunctions
= 0;
975 SaturatingUINT64 NumFuncsWithSrcLoc
= 0;
976 SaturatingUINT64 NumAbstractOrigins
= 0;
977 SaturatingUINT64 ParamTotal
= 0;
978 SaturatingUINT64 ParamWithType
= 0;
979 SaturatingUINT64 ParamWithLoc
= 0;
980 SaturatingUINT64 ParamWithSrcLoc
= 0;
981 SaturatingUINT64 LocalVarTotal
= 0;
982 SaturatingUINT64 LocalVarWithType
= 0;
983 SaturatingUINT64 LocalVarWithSrcLoc
= 0;
984 SaturatingUINT64 LocalVarWithLoc
= 0;
985 for (auto &Entry
: Statistics
) {
986 PerFunctionStats
&Stats
= Entry
.getValue();
987 uint64_t TotalVars
= Stats
.VarsInFunction
.size() *
988 (Stats
.NumFnInlined
+ Stats
.NumFnOutOfLine
);
989 // Count variables in global scope.
990 if (!Stats
.IsFunction
)
992 Stats
.NumLocalVars
+ Stats
.ConstantMembers
+ Stats
.NumArtificial
;
993 uint64_t Constants
= Stats
.ConstantMembers
;
994 VarParamWithLoc
+= Stats
.TotalVarWithLoc
+ Constants
;
995 VarParamTotal
+= TotalVars
;
996 VarParamUnique
+= Stats
.VarsInFunction
.size();
997 LLVM_DEBUG(for (auto &V
998 : Stats
.VarsInFunction
) llvm::dbgs()
999 << Entry
.getKey() << ": " << V
.getKey() << "\n");
1000 NumFunctions
+= Stats
.IsFunction
;
1001 NumFuncsWithSrcLoc
+= Stats
.HasSourceLocation
;
1002 NumInlinedFunctions
+= Stats
.IsFunction
* Stats
.NumFnInlined
;
1003 NumAbstractOrigins
+= Stats
.IsFunction
* Stats
.NumAbstractOrigins
;
1004 ParamTotal
+= Stats
.NumParams
;
1005 ParamWithType
+= Stats
.NumParamTypes
;
1006 ParamWithLoc
+= Stats
.NumParamLocations
;
1007 ParamWithSrcLoc
+= Stats
.NumParamSourceLocations
;
1008 LocalVarTotal
+= Stats
.NumLocalVars
;
1009 LocalVarWithType
+= Stats
.NumLocalVarTypes
;
1010 LocalVarWithLoc
+= Stats
.NumLocalVarLocations
;
1011 LocalVarWithSrcLoc
+= Stats
.NumLocalVarSourceLocations
;
1015 OS
.SetBufferSize(1024);
1016 json::OStream
J(OS
, 2);
1018 J
.attribute("version", Version
);
1019 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
1020 llvm::dbgs() << "---------------------------------\n");
1022 printDatum(J
, "file", Filename
.str());
1023 printDatum(J
, "format", FormatName
);
1025 printDatum(J
, "#functions", NumFunctions
.Value
);
1026 printDatum(J
, "#functions with location", NumFuncsWithSrcLoc
.Value
);
1027 printDatum(J
, "#inlined functions", NumInlinedFunctions
.Value
);
1028 printDatum(J
, "#inlined functions with abstract origins",
1029 NumAbstractOrigins
.Value
);
1031 // This includes local variables and formal parameters.
1032 printDatum(J
, "#unique source variables", VarParamUnique
.Value
);
1033 printDatum(J
, "#source variables", VarParamTotal
.Value
);
1034 printDatum(J
, "#source variables with location", VarParamWithLoc
.Value
);
1036 printDatum(J
, "#call site entries", GlobalStats
.CallSiteEntries
.Value
);
1037 printDatum(J
, "#call site DIEs", GlobalStats
.CallSiteDIEs
.Value
);
1038 printDatum(J
, "#call site parameter DIEs",
1039 GlobalStats
.CallSiteParamDIEs
.Value
);
1041 printDatum(J
, "sum_all_variables(#bytes in parent scope)",
1042 GlobalStats
.ScopeBytes
.Value
);
1044 "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
1045 GlobalStats
.TotalBytesCovered
.Value
);
1047 "sum_all_variables(#bytes in parent scope covered by "
1049 GlobalStats
.ScopeBytesCovered
.Value
);
1051 "sum_all_variables(#bytes in parent scope covered by "
1052 "DW_OP_entry_value)",
1053 GlobalStats
.ScopeEntryValueBytesCovered
.Value
);
1055 printDatum(J
, "sum_all_params(#bytes in parent scope)",
1056 GlobalStats
.ParamScopeBytes
.Value
);
1058 "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
1059 GlobalStats
.ParamScopeBytesCovered
.Value
);
1061 "sum_all_params(#bytes in parent scope covered by "
1062 "DW_OP_entry_value)",
1063 GlobalStats
.ParamScopeEntryValueBytesCovered
.Value
);
1065 printDatum(J
, "sum_all_local_vars(#bytes in parent scope)",
1066 GlobalStats
.LocalVarScopeBytes
.Value
);
1068 "sum_all_local_vars(#bytes in parent scope covered by "
1070 GlobalStats
.LocalVarScopeBytesCovered
.Value
);
1072 "sum_all_local_vars(#bytes in parent scope covered by "
1073 "DW_OP_entry_value)",
1074 GlobalStats
.LocalVarScopeEntryValueBytesCovered
.Value
);
1076 printDatum(J
, "#bytes within functions", GlobalStats
.FunctionSize
.Value
);
1077 printDatum(J
, "#bytes within inlined functions",
1078 GlobalStats
.InlineFunctionSize
.Value
);
1080 // Print the summary for formal parameters.
1081 printDatum(J
, "#params", ParamTotal
.Value
);
1082 printDatum(J
, "#params with source location", ParamWithSrcLoc
.Value
);
1083 printDatum(J
, "#params with type", ParamWithType
.Value
);
1084 printDatum(J
, "#params with binary location", ParamWithLoc
.Value
);
1086 // Print the summary for local variables.
1087 printDatum(J
, "#local vars", LocalVarTotal
.Value
);
1088 printDatum(J
, "#local vars with source location", LocalVarWithSrcLoc
.Value
);
1089 printDatum(J
, "#local vars with type", LocalVarWithType
.Value
);
1090 printDatum(J
, "#local vars with binary location", LocalVarWithLoc
.Value
);
1092 // Print the debug section sizes.
1093 printSectionSizes(J
, Sizes
);
1095 // Print the location statistics for variables (includes local variables
1096 // and formal parameters).
1097 printDatum(J
, "#variables processed by location statistics",
1098 LocStats
.NumVarParam
.Value
);
1099 printLocationStats(J
, "#variables", LocStats
.VarParamLocStats
);
1100 printLocationStats(J
, "#variables - entry values",
1101 LocStats
.VarParamNonEntryValLocStats
);
1103 // Print the location statistics for formal parameters.
1104 printDatum(J
, "#params processed by location statistics",
1105 LocStats
.NumParam
.Value
);
1106 printLocationStats(J
, "#params", LocStats
.ParamLocStats
);
1107 printLocationStats(J
, "#params - entry values",
1108 LocStats
.ParamNonEntryValLocStats
);
1110 // Print the location statistics for local variables.
1111 printDatum(J
, "#local vars processed by location statistics",
1112 LocStats
.NumVar
.Value
);
1113 printLocationStats(J
, "#local vars", LocStats
.LocalVarLocStats
);
1114 printLocationStats(J
, "#local vars - entry values",
1115 LocStats
.LocalVarNonEntryValLocStats
);
1117 // Print line statistics for the object file.
1118 printDatum(J
, "#bytes with line information", LnStats
.NumBytes
.Value
);
1119 printDatum(J
, "#bytes with line-0 locations", LnStats
.NumLineZeroBytes
.Value
);
1120 printDatum(J
, "#line entries", LnStats
.NumEntries
.Value
);
1121 printDatum(J
, "#line entries (is_stmt)", LnStats
.NumIsStmtEntries
.Value
);
1122 printDatum(J
, "#line entries (unique)", LnStats
.NumUniqueEntries
.Value
);
1123 printDatum(J
, "#line entries (unique non-0)",
1124 LnStats
.NumUniqueNonZeroEntries
.Value
);
1129 llvm::dbgs() << "Total Availability: "
1130 << (VarParamTotal
.Value
1131 ? (int)std::round((VarParamWithLoc
.Value
* 100.0) /
1132 VarParamTotal
.Value
)
1135 llvm::dbgs() << "PC Ranges covered: "
1136 << (GlobalStats
.ScopeBytes
.Value
1138 (GlobalStats
.ScopeBytesCovered
.Value
* 100.0) /
1139 GlobalStats
.ScopeBytes
.Value
)