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/StringSet.h"
12 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
13 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
14 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
15 #include "llvm/Object/ObjectFile.h"
16 #include "llvm/Support/JSON.h"
18 #define DEBUG_TYPE "dwarfdump"
20 using namespace llvm::dwarfdump
;
21 using namespace llvm::object
;
24 /// This represents the number of categories of debug location coverage being
25 /// calculated. The first category is the number of variables with 0% location
26 /// coverage, but the last category is the number of variables with 100%
27 /// location coverage.
28 constexpr int NumOfCoverageCategories
= 12;
30 /// This is used for zero location coverage bucket.
31 constexpr unsigned ZeroCoverageBucket
= 0;
33 /// The UINT64_MAX is used as an indication of the overflow.
34 constexpr uint64_t OverflowValue
= std::numeric_limits
<uint64_t>::max();
36 /// This represents variables DIE offsets.
37 using AbstractOriginVarsTy
= llvm::SmallVector
<uint64_t>;
38 /// This maps function DIE offset to its variables.
39 using AbstractOriginVarsTyMap
= llvm::DenseMap
<uint64_t, AbstractOriginVarsTy
>;
40 /// This represents function DIE offsets containing an abstract_origin.
41 using FunctionsWithAbstractOriginTy
= llvm::SmallVector
<uint64_t>;
43 /// This represents a data type for the stats and it helps us to
44 /// detect an overflow.
45 /// NOTE: This can be implemented as a template if there is an another type
47 struct SaturatingUINT64
{
48 /// Number that represents the stats.
51 SaturatingUINT64(uint64_t Value_
) : Value(Value_
) {}
53 void operator++(int) { return *this += 1; }
54 void operator+=(uint64_t Value_
) {
55 if (Value
!= OverflowValue
) {
56 if (Value
< OverflowValue
- Value_
)
59 Value
= OverflowValue
;
64 /// Utility struct to store the full location of a DIE - its CU and offset.
68 DIELocation(DWARFUnit
*_DwUnit
, uint64_t _DIEOffset
)
69 : DwUnit(_DwUnit
), DIEOffset(_DIEOffset
) {}
71 /// This represents DWARF locations of CrossCU referencing DIEs.
72 using CrossCUReferencingDIELocationTy
= llvm::SmallVector
<DIELocation
>;
74 /// This maps function DIE offset to its DWARF CU.
75 using FunctionDIECUTyMap
= llvm::DenseMap
<uint64_t, DWARFUnit
*>;
77 /// Holds statistics for one function (or other entity that has a PC range and
78 /// contains variables, such as a compile unit).
79 struct PerFunctionStats
{
80 /// Number of inlined instances of this function.
81 uint64_t NumFnInlined
= 0;
82 /// Number of out-of-line instances of this function.
83 uint64_t NumFnOutOfLine
= 0;
84 /// Number of inlined instances that have abstract origins.
85 uint64_t NumAbstractOrigins
= 0;
86 /// Number of variables and parameters with location across all inlined
88 uint64_t TotalVarWithLoc
= 0;
89 /// Number of constants with location across all inlined instances.
90 uint64_t ConstantMembers
= 0;
91 /// Number of arificial variables, parameters or members across all instances.
92 uint64_t NumArtificial
= 0;
93 /// List of all Variables and parameters in this function.
94 StringSet
<> VarsInFunction
;
95 /// Compile units also cover a PC range, but have this flag set to false.
96 bool IsFunction
= false;
97 /// Function has source location information.
98 bool HasSourceLocation
= false;
99 /// Number of function parameters.
100 uint64_t NumParams
= 0;
101 /// Number of function parameters with source location.
102 uint64_t NumParamSourceLocations
= 0;
103 /// Number of function parameters with type.
104 uint64_t NumParamTypes
= 0;
105 /// Number of function parameters with a DW_AT_location.
106 uint64_t NumParamLocations
= 0;
107 /// Number of local variables.
108 uint64_t NumLocalVars
= 0;
109 /// Number of local variables with source location.
110 uint64_t NumLocalVarSourceLocations
= 0;
111 /// Number of local variables with type.
112 uint64_t NumLocalVarTypes
= 0;
113 /// Number of local variables with DW_AT_location.
114 uint64_t NumLocalVarLocations
= 0;
117 /// Holds accumulated global statistics about DIEs.
119 /// Total number of PC range bytes covered by DW_AT_locations.
120 SaturatingUINT64 TotalBytesCovered
= 0;
121 /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
122 SaturatingUINT64 ScopeBytesCovered
= 0;
123 /// Total number of PC range bytes in each variable's enclosing scope.
124 SaturatingUINT64 ScopeBytes
= 0;
125 /// Total number of PC range bytes covered by DW_AT_locations with
126 /// the debug entry values (DW_OP_entry_value).
127 SaturatingUINT64 ScopeEntryValueBytesCovered
= 0;
128 /// Total number of PC range bytes covered by DW_AT_locations of
129 /// formal parameters.
130 SaturatingUINT64 ParamScopeBytesCovered
= 0;
131 /// Total number of PC range bytes in each parameter's enclosing scope.
132 SaturatingUINT64 ParamScopeBytes
= 0;
133 /// Total number of PC range bytes covered by DW_AT_locations with
134 /// the debug entry values (DW_OP_entry_value) (only for parameters).
135 SaturatingUINT64 ParamScopeEntryValueBytesCovered
= 0;
136 /// Total number of PC range bytes covered by DW_AT_locations (only for local
138 SaturatingUINT64 LocalVarScopeBytesCovered
= 0;
139 /// Total number of PC range bytes in each local variable's enclosing scope.
140 SaturatingUINT64 LocalVarScopeBytes
= 0;
141 /// Total number of PC range bytes covered by DW_AT_locations with
142 /// the debug entry values (DW_OP_entry_value) (only for local variables).
143 SaturatingUINT64 LocalVarScopeEntryValueBytesCovered
= 0;
144 /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
145 SaturatingUINT64 CallSiteEntries
= 0;
146 /// Total number of call site DIEs (DW_TAG_call_site).
147 SaturatingUINT64 CallSiteDIEs
= 0;
148 /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
149 SaturatingUINT64 CallSiteParamDIEs
= 0;
150 /// Total byte size of concrete functions. This byte size includes
151 /// inline functions contained in the concrete functions.
152 SaturatingUINT64 FunctionSize
= 0;
153 /// Total byte size of inlined functions. This is the total number of bytes
154 /// for the top inline functions within concrete functions. This can help
155 /// tune the inline settings when compiling to match user expectations.
156 SaturatingUINT64 InlineFunctionSize
= 0;
159 /// Holds accumulated debug location statistics about local variables and
160 /// formal parameters.
161 struct LocationStats
{
162 /// Map the scope coverage decile to the number of variables in the decile.
163 /// The first element of the array (at the index zero) represents the number
164 /// of variables with the no debug location at all, but the last element
165 /// in the vector represents the number of fully covered variables within
167 std::vector
<SaturatingUINT64
> VarParamLocStats
{
168 std::vector
<SaturatingUINT64
>(NumOfCoverageCategories
, 0)};
169 /// Map non debug entry values coverage.
170 std::vector
<SaturatingUINT64
> VarParamNonEntryValLocStats
{
171 std::vector
<SaturatingUINT64
>(NumOfCoverageCategories
, 0)};
172 /// The debug location statistics for formal parameters.
173 std::vector
<SaturatingUINT64
> ParamLocStats
{
174 std::vector
<SaturatingUINT64
>(NumOfCoverageCategories
, 0)};
175 /// Map non debug entry values coverage for formal parameters.
176 std::vector
<SaturatingUINT64
> ParamNonEntryValLocStats
{
177 std::vector
<SaturatingUINT64
>(NumOfCoverageCategories
, 0)};
178 /// The debug location statistics for local variables.
179 std::vector
<SaturatingUINT64
> LocalVarLocStats
{
180 std::vector
<SaturatingUINT64
>(NumOfCoverageCategories
, 0)};
181 /// Map non debug entry values coverage for local variables.
182 std::vector
<SaturatingUINT64
> LocalVarNonEntryValLocStats
{
183 std::vector
<SaturatingUINT64
>(NumOfCoverageCategories
, 0)};
184 /// Total number of local variables and function parameters processed.
185 SaturatingUINT64 NumVarParam
= 0;
186 /// Total number of formal parameters processed.
187 SaturatingUINT64 NumParam
= 0;
188 /// Total number of local variables processed.
189 SaturatingUINT64 NumVar
= 0;
193 /// Collect debug location statistics for one DIE.
194 static void collectLocStats(uint64_t ScopeBytesCovered
, uint64_t BytesInScope
,
195 std::vector
<SaturatingUINT64
> &VarParamLocStats
,
196 std::vector
<SaturatingUINT64
> &ParamLocStats
,
197 std::vector
<SaturatingUINT64
> &LocalVarLocStats
,
198 bool IsParam
, bool IsLocalVar
) {
199 auto getCoverageBucket
= [ScopeBytesCovered
, BytesInScope
]() -> unsigned {
200 // No debug location at all for the variable.
201 if (ScopeBytesCovered
== 0)
203 // Fully covered variable within its scope.
204 if (ScopeBytesCovered
>= BytesInScope
)
205 return NumOfCoverageCategories
- 1;
206 // Get covered range (e.g. 20%-29%).
207 unsigned LocBucket
= 100 * (double)ScopeBytesCovered
/ BytesInScope
;
209 return LocBucket
+ 1;
212 unsigned CoverageBucket
= getCoverageBucket();
214 VarParamLocStats
[CoverageBucket
].Value
++;
216 ParamLocStats
[CoverageBucket
].Value
++;
218 LocalVarLocStats
[CoverageBucket
].Value
++;
221 /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
222 /// and DeclLine. The identifier aims to be unique for any unique entities,
223 /// but keeping the same among different instances of the same entity.
224 static std::string
constructDieID(DWARFDie Die
,
225 StringRef Prefix
= StringRef()) {
227 llvm::raw_string_ostream
ID(IDStr
);
229 << Die
.getName(DINameKind::LinkageName
);
231 // Prefix + Name is enough for local variables and parameters.
232 if (!Prefix
.empty() && !Prefix
.equals("g"))
235 auto DeclFile
= Die
.findRecursively(dwarf::DW_AT_decl_file
);
238 DWARFUnit
*U
= Die
.getDwarfUnit();
239 if (const auto *LT
= U
->getContext().getLineTableForUnit(U
))
240 if (LT
->getFileNameByIndex(
241 dwarf::toUnsigned(DeclFile
, 0), U
->getCompilationDir(),
242 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath
, File
))
243 File
= std::string(sys::path::filename(File
));
245 ID
<< ":" << (File
.empty() ? "/" : File
);
247 << dwarf::toUnsigned(Die
.findRecursively(dwarf::DW_AT_decl_line
), 0);
251 /// Return the number of bytes in the overlap of ranges A and B.
252 static uint64_t calculateOverlap(DWARFAddressRange A
, DWARFAddressRange B
) {
253 uint64_t Lower
= std::max(A
.LowPC
, B
.LowPC
);
254 uint64_t Upper
= std::min(A
.HighPC
, B
.HighPC
);
257 return Upper
- Lower
;
260 /// Collect debug info quality metrics for one DIE.
261 static void collectStatsForDie(DWARFDie Die
, const std::string
&FnPrefix
,
262 const std::string
&VarPrefix
,
263 uint64_t BytesInScope
, uint32_t InlineDepth
,
264 StringMap
<PerFunctionStats
> &FnStatMap
,
265 GlobalStats
&GlobalStats
,
266 LocationStats
&LocStats
,
267 AbstractOriginVarsTy
*AbstractOriginVariables
) {
268 const dwarf::Tag Tag
= Die
.getTag();
270 if (Tag
== dwarf::DW_TAG_compile_unit
)
274 bool HasSrcLoc
= false;
275 bool HasType
= false;
276 uint64_t TotalBytesCovered
= 0;
277 uint64_t ScopeBytesCovered
= 0;
278 uint64_t BytesEntryValuesCovered
= 0;
279 auto &FnStats
= FnStatMap
[FnPrefix
];
280 bool IsParam
= Tag
== dwarf::DW_TAG_formal_parameter
;
281 bool IsLocalVar
= Tag
== dwarf::DW_TAG_variable
;
282 bool IsConstantMember
= Tag
== dwarf::DW_TAG_member
&&
283 Die
.find(dwarf::DW_AT_const_value
);
285 // For zero covered inlined variables the locstats will be
287 bool DeferLocStats
= false;
289 if (Tag
== dwarf::DW_TAG_call_site
|| Tag
== dwarf::DW_TAG_GNU_call_site
) {
290 GlobalStats
.CallSiteDIEs
++;
294 if (Tag
== dwarf::DW_TAG_call_site_parameter
||
295 Tag
== dwarf::DW_TAG_GNU_call_site_parameter
) {
296 GlobalStats
.CallSiteParamDIEs
++;
300 if (!IsParam
&& !IsLocalVar
&& !IsConstantMember
) {
301 // Not a variable or constant member.
305 // Ignore declarations of global variables.
306 if (IsLocalVar
&& Die
.find(dwarf::DW_AT_declaration
))
309 if (Die
.findRecursively(dwarf::DW_AT_decl_file
) &&
310 Die
.findRecursively(dwarf::DW_AT_decl_line
))
313 if (Die
.findRecursively(dwarf::DW_AT_type
))
316 if (Die
.find(dwarf::DW_AT_abstract_origin
)) {
317 if (Die
.find(dwarf::DW_AT_location
) || Die
.find(dwarf::DW_AT_const_value
)) {
318 if (AbstractOriginVariables
) {
319 auto Offset
= Die
.find(dwarf::DW_AT_abstract_origin
);
320 // Do not track this variable any more, since it has location
322 llvm::erase(*AbstractOriginVariables
, (*Offset
).getRawUValue());
325 // The locstats will be handled at the end of
326 // the collectStatsRecursive().
327 DeferLocStats
= true;
331 auto IsEntryValue
= [&](ArrayRef
<uint8_t> D
) -> bool {
332 DWARFUnit
*U
= Die
.getDwarfUnit();
333 DataExtractor
Data(toStringRef(D
),
334 Die
.getDwarfUnit()->getContext().isLittleEndian(), 0);
335 DWARFExpression
Expression(Data
, U
->getAddressByteSize(),
336 U
->getFormParams().Format
);
337 // Consider the expression containing the DW_OP_entry_value as
339 return llvm::any_of(Expression
, [](const DWARFExpression::Operation
&Op
) {
340 return Op
.getCode() == dwarf::DW_OP_entry_value
||
341 Op
.getCode() == dwarf::DW_OP_GNU_entry_value
;
345 if (Die
.find(dwarf::DW_AT_const_value
)) {
346 // This catches constant members *and* variables.
348 ScopeBytesCovered
= BytesInScope
;
349 TotalBytesCovered
= BytesInScope
;
351 // Handle variables and function arguments.
352 Expected
<std::vector
<DWARFLocationExpression
>> Loc
=
353 Die
.getLocations(dwarf::DW_AT_location
);
355 consumeError(Loc
.takeError());
359 auto Default
= find_if(
360 *Loc
, [](const DWARFLocationExpression
&L
) { return !L
.Range
; });
361 if (Default
!= Loc
->end()) {
362 // Assume the entire range is covered by a single location.
363 ScopeBytesCovered
= BytesInScope
;
364 TotalBytesCovered
= BytesInScope
;
366 // Caller checks this Expected result already, it cannot fail.
367 auto ScopeRanges
= cantFail(Die
.getParent().getAddressRanges());
368 for (auto Entry
: *Loc
) {
369 TotalBytesCovered
+= Entry
.Range
->HighPC
- Entry
.Range
->LowPC
;
370 uint64_t ScopeBytesCoveredByEntry
= 0;
371 // Calculate how many bytes of the parent scope this entry covers.
372 // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
373 // address ranges defined by the bounded location descriptions of a
374 // location list may overlap". So in theory a variable can have
375 // multiple simultaneous locations, which would make this calculation
376 // misleading because we will count the overlapped areas
377 // twice. However, clang does not currently emit DWARF like this.
378 for (DWARFAddressRange R
: ScopeRanges
) {
379 ScopeBytesCoveredByEntry
+= calculateOverlap(*Entry
.Range
, R
);
381 ScopeBytesCovered
+= ScopeBytesCoveredByEntry
;
382 if (IsEntryValue(Entry
.Expr
))
383 BytesEntryValuesCovered
+= ScopeBytesCoveredByEntry
;
389 // Calculate the debug location statistics.
390 if (BytesInScope
&& !DeferLocStats
) {
391 LocStats
.NumVarParam
.Value
++;
393 LocStats
.NumParam
.Value
++;
395 LocStats
.NumVar
.Value
++;
397 collectLocStats(ScopeBytesCovered
, BytesInScope
, LocStats
.VarParamLocStats
,
398 LocStats
.ParamLocStats
, LocStats
.LocalVarLocStats
, IsParam
,
400 // Non debug entry values coverage statistics.
401 collectLocStats(ScopeBytesCovered
- BytesEntryValuesCovered
, BytesInScope
,
402 LocStats
.VarParamNonEntryValLocStats
,
403 LocStats
.ParamNonEntryValLocStats
,
404 LocStats
.LocalVarNonEntryValLocStats
, IsParam
, IsLocalVar
);
407 // Collect PC range coverage data.
409 Die
.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin
))
412 std::string VarID
= constructDieID(Die
, VarPrefix
);
413 FnStats
.VarsInFunction
.insert(VarID
);
415 GlobalStats
.TotalBytesCovered
+= TotalBytesCovered
;
417 GlobalStats
.ScopeBytesCovered
+= ScopeBytesCovered
;
418 GlobalStats
.ScopeBytes
+= BytesInScope
;
419 GlobalStats
.ScopeEntryValueBytesCovered
+= BytesEntryValuesCovered
;
421 GlobalStats
.ParamScopeBytesCovered
+= ScopeBytesCovered
;
422 GlobalStats
.ParamScopeBytes
+= BytesInScope
;
423 GlobalStats
.ParamScopeEntryValueBytesCovered
+= BytesEntryValuesCovered
;
424 } else if (IsLocalVar
) {
425 GlobalStats
.LocalVarScopeBytesCovered
+= ScopeBytesCovered
;
426 GlobalStats
.LocalVarScopeBytes
+= BytesInScope
;
427 GlobalStats
.LocalVarScopeEntryValueBytesCovered
+=
428 BytesEntryValuesCovered
;
430 assert(GlobalStats
.ScopeBytesCovered
.Value
<= GlobalStats
.ScopeBytes
.Value
);
433 if (IsConstantMember
) {
434 FnStats
.ConstantMembers
++;
438 FnStats
.TotalVarWithLoc
+= (unsigned)HasLoc
;
440 if (Die
.find(dwarf::DW_AT_artificial
)) {
441 FnStats
.NumArtificial
++;
448 FnStats
.NumParamTypes
++;
450 FnStats
.NumParamSourceLocations
++;
452 FnStats
.NumParamLocations
++;
453 } else if (IsLocalVar
) {
454 FnStats
.NumLocalVars
++;
456 FnStats
.NumLocalVarTypes
++;
458 FnStats
.NumLocalVarSourceLocations
++;
460 FnStats
.NumLocalVarLocations
++;
464 /// Recursively collect variables from subprogram with DW_AT_inline attribute.
465 static void collectAbstractOriginFnInfo(
466 DWARFDie Die
, uint64_t SPOffset
,
467 AbstractOriginVarsTyMap
&GlobalAbstractOriginFnInfo
,
468 AbstractOriginVarsTyMap
&LocalAbstractOriginFnInfo
) {
469 DWARFDie Child
= Die
.getFirstChild();
471 const dwarf::Tag ChildTag
= Child
.getTag();
472 if (ChildTag
== dwarf::DW_TAG_formal_parameter
||
473 ChildTag
== dwarf::DW_TAG_variable
) {
474 GlobalAbstractOriginFnInfo
[SPOffset
].push_back(Child
.getOffset());
475 LocalAbstractOriginFnInfo
[SPOffset
].push_back(Child
.getOffset());
476 } else if (ChildTag
== dwarf::DW_TAG_lexical_block
)
477 collectAbstractOriginFnInfo(Child
, SPOffset
, GlobalAbstractOriginFnInfo
,
478 LocalAbstractOriginFnInfo
);
479 Child
= Child
.getSibling();
483 /// Recursively collect debug info quality metrics.
484 static void collectStatsRecursive(
485 DWARFDie Die
, std::string FnPrefix
, std::string VarPrefix
,
486 uint64_t BytesInScope
, uint32_t InlineDepth
,
487 StringMap
<PerFunctionStats
> &FnStatMap
, GlobalStats
&GlobalStats
,
488 LocationStats
&LocStats
, FunctionDIECUTyMap
&AbstractOriginFnCUs
,
489 AbstractOriginVarsTyMap
&GlobalAbstractOriginFnInfo
,
490 AbstractOriginVarsTyMap
&LocalAbstractOriginFnInfo
,
491 FunctionsWithAbstractOriginTy
&FnsWithAbstractOriginToBeProcessed
,
492 AbstractOriginVarsTy
*AbstractOriginVarsPtr
= nullptr) {
497 const dwarf::Tag Tag
= Die
.getTag();
498 // Skip function types.
499 if (Tag
== dwarf::DW_TAG_subroutine_type
)
502 // Handle any kind of lexical scope.
503 const bool HasAbstractOrigin
=
504 Die
.find(dwarf::DW_AT_abstract_origin
) != std::nullopt
;
505 const bool IsFunction
= Tag
== dwarf::DW_TAG_subprogram
;
506 const bool IsBlock
= Tag
== dwarf::DW_TAG_lexical_block
;
507 const bool IsInlinedFunction
= Tag
== dwarf::DW_TAG_inlined_subroutine
;
508 // We want to know how many variables (with abstract_origin) don't have
510 const bool IsCandidateForZeroLocCovTracking
=
511 (IsInlinedFunction
|| (IsFunction
&& HasAbstractOrigin
));
513 AbstractOriginVarsTy AbstractOriginVars
;
515 // Get the vars of the inlined fn, so the locstats
516 // reports the missing vars (with coverage 0%).
517 if (IsCandidateForZeroLocCovTracking
) {
518 auto OffsetFn
= Die
.find(dwarf::DW_AT_abstract_origin
);
520 uint64_t OffsetOfInlineFnCopy
= (*OffsetFn
).getRawUValue();
521 if (LocalAbstractOriginFnInfo
.count(OffsetOfInlineFnCopy
)) {
522 AbstractOriginVars
= LocalAbstractOriginFnInfo
[OffsetOfInlineFnCopy
];
523 AbstractOriginVarsPtr
= &AbstractOriginVars
;
525 // This means that the DW_AT_inline fn copy is out of order
526 // or that the abstract_origin references another CU,
527 // so this abstract origin instance will be processed later.
528 FnsWithAbstractOriginToBeProcessed
.push_back(Die
.getOffset());
529 AbstractOriginVarsPtr
= nullptr;
534 if (IsFunction
|| IsInlinedFunction
|| IsBlock
) {
535 // Reset VarPrefix when entering a new function.
536 if (IsFunction
|| IsInlinedFunction
)
539 // Ignore forward declarations.
540 if (Die
.find(dwarf::DW_AT_declaration
))
543 // Check for call sites.
544 if (Die
.find(dwarf::DW_AT_call_file
) && Die
.find(dwarf::DW_AT_call_line
))
545 GlobalStats
.CallSiteEntries
++;
548 auto RangesOrError
= Die
.getAddressRanges();
549 if (!RangesOrError
) {
550 llvm::consumeError(RangesOrError
.takeError());
554 auto Ranges
= RangesOrError
.get();
555 uint64_t BytesInThisScope
= 0;
556 for (auto Range
: Ranges
)
557 BytesInThisScope
+= Range
.HighPC
- Range
.LowPC
;
559 // Count the function.
561 // Skip over abstract origins, but collect variables
562 // from it so it can be used for location statistics
563 // for inlined instancies.
564 if (Die
.find(dwarf::DW_AT_inline
)) {
565 uint64_t SPOffset
= Die
.getOffset();
566 AbstractOriginFnCUs
[SPOffset
] = Die
.getDwarfUnit();
567 collectAbstractOriginFnInfo(Die
, SPOffset
, GlobalAbstractOriginFnInfo
,
568 LocalAbstractOriginFnInfo
);
572 std::string FnID
= constructDieID(Die
);
573 // We've seen an instance of this function.
574 auto &FnStats
= FnStatMap
[FnID
];
575 FnStats
.IsFunction
= true;
576 if (IsInlinedFunction
) {
577 FnStats
.NumFnInlined
++;
578 if (Die
.findRecursively(dwarf::DW_AT_abstract_origin
))
579 FnStats
.NumAbstractOrigins
++;
581 FnStats
.NumFnOutOfLine
++;
583 if (Die
.findRecursively(dwarf::DW_AT_decl_file
) &&
584 Die
.findRecursively(dwarf::DW_AT_decl_line
))
585 FnStats
.HasSourceLocation
= true;
586 // Update function prefix.
590 if (BytesInThisScope
) {
591 BytesInScope
= BytesInThisScope
;
593 GlobalStats
.FunctionSize
+= BytesInThisScope
;
594 else if (IsInlinedFunction
&& InlineDepth
== 0)
595 GlobalStats
.InlineFunctionSize
+= BytesInThisScope
;
598 // Not a scope, visit the Die itself. It could be a variable.
599 collectStatsForDie(Die
, FnPrefix
, VarPrefix
, BytesInScope
, InlineDepth
,
600 FnStatMap
, GlobalStats
, LocStats
, AbstractOriginVarsPtr
);
603 // Set InlineDepth correctly for child recursion
606 else if (IsInlinedFunction
)
609 // Traverse children.
610 unsigned LexicalBlockIndex
= 0;
611 unsigned FormalParameterIndex
= 0;
612 DWARFDie Child
= Die
.getFirstChild();
614 std::string ChildVarPrefix
= VarPrefix
;
615 if (Child
.getTag() == dwarf::DW_TAG_lexical_block
)
616 ChildVarPrefix
+= toHex(LexicalBlockIndex
++) + '.';
617 if (Child
.getTag() == dwarf::DW_TAG_formal_parameter
)
618 ChildVarPrefix
+= 'p' + toHex(FormalParameterIndex
++) + '.';
620 collectStatsRecursive(
621 Child
, FnPrefix
, ChildVarPrefix
, BytesInScope
, InlineDepth
, FnStatMap
,
622 GlobalStats
, LocStats
, AbstractOriginFnCUs
, GlobalAbstractOriginFnInfo
,
623 LocalAbstractOriginFnInfo
, FnsWithAbstractOriginToBeProcessed
,
624 AbstractOriginVarsPtr
);
625 Child
= Child
.getSibling();
628 if (!IsCandidateForZeroLocCovTracking
)
631 // After we have processed all vars of the inlined function (or function with
632 // an abstract_origin), we want to know how many variables have no location.
633 for (auto Offset
: AbstractOriginVars
) {
634 LocStats
.NumVarParam
++;
635 LocStats
.VarParamLocStats
[ZeroCoverageBucket
]++;
636 auto FnDie
= Die
.getDwarfUnit()->getDIEForOffset(Offset
);
639 auto Tag
= FnDie
.getTag();
640 if (Tag
== dwarf::DW_TAG_formal_parameter
) {
642 LocStats
.ParamLocStats
[ZeroCoverageBucket
]++;
643 } else if (Tag
== dwarf::DW_TAG_variable
) {
645 LocStats
.LocalVarLocStats
[ZeroCoverageBucket
]++;
650 /// Print human-readable output.
652 static void printDatum(json::OStream
&J
, const char *Key
, json::Value Value
) {
653 if (Value
== OverflowValue
)
654 J
.attribute(Key
, "overflowed");
656 J
.attribute(Key
, Value
);
658 LLVM_DEBUG(llvm::dbgs() << Key
<< ": " << Value
<< '\n');
661 static void printLocationStats(json::OStream
&J
, const char *Key
,
662 std::vector
<SaturatingUINT64
> &LocationStats
) {
663 if (LocationStats
[0].Value
== OverflowValue
)
664 J
.attribute((Twine(Key
) +
665 " with (0%,10%) of parent scope covered by DW_AT_location")
670 (Twine(Key
) + " with 0% of parent scope covered by DW_AT_location")
672 LocationStats
[0].Value
);
675 << " with 0% of parent scope covered by DW_AT_location: \\"
676 << LocationStats
[0].Value
<< '\n');
678 if (LocationStats
[1].Value
== OverflowValue
)
679 J
.attribute((Twine(Key
) +
680 " with (0%,10%) of parent scope covered by DW_AT_location")
684 J
.attribute((Twine(Key
) +
685 " with (0%,10%) of parent scope covered by DW_AT_location")
687 LocationStats
[1].Value
);
688 LLVM_DEBUG(llvm::dbgs()
690 << " with (0%,10%) of parent scope covered by DW_AT_location: "
691 << LocationStats
[1].Value
<< '\n');
693 for (unsigned i
= 2; i
< NumOfCoverageCategories
- 1; ++i
) {
694 if (LocationStats
[i
].Value
== OverflowValue
)
695 J
.attribute((Twine(Key
) + " with [" + Twine((i
- 1) * 10) + "%," +
697 "%) of parent scope covered by DW_AT_location")
701 J
.attribute((Twine(Key
) + " with [" + Twine((i
- 1) * 10) + "%," +
703 "%) of parent scope covered by DW_AT_location")
705 LocationStats
[i
].Value
);
706 LLVM_DEBUG(llvm::dbgs()
707 << Key
<< " with [" << (i
- 1) * 10 << "%," << i
* 10
708 << "%) of parent scope covered by DW_AT_location: "
709 << LocationStats
[i
].Value
);
711 if (LocationStats
[NumOfCoverageCategories
- 1].Value
== OverflowValue
)
713 (Twine(Key
) + " with 100% of parent scope covered by DW_AT_location")
718 (Twine(Key
) + " with 100% of parent scope covered by DW_AT_location")
720 LocationStats
[NumOfCoverageCategories
- 1].Value
);
723 << " with 100% of parent scope covered by DW_AT_location: "
724 << LocationStats
[NumOfCoverageCategories
- 1].Value
);
727 static void printSectionSizes(json::OStream
&J
, const SectionSizes
&Sizes
) {
728 for (const auto &It
: Sizes
.DebugSectionSizes
)
729 J
.attribute((Twine("#bytes in ") + It
.first
).str(), int64_t(It
.second
));
732 /// Stop tracking variables that contain abstract_origin with a location.
733 /// This is used for out-of-order DW_AT_inline subprograms only.
734 static void updateVarsWithAbstractOriginLocCovInfo(
735 DWARFDie FnDieWithAbstractOrigin
,
736 AbstractOriginVarsTy
&AbstractOriginVars
) {
737 DWARFDie Child
= FnDieWithAbstractOrigin
.getFirstChild();
739 const dwarf::Tag ChildTag
= Child
.getTag();
740 if ((ChildTag
== dwarf::DW_TAG_formal_parameter
||
741 ChildTag
== dwarf::DW_TAG_variable
) &&
742 (Child
.find(dwarf::DW_AT_location
) ||
743 Child
.find(dwarf::DW_AT_const_value
))) {
744 auto OffsetVar
= Child
.find(dwarf::DW_AT_abstract_origin
);
746 llvm::erase(AbstractOriginVars
, (*OffsetVar
).getRawUValue());
747 } else if (ChildTag
== dwarf::DW_TAG_lexical_block
)
748 updateVarsWithAbstractOriginLocCovInfo(Child
, AbstractOriginVars
);
749 Child
= Child
.getSibling();
753 /// Collect zero location coverage for inlined variables which refer to
754 /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
755 /// Also cover the variables of a concrete function (represented with
756 /// the DW_TAG_subprogram) with an abstract_origin attribute.
757 static void collectZeroLocCovForVarsWithAbstractOrigin(
758 DWARFUnit
*DwUnit
, GlobalStats
&GlobalStats
, LocationStats
&LocStats
,
759 AbstractOriginVarsTyMap
&LocalAbstractOriginFnInfo
,
760 FunctionsWithAbstractOriginTy
&FnsWithAbstractOriginToBeProcessed
) {
761 // The next variable is used to filter out functions that have been processed,
762 // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
763 FunctionsWithAbstractOriginTy ProcessedFns
;
764 for (auto FnOffset
: FnsWithAbstractOriginToBeProcessed
) {
765 DWARFDie FnDieWithAbstractOrigin
= DwUnit
->getDIEForOffset(FnOffset
);
766 auto FnCopy
= FnDieWithAbstractOrigin
.find(dwarf::DW_AT_abstract_origin
);
767 AbstractOriginVarsTy AbstractOriginVars
;
770 uint64_t FnCopyRawUValue
= (*FnCopy
).getRawUValue();
771 // If there is no entry within LocalAbstractOriginFnInfo for the given
772 // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
773 // CrossCU referencing.
774 if (!LocalAbstractOriginFnInfo
.count(FnCopyRawUValue
))
776 AbstractOriginVars
= LocalAbstractOriginFnInfo
[FnCopyRawUValue
];
777 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin
,
780 for (auto Offset
: AbstractOriginVars
) {
781 LocStats
.NumVarParam
++;
782 LocStats
.VarParamLocStats
[ZeroCoverageBucket
]++;
783 auto Tag
= DwUnit
->getDIEForOffset(Offset
).getTag();
784 if (Tag
== dwarf::DW_TAG_formal_parameter
) {
786 LocStats
.ParamLocStats
[ZeroCoverageBucket
]++;
787 } else if (Tag
== dwarf::DW_TAG_variable
) {
789 LocStats
.LocalVarLocStats
[ZeroCoverageBucket
]++;
792 ProcessedFns
.push_back(FnOffset
);
794 for (auto ProcessedFn
: ProcessedFns
)
795 llvm::erase(FnsWithAbstractOriginToBeProcessed
, ProcessedFn
);
798 /// Collect zero location coverage for inlined variables which refer to
799 /// a DW_AT_inline copy of subprogram that is in a different CU.
800 static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
801 LocationStats
&LocStats
, FunctionDIECUTyMap AbstractOriginFnCUs
,
802 AbstractOriginVarsTyMap
&GlobalAbstractOriginFnInfo
,
803 CrossCUReferencingDIELocationTy
&CrossCUReferencesToBeResolved
) {
804 for (const auto &CrossCUReferenceToBeResolved
:
805 CrossCUReferencesToBeResolved
) {
806 DWARFUnit
*DwUnit
= CrossCUReferenceToBeResolved
.DwUnit
;
807 DWARFDie FnDIEWithCrossCUReferencing
=
808 DwUnit
->getDIEForOffset(CrossCUReferenceToBeResolved
.DIEOffset
);
810 FnDIEWithCrossCUReferencing
.find(dwarf::DW_AT_abstract_origin
);
813 uint64_t FnCopyRawUValue
= (*FnCopy
).getRawUValue();
814 AbstractOriginVarsTy AbstractOriginVars
=
815 GlobalAbstractOriginFnInfo
[FnCopyRawUValue
];
816 updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing
,
818 for (auto Offset
: AbstractOriginVars
) {
819 LocStats
.NumVarParam
++;
820 LocStats
.VarParamLocStats
[ZeroCoverageBucket
]++;
821 auto Tag
= (AbstractOriginFnCUs
[FnCopyRawUValue
])
822 ->getDIEForOffset(Offset
)
824 if (Tag
== dwarf::DW_TAG_formal_parameter
) {
826 LocStats
.ParamLocStats
[ZeroCoverageBucket
]++;
827 } else if (Tag
== dwarf::DW_TAG_variable
) {
829 LocStats
.LocalVarLocStats
[ZeroCoverageBucket
]++;
837 /// Collect debug info quality metrics for an entire DIContext.
839 /// Do the impossible and reduce the quality of the debug info down to a few
840 /// numbers. The idea is to condense the data into numbers that can be tracked
841 /// over time to identify trends in newer compiler versions and gauge the effect
842 /// of particular optimizations. The raw numbers themselves are not particularly
843 /// useful, only the delta between compiling the same program with different
845 bool dwarfdump::collectStatsForObjectFile(ObjectFile
&Obj
, DWARFContext
&DICtx
,
846 const Twine
&Filename
,
848 StringRef FormatName
= Obj
.getFileFormatName();
849 GlobalStats GlobalStats
;
850 LocationStats LocStats
;
851 StringMap
<PerFunctionStats
> Statistics
;
852 // This variable holds variable information for functions with
853 // abstract_origin globally, across all CUs.
854 AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo
;
855 // This variable holds information about the CU of a function with
857 FunctionDIECUTyMap AbstractOriginFnCUs
;
858 CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved
;
859 for (const auto &CU
: static_cast<DWARFContext
*>(&DICtx
)->compile_units()) {
860 if (DWARFDie CUDie
= CU
->getNonSkeletonUnitDIE(false)) {
861 // This variable holds variable information for functions with
862 // abstract_origin, but just for the current CU.
863 AbstractOriginVarsTyMap LocalAbstractOriginFnInfo
;
864 FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed
;
866 collectStatsRecursive(
867 CUDie
, "/", "g", 0, 0, Statistics
, GlobalStats
, LocStats
,
868 AbstractOriginFnCUs
, GlobalAbstractOriginFnInfo
,
869 LocalAbstractOriginFnInfo
, FnsWithAbstractOriginToBeProcessed
);
871 // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
872 // out-of-order DWARF functions that have been processed within it,
873 // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
875 collectZeroLocCovForVarsWithAbstractOrigin(
876 CUDie
.getDwarfUnit(), GlobalStats
, LocStats
,
877 LocalAbstractOriginFnInfo
, FnsWithAbstractOriginToBeProcessed
);
879 // Collect all CrossCU references into CrossCUReferencesToBeResolved.
880 for (auto CrossCUReferencingDIEOffset
:
881 FnsWithAbstractOriginToBeProcessed
)
882 CrossCUReferencesToBeResolved
.push_back(
883 DIELocation(CUDie
.getDwarfUnit(), CrossCUReferencingDIEOffset
));
887 /// Resolve CrossCU references.
888 collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
889 LocStats
, AbstractOriginFnCUs
, GlobalAbstractOriginFnInfo
,
890 CrossCUReferencesToBeResolved
);
892 /// Collect the sizes of debug sections.
894 calculateSectionSizes(Obj
, Sizes
, Filename
);
896 /// The version number should be increased every time the algorithm is changed
897 /// (including bug fixes). New metrics may be added without increasing the
899 unsigned Version
= 9;
900 SaturatingUINT64 VarParamTotal
= 0;
901 SaturatingUINT64 VarParamUnique
= 0;
902 SaturatingUINT64 VarParamWithLoc
= 0;
903 SaturatingUINT64 NumFunctions
= 0;
904 SaturatingUINT64 NumInlinedFunctions
= 0;
905 SaturatingUINT64 NumFuncsWithSrcLoc
= 0;
906 SaturatingUINT64 NumAbstractOrigins
= 0;
907 SaturatingUINT64 ParamTotal
= 0;
908 SaturatingUINT64 ParamWithType
= 0;
909 SaturatingUINT64 ParamWithLoc
= 0;
910 SaturatingUINT64 ParamWithSrcLoc
= 0;
911 SaturatingUINT64 LocalVarTotal
= 0;
912 SaturatingUINT64 LocalVarWithType
= 0;
913 SaturatingUINT64 LocalVarWithSrcLoc
= 0;
914 SaturatingUINT64 LocalVarWithLoc
= 0;
915 for (auto &Entry
: Statistics
) {
916 PerFunctionStats
&Stats
= Entry
.getValue();
917 uint64_t TotalVars
= Stats
.VarsInFunction
.size() *
918 (Stats
.NumFnInlined
+ Stats
.NumFnOutOfLine
);
919 // Count variables in global scope.
920 if (!Stats
.IsFunction
)
922 Stats
.NumLocalVars
+ Stats
.ConstantMembers
+ Stats
.NumArtificial
;
923 uint64_t Constants
= Stats
.ConstantMembers
;
924 VarParamWithLoc
+= Stats
.TotalVarWithLoc
+ Constants
;
925 VarParamTotal
+= TotalVars
;
926 VarParamUnique
+= Stats
.VarsInFunction
.size();
927 LLVM_DEBUG(for (auto &V
928 : Stats
.VarsInFunction
) llvm::dbgs()
929 << Entry
.getKey() << ": " << V
.getKey() << "\n");
930 NumFunctions
+= Stats
.IsFunction
;
931 NumFuncsWithSrcLoc
+= Stats
.HasSourceLocation
;
932 NumInlinedFunctions
+= Stats
.IsFunction
* Stats
.NumFnInlined
;
933 NumAbstractOrigins
+= Stats
.IsFunction
* Stats
.NumAbstractOrigins
;
934 ParamTotal
+= Stats
.NumParams
;
935 ParamWithType
+= Stats
.NumParamTypes
;
936 ParamWithLoc
+= Stats
.NumParamLocations
;
937 ParamWithSrcLoc
+= Stats
.NumParamSourceLocations
;
938 LocalVarTotal
+= Stats
.NumLocalVars
;
939 LocalVarWithType
+= Stats
.NumLocalVarTypes
;
940 LocalVarWithLoc
+= Stats
.NumLocalVarLocations
;
941 LocalVarWithSrcLoc
+= Stats
.NumLocalVarSourceLocations
;
945 OS
.SetBufferSize(1024);
946 json::OStream
J(OS
, 2);
948 J
.attribute("version", Version
);
949 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
950 llvm::dbgs() << "---------------------------------\n");
952 printDatum(J
, "file", Filename
.str());
953 printDatum(J
, "format", FormatName
);
955 printDatum(J
, "#functions", NumFunctions
.Value
);
956 printDatum(J
, "#functions with location", NumFuncsWithSrcLoc
.Value
);
957 printDatum(J
, "#inlined functions", NumInlinedFunctions
.Value
);
958 printDatum(J
, "#inlined functions with abstract origins",
959 NumAbstractOrigins
.Value
);
961 // This includes local variables and formal parameters.
962 printDatum(J
, "#unique source variables", VarParamUnique
.Value
);
963 printDatum(J
, "#source variables", VarParamTotal
.Value
);
964 printDatum(J
, "#source variables with location", VarParamWithLoc
.Value
);
966 printDatum(J
, "#call site entries", GlobalStats
.CallSiteEntries
.Value
);
967 printDatum(J
, "#call site DIEs", GlobalStats
.CallSiteDIEs
.Value
);
968 printDatum(J
, "#call site parameter DIEs",
969 GlobalStats
.CallSiteParamDIEs
.Value
);
971 printDatum(J
, "sum_all_variables(#bytes in parent scope)",
972 GlobalStats
.ScopeBytes
.Value
);
974 "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
975 GlobalStats
.TotalBytesCovered
.Value
);
977 "sum_all_variables(#bytes in parent scope covered by "
979 GlobalStats
.ScopeBytesCovered
.Value
);
981 "sum_all_variables(#bytes in parent scope covered by "
982 "DW_OP_entry_value)",
983 GlobalStats
.ScopeEntryValueBytesCovered
.Value
);
985 printDatum(J
, "sum_all_params(#bytes in parent scope)",
986 GlobalStats
.ParamScopeBytes
.Value
);
988 "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
989 GlobalStats
.ParamScopeBytesCovered
.Value
);
991 "sum_all_params(#bytes in parent scope covered by "
992 "DW_OP_entry_value)",
993 GlobalStats
.ParamScopeEntryValueBytesCovered
.Value
);
995 printDatum(J
, "sum_all_local_vars(#bytes in parent scope)",
996 GlobalStats
.LocalVarScopeBytes
.Value
);
998 "sum_all_local_vars(#bytes in parent scope covered by "
1000 GlobalStats
.LocalVarScopeBytesCovered
.Value
);
1002 "sum_all_local_vars(#bytes in parent scope covered by "
1003 "DW_OP_entry_value)",
1004 GlobalStats
.LocalVarScopeEntryValueBytesCovered
.Value
);
1006 printDatum(J
, "#bytes within functions", GlobalStats
.FunctionSize
.Value
);
1007 printDatum(J
, "#bytes within inlined functions",
1008 GlobalStats
.InlineFunctionSize
.Value
);
1010 // Print the summary for formal parameters.
1011 printDatum(J
, "#params", ParamTotal
.Value
);
1012 printDatum(J
, "#params with source location", ParamWithSrcLoc
.Value
);
1013 printDatum(J
, "#params with type", ParamWithType
.Value
);
1014 printDatum(J
, "#params with binary location", ParamWithLoc
.Value
);
1016 // Print the summary for local variables.
1017 printDatum(J
, "#local vars", LocalVarTotal
.Value
);
1018 printDatum(J
, "#local vars with source location", LocalVarWithSrcLoc
.Value
);
1019 printDatum(J
, "#local vars with type", LocalVarWithType
.Value
);
1020 printDatum(J
, "#local vars with binary location", LocalVarWithLoc
.Value
);
1022 // Print the debug section sizes.
1023 printSectionSizes(J
, Sizes
);
1025 // Print the location statistics for variables (includes local variables
1026 // and formal parameters).
1027 printDatum(J
, "#variables processed by location statistics",
1028 LocStats
.NumVarParam
.Value
);
1029 printLocationStats(J
, "#variables", LocStats
.VarParamLocStats
);
1030 printLocationStats(J
, "#variables - entry values",
1031 LocStats
.VarParamNonEntryValLocStats
);
1033 // Print the location statistics for formal parameters.
1034 printDatum(J
, "#params processed by location statistics",
1035 LocStats
.NumParam
.Value
);
1036 printLocationStats(J
, "#params", LocStats
.ParamLocStats
);
1037 printLocationStats(J
, "#params - entry values",
1038 LocStats
.ParamNonEntryValLocStats
);
1040 // Print the location statistics for local variables.
1041 printDatum(J
, "#local vars processed by location statistics",
1042 LocStats
.NumVar
.Value
);
1043 printLocationStats(J
, "#local vars", LocStats
.LocalVarLocStats
);
1044 printLocationStats(J
, "#local vars - entry values",
1045 LocStats
.LocalVarNonEntryValLocStats
);
1049 llvm::dbgs() << "Total Availability: "
1050 << (VarParamTotal
.Value
1051 ? (int)std::round((VarParamWithLoc
.Value
* 100.0) /
1052 VarParamTotal
.Value
)
1055 llvm::dbgs() << "PC Ranges covered: "
1056 << (GlobalStats
.ScopeBytes
.Value
1058 (GlobalStats
.ScopeBytesCovered
.Value
* 100.0) /
1059 GlobalStats
.ScopeBytes
.Value
)