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