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