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/StringExtras.h"
12 #include "llvm/ADT/StringSet.h"
13 #include "llvm/DebugInfo/DIContext.h"
14 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
15 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.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 /// 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 namespace {
31 /// Holds statistics for one function (or other entity that has a PC range and
32 /// contains variables, such as a compile unit).
33 struct PerFunctionStats {
34 /// Number of inlined instances of this function.
35 unsigned NumFnInlined = 0;
36 /// Number of out-of-line instances of this function.
37 unsigned NumFnOutOfLine = 0;
38 /// Number of inlined instances that have abstract origins.
39 unsigned NumAbstractOrigins = 0;
40 /// Number of variables and parameters with location across all inlined
41 /// instances.
42 unsigned TotalVarWithLoc = 0;
43 /// Number of constants with location across all inlined instances.
44 unsigned ConstantMembers = 0;
45 /// Number of arificial variables, parameters or members across all instances.
46 unsigned NumArtificial = 0;
47 /// List of all Variables and parameters in this function.
48 StringSet<> VarsInFunction;
49 /// Compile units also cover a PC range, but have this flag set to false.
50 bool IsFunction = false;
51 /// Function has source location information.
52 bool HasSourceLocation = false;
53 /// Number of function parameters.
54 unsigned NumParams = 0;
55 /// Number of function parameters with source location.
56 unsigned NumParamSourceLocations = 0;
57 /// Number of function parameters with type.
58 unsigned NumParamTypes = 0;
59 /// Number of function parameters with a DW_AT_location.
60 unsigned NumParamLocations = 0;
61 /// Number of local variables.
62 unsigned NumLocalVars = 0;
63 /// Number of local variables with source location.
64 unsigned NumLocalVarSourceLocations = 0;
65 /// Number of local variables with type.
66 unsigned NumLocalVarTypes = 0;
67 /// Number of local variables with DW_AT_location.
68 unsigned NumLocalVarLocations = 0;
69 };
70
71 /// Holds accumulated global statistics about DIEs.
72 struct GlobalStats {
73 /// Total number of PC range bytes covered by DW_AT_locations.
74 unsigned TotalBytesCovered = 0;
75 /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
76 unsigned ScopeBytesCovered = 0;
77 /// Total number of PC range bytes in each variable's enclosing scope.
78 unsigned ScopeBytes = 0;
79 /// Total number of PC range bytes covered by DW_AT_locations with
80 /// the debug entry values (DW_OP_entry_value).
81 unsigned ScopeEntryValueBytesCovered = 0;
82 /// Total number of PC range bytes covered by DW_AT_locations of
83 /// formal parameters.
84 unsigned ParamScopeBytesCovered = 0;
85 /// Total number of PC range bytes in each parameter's enclosing scope.
86 unsigned ParamScopeBytes = 0;
87 /// Total number of PC range bytes covered by DW_AT_locations with
88 /// the debug entry values (DW_OP_entry_value) (only for parameters).
89 unsigned ParamScopeEntryValueBytesCovered = 0;
90 /// Total number of PC range bytes covered by DW_AT_locations (only for local
91 /// variables).
92 unsigned LocalVarScopeBytesCovered = 0;
93 /// Total number of PC range bytes in each local variable's enclosing scope.
94 unsigned LocalVarScopeBytes = 0;
95 /// Total number of PC range bytes covered by DW_AT_locations with
96 /// the debug entry values (DW_OP_entry_value) (only for local variables).
97 unsigned LocalVarScopeEntryValueBytesCovered = 0;
98 /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
99 unsigned CallSiteEntries = 0;
100 /// Total number of call site DIEs (DW_TAG_call_site).
101 unsigned CallSiteDIEs = 0;
102 /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
103 unsigned CallSiteParamDIEs = 0;
104 /// Total byte size of concrete functions. This byte size includes
105 /// inline functions contained in the concrete functions.
106 unsigned FunctionSize = 0;
107 /// Total byte size of inlined functions. This is the total number of bytes
108 /// for the top inline functions within concrete functions. This can help
109 /// tune the inline settings when compiling to match user expectations.
110 unsigned InlineFunctionSize = 0;
111 };
112
113 /// Holds accumulated debug location statistics about local variables and
114 /// formal parameters.
115 struct LocationStats {
116 /// Map the scope coverage decile to the number of variables in the decile.
117 /// The first element of the array (at the index zero) represents the number
118 /// of variables with the no debug location at all, but the last element
119 /// in the vector represents the number of fully covered variables within
120 /// its scope.
121 std::vector<unsigned> VarParamLocStats{
122 std::vector<unsigned>(NumOfCoverageCategories, 0)};
123 /// Map non debug entry values coverage.
124 std::vector<unsigned> VarParamNonEntryValLocStats{
125 std::vector<unsigned>(NumOfCoverageCategories, 0)};
126 /// The debug location statistics for formal parameters.
127 std::vector<unsigned> ParamLocStats{
128 std::vector<unsigned>(NumOfCoverageCategories, 0)};
129 /// Map non debug entry values coverage for formal parameters.
130 std::vector<unsigned> ParamNonEntryValLocStats{
131 std::vector<unsigned>(NumOfCoverageCategories, 0)};
132 /// The debug location statistics for local variables.
133 std::vector<unsigned> LocalVarLocStats{
134 std::vector<unsigned>(NumOfCoverageCategories, 0)};
135 /// Map non debug entry values coverage for local variables.
136 std::vector<unsigned> LocalVarNonEntryValLocStats{
137 std::vector<unsigned>(NumOfCoverageCategories, 0)};
138 /// Total number of local variables and function parameters processed.
139 unsigned NumVarParam = 0;
140 /// Total number of formal parameters processed.
141 unsigned NumParam = 0;
142 /// Total number of local variables processed.
143 unsigned NumVar = 0;
144 };
145 } // namespace
146
147 /// Collect debug location statistics for one DIE.
collectLocStats(uint64_t ScopeBytesCovered,uint64_t BytesInScope,std::vector<unsigned> & VarParamLocStats,std::vector<unsigned> & ParamLocStats,std::vector<unsigned> & LocalVarLocStats,bool IsParam,bool IsLocalVar)148 static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
149 std::vector<unsigned> &VarParamLocStats,
150 std::vector<unsigned> &ParamLocStats,
151 std::vector<unsigned> &LocalVarLocStats,
152 bool IsParam, bool IsLocalVar) {
153 auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
154 // No debug location at all for the variable.
155 if (ScopeBytesCovered == 0)
156 return 0;
157 // Fully covered variable within its scope.
158 if (ScopeBytesCovered >= BytesInScope)
159 return NumOfCoverageCategories - 1;
160 // Get covered range (e.g. 20%-29%).
161 unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
162 LocBucket /= 10;
163 return LocBucket + 1;
164 };
165
166 unsigned CoverageBucket = getCoverageBucket();
167 VarParamLocStats[CoverageBucket]++;
168 if (IsParam)
169 ParamLocStats[CoverageBucket]++;
170 else if (IsLocalVar)
171 LocalVarLocStats[CoverageBucket]++;
172 }
173 /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
174 /// and DeclLine. The identifier aims to be unique for any unique entities,
175 /// but keeping the same among different instances of the same entity.
constructDieID(DWARFDie Die,StringRef Prefix=StringRef ())176 static std::string constructDieID(DWARFDie Die,
177 StringRef Prefix = StringRef()) {
178 std::string IDStr;
179 llvm::raw_string_ostream ID(IDStr);
180 ID << Prefix
181 << Die.getName(DINameKind::LinkageName);
182
183 // Prefix + Name is enough for local variables and parameters.
184 if (!Prefix.empty() && !Prefix.equals("g"))
185 return ID.str();
186
187 auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
188 std::string File;
189 if (DeclFile) {
190 DWARFUnit *U = Die.getDwarfUnit();
191 if (const auto *LT = U->getContext().getLineTableForUnit(U))
192 if (LT->getFileNameByIndex(
193 dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
194 DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
195 File = std::string(sys::path::filename(File));
196 }
197 ID << ":" << (File.empty() ? "/" : File);
198 ID << ":"
199 << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
200 return ID.str();
201 }
202
203 /// Return the number of bytes in the overlap of ranges A and B.
calculateOverlap(DWARFAddressRange A,DWARFAddressRange B)204 static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
205 uint64_t Lower = std::max(A.LowPC, B.LowPC);
206 uint64_t Upper = std::min(A.HighPC, B.HighPC);
207 if (Lower >= Upper)
208 return 0;
209 return Upper - Lower;
210 }
211
212 /// Collect debug info quality metrics for one DIE.
collectStatsForDie(DWARFDie Die,std::string FnPrefix,std::string VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats)213 static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
214 std::string VarPrefix, uint64_t BytesInScope,
215 uint32_t InlineDepth,
216 StringMap<PerFunctionStats> &FnStatMap,
217 GlobalStats &GlobalStats,
218 LocationStats &LocStats) {
219 bool HasLoc = false;
220 bool HasSrcLoc = false;
221 bool HasType = false;
222 uint64_t TotalBytesCovered = 0;
223 uint64_t ScopeBytesCovered = 0;
224 uint64_t BytesEntryValuesCovered = 0;
225 auto &FnStats = FnStatMap[FnPrefix];
226 bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter;
227 bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable;
228 bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member &&
229 Die.find(dwarf::DW_AT_const_value);
230
231 if (Die.getTag() == dwarf::DW_TAG_call_site ||
232 Die.getTag() == dwarf::DW_TAG_GNU_call_site) {
233 GlobalStats.CallSiteDIEs++;
234 return;
235 }
236
237 if (Die.getTag() == dwarf::DW_TAG_call_site_parameter ||
238 Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) {
239 GlobalStats.CallSiteParamDIEs++;
240 return;
241 }
242
243 if (!IsParam && !IsLocalVar && !IsConstantMember) {
244 // Not a variable or constant member.
245 return;
246 }
247
248 // Ignore declarations of global variables.
249 if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
250 return;
251
252 if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
253 Die.findRecursively(dwarf::DW_AT_decl_line))
254 HasSrcLoc = true;
255
256 if (Die.findRecursively(dwarf::DW_AT_type))
257 HasType = true;
258
259 auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
260 DWARFUnit *U = Die.getDwarfUnit();
261 DataExtractor Data(toStringRef(D),
262 Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
263 DWARFExpression Expression(Data, U->getAddressByteSize(),
264 U->getFormParams().Format);
265 // Consider the expression containing the DW_OP_entry_value as
266 // an entry value.
267 return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) {
268 return Op.getCode() == dwarf::DW_OP_entry_value ||
269 Op.getCode() == dwarf::DW_OP_GNU_entry_value;
270 });
271 };
272
273 if (Die.find(dwarf::DW_AT_const_value)) {
274 // This catches constant members *and* variables.
275 HasLoc = true;
276 ScopeBytesCovered = BytesInScope;
277 TotalBytesCovered = BytesInScope;
278 } else {
279 // Handle variables and function arguments.
280 Expected<std::vector<DWARFLocationExpression>> Loc =
281 Die.getLocations(dwarf::DW_AT_location);
282 if (!Loc) {
283 consumeError(Loc.takeError());
284 } else {
285 HasLoc = true;
286 // Get PC coverage.
287 auto Default = find_if(
288 *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
289 if (Default != Loc->end()) {
290 // Assume the entire range is covered by a single location.
291 ScopeBytesCovered = BytesInScope;
292 TotalBytesCovered = BytesInScope;
293 } else {
294 // Caller checks this Expected result already, it cannot fail.
295 auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
296 for (auto Entry : *Loc) {
297 TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
298 uint64_t ScopeBytesCoveredByEntry = 0;
299 // Calculate how many bytes of the parent scope this entry covers.
300 // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
301 // address ranges defined by the bounded location descriptions of a
302 // location list may overlap". So in theory a variable can have
303 // multiple simultaneous locations, which would make this calculation
304 // misleading because we will count the overlapped areas
305 // twice. However, clang does not currently emit DWARF like this.
306 for (DWARFAddressRange R : ScopeRanges) {
307 ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
308 }
309 ScopeBytesCovered += ScopeBytesCoveredByEntry;
310 if (IsEntryValue(Entry.Expr))
311 BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
312 }
313 }
314 }
315 }
316
317 // Calculate the debug location statistics.
318 if (BytesInScope) {
319 LocStats.NumVarParam++;
320 if (IsParam)
321 LocStats.NumParam++;
322 else if (IsLocalVar)
323 LocStats.NumVar++;
324
325 collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
326 LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
327 IsLocalVar);
328 // Non debug entry values coverage statistics.
329 collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
330 LocStats.VarParamNonEntryValLocStats,
331 LocStats.ParamNonEntryValLocStats,
332 LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
333 }
334
335 // Collect PC range coverage data.
336 if (DWARFDie D =
337 Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
338 Die = D;
339
340 std::string VarID = constructDieID(Die, VarPrefix);
341 FnStats.VarsInFunction.insert(VarID);
342
343 GlobalStats.TotalBytesCovered += TotalBytesCovered;
344 if (BytesInScope) {
345 GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
346 GlobalStats.ScopeBytes += BytesInScope;
347 GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
348 if (IsParam) {
349 GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
350 GlobalStats.ParamScopeBytes += BytesInScope;
351 GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
352 } else if (IsLocalVar) {
353 GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
354 GlobalStats.LocalVarScopeBytes += BytesInScope;
355 GlobalStats.LocalVarScopeEntryValueBytesCovered +=
356 BytesEntryValuesCovered;
357 }
358 assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes);
359 }
360
361 if (IsConstantMember) {
362 FnStats.ConstantMembers++;
363 return;
364 }
365
366 FnStats.TotalVarWithLoc += (unsigned)HasLoc;
367
368 if (Die.find(dwarf::DW_AT_artificial)) {
369 FnStats.NumArtificial++;
370 return;
371 }
372
373 if (IsParam) {
374 FnStats.NumParams++;
375 if (HasType)
376 FnStats.NumParamTypes++;
377 if (HasSrcLoc)
378 FnStats.NumParamSourceLocations++;
379 if (HasLoc)
380 FnStats.NumParamLocations++;
381 } else if (IsLocalVar) {
382 FnStats.NumLocalVars++;
383 if (HasType)
384 FnStats.NumLocalVarTypes++;
385 if (HasSrcLoc)
386 FnStats.NumLocalVarSourceLocations++;
387 if (HasLoc)
388 FnStats.NumLocalVarLocations++;
389 }
390 }
391
392 /// 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)393 static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
394 std::string VarPrefix, uint64_t BytesInScope,
395 uint32_t InlineDepth,
396 StringMap<PerFunctionStats> &FnStatMap,
397 GlobalStats &GlobalStats,
398 LocationStats &LocStats) {
399 const dwarf::Tag Tag = Die.getTag();
400 // Skip function types.
401 if (Tag == dwarf::DW_TAG_subroutine_type)
402 return;
403
404 // Handle any kind of lexical scope.
405 const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
406 const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
407 const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
408 if (IsFunction || IsInlinedFunction || IsBlock) {
409
410 // Reset VarPrefix when entering a new function.
411 if (Die.getTag() == dwarf::DW_TAG_subprogram ||
412 Die.getTag() == dwarf::DW_TAG_inlined_subroutine)
413 VarPrefix = "v";
414
415 // Ignore forward declarations.
416 if (Die.find(dwarf::DW_AT_declaration))
417 return;
418
419 // Check for call sites.
420 if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
421 GlobalStats.CallSiteEntries++;
422
423 // PC Ranges.
424 auto RangesOrError = Die.getAddressRanges();
425 if (!RangesOrError) {
426 llvm::consumeError(RangesOrError.takeError());
427 return;
428 }
429
430 auto Ranges = RangesOrError.get();
431 uint64_t BytesInThisScope = 0;
432 for (auto Range : Ranges)
433 BytesInThisScope += Range.HighPC - Range.LowPC;
434
435 // Count the function.
436 if (!IsBlock) {
437 // Skip over abstract origins.
438 if (Die.find(dwarf::DW_AT_inline))
439 return;
440 std::string FnID = constructDieID(Die);
441 // We've seen an instance of this function.
442 auto &FnStats = FnStatMap[FnID];
443 FnStats.IsFunction = true;
444 if (IsInlinedFunction) {
445 FnStats.NumFnInlined++;
446 if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
447 FnStats.NumAbstractOrigins++;
448 } else {
449 FnStats.NumFnOutOfLine++;
450 }
451 if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
452 Die.findRecursively(dwarf::DW_AT_decl_line))
453 FnStats.HasSourceLocation = true;
454 // Update function prefix.
455 FnPrefix = FnID;
456 }
457
458 if (BytesInThisScope) {
459 BytesInScope = BytesInThisScope;
460 if (IsFunction)
461 GlobalStats.FunctionSize += BytesInThisScope;
462 else if (IsInlinedFunction && InlineDepth == 0)
463 GlobalStats.InlineFunctionSize += BytesInThisScope;
464 }
465 } else {
466 // Not a scope, visit the Die itself. It could be a variable.
467 collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
468 FnStatMap, GlobalStats, LocStats);
469 }
470
471 // Set InlineDepth correctly for child recursion
472 if (IsFunction)
473 InlineDepth = 0;
474 else if (IsInlinedFunction)
475 ++InlineDepth;
476
477 // Traverse children.
478 unsigned LexicalBlockIndex = 0;
479 unsigned FormalParameterIndex = 0;
480 DWARFDie Child = Die.getFirstChild();
481 while (Child) {
482 std::string ChildVarPrefix = VarPrefix;
483 if (Child.getTag() == dwarf::DW_TAG_lexical_block)
484 ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
485 if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
486 ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
487
488 collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope,
489 InlineDepth, FnStatMap, GlobalStats, LocStats);
490 Child = Child.getSibling();
491 }
492 }
493
494 /// Print human-readable output.
495 /// \{
printDatum(json::OStream & J,const char * Key,json::Value Value)496 static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
497 J.attribute(Key, Value);
498 LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
499 }
500
printLocationStats(json::OStream & J,const char * Key,std::vector<unsigned> & LocationStats)501 static void printLocationStats(json::OStream &J, const char *Key,
502 std::vector<unsigned> &LocationStats) {
503 J.attribute(
504 (Twine(Key) + " with 0% of parent scope covered by DW_AT_location").str(),
505 LocationStats[0]);
506 LLVM_DEBUG(
507 llvm::dbgs() << Key
508 << " with 0% of parent scope covered by DW_AT_location: \\"
509 << LocationStats[0] << '\n');
510 J.attribute(
511 (Twine(Key) + " with (0%,10%) of parent scope covered by DW_AT_location")
512 .str(),
513 LocationStats[1]);
514 LLVM_DEBUG(llvm::dbgs()
515 << Key
516 << " with (0%,10%) of parent scope covered by DW_AT_location: "
517 << LocationStats[1] << '\n');
518 for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
519 J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
520 Twine(i * 10) + "%) of parent scope covered by DW_AT_location")
521 .str(),
522 LocationStats[i]);
523 LLVM_DEBUG(llvm::dbgs()
524 << Key << " with [" << (i - 1) * 10 << "%," << i * 10
525 << "%) of parent scope covered by DW_AT_location: "
526 << LocationStats[i]);
527 }
528 J.attribute(
529 (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
530 .str(),
531 LocationStats[NumOfCoverageCategories - 1]);
532 LLVM_DEBUG(
533 llvm::dbgs() << Key
534 << " with 100% of parent scope covered by DW_AT_location: "
535 << LocationStats[NumOfCoverageCategories - 1]);
536 }
537
printSectionSizes(json::OStream & J,const SectionSizes & Sizes)538 static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
539 for (const auto &DebugSec : Sizes.DebugSectionSizes)
540 J.attribute((Twine("#bytes in ") + DebugSec.getKey()).str(),
541 int64_t(DebugSec.getValue()));
542 }
543
544 /// \}
545
546 /// Collect debug info quality metrics for an entire DIContext.
547 ///
548 /// Do the impossible and reduce the quality of the debug info down to a few
549 /// numbers. The idea is to condense the data into numbers that can be tracked
550 /// over time to identify trends in newer compiler versions and gauge the effect
551 /// of particular optimizations. The raw numbers themselves are not particularly
552 /// useful, only the delta between compiling the same program with different
553 /// compilers is.
collectStatsForObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)554 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
555 const Twine &Filename,
556 raw_ostream &OS) {
557 StringRef FormatName = Obj.getFileFormatName();
558 GlobalStats GlobalStats;
559 LocationStats LocStats;
560 StringMap<PerFunctionStats> Statistics;
561 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
562 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
563 collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats,
564 LocStats);
565
566 /// Collect the sizes of debug sections.
567 SectionSizes Sizes;
568 calculateSectionSizes(Obj, Sizes, Filename);
569
570 /// The version number should be increased every time the algorithm is changed
571 /// (including bug fixes). New metrics may be added without increasing the
572 /// version.
573 unsigned Version = 6;
574 unsigned VarParamTotal = 0;
575 unsigned VarParamUnique = 0;
576 unsigned VarParamWithLoc = 0;
577 unsigned NumFunctions = 0;
578 unsigned NumInlinedFunctions = 0;
579 unsigned NumFuncsWithSrcLoc = 0;
580 unsigned NumAbstractOrigins = 0;
581 unsigned ParamTotal = 0;
582 unsigned ParamWithType = 0;
583 unsigned ParamWithLoc = 0;
584 unsigned ParamWithSrcLoc = 0;
585 unsigned LocalVarTotal = 0;
586 unsigned LocalVarWithType = 0;
587 unsigned LocalVarWithSrcLoc = 0;
588 unsigned LocalVarWithLoc = 0;
589 for (auto &Entry : Statistics) {
590 PerFunctionStats &Stats = Entry.getValue();
591 unsigned TotalVars = Stats.VarsInFunction.size() *
592 (Stats.NumFnInlined + Stats.NumFnOutOfLine);
593 // Count variables in global scope.
594 if (!Stats.IsFunction)
595 TotalVars =
596 Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
597 unsigned Constants = Stats.ConstantMembers;
598 VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
599 VarParamTotal += TotalVars;
600 VarParamUnique += Stats.VarsInFunction.size();
601 LLVM_DEBUG(for (auto &V
602 : Stats.VarsInFunction) llvm::dbgs()
603 << Entry.getKey() << ": " << V.getKey() << "\n");
604 NumFunctions += Stats.IsFunction;
605 NumFuncsWithSrcLoc += Stats.HasSourceLocation;
606 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
607 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
608 ParamTotal += Stats.NumParams;
609 ParamWithType += Stats.NumParamTypes;
610 ParamWithLoc += Stats.NumParamLocations;
611 ParamWithSrcLoc += Stats.NumParamSourceLocations;
612 LocalVarTotal += Stats.NumLocalVars;
613 LocalVarWithType += Stats.NumLocalVarTypes;
614 LocalVarWithLoc += Stats.NumLocalVarLocations;
615 LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
616 }
617
618 // Print summary.
619 OS.SetBufferSize(1024);
620 json::OStream J(OS, 2);
621 J.objectBegin();
622 J.attribute("version", Version);
623 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
624 llvm::dbgs() << "---------------------------------\n");
625
626 printDatum(J, "file", Filename.str());
627 printDatum(J, "format", FormatName);
628
629 printDatum(J, "#functions", NumFunctions);
630 printDatum(J, "#functions with location", NumFuncsWithSrcLoc);
631 printDatum(J, "#inlined functions", NumInlinedFunctions);
632 printDatum(J, "#inlined functions with abstract origins", NumAbstractOrigins);
633
634 // This includes local variables and formal parameters.
635 printDatum(J, "#unique source variables", VarParamUnique);
636 printDatum(J, "#source variables", VarParamTotal);
637 printDatum(J, "#source variables with location", VarParamWithLoc);
638
639 printDatum(J, "#call site entries", GlobalStats.CallSiteEntries);
640 printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs);
641 printDatum(J, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
642
643 printDatum(J, "sum_all_variables(#bytes in parent scope)",
644 GlobalStats.ScopeBytes);
645 printDatum(J,
646 "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
647 GlobalStats.TotalBytesCovered);
648 printDatum(J,
649 "sum_all_variables(#bytes in parent scope covered by "
650 "DW_AT_location)",
651 GlobalStats.ScopeBytesCovered);
652 printDatum(J,
653 "sum_all_variables(#bytes in parent scope covered by "
654 "DW_OP_entry_value)",
655 GlobalStats.ScopeEntryValueBytesCovered);
656
657 printDatum(J, "sum_all_params(#bytes in parent scope)",
658 GlobalStats.ParamScopeBytes);
659 printDatum(J,
660 "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
661 GlobalStats.ParamScopeBytesCovered);
662 printDatum(J,
663 "sum_all_params(#bytes in parent scope covered by "
664 "DW_OP_entry_value)",
665 GlobalStats.ParamScopeEntryValueBytesCovered);
666
667 printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
668 GlobalStats.LocalVarScopeBytes);
669 printDatum(J,
670 "sum_all_local_vars(#bytes in parent scope covered by "
671 "DW_AT_location)",
672 GlobalStats.LocalVarScopeBytesCovered);
673 printDatum(J,
674 "sum_all_local_vars(#bytes in parent scope covered by "
675 "DW_OP_entry_value)",
676 GlobalStats.LocalVarScopeEntryValueBytesCovered);
677
678 printDatum(J, "#bytes within functions", GlobalStats.FunctionSize);
679 printDatum(J, "#bytes within inlined functions",
680 GlobalStats.InlineFunctionSize);
681
682 // Print the summary for formal parameters.
683 printDatum(J, "#params", ParamTotal);
684 printDatum(J, "#params with source location", ParamWithSrcLoc);
685 printDatum(J, "#params with type", ParamWithType);
686 printDatum(J, "#params with binary location", ParamWithLoc);
687
688 // Print the summary for local variables.
689 printDatum(J, "#local vars", LocalVarTotal);
690 printDatum(J, "#local vars with source location", LocalVarWithSrcLoc);
691 printDatum(J, "#local vars with type", LocalVarWithType);
692 printDatum(J, "#local vars with binary location", LocalVarWithLoc);
693
694 // Print the debug section sizes.
695 printSectionSizes(J, Sizes);
696
697 // Print the location statistics for variables (includes local variables
698 // and formal parameters).
699 printDatum(J, "#variables processed by location statistics",
700 LocStats.NumVarParam);
701 printLocationStats(J, "#variables", LocStats.VarParamLocStats);
702 printLocationStats(J, "#variables - entry values",
703 LocStats.VarParamNonEntryValLocStats);
704
705 // Print the location statistics for formal parameters.
706 printDatum(J, "#params processed by location statistics", LocStats.NumParam);
707 printLocationStats(J, "#params", LocStats.ParamLocStats);
708 printLocationStats(J, "#params - entry values",
709 LocStats.ParamNonEntryValLocStats);
710
711 // Print the location statistics for local variables.
712 printDatum(J, "#local vars processed by location statistics",
713 LocStats.NumVar);
714 printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
715 printLocationStats(J, "#local vars - entry values",
716 LocStats.LocalVarNonEntryValLocStats);
717 J.objectEnd();
718 OS << '\n';
719 LLVM_DEBUG(
720 llvm::dbgs() << "Total Availability: "
721 << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal)
722 << "%\n";
723 llvm::dbgs() << "PC Ranges covered: "
724 << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
725 GlobalStats.ScopeBytes)
726 << "%\n");
727 return true;
728 }
729