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