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/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"
17
18 #define DEBUG_TYPE "dwarfdump"
19 using namespace llvm;
20 using namespace llvm::dwarfdump;
21 using namespace llvm::object;
22
23 namespace {
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;
29
30 /// This is used for zero location coverage bucket.
31 constexpr unsigned ZeroCoverageBucket = 0;
32
33 /// The UINT64_MAX is used as an indication of the overflow.
34 constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
35
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>;
42
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
46 /// needing this.
47 struct SaturatingUINT64 {
48 /// Number that represents the stats.
49 uint64_t Value;
50
SaturatingUINT64__anon1dedb3a60111::SaturatingUINT6451 SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
52
operator ++__anon1dedb3a60111::SaturatingUINT6453 void operator++(int) { return *this += 1; }
operator +=__anon1dedb3a60111::SaturatingUINT6454 void operator+=(uint64_t Value_) {
55 if (Value != OverflowValue) {
56 if (Value < OverflowValue - Value_)
57 Value += Value_;
58 else
59 Value = OverflowValue;
60 }
61 }
62 };
63
64 /// Utility struct to store the full location of a DIE - its CU and offset.
65 struct DIELocation {
66 DWARFUnit *DwUnit;
67 uint64_t DIEOffset;
DIELocation__anon1dedb3a60111::DIELocation68 DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
69 : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
70 };
71 /// This represents DWARF locations of CrossCU referencing DIEs.
72 using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
73
74 /// This maps function DIE offset to its DWARF CU.
75 using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
76
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
87 /// instances.
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;
115 };
116
117 /// Holds accumulated global statistics about DIEs.
118 struct GlobalStats {
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
137 /// variables).
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;
157 };
158
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
166 /// its scope.
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;
190 };
191 } // namespace
192
193 /// Collect debug location statistics for one DIE.
collectLocStats(uint64_t ScopeBytesCovered,uint64_t BytesInScope,std::vector<SaturatingUINT64> & VarParamLocStats,std::vector<SaturatingUINT64> & ParamLocStats,std::vector<SaturatingUINT64> & LocalVarLocStats,bool IsParam,bool IsLocalVar)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)
202 return 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;
208 LocBucket /= 10;
209 return LocBucket + 1;
210 };
211
212 unsigned CoverageBucket = getCoverageBucket();
213
214 VarParamLocStats[CoverageBucket].Value++;
215 if (IsParam)
216 ParamLocStats[CoverageBucket].Value++;
217 else if (IsLocalVar)
218 LocalVarLocStats[CoverageBucket].Value++;
219 }
220
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.
constructDieID(DWARFDie Die,StringRef Prefix=StringRef ())224 static std::string constructDieID(DWARFDie Die,
225 StringRef Prefix = StringRef()) {
226 std::string IDStr;
227 llvm::raw_string_ostream ID(IDStr);
228 ID << Prefix
229 << Die.getName(DINameKind::LinkageName);
230
231 // Prefix + Name is enough for local variables and parameters.
232 if (!Prefix.empty() && !Prefix.equals("g"))
233 return ID.str();
234
235 auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
236 std::string File;
237 if (DeclFile) {
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));
244 }
245 ID << ":" << (File.empty() ? "/" : File);
246 ID << ":"
247 << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
248 return ID.str();
249 }
250
251 /// Return the number of bytes in the overlap of ranges A and B.
calculateOverlap(DWARFAddressRange A,DWARFAddressRange 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);
255 if (Lower >= Upper)
256 return 0;
257 return Upper - Lower;
258 }
259
260 /// Collect debug info quality metrics for one DIE.
collectStatsForDie(DWARFDie Die,const std::string & FnPrefix,const std::string & VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTy * AbstractOriginVariables)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();
269 // Skip CU node.
270 if (Tag == dwarf::DW_TAG_compile_unit)
271 return;
272
273 bool HasLoc = false;
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);
284
285 // For zero covered inlined variables the locstats will be
286 // calculated later.
287 bool DeferLocStats = false;
288
289 if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
290 GlobalStats.CallSiteDIEs++;
291 return;
292 }
293
294 if (Tag == dwarf::DW_TAG_call_site_parameter ||
295 Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
296 GlobalStats.CallSiteParamDIEs++;
297 return;
298 }
299
300 if (!IsParam && !IsLocalVar && !IsConstantMember) {
301 // Not a variable or constant member.
302 return;
303 }
304
305 // Ignore declarations of global variables.
306 if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
307 return;
308
309 if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
310 Die.findRecursively(dwarf::DW_AT_decl_line))
311 HasSrcLoc = true;
312
313 if (Die.findRecursively(dwarf::DW_AT_type))
314 HasType = true;
315
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
321 // coverage.
322 llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue());
323 }
324 } else {
325 // The locstats will be handled at the end of
326 // the collectStatsRecursive().
327 DeferLocStats = true;
328 }
329 }
330
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
338 // an entry value.
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;
342 });
343 };
344
345 if (Die.find(dwarf::DW_AT_const_value)) {
346 // This catches constant members *and* variables.
347 HasLoc = true;
348 ScopeBytesCovered = BytesInScope;
349 TotalBytesCovered = BytesInScope;
350 } else {
351 // Handle variables and function arguments.
352 Expected<std::vector<DWARFLocationExpression>> Loc =
353 Die.getLocations(dwarf::DW_AT_location);
354 if (!Loc) {
355 consumeError(Loc.takeError());
356 } else {
357 HasLoc = true;
358 // Get PC coverage.
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;
365 } else {
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);
380 }
381 ScopeBytesCovered += ScopeBytesCoveredByEntry;
382 if (IsEntryValue(Entry.Expr))
383 BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
384 }
385 }
386 }
387 }
388
389 // Calculate the debug location statistics.
390 if (BytesInScope && !DeferLocStats) {
391 LocStats.NumVarParam.Value++;
392 if (IsParam)
393 LocStats.NumParam.Value++;
394 else if (IsLocalVar)
395 LocStats.NumVar.Value++;
396
397 collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
398 LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
399 IsLocalVar);
400 // Non debug entry values coverage statistics.
401 collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
402 LocStats.VarParamNonEntryValLocStats,
403 LocStats.ParamNonEntryValLocStats,
404 LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
405 }
406
407 // Collect PC range coverage data.
408 if (DWARFDie D =
409 Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
410 Die = D;
411
412 std::string VarID = constructDieID(Die, VarPrefix);
413 FnStats.VarsInFunction.insert(VarID);
414
415 GlobalStats.TotalBytesCovered += TotalBytesCovered;
416 if (BytesInScope) {
417 GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
418 GlobalStats.ScopeBytes += BytesInScope;
419 GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
420 if (IsParam) {
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;
429 }
430 assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
431 }
432
433 if (IsConstantMember) {
434 FnStats.ConstantMembers++;
435 return;
436 }
437
438 FnStats.TotalVarWithLoc += (unsigned)HasLoc;
439
440 if (Die.find(dwarf::DW_AT_artificial)) {
441 FnStats.NumArtificial++;
442 return;
443 }
444
445 if (IsParam) {
446 FnStats.NumParams++;
447 if (HasType)
448 FnStats.NumParamTypes++;
449 if (HasSrcLoc)
450 FnStats.NumParamSourceLocations++;
451 if (HasLoc)
452 FnStats.NumParamLocations++;
453 } else if (IsLocalVar) {
454 FnStats.NumLocalVars++;
455 if (HasType)
456 FnStats.NumLocalVarTypes++;
457 if (HasSrcLoc)
458 FnStats.NumLocalVarSourceLocations++;
459 if (HasLoc)
460 FnStats.NumLocalVarLocations++;
461 }
462 }
463
464 /// Recursively collect variables from subprogram with DW_AT_inline attribute.
collectAbstractOriginFnInfo(DWARFDie Die,uint64_t SPOffset,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo)465 static void collectAbstractOriginFnInfo(
466 DWARFDie Die, uint64_t SPOffset,
467 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
468 AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
469 DWARFDie Child = Die.getFirstChild();
470 while (Child) {
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();
480 }
481 }
482
483 /// Recursively collect debug info quality metrics.
collectStatsRecursive(DWARFDie Die,std::string FnPrefix,std::string VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats,FunctionDIECUTyMap & AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed,AbstractOriginVarsTy * AbstractOriginVarsPtr=nullptr)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) {
493 // Skip NULL nodes.
494 if (Die.isNULL())
495 return;
496
497 const dwarf::Tag Tag = Die.getTag();
498 // Skip function types.
499 if (Tag == dwarf::DW_TAG_subroutine_type)
500 return;
501
502 // Handle any kind of lexical scope.
503 const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None;
504 const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
505 const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
506 const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
507 // We want to know how many variables (with abstract_origin) don't have
508 // location info.
509 const bool IsCandidateForZeroLocCovTracking =
510 (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
511
512 AbstractOriginVarsTy AbstractOriginVars;
513
514 // Get the vars of the inlined fn, so the locstats
515 // reports the missing vars (with coverage 0%).
516 if (IsCandidateForZeroLocCovTracking) {
517 auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
518 if (OffsetFn) {
519 uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
520 if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
521 AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
522 AbstractOriginVarsPtr = &AbstractOriginVars;
523 } else {
524 // This means that the DW_AT_inline fn copy is out of order
525 // or that the abstract_origin references another CU,
526 // so this abstract origin instance will be processed later.
527 FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
528 AbstractOriginVarsPtr = nullptr;
529 }
530 }
531 }
532
533 if (IsFunction || IsInlinedFunction || IsBlock) {
534 // Reset VarPrefix when entering a new function.
535 if (IsFunction || IsInlinedFunction)
536 VarPrefix = "v";
537
538 // Ignore forward declarations.
539 if (Die.find(dwarf::DW_AT_declaration))
540 return;
541
542 // Check for call sites.
543 if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
544 GlobalStats.CallSiteEntries++;
545
546 // PC Ranges.
547 auto RangesOrError = Die.getAddressRanges();
548 if (!RangesOrError) {
549 llvm::consumeError(RangesOrError.takeError());
550 return;
551 }
552
553 auto Ranges = RangesOrError.get();
554 uint64_t BytesInThisScope = 0;
555 for (auto Range : Ranges)
556 BytesInThisScope += Range.HighPC - Range.LowPC;
557
558 // Count the function.
559 if (!IsBlock) {
560 // Skip over abstract origins, but collect variables
561 // from it so it can be used for location statistics
562 // for inlined instancies.
563 if (Die.find(dwarf::DW_AT_inline)) {
564 uint64_t SPOffset = Die.getOffset();
565 AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
566 collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
567 LocalAbstractOriginFnInfo);
568 return;
569 }
570
571 std::string FnID = constructDieID(Die);
572 // We've seen an instance of this function.
573 auto &FnStats = FnStatMap[FnID];
574 FnStats.IsFunction = true;
575 if (IsInlinedFunction) {
576 FnStats.NumFnInlined++;
577 if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
578 FnStats.NumAbstractOrigins++;
579 } else {
580 FnStats.NumFnOutOfLine++;
581 }
582 if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
583 Die.findRecursively(dwarf::DW_AT_decl_line))
584 FnStats.HasSourceLocation = true;
585 // Update function prefix.
586 FnPrefix = FnID;
587 }
588
589 if (BytesInThisScope) {
590 BytesInScope = BytesInThisScope;
591 if (IsFunction)
592 GlobalStats.FunctionSize += BytesInThisScope;
593 else if (IsInlinedFunction && InlineDepth == 0)
594 GlobalStats.InlineFunctionSize += BytesInThisScope;
595 }
596 } else {
597 // Not a scope, visit the Die itself. It could be a variable.
598 collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
599 FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
600 }
601
602 // Set InlineDepth correctly for child recursion
603 if (IsFunction)
604 InlineDepth = 0;
605 else if (IsInlinedFunction)
606 ++InlineDepth;
607
608 // Traverse children.
609 unsigned LexicalBlockIndex = 0;
610 unsigned FormalParameterIndex = 0;
611 DWARFDie Child = Die.getFirstChild();
612 while (Child) {
613 std::string ChildVarPrefix = VarPrefix;
614 if (Child.getTag() == dwarf::DW_TAG_lexical_block)
615 ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
616 if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
617 ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
618
619 collectStatsRecursive(
620 Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
621 GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
622 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
623 AbstractOriginVarsPtr);
624 Child = Child.getSibling();
625 }
626
627 if (!IsCandidateForZeroLocCovTracking)
628 return;
629
630 // After we have processed all vars of the inlined function (or function with
631 // an abstract_origin), we want to know how many variables have no location.
632 for (auto Offset : AbstractOriginVars) {
633 LocStats.NumVarParam++;
634 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
635 auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
636 if (!FnDie)
637 continue;
638 auto Tag = FnDie.getTag();
639 if (Tag == dwarf::DW_TAG_formal_parameter) {
640 LocStats.NumParam++;
641 LocStats.ParamLocStats[ZeroCoverageBucket]++;
642 } else if (Tag == dwarf::DW_TAG_variable) {
643 LocStats.NumVar++;
644 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
645 }
646 }
647 }
648
649 /// Print human-readable output.
650 /// \{
printDatum(json::OStream & J,const char * Key,json::Value Value)651 static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
652 if (Value == OverflowValue)
653 J.attribute(Key, "overflowed");
654 else
655 J.attribute(Key, Value);
656
657 LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
658 }
659
printLocationStats(json::OStream & J,const char * Key,std::vector<SaturatingUINT64> & LocationStats)660 static void printLocationStats(json::OStream &J, const char *Key,
661 std::vector<SaturatingUINT64> &LocationStats) {
662 if (LocationStats[0].Value == OverflowValue)
663 J.attribute((Twine(Key) +
664 " with (0%,10%) of parent scope covered by DW_AT_location")
665 .str(),
666 "overflowed");
667 else
668 J.attribute(
669 (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
670 .str(),
671 LocationStats[0].Value);
672 LLVM_DEBUG(
673 llvm::dbgs() << Key
674 << " with 0% of parent scope covered by DW_AT_location: \\"
675 << LocationStats[0].Value << '\n');
676
677 if (LocationStats[1].Value == OverflowValue)
678 J.attribute((Twine(Key) +
679 " with (0%,10%) of parent scope covered by DW_AT_location")
680 .str(),
681 "overflowed");
682 else
683 J.attribute((Twine(Key) +
684 " with (0%,10%) of parent scope covered by DW_AT_location")
685 .str(),
686 LocationStats[1].Value);
687 LLVM_DEBUG(llvm::dbgs()
688 << Key
689 << " with (0%,10%) of parent scope covered by DW_AT_location: "
690 << LocationStats[1].Value << '\n');
691
692 for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
693 if (LocationStats[i].Value == OverflowValue)
694 J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
695 Twine(i * 10) +
696 "%) of parent scope covered by DW_AT_location")
697 .str(),
698 "overflowed");
699 else
700 J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
701 Twine(i * 10) +
702 "%) of parent scope covered by DW_AT_location")
703 .str(),
704 LocationStats[i].Value);
705 LLVM_DEBUG(llvm::dbgs()
706 << Key << " with [" << (i - 1) * 10 << "%," << i * 10
707 << "%) of parent scope covered by DW_AT_location: "
708 << LocationStats[i].Value);
709 }
710 if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
711 J.attribute(
712 (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
713 .str(),
714 "overflowed");
715 else
716 J.attribute(
717 (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
718 .str(),
719 LocationStats[NumOfCoverageCategories - 1].Value);
720 LLVM_DEBUG(
721 llvm::dbgs() << Key
722 << " with 100% of parent scope covered by DW_AT_location: "
723 << LocationStats[NumOfCoverageCategories - 1].Value);
724 }
725
printSectionSizes(json::OStream & J,const SectionSizes & Sizes)726 static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
727 for (const auto &It : Sizes.DebugSectionSizes)
728 J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
729 }
730
731 /// Stop tracking variables that contain abstract_origin with a location.
732 /// This is used for out-of-order DW_AT_inline subprograms only.
updateVarsWithAbstractOriginLocCovInfo(DWARFDie FnDieWithAbstractOrigin,AbstractOriginVarsTy & AbstractOriginVars)733 static void updateVarsWithAbstractOriginLocCovInfo(
734 DWARFDie FnDieWithAbstractOrigin,
735 AbstractOriginVarsTy &AbstractOriginVars) {
736 DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
737 while (Child) {
738 const dwarf::Tag ChildTag = Child.getTag();
739 if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
740 ChildTag == dwarf::DW_TAG_variable) &&
741 (Child.find(dwarf::DW_AT_location) ||
742 Child.find(dwarf::DW_AT_const_value))) {
743 auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
744 if (OffsetVar)
745 llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue());
746 } else if (ChildTag == dwarf::DW_TAG_lexical_block)
747 updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
748 Child = Child.getSibling();
749 }
750 }
751
752 /// Collect zero location coverage for inlined variables which refer to
753 /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
754 /// Also cover the variables of a concrete function (represented with
755 /// the DW_TAG_subprogram) with an abstract_origin attribute.
collectZeroLocCovForVarsWithAbstractOrigin(DWARFUnit * DwUnit,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed)756 static void collectZeroLocCovForVarsWithAbstractOrigin(
757 DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
758 AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
759 FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
760 // The next variable is used to filter out functions that have been processed,
761 // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
762 FunctionsWithAbstractOriginTy ProcessedFns;
763 for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
764 DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
765 auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
766 AbstractOriginVarsTy AbstractOriginVars;
767 if (!FnCopy)
768 continue;
769 uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
770 // If there is no entry within LocalAbstractOriginFnInfo for the given
771 // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
772 // CrossCU referencing.
773 if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
774 continue;
775 AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
776 updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
777 AbstractOriginVars);
778
779 for (auto Offset : AbstractOriginVars) {
780 LocStats.NumVarParam++;
781 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
782 auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
783 if (Tag == dwarf::DW_TAG_formal_parameter) {
784 LocStats.NumParam++;
785 LocStats.ParamLocStats[ZeroCoverageBucket]++;
786 } else if (Tag == dwarf::DW_TAG_variable) {
787 LocStats.NumVar++;
788 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
789 }
790 }
791 ProcessedFns.push_back(FnOffset);
792 }
793 for (auto ProcessedFn : ProcessedFns)
794 llvm::erase_value(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
795 }
796
797 /// Collect zero location coverage for inlined variables which refer to
798 /// a DW_AT_inline copy of subprogram that is in a different CU.
collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(LocationStats & LocStats,FunctionDIECUTyMap AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,CrossCUReferencingDIELocationTy & CrossCUReferencesToBeResolved)799 static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
800 LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
801 AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
802 CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
803 for (const auto &CrossCUReferenceToBeResolved :
804 CrossCUReferencesToBeResolved) {
805 DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
806 DWARFDie FnDIEWithCrossCUReferencing =
807 DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
808 auto FnCopy =
809 FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
810 if (!FnCopy)
811 continue;
812 uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
813 AbstractOriginVarsTy AbstractOriginVars =
814 GlobalAbstractOriginFnInfo[FnCopyRawUValue];
815 updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
816 AbstractOriginVars);
817 for (auto Offset : AbstractOriginVars) {
818 LocStats.NumVarParam++;
819 LocStats.VarParamLocStats[ZeroCoverageBucket]++;
820 auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
821 ->getDIEForOffset(Offset)
822 .getTag();
823 if (Tag == dwarf::DW_TAG_formal_parameter) {
824 LocStats.NumParam++;
825 LocStats.ParamLocStats[ZeroCoverageBucket]++;
826 } else if (Tag == dwarf::DW_TAG_variable) {
827 LocStats.NumVar++;
828 LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
829 }
830 }
831 }
832 }
833
834 /// \}
835
836 /// Collect debug info quality metrics for an entire DIContext.
837 ///
838 /// Do the impossible and reduce the quality of the debug info down to a few
839 /// numbers. The idea is to condense the data into numbers that can be tracked
840 /// over time to identify trends in newer compiler versions and gauge the effect
841 /// of particular optimizations. The raw numbers themselves are not particularly
842 /// useful, only the delta between compiling the same program with different
843 /// compilers is.
collectStatsForObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)844 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
845 const Twine &Filename,
846 raw_ostream &OS) {
847 StringRef FormatName = Obj.getFileFormatName();
848 GlobalStats GlobalStats;
849 LocationStats LocStats;
850 StringMap<PerFunctionStats> Statistics;
851 // This variable holds variable information for functions with
852 // abstract_origin globally, across all CUs.
853 AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
854 // This variable holds information about the CU of a function with
855 // abstract_origin.
856 FunctionDIECUTyMap AbstractOriginFnCUs;
857 CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
858 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
859 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
860 // This variable holds variable information for functions with
861 // abstract_origin, but just for the current CU.
862 AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
863 FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
864
865 collectStatsRecursive(
866 CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
867 AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
868 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
869
870 // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
871 // out-of-order DWARF functions that have been processed within it,
872 // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
873 // references.
874 collectZeroLocCovForVarsWithAbstractOrigin(
875 CUDie.getDwarfUnit(), GlobalStats, LocStats,
876 LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
877
878 // Collect all CrossCU references into CrossCUReferencesToBeResolved.
879 for (auto CrossCUReferencingDIEOffset :
880 FnsWithAbstractOriginToBeProcessed)
881 CrossCUReferencesToBeResolved.push_back(
882 DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
883 }
884 }
885
886 /// Resolve CrossCU references.
887 collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
888 LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
889 CrossCUReferencesToBeResolved);
890
891 /// Collect the sizes of debug sections.
892 SectionSizes Sizes;
893 calculateSectionSizes(Obj, Sizes, Filename);
894
895 /// The version number should be increased every time the algorithm is changed
896 /// (including bug fixes). New metrics may be added without increasing the
897 /// version.
898 unsigned Version = 9;
899 SaturatingUINT64 VarParamTotal = 0;
900 SaturatingUINT64 VarParamUnique = 0;
901 SaturatingUINT64 VarParamWithLoc = 0;
902 SaturatingUINT64 NumFunctions = 0;
903 SaturatingUINT64 NumInlinedFunctions = 0;
904 SaturatingUINT64 NumFuncsWithSrcLoc = 0;
905 SaturatingUINT64 NumAbstractOrigins = 0;
906 SaturatingUINT64 ParamTotal = 0;
907 SaturatingUINT64 ParamWithType = 0;
908 SaturatingUINT64 ParamWithLoc = 0;
909 SaturatingUINT64 ParamWithSrcLoc = 0;
910 SaturatingUINT64 LocalVarTotal = 0;
911 SaturatingUINT64 LocalVarWithType = 0;
912 SaturatingUINT64 LocalVarWithSrcLoc = 0;
913 SaturatingUINT64 LocalVarWithLoc = 0;
914 for (auto &Entry : Statistics) {
915 PerFunctionStats &Stats = Entry.getValue();
916 uint64_t TotalVars = Stats.VarsInFunction.size() *
917 (Stats.NumFnInlined + Stats.NumFnOutOfLine);
918 // Count variables in global scope.
919 if (!Stats.IsFunction)
920 TotalVars =
921 Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
922 uint64_t Constants = Stats.ConstantMembers;
923 VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
924 VarParamTotal += TotalVars;
925 VarParamUnique += Stats.VarsInFunction.size();
926 LLVM_DEBUG(for (auto &V
927 : Stats.VarsInFunction) llvm::dbgs()
928 << Entry.getKey() << ": " << V.getKey() << "\n");
929 NumFunctions += Stats.IsFunction;
930 NumFuncsWithSrcLoc += Stats.HasSourceLocation;
931 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
932 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
933 ParamTotal += Stats.NumParams;
934 ParamWithType += Stats.NumParamTypes;
935 ParamWithLoc += Stats.NumParamLocations;
936 ParamWithSrcLoc += Stats.NumParamSourceLocations;
937 LocalVarTotal += Stats.NumLocalVars;
938 LocalVarWithType += Stats.NumLocalVarTypes;
939 LocalVarWithLoc += Stats.NumLocalVarLocations;
940 LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
941 }
942
943 // Print summary.
944 OS.SetBufferSize(1024);
945 json::OStream J(OS, 2);
946 J.objectBegin();
947 J.attribute("version", Version);
948 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
949 llvm::dbgs() << "---------------------------------\n");
950
951 printDatum(J, "file", Filename.str());
952 printDatum(J, "format", FormatName);
953
954 printDatum(J, "#functions", NumFunctions.Value);
955 printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
956 printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
957 printDatum(J, "#inlined functions with abstract origins",
958 NumAbstractOrigins.Value);
959
960 // This includes local variables and formal parameters.
961 printDatum(J, "#unique source variables", VarParamUnique.Value);
962 printDatum(J, "#source variables", VarParamTotal.Value);
963 printDatum(J, "#source variables with location", VarParamWithLoc.Value);
964
965 printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
966 printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
967 printDatum(J, "#call site parameter DIEs",
968 GlobalStats.CallSiteParamDIEs.Value);
969
970 printDatum(J, "sum_all_variables(#bytes in parent scope)",
971 GlobalStats.ScopeBytes.Value);
972 printDatum(J,
973 "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
974 GlobalStats.TotalBytesCovered.Value);
975 printDatum(J,
976 "sum_all_variables(#bytes in parent scope covered by "
977 "DW_AT_location)",
978 GlobalStats.ScopeBytesCovered.Value);
979 printDatum(J,
980 "sum_all_variables(#bytes in parent scope covered by "
981 "DW_OP_entry_value)",
982 GlobalStats.ScopeEntryValueBytesCovered.Value);
983
984 printDatum(J, "sum_all_params(#bytes in parent scope)",
985 GlobalStats.ParamScopeBytes.Value);
986 printDatum(J,
987 "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
988 GlobalStats.ParamScopeBytesCovered.Value);
989 printDatum(J,
990 "sum_all_params(#bytes in parent scope covered by "
991 "DW_OP_entry_value)",
992 GlobalStats.ParamScopeEntryValueBytesCovered.Value);
993
994 printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
995 GlobalStats.LocalVarScopeBytes.Value);
996 printDatum(J,
997 "sum_all_local_vars(#bytes in parent scope covered by "
998 "DW_AT_location)",
999 GlobalStats.LocalVarScopeBytesCovered.Value);
1000 printDatum(J,
1001 "sum_all_local_vars(#bytes in parent scope covered by "
1002 "DW_OP_entry_value)",
1003 GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
1004
1005 printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
1006 printDatum(J, "#bytes within inlined functions",
1007 GlobalStats.InlineFunctionSize.Value);
1008
1009 // Print the summary for formal parameters.
1010 printDatum(J, "#params", ParamTotal.Value);
1011 printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
1012 printDatum(J, "#params with type", ParamWithType.Value);
1013 printDatum(J, "#params with binary location", ParamWithLoc.Value);
1014
1015 // Print the summary for local variables.
1016 printDatum(J, "#local vars", LocalVarTotal.Value);
1017 printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
1018 printDatum(J, "#local vars with type", LocalVarWithType.Value);
1019 printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
1020
1021 // Print the debug section sizes.
1022 printSectionSizes(J, Sizes);
1023
1024 // Print the location statistics for variables (includes local variables
1025 // and formal parameters).
1026 printDatum(J, "#variables processed by location statistics",
1027 LocStats.NumVarParam.Value);
1028 printLocationStats(J, "#variables", LocStats.VarParamLocStats);
1029 printLocationStats(J, "#variables - entry values",
1030 LocStats.VarParamNonEntryValLocStats);
1031
1032 // Print the location statistics for formal parameters.
1033 printDatum(J, "#params processed by location statistics",
1034 LocStats.NumParam.Value);
1035 printLocationStats(J, "#params", LocStats.ParamLocStats);
1036 printLocationStats(J, "#params - entry values",
1037 LocStats.ParamNonEntryValLocStats);
1038
1039 // Print the location statistics for local variables.
1040 printDatum(J, "#local vars processed by location statistics",
1041 LocStats.NumVar.Value);
1042 printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
1043 printLocationStats(J, "#local vars - entry values",
1044 LocStats.LocalVarNonEntryValLocStats);
1045 J.objectEnd();
1046 OS << '\n';
1047 LLVM_DEBUG(
1048 llvm::dbgs() << "Total Availability: "
1049 << (VarParamTotal.Value
1050 ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1051 VarParamTotal.Value)
1052 : 0)
1053 << "%\n";
1054 llvm::dbgs() << "PC Ranges covered: "
1055 << (GlobalStats.ScopeBytes.Value
1056 ? (int)std::round(
1057 (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1058 GlobalStats.ScopeBytes.Value)
1059 : 0)
1060 << "%\n");
1061 return true;
1062 }
1063