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