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