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