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 InlinedVarsTy = llvm::SmallVector<uint64_t>; 34 /// This maps function DIE offset to its variables. 35 using InlinedVarsTyMap = llvm::DenseMap<uint64_t, InlinedVarsTy>; 36 /// This represents inlined_subroutine DIE offsets. 37 using InlinedFnInstacesTy = 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. 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. 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. 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. 223 static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, 224 std::string VarPrefix, uint64_t BytesInScope, 225 uint32_t InlineDepth, 226 StringMap<PerFunctionStats> &FnStatMap, 227 GlobalStats &GlobalStats, 228 LocationStats &LocStats, 229 InlinedVarsTy *InlinedVariables) { 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 // Check if it is an inlined variable. 279 if (Die.find(dwarf::DW_AT_abstract_origin)) { 280 if (Die.find(dwarf::DW_AT_location) || 281 Die.find(dwarf::DW_AT_const_value)) { 282 if (InlinedVariables) { 283 auto Offset = Die.find(dwarf::DW_AT_abstract_origin); 284 // Do not track this inlined var any more, since it has location 285 // coverage. 286 llvm::erase_value(*InlinedVariables, (*Offset).getRawUValue()); 287 } 288 } else { 289 // The locstats will be handled at the end of 290 // the collectStatsRecursive(). 291 DeferLocStats = true; 292 } 293 } 294 295 auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { 296 DWARFUnit *U = Die.getDwarfUnit(); 297 DataExtractor Data(toStringRef(D), 298 Die.getDwarfUnit()->getContext().isLittleEndian(), 0); 299 DWARFExpression Expression(Data, U->getAddressByteSize(), 300 U->getFormParams().Format); 301 // Consider the expression containing the DW_OP_entry_value as 302 // an entry value. 303 return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { 304 return Op.getCode() == dwarf::DW_OP_entry_value || 305 Op.getCode() == dwarf::DW_OP_GNU_entry_value; 306 }); 307 }; 308 309 if (Die.find(dwarf::DW_AT_const_value)) { 310 // This catches constant members *and* variables. 311 HasLoc = true; 312 ScopeBytesCovered = BytesInScope; 313 TotalBytesCovered = BytesInScope; 314 } else { 315 // Handle variables and function arguments. 316 Expected<std::vector<DWARFLocationExpression>> Loc = 317 Die.getLocations(dwarf::DW_AT_location); 318 if (!Loc) { 319 consumeError(Loc.takeError()); 320 } else { 321 HasLoc = true; 322 // Get PC coverage. 323 auto Default = find_if( 324 *Loc, [](const DWARFLocationExpression &L) { return !L.Range; }); 325 if (Default != Loc->end()) { 326 // Assume the entire range is covered by a single location. 327 ScopeBytesCovered = BytesInScope; 328 TotalBytesCovered = BytesInScope; 329 } else { 330 // Caller checks this Expected result already, it cannot fail. 331 auto ScopeRanges = cantFail(Die.getParent().getAddressRanges()); 332 for (auto Entry : *Loc) { 333 TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC; 334 uint64_t ScopeBytesCoveredByEntry = 0; 335 // Calculate how many bytes of the parent scope this entry covers. 336 // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The 337 // address ranges defined by the bounded location descriptions of a 338 // location list may overlap". So in theory a variable can have 339 // multiple simultaneous locations, which would make this calculation 340 // misleading because we will count the overlapped areas 341 // twice. However, clang does not currently emit DWARF like this. 342 for (DWARFAddressRange R : ScopeRanges) { 343 ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R); 344 } 345 ScopeBytesCovered += ScopeBytesCoveredByEntry; 346 if (IsEntryValue(Entry.Expr)) 347 BytesEntryValuesCovered += ScopeBytesCoveredByEntry; 348 } 349 } 350 } 351 } 352 353 // Calculate the debug location statistics. 354 if (BytesInScope && !DeferLocStats) { 355 LocStats.NumVarParam++; 356 if (IsParam) 357 LocStats.NumParam++; 358 else if (IsLocalVar) 359 LocStats.NumVar++; 360 361 collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats, 362 LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam, 363 IsLocalVar); 364 // Non debug entry values coverage statistics. 365 collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope, 366 LocStats.VarParamNonEntryValLocStats, 367 LocStats.ParamNonEntryValLocStats, 368 LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar); 369 } 370 371 // Collect PC range coverage data. 372 if (DWARFDie D = 373 Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) 374 Die = D; 375 376 std::string VarID = constructDieID(Die, VarPrefix); 377 FnStats.VarsInFunction.insert(VarID); 378 379 GlobalStats.TotalBytesCovered += TotalBytesCovered; 380 if (BytesInScope) { 381 GlobalStats.ScopeBytesCovered += ScopeBytesCovered; 382 GlobalStats.ScopeBytes += BytesInScope; 383 GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered; 384 if (IsParam) { 385 GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered; 386 GlobalStats.ParamScopeBytes += BytesInScope; 387 GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; 388 } else if (IsLocalVar) { 389 GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered; 390 GlobalStats.LocalVarScopeBytes += BytesInScope; 391 GlobalStats.LocalVarScopeEntryValueBytesCovered += 392 BytesEntryValuesCovered; 393 } 394 assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes); 395 } 396 397 if (IsConstantMember) { 398 FnStats.ConstantMembers++; 399 return; 400 } 401 402 FnStats.TotalVarWithLoc += (unsigned)HasLoc; 403 404 if (Die.find(dwarf::DW_AT_artificial)) { 405 FnStats.NumArtificial++; 406 return; 407 } 408 409 if (IsParam) { 410 FnStats.NumParams++; 411 if (HasType) 412 FnStats.NumParamTypes++; 413 if (HasSrcLoc) 414 FnStats.NumParamSourceLocations++; 415 if (HasLoc) 416 FnStats.NumParamLocations++; 417 } else if (IsLocalVar) { 418 FnStats.NumLocalVars++; 419 if (HasType) 420 FnStats.NumLocalVarTypes++; 421 if (HasSrcLoc) 422 FnStats.NumLocalVarSourceLocations++; 423 if (HasLoc) 424 FnStats.NumLocalVarLocations++; 425 } 426 } 427 428 /// Recursively collect variables from subprogram with 429 /// DW_AT_inline attribute. 430 static void collectInlinedFnInfo(DWARFDie Die, 431 uint64_t SPOffset, 432 InlinedVarsTyMap &GlobalInlinedFnInfo) { 433 DWARFDie Child = Die.getFirstChild(); 434 while (Child) { 435 const dwarf::Tag ChildTag = Child.getTag(); 436 if (ChildTag == dwarf::DW_TAG_formal_parameter || 437 ChildTag == dwarf::DW_TAG_variable) 438 GlobalInlinedFnInfo[SPOffset].push_back(Child.getOffset()); 439 else if (ChildTag == dwarf::DW_TAG_lexical_block) 440 collectInlinedFnInfo(Child, SPOffset, GlobalInlinedFnInfo); 441 Child = Child.getSibling(); 442 } 443 } 444 445 /// Recursively collect debug info quality metrics. 446 static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, 447 std::string VarPrefix, uint64_t BytesInScope, 448 uint32_t InlineDepth, 449 StringMap<PerFunctionStats> &FnStatMap, 450 GlobalStats &GlobalStats, 451 LocationStats &LocStats, 452 InlinedVarsTyMap &GlobalInlinedFnInfo, 453 InlinedFnInstacesTy &InlinedFnsToBeProcessed, 454 InlinedVarsTy *InlinedVarsPtr = nullptr) { 455 // Skip NULL nodes. 456 if (Die.isNULL()) 457 return; 458 459 const dwarf::Tag Tag = Die.getTag(); 460 // Skip function types. 461 if (Tag == dwarf::DW_TAG_subroutine_type) 462 return; 463 464 // Handle any kind of lexical scope. 465 const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; 466 const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; 467 const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; 468 InlinedVarsTy InlinedVars; 469 // Get the vars of the inlined fn, so the locstats 470 // reports the missing vars (with coverage 0%). 471 if (IsInlinedFunction) { 472 auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin); 473 if (OffsetFn) { 474 uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue(); 475 if (GlobalInlinedFnInfo.count(OffsetOfInlineFnCopy)) { 476 InlinedVars = GlobalInlinedFnInfo[OffsetOfInlineFnCopy]; 477 InlinedVarsPtr = &InlinedVars; 478 } else { 479 // This means that the DW_AT_inline fn copy is out of order, 480 // so this inlined instance will be processed later. 481 InlinedFnsToBeProcessed.push_back(Die.getOffset()); 482 InlinedVarsPtr = nullptr; 483 } 484 } 485 } 486 487 if (IsFunction || IsInlinedFunction || IsBlock) { 488 // Reset VarPrefix when entering a new function. 489 if (IsFunction || IsInlinedFunction) 490 VarPrefix = "v"; 491 492 // Ignore forward declarations. 493 if (Die.find(dwarf::DW_AT_declaration)) 494 return; 495 496 // Check for call sites. 497 if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line)) 498 GlobalStats.CallSiteEntries++; 499 500 // PC Ranges. 501 auto RangesOrError = Die.getAddressRanges(); 502 if (!RangesOrError) { 503 llvm::consumeError(RangesOrError.takeError()); 504 return; 505 } 506 507 auto Ranges = RangesOrError.get(); 508 uint64_t BytesInThisScope = 0; 509 for (auto Range : Ranges) 510 BytesInThisScope += Range.HighPC - Range.LowPC; 511 512 // Count the function. 513 if (!IsBlock) { 514 // Skip over abstract origins, but collect variables 515 // from it so it can be used for location statistics 516 // for inlined instancies. 517 if (Die.find(dwarf::DW_AT_inline)) { 518 uint64_t SPOffset = Die.getOffset(); 519 collectInlinedFnInfo(Die, SPOffset, GlobalInlinedFnInfo); 520 return; 521 } 522 523 std::string FnID = constructDieID(Die); 524 // We've seen an instance of this function. 525 auto &FnStats = FnStatMap[FnID]; 526 FnStats.IsFunction = true; 527 if (IsInlinedFunction) { 528 FnStats.NumFnInlined++; 529 if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) 530 FnStats.NumAbstractOrigins++; 531 } else { 532 FnStats.NumFnOutOfLine++; 533 } 534 if (Die.findRecursively(dwarf::DW_AT_decl_file) && 535 Die.findRecursively(dwarf::DW_AT_decl_line)) 536 FnStats.HasSourceLocation = true; 537 // Update function prefix. 538 FnPrefix = FnID; 539 } 540 541 if (BytesInThisScope) { 542 BytesInScope = BytesInThisScope; 543 if (IsFunction) 544 GlobalStats.FunctionSize += BytesInThisScope; 545 else if (IsInlinedFunction && InlineDepth == 0) 546 GlobalStats.InlineFunctionSize += BytesInThisScope; 547 } 548 } else { 549 // Not a scope, visit the Die itself. It could be a variable. 550 collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, 551 FnStatMap, GlobalStats, LocStats, InlinedVarsPtr); 552 } 553 554 // Set InlineDepth correctly for child recursion 555 if (IsFunction) 556 InlineDepth = 0; 557 else if (IsInlinedFunction) 558 ++InlineDepth; 559 560 // Traverse children. 561 unsigned LexicalBlockIndex = 0; 562 unsigned FormalParameterIndex = 0; 563 DWARFDie Child = Die.getFirstChild(); 564 while (Child) { 565 std::string ChildVarPrefix = VarPrefix; 566 if (Child.getTag() == dwarf::DW_TAG_lexical_block) 567 ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; 568 if (Child.getTag() == dwarf::DW_TAG_formal_parameter) 569 ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; 570 571 collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, 572 InlineDepth, FnStatMap, GlobalStats, LocStats, 573 GlobalInlinedFnInfo, InlinedFnsToBeProcessed, 574 InlinedVarsPtr); 575 Child = Child.getSibling(); 576 } 577 578 if (!IsInlinedFunction) 579 return; 580 581 // After we have processed all vars of the inlined function, 582 // we want to know how many variables have no location. 583 for (auto Offset : InlinedVars) { 584 LocStats.NumVarParam++; 585 LocStats.VarParamLocStats[ZeroCoverageBucket]++; 586 auto InlineDie = Die.getDwarfUnit()->getDIEForOffset(Offset); 587 if (!InlineDie) 588 continue; 589 auto Tag = InlineDie.getTag(); 590 if (Tag == dwarf::DW_TAG_formal_parameter) { 591 LocStats.NumParam++; 592 LocStats.ParamLocStats[ZeroCoverageBucket]++; 593 } else if (Tag == dwarf::DW_TAG_variable) { 594 LocStats.NumVar++; 595 LocStats.LocalVarLocStats[ZeroCoverageBucket]++; 596 } 597 } 598 } 599 600 /// Print human-readable output. 601 /// \{ 602 static void printDatum(json::OStream &J, const char *Key, json::Value Value) { 603 J.attribute(Key, Value); 604 LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); 605 } 606 607 static void printLocationStats(json::OStream &J, const char *Key, 608 std::vector<unsigned> &LocationStats) { 609 J.attribute( 610 (Twine(Key) + " with 0% of parent scope covered by DW_AT_location").str(), 611 LocationStats[0]); 612 LLVM_DEBUG( 613 llvm::dbgs() << Key 614 << " with 0% of parent scope covered by DW_AT_location: \\" 615 << LocationStats[0] << '\n'); 616 J.attribute( 617 (Twine(Key) + " with (0%,10%) of parent scope covered by DW_AT_location") 618 .str(), 619 LocationStats[1]); 620 LLVM_DEBUG(llvm::dbgs() 621 << Key 622 << " with (0%,10%) of parent scope covered by DW_AT_location: " 623 << LocationStats[1] << '\n'); 624 for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { 625 J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," + 626 Twine(i * 10) + "%) of parent scope covered by DW_AT_location") 627 .str(), 628 LocationStats[i]); 629 LLVM_DEBUG(llvm::dbgs() 630 << Key << " with [" << (i - 1) * 10 << "%," << i * 10 631 << "%) of parent scope covered by DW_AT_location: " 632 << LocationStats[i]); 633 } 634 J.attribute( 635 (Twine(Key) + " with 100% of parent scope covered by DW_AT_location") 636 .str(), 637 LocationStats[NumOfCoverageCategories - 1]); 638 LLVM_DEBUG( 639 llvm::dbgs() << Key 640 << " with 100% of parent scope covered by DW_AT_location: " 641 << LocationStats[NumOfCoverageCategories - 1]); 642 } 643 644 static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) { 645 for (const auto &It : Sizes.DebugSectionSizes) 646 J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second)); 647 } 648 649 /// Stop tracking inlined variables with a location. 650 /// This is used for out-of-order DW_AT_inline subprograms only. 651 static void updateInlinedVarsCovInfo(DWARFDie InlinedFnDie, 652 InlinedVarsTy &InlinedVars) { 653 DWARFDie Child = InlinedFnDie.getFirstChild(); 654 while (Child) { 655 const dwarf::Tag ChildTag = Child.getTag(); 656 if ((ChildTag == dwarf::DW_TAG_formal_parameter || 657 ChildTag == dwarf::DW_TAG_variable) && 658 (Child.find(dwarf::DW_AT_location) || 659 Child.find(dwarf::DW_AT_const_value))) { 660 auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin); 661 if (OffsetVar) 662 llvm::erase_value(InlinedVars, (*OffsetVar).getRawUValue()); 663 } else if (ChildTag == dwarf::DW_TAG_lexical_block) 664 updateInlinedVarsCovInfo(Child, InlinedVars); 665 Child = Child.getSibling(); 666 } 667 } 668 669 /// Collect zero location coverage for inlined variables which refer to 670 /// a DW_AT_inline copy of subprogram that is out of order in the DWARF. 671 static void 672 collectZeroCovInlinedVars(DWARFUnit *DwUnit, GlobalStats &GlobalStats, 673 LocationStats &LocStats, 674 InlinedVarsTyMap &GlobalInlinedFnInfo, 675 InlinedFnInstacesTy &InlinedFnsToBeProcessed) { 676 for (auto FnOffset : InlinedFnsToBeProcessed) { 677 DWARFDie InlinedFnDie = DwUnit->getDIEForOffset(FnOffset); 678 auto InlinedCopy = InlinedFnDie.find(dwarf::DW_AT_abstract_origin); 679 InlinedVarsTy InlinedVars; 680 if (!InlinedCopy) 681 continue; 682 683 InlinedVars = GlobalInlinedFnInfo[(*InlinedCopy).getRawUValue()]; 684 updateInlinedVarsCovInfo(InlinedFnDie, InlinedVars); 685 686 for (auto Offset : InlinedVars) { 687 LocStats.NumVarParam++; 688 LocStats.VarParamLocStats[ZeroCoverageBucket]++; 689 auto Tag = DwUnit->getDIEForOffset(Offset).getTag(); 690 if (Tag == dwarf::DW_TAG_formal_parameter) { 691 LocStats.NumParam++; 692 LocStats.ParamLocStats[ZeroCoverageBucket]++; 693 } else if (Tag == dwarf::DW_TAG_variable) { 694 LocStats.NumVar++; 695 LocStats.LocalVarLocStats[ZeroCoverageBucket]++; 696 } 697 } 698 } 699 } 700 701 /// \} 702 703 /// Collect debug info quality metrics for an entire DIContext. 704 /// 705 /// Do the impossible and reduce the quality of the debug info down to a few 706 /// numbers. The idea is to condense the data into numbers that can be tracked 707 /// over time to identify trends in newer compiler versions and gauge the effect 708 /// of particular optimizations. The raw numbers themselves are not particularly 709 /// useful, only the delta between compiling the same program with different 710 /// compilers is. 711 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 712 const Twine &Filename, 713 raw_ostream &OS) { 714 StringRef FormatName = Obj.getFileFormatName(); 715 GlobalStats GlobalStats; 716 LocationStats LocStats; 717 InlinedVarsTyMap GlobalInlinedFnInfo; 718 InlinedFnInstacesTy InlinedFnsToBeProcessed; 719 StringMap<PerFunctionStats> Statistics; 720 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) { 721 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) { 722 collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, 723 LocStats, GlobalInlinedFnInfo, 724 InlinedFnsToBeProcessed); 725 726 collectZeroCovInlinedVars(CUDie.getDwarfUnit(), GlobalStats, LocStats, 727 GlobalInlinedFnInfo, InlinedFnsToBeProcessed); 728 } 729 } 730 731 /// Collect the sizes of debug sections. 732 SectionSizes Sizes; 733 calculateSectionSizes(Obj, Sizes, Filename); 734 735 /// The version number should be increased every time the algorithm is changed 736 /// (including bug fixes). New metrics may be added without increasing the 737 /// version. 738 unsigned Version = 7; 739 unsigned VarParamTotal = 0; 740 unsigned VarParamUnique = 0; 741 unsigned VarParamWithLoc = 0; 742 unsigned NumFunctions = 0; 743 unsigned NumInlinedFunctions = 0; 744 unsigned NumFuncsWithSrcLoc = 0; 745 unsigned NumAbstractOrigins = 0; 746 unsigned ParamTotal = 0; 747 unsigned ParamWithType = 0; 748 unsigned ParamWithLoc = 0; 749 unsigned ParamWithSrcLoc = 0; 750 unsigned LocalVarTotal = 0; 751 unsigned LocalVarWithType = 0; 752 unsigned LocalVarWithSrcLoc = 0; 753 unsigned LocalVarWithLoc = 0; 754 for (auto &Entry : Statistics) { 755 PerFunctionStats &Stats = Entry.getValue(); 756 unsigned TotalVars = Stats.VarsInFunction.size() * 757 (Stats.NumFnInlined + Stats.NumFnOutOfLine); 758 // Count variables in global scope. 759 if (!Stats.IsFunction) 760 TotalVars = 761 Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial; 762 unsigned Constants = Stats.ConstantMembers; 763 VarParamWithLoc += Stats.TotalVarWithLoc + Constants; 764 VarParamTotal += TotalVars; 765 VarParamUnique += Stats.VarsInFunction.size(); 766 LLVM_DEBUG(for (auto &V 767 : Stats.VarsInFunction) llvm::dbgs() 768 << Entry.getKey() << ": " << V.getKey() << "\n"); 769 NumFunctions += Stats.IsFunction; 770 NumFuncsWithSrcLoc += Stats.HasSourceLocation; 771 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; 772 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; 773 ParamTotal += Stats.NumParams; 774 ParamWithType += Stats.NumParamTypes; 775 ParamWithLoc += Stats.NumParamLocations; 776 ParamWithSrcLoc += Stats.NumParamSourceLocations; 777 LocalVarTotal += Stats.NumLocalVars; 778 LocalVarWithType += Stats.NumLocalVarTypes; 779 LocalVarWithLoc += Stats.NumLocalVarLocations; 780 LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations; 781 } 782 783 // Print summary. 784 OS.SetBufferSize(1024); 785 json::OStream J(OS, 2); 786 J.objectBegin(); 787 J.attribute("version", Version); 788 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; 789 llvm::dbgs() << "---------------------------------\n"); 790 791 printDatum(J, "file", Filename.str()); 792 printDatum(J, "format", FormatName); 793 794 printDatum(J, "#functions", NumFunctions); 795 printDatum(J, "#functions with location", NumFuncsWithSrcLoc); 796 printDatum(J, "#inlined functions", NumInlinedFunctions); 797 printDatum(J, "#inlined functions with abstract origins", NumAbstractOrigins); 798 799 // This includes local variables and formal parameters. 800 printDatum(J, "#unique source variables", VarParamUnique); 801 printDatum(J, "#source variables", VarParamTotal); 802 printDatum(J, "#source variables with location", VarParamWithLoc); 803 804 printDatum(J, "#call site entries", GlobalStats.CallSiteEntries); 805 printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs); 806 printDatum(J, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs); 807 808 printDatum(J, "sum_all_variables(#bytes in parent scope)", 809 GlobalStats.ScopeBytes); 810 printDatum(J, 811 "sum_all_variables(#bytes in any scope covered by DW_AT_location)", 812 GlobalStats.TotalBytesCovered); 813 printDatum(J, 814 "sum_all_variables(#bytes in parent scope covered by " 815 "DW_AT_location)", 816 GlobalStats.ScopeBytesCovered); 817 printDatum(J, 818 "sum_all_variables(#bytes in parent scope covered by " 819 "DW_OP_entry_value)", 820 GlobalStats.ScopeEntryValueBytesCovered); 821 822 printDatum(J, "sum_all_params(#bytes in parent scope)", 823 GlobalStats.ParamScopeBytes); 824 printDatum(J, 825 "sum_all_params(#bytes in parent scope covered by DW_AT_location)", 826 GlobalStats.ParamScopeBytesCovered); 827 printDatum(J, 828 "sum_all_params(#bytes in parent scope covered by " 829 "DW_OP_entry_value)", 830 GlobalStats.ParamScopeEntryValueBytesCovered); 831 832 printDatum(J, "sum_all_local_vars(#bytes in parent scope)", 833 GlobalStats.LocalVarScopeBytes); 834 printDatum(J, 835 "sum_all_local_vars(#bytes in parent scope covered by " 836 "DW_AT_location)", 837 GlobalStats.LocalVarScopeBytesCovered); 838 printDatum(J, 839 "sum_all_local_vars(#bytes in parent scope covered by " 840 "DW_OP_entry_value)", 841 GlobalStats.LocalVarScopeEntryValueBytesCovered); 842 843 printDatum(J, "#bytes within functions", GlobalStats.FunctionSize); 844 printDatum(J, "#bytes within inlined functions", 845 GlobalStats.InlineFunctionSize); 846 847 // Print the summary for formal parameters. 848 printDatum(J, "#params", ParamTotal); 849 printDatum(J, "#params with source location", ParamWithSrcLoc); 850 printDatum(J, "#params with type", ParamWithType); 851 printDatum(J, "#params with binary location", ParamWithLoc); 852 853 // Print the summary for local variables. 854 printDatum(J, "#local vars", LocalVarTotal); 855 printDatum(J, "#local vars with source location", LocalVarWithSrcLoc); 856 printDatum(J, "#local vars with type", LocalVarWithType); 857 printDatum(J, "#local vars with binary location", LocalVarWithLoc); 858 859 // Print the debug section sizes. 860 printSectionSizes(J, Sizes); 861 862 // Print the location statistics for variables (includes local variables 863 // and formal parameters). 864 printDatum(J, "#variables processed by location statistics", 865 LocStats.NumVarParam); 866 printLocationStats(J, "#variables", LocStats.VarParamLocStats); 867 printLocationStats(J, "#variables - entry values", 868 LocStats.VarParamNonEntryValLocStats); 869 870 // Print the location statistics for formal parameters. 871 printDatum(J, "#params processed by location statistics", LocStats.NumParam); 872 printLocationStats(J, "#params", LocStats.ParamLocStats); 873 printLocationStats(J, "#params - entry values", 874 LocStats.ParamNonEntryValLocStats); 875 876 // Print the location statistics for local variables. 877 printDatum(J, "#local vars processed by location statistics", 878 LocStats.NumVar); 879 printLocationStats(J, "#local vars", LocStats.LocalVarLocStats); 880 printLocationStats(J, "#local vars - entry values", 881 LocStats.LocalVarNonEntryValLocStats); 882 J.objectEnd(); 883 OS << '\n'; 884 LLVM_DEBUG( 885 llvm::dbgs() << "Total Availability: " 886 << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal) 887 << "%\n"; 888 llvm::dbgs() << "PC Ranges covered: " 889 << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) / 890 GlobalStats.ScopeBytes) 891 << "%\n"); 892 return true; 893 } 894