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