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 // Consider the expression containing the DW_OP_entry_value as 253 // an entry value. 254 return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { 255 return Op.getCode() == dwarf::DW_OP_entry_value || 256 Op.getCode() == dwarf::DW_OP_GNU_entry_value; 257 }); 258 }; 259 260 if (Die.find(dwarf::DW_AT_const_value)) { 261 // This catches constant members *and* variables. 262 HasLoc = true; 263 BytesCovered = BytesInScope; 264 } else { 265 // Handle variables and function arguments. 266 Expected<std::vector<DWARFLocationExpression>> Loc = 267 Die.getLocations(dwarf::DW_AT_location); 268 if (!Loc) { 269 consumeError(Loc.takeError()); 270 } else { 271 HasLoc = true; 272 // Get PC coverage. 273 auto Default = find_if( 274 *Loc, [](const DWARFLocationExpression &L) { return !L.Range; }); 275 if (Default != Loc->end()) { 276 // Assume the entire range is covered by a single location. 277 BytesCovered = BytesInScope; 278 } else { 279 for (auto Entry : *Loc) { 280 uint64_t BytesEntryCovered = Entry.Range->HighPC - Entry.Range->LowPC; 281 BytesCovered += BytesEntryCovered; 282 if (IsEntryValue(Entry.Expr)) 283 BytesEntryValuesCovered += BytesEntryCovered; 284 } 285 } 286 } 287 } 288 289 // Calculate the debug location statistics. 290 if (BytesInScope) { 291 LocStats.NumVarParam++; 292 if (IsParam) 293 LocStats.NumParam++; 294 else if (IsLocalVar) 295 LocStats.NumVar++; 296 297 collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats, 298 LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam, 299 IsLocalVar); 300 // Non debug entry values coverage statistics. 301 collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope, 302 LocStats.VarParamNonEntryValLocStats, 303 LocStats.ParamNonEntryValLocStats, 304 LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar); 305 } 306 307 // Collect PC range coverage data. 308 if (DWARFDie D = 309 Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) 310 Die = D; 311 312 std::string VarID = constructDieID(Die, VarPrefix); 313 FnStats.VarsInFunction.insert(VarID); 314 315 if (BytesInScope) { 316 // Turns out we have a lot of ranges that extend past the lexical scope. 317 GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered); 318 GlobalStats.ScopeBytes += BytesInScope; 319 GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered; 320 if (IsParam) { 321 GlobalStats.ParamScopeBytesCovered += 322 std::min(BytesInScope, BytesCovered); 323 GlobalStats.ParamScopeBytes += BytesInScope; 324 GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; 325 } else if (IsLocalVar) { 326 GlobalStats.LocalVarScopeBytesCovered += 327 std::min(BytesInScope, BytesCovered); 328 GlobalStats.LocalVarScopeBytes += BytesInScope; 329 GlobalStats.LocalVarScopeEntryValueBytesCovered += 330 BytesEntryValuesCovered; 331 } 332 assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes); 333 } 334 335 if (IsConstantMember) { 336 FnStats.ConstantMembers++; 337 return; 338 } 339 340 FnStats.TotalVarWithLoc += (unsigned)HasLoc; 341 342 if (Die.find(dwarf::DW_AT_artificial)) { 343 FnStats.NumArtificial++; 344 return; 345 } 346 347 if (IsParam) { 348 FnStats.NumParams++; 349 if (HasType) 350 FnStats.NumParamTypes++; 351 if (HasSrcLoc) 352 FnStats.NumParamSourceLocations++; 353 if (HasLoc) 354 FnStats.NumParamLocations++; 355 } else if (IsLocalVar) { 356 FnStats.NumLocalVars++; 357 if (HasType) 358 FnStats.NumLocalVarTypes++; 359 if (HasSrcLoc) 360 FnStats.NumLocalVarSourceLocations++; 361 if (HasLoc) 362 FnStats.NumLocalVarLocations++; 363 } 364 } 365 366 /// Recursively collect debug info quality metrics. 367 static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, 368 std::string VarPrefix, uint64_t BytesInScope, 369 uint32_t InlineDepth, 370 StringMap<PerFunctionStats> &FnStatMap, 371 GlobalStats &GlobalStats, 372 LocationStats &LocStats) { 373 const dwarf::Tag Tag = Die.getTag(); 374 // Skip function types. 375 if (Tag == dwarf::DW_TAG_subroutine_type) 376 return; 377 378 // Handle any kind of lexical scope. 379 const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; 380 const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; 381 const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; 382 if (IsFunction || IsInlinedFunction || IsBlock) { 383 384 // Reset VarPrefix when entering a new function. 385 if (Die.getTag() == dwarf::DW_TAG_subprogram || 386 Die.getTag() == dwarf::DW_TAG_inlined_subroutine) 387 VarPrefix = "v"; 388 389 // Ignore forward declarations. 390 if (Die.find(dwarf::DW_AT_declaration)) 391 return; 392 393 // Check for call sites. 394 if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line)) 395 GlobalStats.CallSiteEntries++; 396 397 // PC Ranges. 398 auto RangesOrError = Die.getAddressRanges(); 399 if (!RangesOrError) { 400 llvm::consumeError(RangesOrError.takeError()); 401 return; 402 } 403 404 auto Ranges = RangesOrError.get(); 405 uint64_t BytesInThisScope = 0; 406 for (auto Range : Ranges) 407 BytesInThisScope += Range.HighPC - Range.LowPC; 408 409 // Count the function. 410 if (!IsBlock) { 411 // Skip over abstract origins. 412 if (Die.find(dwarf::DW_AT_inline)) 413 return; 414 std::string FnID = constructDieID(Die); 415 // We've seen an instance of this function. 416 auto &FnStats = FnStatMap[FnID]; 417 FnStats.IsFunction = true; 418 if (IsInlinedFunction) { 419 FnStats.NumFnInlined++; 420 if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) 421 FnStats.NumAbstractOrigins++; 422 } else { 423 FnStats.NumFnOutOfLine++; 424 } 425 if (Die.findRecursively(dwarf::DW_AT_decl_file) && 426 Die.findRecursively(dwarf::DW_AT_decl_line)) 427 FnStats.HasSourceLocation = true; 428 // Update function prefix. 429 FnPrefix = FnID; 430 } 431 432 if (BytesInThisScope) { 433 BytesInScope = BytesInThisScope; 434 if (IsFunction) 435 GlobalStats.FunctionSize += BytesInThisScope; 436 else if (IsInlinedFunction && InlineDepth == 0) 437 GlobalStats.InlineFunctionSize += BytesInThisScope; 438 } 439 } else { 440 // Not a scope, visit the Die itself. It could be a variable. 441 collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, 442 FnStatMap, GlobalStats, LocStats); 443 } 444 445 // Set InlineDepth correctly for child recursion 446 if (IsFunction) 447 InlineDepth = 0; 448 else if (IsInlinedFunction) 449 ++InlineDepth; 450 451 // Traverse children. 452 unsigned LexicalBlockIndex = 0; 453 unsigned FormalParameterIndex = 0; 454 DWARFDie Child = Die.getFirstChild(); 455 while (Child) { 456 std::string ChildVarPrefix = VarPrefix; 457 if (Child.getTag() == dwarf::DW_TAG_lexical_block) 458 ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; 459 if (Child.getTag() == dwarf::DW_TAG_formal_parameter) 460 ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; 461 462 collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, 463 InlineDepth, FnStatMap, GlobalStats, LocStats); 464 Child = Child.getSibling(); 465 } 466 } 467 468 /// Print machine-readable output. 469 /// The machine-readable format is single-line JSON output. 470 /// \{ 471 static void printDatum(raw_ostream &OS, const char *Key, json::Value Value) { 472 OS << ",\"" << Key << "\":" << Value; 473 LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); 474 } 475 476 static void printLocationStats(raw_ostream &OS, const char *Key, 477 std::vector<unsigned> &LocationStats) { 478 OS << ",\"" << Key << " with 0% of parent scope covered by DW_AT_location\":" 479 << LocationStats[0]; 480 LLVM_DEBUG( 481 llvm::dbgs() << Key 482 << " with 0% of parent scope covered by DW_AT_location: \\" 483 << LocationStats[0] << '\n'); 484 OS << ",\"" << Key 485 << " with (0%,10%) of parent scope covered by DW_AT_location\":" 486 << LocationStats[1]; 487 LLVM_DEBUG(llvm::dbgs() 488 << Key 489 << " with (0%,10%) of parent scope covered by DW_AT_location: " 490 << LocationStats[1] << '\n'); 491 for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { 492 OS << ",\"" << Key << " with [" << (i - 1) * 10 << "%," << i * 10 493 << "%) of parent scope covered by DW_AT_location\":" << LocationStats[i]; 494 LLVM_DEBUG(llvm::dbgs() 495 << Key << " with [" << (i - 1) * 10 << "%," << i * 10 496 << "%) of parent scope covered by DW_AT_location: " 497 << LocationStats[i]); 498 } 499 OS << ",\"" << Key 500 << " with 100% of parent scope covered by DW_AT_location\":" 501 << LocationStats[NumOfCoverageCategories - 1]; 502 LLVM_DEBUG( 503 llvm::dbgs() << Key 504 << " with 100% of parent scope covered by DW_AT_location: " 505 << LocationStats[NumOfCoverageCategories - 1]); 506 } 507 508 static void printSectionSizes(raw_ostream &OS, const SectionSizes &Sizes) { 509 for (const auto &DebugSec : Sizes.DebugSectionSizes) 510 OS << ",\"#bytes in " << DebugSec.getKey() << "\":" << DebugSec.getValue(); 511 } 512 513 /// \} 514 515 /// Collect debug info quality metrics for an entire DIContext. 516 /// 517 /// Do the impossible and reduce the quality of the debug info down to a few 518 /// numbers. The idea is to condense the data into numbers that can be tracked 519 /// over time to identify trends in newer compiler versions and gauge the effect 520 /// of particular optimizations. The raw numbers themselves are not particularly 521 /// useful, only the delta between compiling the same program with different 522 /// compilers is. 523 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 524 const Twine &Filename, 525 raw_ostream &OS) { 526 StringRef FormatName = Obj.getFileFormatName(); 527 GlobalStats GlobalStats; 528 LocationStats LocStats; 529 StringMap<PerFunctionStats> Statistics; 530 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) 531 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) 532 collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, 533 LocStats); 534 535 /// Collect the sizes of debug sections. 536 SectionSizes Sizes; 537 calculateSectionSizes(Obj, Sizes, Filename); 538 539 /// The version number should be increased every time the algorithm is changed 540 /// (including bug fixes). New metrics may be added without increasing the 541 /// version. 542 unsigned Version = 5; 543 unsigned VarParamTotal = 0; 544 unsigned VarParamUnique = 0; 545 unsigned VarParamWithLoc = 0; 546 unsigned NumFunctions = 0; 547 unsigned NumInlinedFunctions = 0; 548 unsigned NumFuncsWithSrcLoc = 0; 549 unsigned NumAbstractOrigins = 0; 550 unsigned ParamTotal = 0; 551 unsigned ParamWithType = 0; 552 unsigned ParamWithLoc = 0; 553 unsigned ParamWithSrcLoc = 0; 554 unsigned LocalVarTotal = 0; 555 unsigned LocalVarWithType = 0; 556 unsigned LocalVarWithSrcLoc = 0; 557 unsigned LocalVarWithLoc = 0; 558 for (auto &Entry : Statistics) { 559 PerFunctionStats &Stats = Entry.getValue(); 560 unsigned TotalVars = Stats.VarsInFunction.size() * 561 (Stats.NumFnInlined + Stats.NumFnOutOfLine); 562 // Count variables in global scope. 563 if (!Stats.IsFunction) 564 TotalVars = 565 Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial; 566 unsigned Constants = Stats.ConstantMembers; 567 VarParamWithLoc += Stats.TotalVarWithLoc + Constants; 568 VarParamTotal += TotalVars; 569 VarParamUnique += Stats.VarsInFunction.size(); 570 LLVM_DEBUG(for (auto &V 571 : Stats.VarsInFunction) llvm::dbgs() 572 << Entry.getKey() << ": " << V.getKey() << "\n"); 573 NumFunctions += Stats.IsFunction; 574 NumFuncsWithSrcLoc += Stats.HasSourceLocation; 575 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; 576 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; 577 ParamTotal += Stats.NumParams; 578 ParamWithType += Stats.NumParamTypes; 579 ParamWithLoc += Stats.NumParamLocations; 580 ParamWithSrcLoc += Stats.NumParamSourceLocations; 581 LocalVarTotal += Stats.NumLocalVars; 582 LocalVarWithType += Stats.NumLocalVarTypes; 583 LocalVarWithLoc += Stats.NumLocalVarLocations; 584 LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations; 585 } 586 587 // Print summary. 588 OS.SetBufferSize(1024); 589 OS << "{\"version\":" << Version; 590 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; 591 llvm::dbgs() << "---------------------------------\n"); 592 593 printDatum(OS, "file", Filename.str()); 594 printDatum(OS, "format", FormatName); 595 596 printDatum(OS, "#functions", NumFunctions); 597 printDatum(OS, "#functions with location", NumFuncsWithSrcLoc); 598 printDatum(OS, "#inlined functions", NumInlinedFunctions); 599 printDatum(OS, "#inlined functions with abstract origins", 600 NumAbstractOrigins); 601 602 // This includes local variables and formal parameters. 603 printDatum(OS, "#unique source variables", VarParamUnique); 604 printDatum(OS, "#source variables", VarParamTotal); 605 printDatum(OS, "#source variables with location", VarParamWithLoc); 606 607 printDatum(OS, "#call site entries", GlobalStats.CallSiteEntries); 608 printDatum(OS, "#call site DIEs", GlobalStats.CallSiteDIEs); 609 printDatum(OS, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs); 610 611 printDatum(OS, "sum_all_variables(#bytes in parent scope)", 612 GlobalStats.ScopeBytes); 613 printDatum(OS, 614 "sum_all_variables(#bytes in parent scope covered by " 615 "DW_AT_location)", 616 GlobalStats.ScopeBytesCovered); 617 printDatum(OS, 618 "sum_all_variables(#bytes in parent scope covered by " 619 "DW_OP_entry_value)", 620 GlobalStats.ScopeEntryValueBytesCovered); 621 622 printDatum(OS, "sum_all_params(#bytes in parent scope)", 623 GlobalStats.ParamScopeBytes); 624 printDatum( 625 OS, 626 "sum_all_params(#bytes in parent scope covered by DW_AT_location)", 627 GlobalStats.ParamScopeBytesCovered); 628 printDatum(OS, 629 "sum_all_params(#bytes in parent scope covered by " 630 "DW_OP_entry_value)", 631 GlobalStats.ParamScopeEntryValueBytesCovered); 632 633 printDatum(OS, "sum_all_local_vars(#bytes in parent scope)", 634 GlobalStats.LocalVarScopeBytes); 635 printDatum(OS, 636 "sum_all_local_vars(#bytes in parent scope covered by " 637 "DW_AT_location)", 638 GlobalStats.LocalVarScopeBytesCovered); 639 printDatum(OS, 640 "sum_all_local_vars(#bytes in parent scope covered by " 641 "DW_OP_entry_value)", 642 GlobalStats.LocalVarScopeEntryValueBytesCovered); 643 644 printDatum(OS, "#bytes witin functions", GlobalStats.FunctionSize); 645 printDatum(OS, "#bytes witin inlined functions", 646 GlobalStats.InlineFunctionSize); 647 648 // Print the summary for formal parameters. 649 printDatum(OS, "#params", ParamTotal); 650 printDatum(OS, "#params with source location", ParamWithSrcLoc); 651 printDatum(OS, "#params with type", ParamWithType); 652 printDatum(OS, "#params with binary location", ParamWithLoc); 653 654 // Print the summary for local variables. 655 printDatum(OS, "#local vars", LocalVarTotal); 656 printDatum(OS, "#local vars with source location", LocalVarWithSrcLoc); 657 printDatum(OS, "#local vars with type", LocalVarWithType); 658 printDatum(OS, "#local vars with binary location", LocalVarWithLoc); 659 660 // Print the debug section sizes. 661 printSectionSizes(OS, Sizes); 662 663 // Print the location statistics for variables (includes local variables 664 // and formal parameters). 665 printDatum(OS, "#variables processed by location statistics", 666 LocStats.NumVarParam); 667 printLocationStats(OS, "#variables", LocStats.VarParamLocStats); 668 printLocationStats(OS, "#variables - entry values", 669 LocStats.VarParamNonEntryValLocStats); 670 671 // Print the location statistics for formal parameters. 672 printDatum(OS, "#params processed by location statistics", LocStats.NumParam); 673 printLocationStats(OS, "#params", LocStats.ParamLocStats); 674 printLocationStats(OS, "#params - entry values", 675 LocStats.ParamNonEntryValLocStats); 676 677 // Print the location statistics for local variables. 678 printDatum(OS, "#local vars processed by location statistics", 679 LocStats.NumVar); 680 printLocationStats(OS, "#local vars", LocStats.LocalVarLocStats); 681 printLocationStats(OS, "#local vars - entry values", 682 LocStats.LocalVarNonEntryValLocStats); 683 OS << "}\n"; 684 LLVM_DEBUG( 685 llvm::dbgs() << "Total Availability: " 686 << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal) 687 << "%\n"; 688 llvm::dbgs() << "PC Ranges covered: " 689 << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) / 690 GlobalStats.ScopeBytes) 691 << "%\n"); 692 return true; 693 } 694