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 variables. 62 unsigned NumVars = 0; 63 /// Number of variables with source location. 64 unsigned NumVarSourceLocations = 0; 65 /// Number of variables with type. 66 unsigned NumVarTypes = 0; 67 /// Number of variables with DW_AT_location. 68 unsigned NumVarLocations = 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 variable's enclosing scope 84 /// (only for parameters). 85 unsigned ParamScopeBytes = 0; 86 /// Total number of PC range bytes covered by DW_AT_locations with 87 /// the debug entry values (DW_OP_entry_value) (only for parameters). 88 unsigned ParamScopeEntryValueBytesCovered = 0; 89 /// Total number of PC range bytes covered by DW_AT_locations (only for local 90 /// variables). 91 unsigned VarScopeBytesCovered = 0; 92 /// Total number of PC range bytes in each variable's enclosing scope 93 /// (only for local variables). 94 unsigned VarScopeBytes = 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 VarScopeEntryValueBytesCovered = 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> VarLocStats{ 134 std::vector<unsigned>(NumOfCoverageCategories, 0)}; 135 /// Map non debug entry values coverage for local variables. 136 std::vector<unsigned> VarNonEntryValLocStats{ 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 BytesCovered, uint64_t BytesInScope, 149 std::vector<unsigned> &VarParamLocStats, 150 std::vector<unsigned> &ParamLocStats, 151 std::vector<unsigned> &VarLocStats, bool IsParam, 152 bool IsLocalVar) { 153 auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned { 154 // No debug location at all for the variable. 155 if (BytesCovered == 0) 156 return 0; 157 // Fully covered variable within its scope. 158 if (BytesCovered >= BytesInScope) 159 return NumOfCoverageCategories - 1; 160 // Get covered range (e.g. 20%-29%). 161 unsigned LocBucket = 100 * (double)BytesCovered / 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 VarLocStats[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 /// Collect debug info quality metrics for one DIE. 204 static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, 205 std::string VarPrefix, uint64_t BytesInScope, 206 uint32_t InlineDepth, 207 StringMap<PerFunctionStats> &FnStatMap, 208 GlobalStats &GlobalStats, 209 LocationStats &LocStats) { 210 bool HasLoc = false; 211 bool HasSrcLoc = false; 212 bool HasType = false; 213 uint64_t BytesCovered = 0; 214 uint64_t BytesEntryValuesCovered = 0; 215 auto &FnStats = FnStatMap[FnPrefix]; 216 bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter; 217 bool IsVariable = Die.getTag() == dwarf::DW_TAG_variable; 218 bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member && 219 Die.find(dwarf::DW_AT_const_value); 220 221 if (Die.getTag() == dwarf::DW_TAG_call_site || 222 Die.getTag() == dwarf::DW_TAG_GNU_call_site) { 223 GlobalStats.CallSiteDIEs++; 224 return; 225 } 226 227 if (Die.getTag() == dwarf::DW_TAG_call_site_parameter || 228 Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) { 229 GlobalStats.CallSiteParamDIEs++; 230 return; 231 } 232 233 if (!IsParam && !IsVariable && !IsConstantMember) { 234 // Not a variable or constant member. 235 return; 236 } 237 238 // Ignore declarations of global variables. 239 if (IsVariable && Die.find(dwarf::DW_AT_declaration)) 240 return; 241 242 if (Die.findRecursively(dwarf::DW_AT_decl_file) && 243 Die.findRecursively(dwarf::DW_AT_decl_line)) 244 HasSrcLoc = true; 245 246 if (Die.findRecursively(dwarf::DW_AT_type)) 247 HasType = true; 248 249 auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { 250 DWARFUnit *U = Die.getDwarfUnit(); 251 DataExtractor Data(toStringRef(D), 252 Die.getDwarfUnit()->getContext().isLittleEndian(), 0); 253 DWARFExpression Expression(Data, U->getAddressByteSize()); 254 // Consider the expression containing the DW_OP_entry_value as 255 // an entry value. 256 return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { 257 return Op.getCode() == dwarf::DW_OP_entry_value || 258 Op.getCode() == dwarf::DW_OP_GNU_entry_value; 259 }); 260 }; 261 262 if (Die.find(dwarf::DW_AT_const_value)) { 263 // This catches constant members *and* variables. 264 HasLoc = true; 265 BytesCovered = BytesInScope; 266 } else { 267 // Handle variables and function arguments. 268 Expected<std::vector<DWARFLocationExpression>> Loc = 269 Die.getLocations(dwarf::DW_AT_location); 270 if (!Loc) { 271 consumeError(Loc.takeError()); 272 } else { 273 HasLoc = true; 274 // Get PC coverage. 275 auto Default = find_if( 276 *Loc, [](const DWARFLocationExpression &L) { return !L.Range; }); 277 if (Default != Loc->end()) { 278 // Assume the entire range is covered by a single location. 279 BytesCovered = BytesInScope; 280 } else { 281 for (auto Entry : *Loc) { 282 uint64_t BytesEntryCovered = Entry.Range->HighPC - Entry.Range->LowPC; 283 BytesCovered += BytesEntryCovered; 284 if (IsEntryValue(Entry.Expr)) 285 BytesEntryValuesCovered += BytesEntryCovered; 286 } 287 } 288 } 289 } 290 291 // Calculate the debug location statistics. 292 if (BytesInScope) { 293 LocStats.NumVarParam++; 294 if (IsParam) 295 LocStats.NumParam++; 296 else if (IsVariable) 297 LocStats.NumVar++; 298 299 collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats, 300 LocStats.ParamLocStats, LocStats.VarLocStats, IsParam, 301 IsVariable); 302 // Non debug entry values coverage statistics. 303 collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope, 304 LocStats.VarParamNonEntryValLocStats, 305 LocStats.ParamNonEntryValLocStats, 306 LocStats.VarNonEntryValLocStats, IsParam, IsVariable); 307 } 308 309 // Collect PC range coverage data. 310 if (DWARFDie D = 311 Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) 312 Die = D; 313 314 std::string VarID = constructDieID(Die, VarPrefix); 315 FnStats.VarsInFunction.insert(VarID); 316 317 if (BytesInScope) { 318 // Turns out we have a lot of ranges that extend past the lexical scope. 319 GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered); 320 GlobalStats.ScopeBytes += BytesInScope; 321 GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered; 322 if (IsParam) { 323 GlobalStats.ParamScopeBytesCovered += 324 std::min(BytesInScope, BytesCovered); 325 GlobalStats.ParamScopeBytes += BytesInScope; 326 GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; 327 } else if (IsVariable) { 328 GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered); 329 GlobalStats.VarScopeBytes += BytesInScope; 330 GlobalStats.VarScopeEntryValueBytesCovered += 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 (IsVariable) { 356 FnStats.NumVars++; 357 if (HasType) 358 FnStats.NumVarTypes++; 359 if (HasSrcLoc) 360 FnStats.NumVarSourceLocations++; 361 if (HasLoc) 362 FnStats.NumVarLocations++; 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, 477 const char *Key, 478 std::vector<unsigned> &LocationStats) { 479 OS << ",\"" << Key << " with 0% of its scope covered\":" 480 << LocationStats[0]; 481 LLVM_DEBUG(llvm::dbgs() << Key << " with 0% of its scope covered: " 482 << LocationStats[0] << '\n'); 483 OS << ",\"" << Key << " with (0%,10%) of its scope covered\":" 484 << LocationStats[1]; 485 LLVM_DEBUG(llvm::dbgs() << Key << " with (0%,10%) of its scope covered: " 486 << LocationStats[1] << '\n'); 487 for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { 488 OS << ",\"" << Key << " with [" << (i - 1) * 10 << "%," << i * 10 489 << "%) of its scope covered\":" << LocationStats[i]; 490 LLVM_DEBUG(llvm::dbgs() 491 << Key << " with [" << (i - 1) * 10 << "%," << i * 10 492 << "%) of its scope covered: " << LocationStats[i]); 493 } 494 OS << ",\"" << Key << " with 100% of its scope covered\":" 495 << LocationStats[NumOfCoverageCategories - 1]; 496 LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: " 497 << LocationStats[NumOfCoverageCategories - 1]); 498 } 499 500 static void printSectionSizes(raw_ostream &OS, const SectionSizes &Sizes) { 501 for (const auto &DebugSec : Sizes.DebugSectionSizes) 502 OS << ",\"size of " << DebugSec.getKey() << "\":" << DebugSec.getValue(); 503 } 504 505 /// \} 506 507 /// Collect debug info quality metrics for an entire DIContext. 508 /// 509 /// Do the impossible and reduce the quality of the debug info down to a few 510 /// numbers. The idea is to condense the data into numbers that can be tracked 511 /// over time to identify trends in newer compiler versions and gauge the effect 512 /// of particular optimizations. The raw numbers themselves are not particularly 513 /// useful, only the delta between compiling the same program with different 514 /// compilers is. 515 bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 516 const Twine &Filename, 517 raw_ostream &OS) { 518 StringRef FormatName = Obj.getFileFormatName(); 519 GlobalStats GlobalStats; 520 LocationStats LocStats; 521 StringMap<PerFunctionStats> Statistics; 522 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) 523 if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) 524 collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, 525 LocStats); 526 527 /// Collect the sizes of debug sections. 528 SectionSizes Sizes; 529 calculateSectionSizes(Obj, Sizes, Filename); 530 531 /// The version number should be increased every time the algorithm is changed 532 /// (including bug fixes). New metrics may be added without increasing the 533 /// version. 534 unsigned Version = 4; 535 unsigned VarParamTotal = 0; 536 unsigned VarParamUnique = 0; 537 unsigned VarParamWithLoc = 0; 538 unsigned NumFunctions = 0; 539 unsigned NumInlinedFunctions = 0; 540 unsigned NumFuncsWithSrcLoc = 0; 541 unsigned NumAbstractOrigins = 0; 542 unsigned ParamTotal = 0; 543 unsigned ParamWithType = 0; 544 unsigned ParamWithLoc = 0; 545 unsigned ParamWithSrcLoc = 0; 546 unsigned VarTotal = 0; 547 unsigned VarWithType = 0; 548 unsigned VarWithSrcLoc = 0; 549 unsigned VarWithLoc = 0; 550 for (auto &Entry : Statistics) { 551 PerFunctionStats &Stats = Entry.getValue(); 552 unsigned TotalVars = Stats.VarsInFunction.size() * 553 (Stats.NumFnInlined + Stats.NumFnOutOfLine); 554 // Count variables in global scope. 555 if (!Stats.IsFunction) 556 TotalVars = Stats.NumVars + Stats.ConstantMembers + Stats.NumArtificial; 557 unsigned Constants = Stats.ConstantMembers; 558 VarParamWithLoc += Stats.TotalVarWithLoc + Constants; 559 VarParamTotal += TotalVars; 560 VarParamUnique += Stats.VarsInFunction.size(); 561 LLVM_DEBUG(for (auto &V 562 : Stats.VarsInFunction) llvm::dbgs() 563 << Entry.getKey() << ": " << V.getKey() << "\n"); 564 NumFunctions += Stats.IsFunction; 565 NumFuncsWithSrcLoc += Stats.HasSourceLocation; 566 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; 567 NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; 568 ParamTotal += Stats.NumParams; 569 ParamWithType += Stats.NumParamTypes; 570 ParamWithLoc += Stats.NumParamLocations; 571 ParamWithSrcLoc += Stats.NumParamSourceLocations; 572 VarTotal += Stats.NumVars; 573 VarWithType += Stats.NumVarTypes; 574 VarWithLoc += Stats.NumVarLocations; 575 VarWithSrcLoc += Stats.NumVarSourceLocations; 576 } 577 578 // Print summary. 579 OS.SetBufferSize(1024); 580 OS << "{\"version\":" << Version; 581 LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; 582 llvm::dbgs() << "---------------------------------\n"); 583 printDatum(OS, "file", Filename.str()); 584 printDatum(OS, "format", FormatName); 585 printDatum(OS, "source functions", NumFunctions); 586 printDatum(OS, "source functions with location", NumFuncsWithSrcLoc); 587 printDatum(OS, "inlined functions", NumInlinedFunctions); 588 printDatum(OS, "inlined funcs with abstract origins", NumAbstractOrigins); 589 printDatum(OS, "unique source variables", VarParamUnique); 590 printDatum(OS, "source variables", VarParamTotal); 591 printDatum(OS, "variables with location", VarParamWithLoc); 592 printDatum(OS, "call site entries", GlobalStats.CallSiteEntries); 593 printDatum(OS, "call site DIEs", GlobalStats.CallSiteDIEs); 594 printDatum(OS, "call site parameter DIEs", GlobalStats.CallSiteParamDIEs); 595 printDatum(OS, "scope bytes total", GlobalStats.ScopeBytes); 596 printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); 597 printDatum(OS, "entry value scope bytes covered", 598 GlobalStats.ScopeEntryValueBytesCovered); 599 printDatum(OS, "formal params scope bytes total", 600 GlobalStats.ParamScopeBytes); 601 printDatum(OS, "formal params scope bytes covered", 602 GlobalStats.ParamScopeBytesCovered); 603 printDatum(OS, "formal params entry value scope bytes covered", 604 GlobalStats.ParamScopeEntryValueBytesCovered); 605 printDatum(OS, "vars scope bytes total", GlobalStats.VarScopeBytes); 606 printDatum(OS, "vars scope bytes covered", GlobalStats.VarScopeBytesCovered); 607 printDatum(OS, "vars entry value scope bytes covered", 608 GlobalStats.VarScopeEntryValueBytesCovered); 609 printDatum(OS, "total function size", GlobalStats.FunctionSize); 610 printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize); 611 printDatum(OS, "total formal params", ParamTotal); 612 printDatum(OS, "formal params with source location", ParamWithSrcLoc); 613 printDatum(OS, "formal params with type", ParamWithType); 614 printDatum(OS, "formal params with binary location", ParamWithLoc); 615 printDatum(OS, "total vars", VarTotal); 616 printDatum(OS, "vars with source location", VarWithSrcLoc); 617 printDatum(OS, "vars with type", VarWithType); 618 printDatum(OS, "vars with binary location", VarWithLoc); 619 printDatum(OS, "total variables procesed by location statistics", 620 LocStats.NumVarParam); 621 printSectionSizes(OS, Sizes); 622 printLocationStats(OS, "variables", LocStats.VarParamLocStats); 623 printLocationStats(OS, "variables (excluding the debug entry values)", 624 LocStats.VarParamNonEntryValLocStats); 625 printDatum(OS, "total params procesed by location statistics", 626 LocStats.NumParam); 627 printLocationStats(OS, "params", LocStats.ParamLocStats); 628 printLocationStats(OS, "params (excluding the debug entry values)", 629 LocStats.ParamNonEntryValLocStats); 630 printDatum(OS, "total vars procesed by location statistics", LocStats.NumVar); 631 printLocationStats(OS, "vars", LocStats.VarLocStats); 632 printLocationStats(OS, "vars (excluding the debug entry values)", 633 LocStats.VarNonEntryValLocStats); 634 OS << "}\n"; 635 LLVM_DEBUG( 636 llvm::dbgs() << "Total Availability: " 637 << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal) 638 << "%\n"; 639 llvm::dbgs() << "PC Ranges covered: " 640 << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) / 641 GlobalStats.ScopeBytes) 642 << "%\n"); 643 return true; 644 } 645