brintos

brintos / llvm-project-archived public Read only

0
0
Text · 46.3 KiB · a705e7d Raw
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