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