Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp
35231 views
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/DWARFExpression.h"
16
#include "llvm/Object/ObjectFile.h"
17
#include "llvm/Support/JSON.h"
18
19
#define DEBUG_TYPE "dwarfdump"
20
using namespace llvm;
21
using namespace llvm::dwarfdump;
22
using namespace llvm::object;
23
24
namespace {
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;
30
31
/// This is used for zero location coverage bucket.
32
constexpr unsigned ZeroCoverageBucket = 0;
33
34
/// The UINT64_MAX is used as an indication of the overflow.
35
constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
36
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>;
43
44
/// This represents a data type for the stats and it helps us to
45
/// detect an overflow.
46
/// NOTE: This can be implemented as a template if there is an another type
47
/// needing this.
48
struct SaturatingUINT64 {
49
/// Number that represents the stats.
50
uint64_t Value;
51
52
SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
53
54
void operator++(int) { return *this += 1; }
55
void operator+=(uint64_t Value_) {
56
if (Value != OverflowValue) {
57
if (Value < OverflowValue - Value_)
58
Value += Value_;
59
else
60
Value = OverflowValue;
61
}
62
}
63
};
64
65
/// Utility struct to store the full location of a DIE - its CU and offset.
66
struct DIELocation {
67
DWARFUnit *DwUnit;
68
uint64_t DIEOffset;
69
DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
70
: DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
71
};
72
/// This represents DWARF locations of CrossCU referencing DIEs.
73
using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
74
75
/// This maps function DIE offset to its DWARF CU.
76
using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
77
78
/// Holds statistics for one function (or other entity that has a PC range and
79
/// contains variables, such as a compile unit).
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
88
/// instances.
89
uint64_t TotalVarWithLoc = 0;
90
/// Number of constants with location across all inlined instances.
91
uint64_t ConstantMembers = 0;
92
/// Number of arificial variables, parameters or members across all instances.
93
uint64_t NumArtificial = 0;
94
/// List of all Variables and parameters in this function.
95
StringSet<> VarsInFunction;
96
/// Compile units also cover a PC range, but have this flag set to false.
97
bool IsFunction = false;
98
/// Function has source location information.
99
bool HasSourceLocation = false;
100
/// Number of function parameters.
101
uint64_t NumParams = 0;
102
/// Number of function parameters with source location.
103
uint64_t NumParamSourceLocations = 0;
104
/// Number of function parameters with type.
105
uint64_t NumParamTypes = 0;
106
/// Number of function parameters with a DW_AT_location.
107
uint64_t NumParamLocations = 0;
108
/// Number of local variables.
109
uint64_t NumLocalVars = 0;
110
/// Number of local variables with source location.
111
uint64_t NumLocalVarSourceLocations = 0;
112
/// Number of local variables with type.
113
uint64_t NumLocalVarTypes = 0;
114
/// Number of local variables with DW_AT_location.
115
uint64_t NumLocalVarLocations = 0;
116
};
117
118
/// Holds accumulated global statistics about DIEs.
119
struct GlobalStats {
120
/// Total number of PC range bytes covered by DW_AT_locations.
121
SaturatingUINT64 TotalBytesCovered = 0;
122
/// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
123
SaturatingUINT64 ScopeBytesCovered = 0;
124
/// Total number of PC range bytes in each variable's enclosing scope.
125
SaturatingUINT64 ScopeBytes = 0;
126
/// Total number of PC range bytes covered by DW_AT_locations with
127
/// the debug entry values (DW_OP_entry_value).
128
SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
129
/// Total number of PC range bytes covered by DW_AT_locations of
130
/// formal parameters.
131
SaturatingUINT64 ParamScopeBytesCovered = 0;
132
/// Total number of PC range bytes in each parameter's enclosing scope.
133
SaturatingUINT64 ParamScopeBytes = 0;
134
/// Total number of PC range bytes covered by DW_AT_locations with
135
/// the debug entry values (DW_OP_entry_value) (only for parameters).
136
SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
137
/// Total number of PC range bytes covered by DW_AT_locations (only for local
138
/// variables).
139
SaturatingUINT64 LocalVarScopeBytesCovered = 0;
140
/// Total number of PC range bytes in each local variable's enclosing scope.
141
SaturatingUINT64 LocalVarScopeBytes = 0;
142
/// Total number of PC range bytes covered by DW_AT_locations with
143
/// the debug entry values (DW_OP_entry_value) (only for local variables).
144
SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
145
/// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
146
SaturatingUINT64 CallSiteEntries = 0;
147
/// Total number of call site DIEs (DW_TAG_call_site).
148
SaturatingUINT64 CallSiteDIEs = 0;
149
/// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
150
SaturatingUINT64 CallSiteParamDIEs = 0;
151
/// Total byte size of concrete functions. This byte size includes
152
/// inline functions contained in the concrete functions.
153
SaturatingUINT64 FunctionSize = 0;
154
/// Total byte size of inlined functions. This is the total number of bytes
155
/// for the top inline functions within concrete functions. This can help
156
/// tune the inline settings when compiling to match user expectations.
157
SaturatingUINT64 InlineFunctionSize = 0;
158
};
159
160
/// Holds accumulated debug location statistics about local variables and
161
/// formal parameters.
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
167
/// its scope.
168
std::vector<SaturatingUINT64> VarParamLocStats{
169
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
170
/// Map non debug entry values coverage.
171
std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
172
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
173
/// The debug location statistics for formal parameters.
174
std::vector<SaturatingUINT64> ParamLocStats{
175
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
176
/// Map non debug entry values coverage for formal parameters.
177
std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
178
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
179
/// The debug location statistics for local variables.
180
std::vector<SaturatingUINT64> LocalVarLocStats{
181
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
182
/// Map non debug entry values coverage for local variables.
183
std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
184
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
185
/// Total number of local variables and function parameters processed.
186
SaturatingUINT64 NumVarParam = 0;
187
/// Total number of formal parameters processed.
188
SaturatingUINT64 NumParam = 0;
189
/// Total number of local variables processed.
190
SaturatingUINT64 NumVar = 0;
191
};
192
193
/// Holds accumulated debug line statistics across all CUs.
194
struct LineStats {
195
SaturatingUINT64 NumBytes = 0;
196
SaturatingUINT64 NumLineZeroBytes = 0;
197
SaturatingUINT64 NumEntries = 0;
198
SaturatingUINT64 NumIsStmtEntries = 0;
199
SaturatingUINT64 NumUniqueEntries = 0;
200
SaturatingUINT64 NumUniqueNonZeroEntries = 0;
201
};
202
} // namespace
203
204
/// Collect debug location statistics for one DIE.
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)
213
return 0;
214
// Fully covered variable within its scope.
215
if (ScopeBytesCovered >= BytesInScope)
216
return NumOfCoverageCategories - 1;
217
// Get covered range (e.g. 20%-29%).
218
unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
219
LocBucket /= 10;
220
return LocBucket + 1;
221
};
222
223
unsigned CoverageBucket = getCoverageBucket();
224
225
VarParamLocStats[CoverageBucket].Value++;
226
if (IsParam)
227
ParamLocStats[CoverageBucket].Value++;
228
else if (IsLocalVar)
229
LocalVarLocStats[CoverageBucket].Value++;
230
}
231
232
/// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
233
/// and DeclLine. The identifier aims to be unique for any unique entities,
234
/// but keeping the same among different instances of the same entity.
235
static std::string constructDieID(DWARFDie Die,
236
StringRef Prefix = StringRef()) {
237
std::string IDStr;
238
llvm::raw_string_ostream ID(IDStr);
239
ID << Prefix
240
<< Die.getName(DINameKind::LinkageName);
241
242
// Prefix + Name is enough for local variables and parameters.
243
if (!Prefix.empty() && Prefix != "g")
244
return ID.str();
245
246
auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
247
std::string File;
248
if (DeclFile) {
249
DWARFUnit *U = Die.getDwarfUnit();
250
if (const auto *LT = U->getContext().getLineTableForUnit(U))
251
if (LT->getFileNameByIndex(
252
dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
253
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
254
File = std::string(sys::path::filename(File));
255
}
256
ID << ":" << (File.empty() ? "/" : File);
257
ID << ":"
258
<< dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
259
return ID.str();
260
}
261
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);
266
if (Lower >= Upper)
267
return 0;
268
return Upper - Lower;
269
}
270
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();
280
// Skip CU node.
281
if (Tag == dwarf::DW_TAG_compile_unit)
282
return;
283
284
bool HasLoc = false;
285
bool HasSrcLoc = false;
286
bool HasType = false;
287
uint64_t TotalBytesCovered = 0;
288
uint64_t ScopeBytesCovered = 0;
289
uint64_t BytesEntryValuesCovered = 0;
290
auto &FnStats = FnStatMap[FnPrefix];
291
bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
292
bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
293
bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
294
Die.find(dwarf::DW_AT_const_value);
295
296
// For zero covered inlined variables the locstats will be
297
// calculated later.
298
bool DeferLocStats = false;
299
300
if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
301
GlobalStats.CallSiteDIEs++;
302
return;
303
}
304
305
if (Tag == dwarf::DW_TAG_call_site_parameter ||
306
Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
307
GlobalStats.CallSiteParamDIEs++;
308
return;
309
}
310
311
if (!IsParam && !IsLocalVar && !IsConstantMember) {
312
// Not a variable or constant member.
313
return;
314
}
315
316
// Ignore declarations of global variables.
317
if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
318
return;
319
320
if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
321
Die.findRecursively(dwarf::DW_AT_decl_line))
322
HasSrcLoc = true;
323
324
if (Die.findRecursively(dwarf::DW_AT_type))
325
HasType = true;
326
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
332
// coverage.
333
llvm::erase(*AbstractOriginVariables, (*Offset).getRawUValue());
334
}
335
} else {
336
// The locstats will be handled at the end of
337
// the collectStatsRecursive().
338
DeferLocStats = true;
339
}
340
}
341
342
auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
343
DWARFUnit *U = Die.getDwarfUnit();
344
DataExtractor Data(toStringRef(D),
345
Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
346
DWARFExpression Expression(Data, U->getAddressByteSize(),
347
U->getFormParams().Format);
348
// Consider the expression containing the DW_OP_entry_value as
349
// an entry value.
350
return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
351
return Op.getCode() == dwarf::DW_OP_entry_value ||
352
Op.getCode() == dwarf::DW_OP_GNU_entry_value;
353
});
354
};
355
356
if (Die.find(dwarf::DW_AT_const_value)) {
357
// This catches constant members *and* variables.
358
HasLoc = true;
359
ScopeBytesCovered = BytesInScope;
360
TotalBytesCovered = BytesInScope;
361
} else {
362
// Handle variables and function arguments.
363
Expected<std::vector<DWARFLocationExpression>> Loc =
364
Die.getLocations(dwarf::DW_AT_location);
365
if (!Loc) {
366
consumeError(Loc.takeError());
367
} else {
368
HasLoc = true;
369
// Get PC coverage.
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;
376
} else {
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);
391
}
392
ScopeBytesCovered += ScopeBytesCoveredByEntry;
393
if (IsEntryValue(Entry.Expr))
394
BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
395
}
396
}
397
}
398
}
399
400
// Calculate the debug location statistics.
401
if (BytesInScope && !DeferLocStats) {
402
LocStats.NumVarParam.Value++;
403
if (IsParam)
404
LocStats.NumParam.Value++;
405
else if (IsLocalVar)
406
LocStats.NumVar.Value++;
407
408
collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
409
LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
410
IsLocalVar);
411
// Non debug entry values coverage statistics.
412
collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
413
LocStats.VarParamNonEntryValLocStats,
414
LocStats.ParamNonEntryValLocStats,
415
LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
416
}
417
418
// Collect PC range coverage data.
419
if (DWARFDie D =
420
Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
421
Die = D;
422
423
std::string VarID = constructDieID(Die, VarPrefix);
424
FnStats.VarsInFunction.insert(VarID);
425
426
GlobalStats.TotalBytesCovered += TotalBytesCovered;
427
if (BytesInScope) {
428
GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
429
GlobalStats.ScopeBytes += BytesInScope;
430
GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
431
if (IsParam) {
432
GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
433
GlobalStats.ParamScopeBytes += BytesInScope;
434
GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
435
} else if (IsLocalVar) {
436
GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
437
GlobalStats.LocalVarScopeBytes += BytesInScope;
438
GlobalStats.LocalVarScopeEntryValueBytesCovered +=
439
BytesEntryValuesCovered;
440
}
441
assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
442
}
443
444
if (IsConstantMember) {
445
FnStats.ConstantMembers++;
446
return;
447
}
448
449
FnStats.TotalVarWithLoc += (unsigned)HasLoc;
450
451
if (Die.find(dwarf::DW_AT_artificial)) {
452
FnStats.NumArtificial++;
453
return;
454
}
455
456
if (IsParam) {
457
FnStats.NumParams++;
458
if (HasType)
459
FnStats.NumParamTypes++;
460
if (HasSrcLoc)
461
FnStats.NumParamSourceLocations++;
462
if (HasLoc)
463
FnStats.NumParamLocations++;
464
} else if (IsLocalVar) {
465
FnStats.NumLocalVars++;
466
if (HasType)
467
FnStats.NumLocalVarTypes++;
468
if (HasSrcLoc)
469
FnStats.NumLocalVarSourceLocations++;
470
if (HasLoc)
471
FnStats.NumLocalVarLocations++;
472
}
473
}
474
475
/// Recursively collect variables from subprogram with DW_AT_inline attribute.
476
static void collectAbstractOriginFnInfo(
477
DWARFDie Die, uint64_t SPOffset,
478
AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
479
AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
480
DWARFDie Child = Die.getFirstChild();
481
while (Child) {
482
const dwarf::Tag ChildTag = Child.getTag();
483
if (ChildTag == dwarf::DW_TAG_formal_parameter ||
484
ChildTag == dwarf::DW_TAG_variable) {
485
GlobalAbstractOriginFnInfo[SPOffset].push_back(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();
491
}
492
}
493
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) {
504
// Skip NULL nodes.
505
if (Die.isNULL())
506
return;
507
508
const dwarf::Tag Tag = Die.getTag();
509
// Skip function types.
510
if (Tag == dwarf::DW_TAG_subroutine_type)
511
return;
512
513
// Handle any kind of lexical scope.
514
const bool HasAbstractOrigin =
515
Die.find(dwarf::DW_AT_abstract_origin) != std::nullopt;
516
const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
517
const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
518
const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
519
// We want to know how many variables (with abstract_origin) don't have
520
// location info.
521
const bool IsCandidateForZeroLocCovTracking =
522
(IsInlinedFunction || (IsFunction && HasAbstractOrigin));
523
524
AbstractOriginVarsTy AbstractOriginVars;
525
526
// Get the vars of the inlined fn, so the locstats
527
// reports the missing vars (with coverage 0%).
528
if (IsCandidateForZeroLocCovTracking) {
529
auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
530
if (OffsetFn) {
531
uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
532
if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
533
AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
534
AbstractOriginVarsPtr = &AbstractOriginVars;
535
} else {
536
// This means that the DW_AT_inline fn copy is out of order
537
// or that the abstract_origin references another CU,
538
// so this abstract origin instance will be processed later.
539
FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
540
AbstractOriginVarsPtr = nullptr;
541
}
542
}
543
}
544
545
if (IsFunction || IsInlinedFunction || IsBlock) {
546
// Reset VarPrefix when entering a new function.
547
if (IsFunction || IsInlinedFunction)
548
VarPrefix = "v";
549
550
// Ignore forward declarations.
551
if (Die.find(dwarf::DW_AT_declaration))
552
return;
553
554
// Check for call sites.
555
if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
556
GlobalStats.CallSiteEntries++;
557
558
// PC Ranges.
559
auto RangesOrError = Die.getAddressRanges();
560
if (!RangesOrError) {
561
llvm::consumeError(RangesOrError.takeError());
562
return;
563
}
564
565
auto Ranges = RangesOrError.get();
566
uint64_t BytesInThisScope = 0;
567
for (auto Range : Ranges)
568
BytesInThisScope += Range.HighPC - Range.LowPC;
569
570
// Count the function.
571
if (!IsBlock) {
572
// Skip over abstract origins, but collect variables
573
// from it so it can be used for location statistics
574
// for inlined instancies.
575
if (Die.find(dwarf::DW_AT_inline)) {
576
uint64_t SPOffset = Die.getOffset();
577
AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
578
collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
579
LocalAbstractOriginFnInfo);
580
return;
581
}
582
583
std::string FnID = constructDieID(Die);
584
// We've seen an instance of this function.
585
auto &FnStats = FnStatMap[FnID];
586
FnStats.IsFunction = true;
587
if (IsInlinedFunction) {
588
FnStats.NumFnInlined++;
589
if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
590
FnStats.NumAbstractOrigins++;
591
} else {
592
FnStats.NumFnOutOfLine++;
593
}
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.
598
FnPrefix = FnID;
599
}
600
601
if (BytesInThisScope) {
602
BytesInScope = BytesInThisScope;
603
if (IsFunction)
604
GlobalStats.FunctionSize += BytesInThisScope;
605
else if (IsInlinedFunction && InlineDepth == 0)
606
GlobalStats.InlineFunctionSize += BytesInThisScope;
607
}
608
} else {
609
// Not a scope, visit the Die itself. It could be a variable.
610
collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
611
FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
612
}
613
614
// Set InlineDepth correctly for child recursion
615
if (IsFunction)
616
InlineDepth = 0;
617
else if (IsInlinedFunction)
618
++InlineDepth;
619
620
// Traverse children.
621
unsigned LexicalBlockIndex = 0;
622
unsigned FormalParameterIndex = 0;
623
DWARFDie Child = Die.getFirstChild();
624
while (Child) {
625
std::string ChildVarPrefix = VarPrefix;
626
if (Child.getTag() == dwarf::DW_TAG_lexical_block)
627
ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
628
if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
629
ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
630
631
collectStatsRecursive(
632
Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
633
GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
634
LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
635
AbstractOriginVarsPtr);
636
Child = Child.getSibling();
637
}
638
639
if (!IsCandidateForZeroLocCovTracking)
640
return;
641
642
// After we have processed all vars of the inlined function (or function with
643
// an abstract_origin), we want to know how many variables have no location.
644
for (auto Offset : AbstractOriginVars) {
645
LocStats.NumVarParam++;
646
LocStats.VarParamLocStats[ZeroCoverageBucket]++;
647
auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
648
if (!FnDie)
649
continue;
650
auto Tag = FnDie.getTag();
651
if (Tag == dwarf::DW_TAG_formal_parameter) {
652
LocStats.NumParam++;
653
LocStats.ParamLocStats[ZeroCoverageBucket]++;
654
} else if (Tag == dwarf::DW_TAG_variable) {
655
LocStats.NumVar++;
656
LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
657
}
658
}
659
}
660
661
/// Print human-readable output.
662
/// \{
663
static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
664
if (Value == OverflowValue)
665
J.attribute(Key, "overflowed");
666
else
667
J.attribute(Key, Value);
668
669
LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
670
}
671
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")
677
.str(),
678
"overflowed");
679
else
680
J.attribute(
681
(Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
682
.str(),
683
LocationStats[0].Value);
684
LLVM_DEBUG(
685
llvm::dbgs() << Key
686
<< " with 0% of parent scope covered by DW_AT_location: \\"
687
<< LocationStats[0].Value << '\n');
688
689
if (LocationStats[1].Value == OverflowValue)
690
J.attribute((Twine(Key) +
691
" with (0%,10%) of parent scope covered by DW_AT_location")
692
.str(),
693
"overflowed");
694
else
695
J.attribute((Twine(Key) +
696
" with (0%,10%) of parent scope covered by DW_AT_location")
697
.str(),
698
LocationStats[1].Value);
699
LLVM_DEBUG(llvm::dbgs()
700
<< Key
701
<< " with (0%,10%) of parent scope covered by DW_AT_location: "
702
<< LocationStats[1].Value << '\n');
703
704
for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
705
if (LocationStats[i].Value == OverflowValue)
706
J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
707
Twine(i * 10) +
708
"%) of parent scope covered by DW_AT_location")
709
.str(),
710
"overflowed");
711
else
712
J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
713
Twine(i * 10) +
714
"%) of parent scope covered by DW_AT_location")
715
.str(),
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);
721
}
722
if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
723
J.attribute(
724
(Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
725
.str(),
726
"overflowed");
727
else
728
J.attribute(
729
(Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
730
.str(),
731
LocationStats[NumOfCoverageCategories - 1].Value);
732
LLVM_DEBUG(
733
llvm::dbgs() << Key
734
<< " with 100% of parent scope covered by DW_AT_location: "
735
<< LocationStats[NumOfCoverageCategories - 1].Value);
736
}
737
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));
741
}
742
743
/// Stop tracking variables that contain abstract_origin with a location.
744
/// This is used for out-of-order DW_AT_inline subprograms only.
745
static void updateVarsWithAbstractOriginLocCovInfo(
746
DWARFDie FnDieWithAbstractOrigin,
747
AbstractOriginVarsTy &AbstractOriginVars) {
748
DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
749
while (Child) {
750
const dwarf::Tag ChildTag = Child.getTag();
751
if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
752
ChildTag == dwarf::DW_TAG_variable) &&
753
(Child.find(dwarf::DW_AT_location) ||
754
Child.find(dwarf::DW_AT_const_value))) {
755
auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
756
if (OffsetVar)
757
llvm::erase(AbstractOriginVars, (*OffsetVar).getRawUValue());
758
} else if (ChildTag == dwarf::DW_TAG_lexical_block)
759
updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
760
Child = Child.getSibling();
761
}
762
}
763
764
/// Collect zero location coverage for inlined variables which refer to
765
/// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
766
/// Also cover the variables of a concrete function (represented with
767
/// the DW_TAG_subprogram) with an abstract_origin attribute.
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;
779
if (!FnCopy)
780
continue;
781
uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
782
// If there is no entry within LocalAbstractOriginFnInfo for the given
783
// FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
784
// CrossCU referencing.
785
if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
786
continue;
787
AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
788
updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
789
AbstractOriginVars);
790
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) {
796
LocStats.NumParam++;
797
LocStats.ParamLocStats[ZeroCoverageBucket]++;
798
} else if (Tag == dwarf::DW_TAG_variable) {
799
LocStats.NumVar++;
800
LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
801
}
802
}
803
ProcessedFns.push_back(FnOffset);
804
}
805
for (auto ProcessedFn : ProcessedFns)
806
llvm::erase(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
807
}
808
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);
820
auto FnCopy =
821
FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
822
if (!FnCopy)
823
continue;
824
uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
825
AbstractOriginVarsTy AbstractOriginVars =
826
GlobalAbstractOriginFnInfo[FnCopyRawUValue];
827
updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
828
AbstractOriginVars);
829
for (auto Offset : AbstractOriginVars) {
830
LocStats.NumVarParam++;
831
LocStats.VarParamLocStats[ZeroCoverageBucket]++;
832
auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
833
->getDIEForOffset(Offset)
834
.getTag();
835
if (Tag == dwarf::DW_TAG_formal_parameter) {
836
LocStats.NumParam++;
837
LocStats.ParamLocStats[ZeroCoverageBucket]++;
838
} else if (Tag == dwarf::DW_TAG_variable) {
839
LocStats.NumVar++;
840
LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
841
}
842
}
843
}
844
}
845
846
/// \}
847
848
/// Collect debug info quality metrics for an entire DIContext.
849
///
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
855
/// compilers is.
856
bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
857
const Twine &Filename,
858
raw_ostream &OS) {
859
StringRef FormatName = Obj.getFileFormatName();
860
GlobalStats GlobalStats;
861
LocationStats LocStats;
862
LineStats LnStats;
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
868
// abstract_origin.
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
873
// vector below.
874
using LineTuple = std::tuple<uint32_t, uint16_t, uint16_t>;
875
SmallVector<std::string> Files;
876
DenseSet<LineTuple> UniqueLines;
877
DenseSet<LineTuple> UniqueNonZeroLines;
878
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;
885
886
collectStatsRecursive(
887
CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
888
AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
889
LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
890
891
// collectZeroLocCovForVarsWithAbstractOrigin will filter out all
892
// out-of-order DWARF functions that have been processed within it,
893
// leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
894
// references.
895
collectZeroLocCovForVarsWithAbstractOrigin(
896
CUDie.getDwarfUnit(), GlobalStats, LocStats,
897
LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
898
899
// Collect all CrossCU references into CrossCUReferencesToBeResolved.
900
for (auto CrossCUReferencingDIEOffset :
901
FnsWithAbstractOriginToBeProcessed)
902
CrossCUReferencesToBeResolved.push_back(
903
DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
904
}
905
const auto *LineTable = DICtx.getLineTableForUnit(CU.get());
906
std::optional<uint64_t> LastFileIdxOpt;
907
if (LineTable)
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) {
915
std::string File;
916
if (LineTable->getFileNameByIndex(
917
FileIdx, CU->getCompilationDir(),
918
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
919
File)) {
920
auto ExistingFile = llvm::find(Files, File);
921
if (ExistingFile != Files.end()) {
922
CUFileMapping[FileIdx] = std::distance(Files.begin(), ExistingFile);
923
} else {
924
CUFileMapping[FileIdx] = Files.size();
925
Files.push_back(File);
926
}
927
}
928
}
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;
934
++RowIdx) {
935
auto Entry = LineTable->Rows[RowIdx];
936
if (Entry.IsStmt)
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});
944
} else {
945
auto EntryStartAddress = Entry.Address.Address;
946
auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address;
947
LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress;
948
}
949
}
950
}
951
}
952
}
953
954
LnStats.NumUniqueEntries = UniqueLines.size();
955
LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size();
956
957
/// Resolve CrossCU references.
958
collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
959
LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
960
CrossCUReferencesToBeResolved);
961
962
/// Collect the sizes of debug sections.
963
SectionSizes Sizes;
964
calculateSectionSizes(Obj, Sizes, Filename);
965
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
968
/// version.
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)
991
TotalVars =
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;
1012
}
1013
1014
// Print summary.
1015
OS.SetBufferSize(1024);
1016
json::OStream J(OS, 2);
1017
J.objectBegin();
1018
J.attribute("version", Version);
1019
LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
1020
llvm::dbgs() << "---------------------------------\n");
1021
1022
printDatum(J, "file", Filename.str());
1023
printDatum(J, "format", FormatName);
1024
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);
1030
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);
1035
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);
1040
1041
printDatum(J, "sum_all_variables(#bytes in parent scope)",
1042
GlobalStats.ScopeBytes.Value);
1043
printDatum(J,
1044
"sum_all_variables(#bytes in any scope covered by DW_AT_location)",
1045
GlobalStats.TotalBytesCovered.Value);
1046
printDatum(J,
1047
"sum_all_variables(#bytes in parent scope covered by "
1048
"DW_AT_location)",
1049
GlobalStats.ScopeBytesCovered.Value);
1050
printDatum(J,
1051
"sum_all_variables(#bytes in parent scope covered by "
1052
"DW_OP_entry_value)",
1053
GlobalStats.ScopeEntryValueBytesCovered.Value);
1054
1055
printDatum(J, "sum_all_params(#bytes in parent scope)",
1056
GlobalStats.ParamScopeBytes.Value);
1057
printDatum(J,
1058
"sum_all_params(#bytes in parent scope covered by DW_AT_location)",
1059
GlobalStats.ParamScopeBytesCovered.Value);
1060
printDatum(J,
1061
"sum_all_params(#bytes in parent scope covered by "
1062
"DW_OP_entry_value)",
1063
GlobalStats.ParamScopeEntryValueBytesCovered.Value);
1064
1065
printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
1066
GlobalStats.LocalVarScopeBytes.Value);
1067
printDatum(J,
1068
"sum_all_local_vars(#bytes in parent scope covered by "
1069
"DW_AT_location)",
1070
GlobalStats.LocalVarScopeBytesCovered.Value);
1071
printDatum(J,
1072
"sum_all_local_vars(#bytes in parent scope covered by "
1073
"DW_OP_entry_value)",
1074
GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
1075
1076
printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
1077
printDatum(J, "#bytes within inlined functions",
1078
GlobalStats.InlineFunctionSize.Value);
1079
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);
1085
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);
1091
1092
// Print the debug section sizes.
1093
printSectionSizes(J, Sizes);
1094
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);
1102
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);
1109
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);
1116
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);
1125
1126
J.objectEnd();
1127
OS << '\n';
1128
LLVM_DEBUG(
1129
llvm::dbgs() << "Total Availability: "
1130
<< (VarParamTotal.Value
1131
? (int)std::round((VarParamWithLoc.Value * 100.0) /
1132
VarParamTotal.Value)
1133
: 0)
1134
<< "%\n";
1135
llvm::dbgs() << "PC Ranges covered: "
1136
<< (GlobalStats.ScopeBytes.Value
1137
? (int)std::round(
1138
(GlobalStats.ScopeBytesCovered.Value * 100.0) /
1139
GlobalStats.ScopeBytes.Value)
1140
: 0)
1141
<< "%\n");
1142
return true;
1143
}
1144
1145