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 StringMap<PerFunctionStats> Statistics; 718 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) { 719 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) { 720 // These variables are being reset for each CU, since there could be 721 // a situation where we have two subprogram DIEs with the same offsets 722 // in two diferent CUs, and we can end up using wrong variables info 723 // when trying to resolve abstract_orign attribute. 724 // TODO: Handle LTO cases where the abstract origin of 725 // the function is in a different CU than the one it's 726 // referenced from or inlined into. 727 InlinedVarsTyMap GlobalInlinedFnInfo; 728 InlinedFnInstacesTy InlinedFnsToBeProcessed; 729 730 collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, 731 LocStats, GlobalInlinedFnInfo, 732 InlinedFnsToBeProcessed); 733 734 collectZeroCovInlinedVars(CUDie.getDwarfUnit(), GlobalStats, LocStats, 735 GlobalInlinedFnInfo, InlinedFnsToBeProcessed); 736 } 737 } 738 739 /// Collect the sizes of debug sections. 740 SectionSizes Sizes; 741 calculateSectionSizes(Obj, Sizes, Filename); 742 743 /// The version number should be increased every time the algorithm is changed 744 /// (including bug fixes). New metrics may be added without increasing the 745 /// version. 746 unsigned Version = 7; 747 unsigned VarParamTotal = 0; 748 unsigned VarParamUnique = 0; 749 unsigned VarParamWithLoc = 0; 750 unsigned NumFunctions = 0; 751 unsigned NumInlinedFunctions = 0; 752 unsigned NumFuncsWithSrcLoc = 0; 753 unsigned NumAbstractOrigins = 0; 754 unsigned ParamTotal = 0; 755 unsigned ParamWithType = 0; 756 unsigned ParamWithLoc = 0; 757 unsigned ParamWithSrcLoc = 0; 758 unsigned LocalVarTotal = 0; 759 unsigned LocalVarWithType = 0; 760 unsigned LocalVarWithSrcLoc = 0; 761 unsigned LocalVarWithLoc = 0; 762 for (auto &Entry : Statistics) { 763 PerFunctionStats &Stats = Entry.getValue(); 764 unsigned TotalVars = Stats.VarsInFunction.size() * 765 (Stats.NumFnInlined + Stats.NumFnOutOfLine); 766 // Count variables in global scope. 767 if (!Stats.IsFunction) 768 TotalVars = 769 Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial; 770 unsigned Constants = Stats.ConstantMembers; 771 VarParamWithLoc += Stats.TotalVarWithLoc + Constants; 772 VarParamTotal += TotalVars; 773 VarParamUnique += Stats.VarsInFunction.size(); 774 LLVM_DEBUG(for (auto &V 775 : Stats.VarsInFunction) llvm::dbgs() 776 << Entry.getKey() << ": " << V.getKey() << "\n"); 777 NumFunctions += Stats.IsFunction; 778 NumFuncsWithSrcLoc += Stats.HasSourceLocation; 779 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; 780 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; 781 ParamTotal += Stats.NumParams; 782 ParamWithType += Stats.NumParamTypes; 783 ParamWithLoc += Stats.NumParamLocations; 784 ParamWithSrcLoc += Stats.NumParamSourceLocations; 785 LocalVarTotal += Stats.NumLocalVars; 786 LocalVarWithType += Stats.NumLocalVarTypes; 787 LocalVarWithLoc += Stats.NumLocalVarLocations; 788 LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations; 789 } 790 791 // Print summary. 792 OS.SetBufferSize(1024); 793 json::OStream J(OS, 2); 794 J.objectBegin(); 795 J.attribute("version", Version); 796 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; 797 llvm::dbgs() << "---------------------------------\n"); 798 799 printDatum(J, "file", Filename.str()); 800 printDatum(J, "format", FormatName); 801 802 printDatum(J, "#functions", NumFunctions); 803 printDatum(J, "#functions with location", NumFuncsWithSrcLoc); 804 printDatum(J, "#inlined functions", NumInlinedFunctions); 805 printDatum(J, "#inlined functions with abstract origins", NumAbstractOrigins); 806 807 // This includes local variables and formal parameters. 808 printDatum(J, "#unique source variables", VarParamUnique); 809 printDatum(J, "#source variables", VarParamTotal); 810 printDatum(J, "#source variables with location", VarParamWithLoc); 811 812 printDatum(J, "#call site entries", GlobalStats.CallSiteEntries); 813 printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs); 814 printDatum(J, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs); 815 816 printDatum(J, "sum_all_variables(#bytes in parent scope)", 817 GlobalStats.ScopeBytes); 818 printDatum(J, 819 "sum_all_variables(#bytes in any scope covered by DW_AT_location)", 820 GlobalStats.TotalBytesCovered); 821 printDatum(J, 822 "sum_all_variables(#bytes in parent scope covered by " 823 "DW_AT_location)", 824 GlobalStats.ScopeBytesCovered); 825 printDatum(J, 826 "sum_all_variables(#bytes in parent scope covered by " 827 "DW_OP_entry_value)", 828 GlobalStats.ScopeEntryValueBytesCovered); 829 830 printDatum(J, "sum_all_params(#bytes in parent scope)", 831 GlobalStats.ParamScopeBytes); 832 printDatum(J, 833 "sum_all_params(#bytes in parent scope covered by DW_AT_location)", 834 GlobalStats.ParamScopeBytesCovered); 835 printDatum(J, 836 "sum_all_params(#bytes in parent scope covered by " 837 "DW_OP_entry_value)", 838 GlobalStats.ParamScopeEntryValueBytesCovered); 839 840 printDatum(J, "sum_all_local_vars(#bytes in parent scope)", 841 GlobalStats.LocalVarScopeBytes); 842 printDatum(J, 843 "sum_all_local_vars(#bytes in parent scope covered by " 844 "DW_AT_location)", 845 GlobalStats.LocalVarScopeBytesCovered); 846 printDatum(J, 847 "sum_all_local_vars(#bytes in parent scope covered by " 848 "DW_OP_entry_value)", 849 GlobalStats.LocalVarScopeEntryValueBytesCovered); 850 851 printDatum(J, "#bytes within functions", GlobalStats.FunctionSize); 852 printDatum(J, "#bytes within inlined functions", 853 GlobalStats.InlineFunctionSize); 854 855 // Print the summary for formal parameters. 856 printDatum(J, "#params", ParamTotal); 857 printDatum(J, "#params with source location", ParamWithSrcLoc); 858 printDatum(J, "#params with type", ParamWithType); 859 printDatum(J, "#params with binary location", ParamWithLoc); 860 861 // Print the summary for local variables. 862 printDatum(J, "#local vars", LocalVarTotal); 863 printDatum(J, "#local vars with source location", LocalVarWithSrcLoc); 864 printDatum(J, "#local vars with type", LocalVarWithType); 865 printDatum(J, "#local vars with binary location", LocalVarWithLoc); 866 867 // Print the debug section sizes. 868 printSectionSizes(J, Sizes); 869 870 // Print the location statistics for variables (includes local variables 871 // and formal parameters). 872 printDatum(J, "#variables processed by location statistics", 873 LocStats.NumVarParam); 874 printLocationStats(J, "#variables", LocStats.VarParamLocStats); 875 printLocationStats(J, "#variables - entry values", 876 LocStats.VarParamNonEntryValLocStats); 877 878 // Print the location statistics for formal parameters. 879 printDatum(J, "#params processed by location statistics", LocStats.NumParam); 880 printLocationStats(J, "#params", LocStats.ParamLocStats); 881 printLocationStats(J, "#params - entry values", 882 LocStats.ParamNonEntryValLocStats); 883 884 // Print the location statistics for local variables. 885 printDatum(J, "#local vars processed by location statistics", 886 LocStats.NumVar); 887 printLocationStats(J, "#local vars", LocStats.LocalVarLocStats); 888 printLocationStats(J, "#local vars - entry values", 889 LocStats.LocalVarNonEntryValLocStats); 890 J.objectEnd(); 891 OS << '\n'; 892 LLVM_DEBUG( 893 llvm::dbgs() << "Total Availability: " 894 << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal) 895 << "%\n"; 896 llvm::dbgs() << "PC Ranges covered: " 897 << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) / 898 GlobalStats.ScopeBytes) 899 << "%\n"); 900 return true; 901 } 902