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