Path: blob/main/contrib/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp
35231 views
//===-- Statistics.cpp - Debug Info quality metrics -----------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm-dwarfdump.h"9#include "llvm/ADT/DenseMap.h"10#include "llvm/ADT/DenseSet.h"11#include "llvm/ADT/StringSet.h"12#include "llvm/DebugInfo/DWARF/DWARFContext.h"13#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"14#include "llvm/DebugInfo/DWARF/DWARFExpression.h"15#include "llvm/Object/ObjectFile.h"16#include "llvm/Support/JSON.h"1718#define DEBUG_TYPE "dwarfdump"19using namespace llvm;20using namespace llvm::dwarfdump;21using namespace llvm::object;2223namespace {24/// This represents the number of categories of debug location coverage being25/// calculated. The first category is the number of variables with 0% location26/// coverage, but the last category is the number of variables with 100%27/// location coverage.28constexpr int NumOfCoverageCategories = 12;2930/// This is used for zero location coverage bucket.31constexpr unsigned ZeroCoverageBucket = 0;3233/// The UINT64_MAX is used as an indication of the overflow.34constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();3536/// This represents variables DIE offsets.37using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;38/// This maps function DIE offset to its variables.39using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;40/// This represents function DIE offsets containing an abstract_origin.41using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;4243/// This represents a data type for the stats and it helps us to44/// detect an overflow.45/// NOTE: This can be implemented as a template if there is an another type46/// needing this.47struct SaturatingUINT64 {48/// Number that represents the stats.49uint64_t Value;5051SaturatingUINT64(uint64_t Value_) : Value(Value_) {}5253void operator++(int) { return *this += 1; }54void operator+=(uint64_t Value_) {55if (Value != OverflowValue) {56if (Value < OverflowValue - Value_)57Value += Value_;58else59Value = OverflowValue;60}61}62};6364/// Utility struct to store the full location of a DIE - its CU and offset.65struct DIELocation {66DWARFUnit *DwUnit;67uint64_t DIEOffset;68DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)69: DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}70};71/// This represents DWARF locations of CrossCU referencing DIEs.72using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;7374/// This maps function DIE offset to its DWARF CU.75using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;7677/// Holds statistics for one function (or other entity that has a PC range and78/// contains variables, such as a compile unit).79struct PerFunctionStats {80/// Number of inlined instances of this function.81uint64_t NumFnInlined = 0;82/// Number of out-of-line instances of this function.83uint64_t NumFnOutOfLine = 0;84/// Number of inlined instances that have abstract origins.85uint64_t NumAbstractOrigins = 0;86/// Number of variables and parameters with location across all inlined87/// instances.88uint64_t TotalVarWithLoc = 0;89/// Number of constants with location across all inlined instances.90uint64_t ConstantMembers = 0;91/// Number of arificial variables, parameters or members across all instances.92uint64_t NumArtificial = 0;93/// List of all Variables and parameters in this function.94StringSet<> VarsInFunction;95/// Compile units also cover a PC range, but have this flag set to false.96bool IsFunction = false;97/// Function has source location information.98bool HasSourceLocation = false;99/// Number of function parameters.100uint64_t NumParams = 0;101/// Number of function parameters with source location.102uint64_t NumParamSourceLocations = 0;103/// Number of function parameters with type.104uint64_t NumParamTypes = 0;105/// Number of function parameters with a DW_AT_location.106uint64_t NumParamLocations = 0;107/// Number of local variables.108uint64_t NumLocalVars = 0;109/// Number of local variables with source location.110uint64_t NumLocalVarSourceLocations = 0;111/// Number of local variables with type.112uint64_t NumLocalVarTypes = 0;113/// Number of local variables with DW_AT_location.114uint64_t NumLocalVarLocations = 0;115};116117/// Holds accumulated global statistics about DIEs.118struct GlobalStats {119/// Total number of PC range bytes covered by DW_AT_locations.120SaturatingUINT64 TotalBytesCovered = 0;121/// Total number of parent DIE PC range bytes covered by DW_AT_Locations.122SaturatingUINT64 ScopeBytesCovered = 0;123/// Total number of PC range bytes in each variable's enclosing scope.124SaturatingUINT64 ScopeBytes = 0;125/// Total number of PC range bytes covered by DW_AT_locations with126/// the debug entry values (DW_OP_entry_value).127SaturatingUINT64 ScopeEntryValueBytesCovered = 0;128/// Total number of PC range bytes covered by DW_AT_locations of129/// formal parameters.130SaturatingUINT64 ParamScopeBytesCovered = 0;131/// Total number of PC range bytes in each parameter's enclosing scope.132SaturatingUINT64 ParamScopeBytes = 0;133/// Total number of PC range bytes covered by DW_AT_locations with134/// the debug entry values (DW_OP_entry_value) (only for parameters).135SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;136/// Total number of PC range bytes covered by DW_AT_locations (only for local137/// variables).138SaturatingUINT64 LocalVarScopeBytesCovered = 0;139/// Total number of PC range bytes in each local variable's enclosing scope.140SaturatingUINT64 LocalVarScopeBytes = 0;141/// Total number of PC range bytes covered by DW_AT_locations with142/// the debug entry values (DW_OP_entry_value) (only for local variables).143SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;144/// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).145SaturatingUINT64 CallSiteEntries = 0;146/// Total number of call site DIEs (DW_TAG_call_site).147SaturatingUINT64 CallSiteDIEs = 0;148/// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).149SaturatingUINT64 CallSiteParamDIEs = 0;150/// Total byte size of concrete functions. This byte size includes151/// inline functions contained in the concrete functions.152SaturatingUINT64 FunctionSize = 0;153/// Total byte size of inlined functions. This is the total number of bytes154/// for the top inline functions within concrete functions. This can help155/// tune the inline settings when compiling to match user expectations.156SaturatingUINT64 InlineFunctionSize = 0;157};158159/// Holds accumulated debug location statistics about local variables and160/// formal parameters.161struct LocationStats {162/// Map the scope coverage decile to the number of variables in the decile.163/// The first element of the array (at the index zero) represents the number164/// of variables with the no debug location at all, but the last element165/// in the vector represents the number of fully covered variables within166/// its scope.167std::vector<SaturatingUINT64> VarParamLocStats{168std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};169/// Map non debug entry values coverage.170std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{171std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};172/// The debug location statistics for formal parameters.173std::vector<SaturatingUINT64> ParamLocStats{174std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};175/// Map non debug entry values coverage for formal parameters.176std::vector<SaturatingUINT64> ParamNonEntryValLocStats{177std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};178/// The debug location statistics for local variables.179std::vector<SaturatingUINT64> LocalVarLocStats{180std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};181/// Map non debug entry values coverage for local variables.182std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{183std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};184/// Total number of local variables and function parameters processed.185SaturatingUINT64 NumVarParam = 0;186/// Total number of formal parameters processed.187SaturatingUINT64 NumParam = 0;188/// Total number of local variables processed.189SaturatingUINT64 NumVar = 0;190};191192/// Holds accumulated debug line statistics across all CUs.193struct LineStats {194SaturatingUINT64 NumBytes = 0;195SaturatingUINT64 NumLineZeroBytes = 0;196SaturatingUINT64 NumEntries = 0;197SaturatingUINT64 NumIsStmtEntries = 0;198SaturatingUINT64 NumUniqueEntries = 0;199SaturatingUINT64 NumUniqueNonZeroEntries = 0;200};201} // namespace202203/// Collect debug location statistics for one DIE.204static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,205std::vector<SaturatingUINT64> &VarParamLocStats,206std::vector<SaturatingUINT64> &ParamLocStats,207std::vector<SaturatingUINT64> &LocalVarLocStats,208bool IsParam, bool IsLocalVar) {209auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {210// No debug location at all for the variable.211if (ScopeBytesCovered == 0)212return 0;213// Fully covered variable within its scope.214if (ScopeBytesCovered >= BytesInScope)215return NumOfCoverageCategories - 1;216// Get covered range (e.g. 20%-29%).217unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;218LocBucket /= 10;219return LocBucket + 1;220};221222unsigned CoverageBucket = getCoverageBucket();223224VarParamLocStats[CoverageBucket].Value++;225if (IsParam)226ParamLocStats[CoverageBucket].Value++;227else if (IsLocalVar)228LocalVarLocStats[CoverageBucket].Value++;229}230231/// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName232/// and DeclLine. The identifier aims to be unique for any unique entities,233/// but keeping the same among different instances of the same entity.234static std::string constructDieID(DWARFDie Die,235StringRef Prefix = StringRef()) {236std::string IDStr;237llvm::raw_string_ostream ID(IDStr);238ID << Prefix239<< Die.getName(DINameKind::LinkageName);240241// Prefix + Name is enough for local variables and parameters.242if (!Prefix.empty() && Prefix != "g")243return ID.str();244245auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);246std::string File;247if (DeclFile) {248DWARFUnit *U = Die.getDwarfUnit();249if (const auto *LT = U->getContext().getLineTableForUnit(U))250if (LT->getFileNameByIndex(251dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),252DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))253File = std::string(sys::path::filename(File));254}255ID << ":" << (File.empty() ? "/" : File);256ID << ":"257<< dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);258return ID.str();259}260261/// Return the number of bytes in the overlap of ranges A and B.262static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {263uint64_t Lower = std::max(A.LowPC, B.LowPC);264uint64_t Upper = std::min(A.HighPC, B.HighPC);265if (Lower >= Upper)266return 0;267return Upper - Lower;268}269270/// Collect debug info quality metrics for one DIE.271static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,272const std::string &VarPrefix,273uint64_t BytesInScope, uint32_t InlineDepth,274StringMap<PerFunctionStats> &FnStatMap,275GlobalStats &GlobalStats,276LocationStats &LocStats,277AbstractOriginVarsTy *AbstractOriginVariables) {278const dwarf::Tag Tag = Die.getTag();279// Skip CU node.280if (Tag == dwarf::DW_TAG_compile_unit)281return;282283bool HasLoc = false;284bool HasSrcLoc = false;285bool HasType = false;286uint64_t TotalBytesCovered = 0;287uint64_t ScopeBytesCovered = 0;288uint64_t BytesEntryValuesCovered = 0;289auto &FnStats = FnStatMap[FnPrefix];290bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;291bool IsLocalVar = Tag == dwarf::DW_TAG_variable;292bool IsConstantMember = Tag == dwarf::DW_TAG_member &&293Die.find(dwarf::DW_AT_const_value);294295// For zero covered inlined variables the locstats will be296// calculated later.297bool DeferLocStats = false;298299if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {300GlobalStats.CallSiteDIEs++;301return;302}303304if (Tag == dwarf::DW_TAG_call_site_parameter ||305Tag == dwarf::DW_TAG_GNU_call_site_parameter) {306GlobalStats.CallSiteParamDIEs++;307return;308}309310if (!IsParam && !IsLocalVar && !IsConstantMember) {311// Not a variable or constant member.312return;313}314315// Ignore declarations of global variables.316if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))317return;318319if (Die.findRecursively(dwarf::DW_AT_decl_file) &&320Die.findRecursively(dwarf::DW_AT_decl_line))321HasSrcLoc = true;322323if (Die.findRecursively(dwarf::DW_AT_type))324HasType = true;325326if (Die.find(dwarf::DW_AT_abstract_origin)) {327if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {328if (AbstractOriginVariables) {329auto Offset = Die.find(dwarf::DW_AT_abstract_origin);330// Do not track this variable any more, since it has location331// coverage.332llvm::erase(*AbstractOriginVariables, (*Offset).getRawUValue());333}334} else {335// The locstats will be handled at the end of336// the collectStatsRecursive().337DeferLocStats = true;338}339}340341auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {342DWARFUnit *U = Die.getDwarfUnit();343DataExtractor Data(toStringRef(D),344Die.getDwarfUnit()->getContext().isLittleEndian(), 0);345DWARFExpression Expression(Data, U->getAddressByteSize(),346U->getFormParams().Format);347// Consider the expression containing the DW_OP_entry_value as348// an entry value.349return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {350return Op.getCode() == dwarf::DW_OP_entry_value ||351Op.getCode() == dwarf::DW_OP_GNU_entry_value;352});353};354355if (Die.find(dwarf::DW_AT_const_value)) {356// This catches constant members *and* variables.357HasLoc = true;358ScopeBytesCovered = BytesInScope;359TotalBytesCovered = BytesInScope;360} else {361// Handle variables and function arguments.362Expected<std::vector<DWARFLocationExpression>> Loc =363Die.getLocations(dwarf::DW_AT_location);364if (!Loc) {365consumeError(Loc.takeError());366} else {367HasLoc = true;368// Get PC coverage.369auto Default = find_if(370*Loc, [](const DWARFLocationExpression &L) { return !L.Range; });371if (Default != Loc->end()) {372// Assume the entire range is covered by a single location.373ScopeBytesCovered = BytesInScope;374TotalBytesCovered = BytesInScope;375} else {376// Caller checks this Expected result already, it cannot fail.377auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());378for (auto Entry : *Loc) {379TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;380uint64_t ScopeBytesCoveredByEntry = 0;381// Calculate how many bytes of the parent scope this entry covers.382// FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The383// address ranges defined by the bounded location descriptions of a384// location list may overlap". So in theory a variable can have385// multiple simultaneous locations, which would make this calculation386// misleading because we will count the overlapped areas387// twice. However, clang does not currently emit DWARF like this.388for (DWARFAddressRange R : ScopeRanges) {389ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);390}391ScopeBytesCovered += ScopeBytesCoveredByEntry;392if (IsEntryValue(Entry.Expr))393BytesEntryValuesCovered += ScopeBytesCoveredByEntry;394}395}396}397}398399// Calculate the debug location statistics.400if (BytesInScope && !DeferLocStats) {401LocStats.NumVarParam.Value++;402if (IsParam)403LocStats.NumParam.Value++;404else if (IsLocalVar)405LocStats.NumVar.Value++;406407collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,408LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,409IsLocalVar);410// Non debug entry values coverage statistics.411collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,412LocStats.VarParamNonEntryValLocStats,413LocStats.ParamNonEntryValLocStats,414LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);415}416417// Collect PC range coverage data.418if (DWARFDie D =419Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))420Die = D;421422std::string VarID = constructDieID(Die, VarPrefix);423FnStats.VarsInFunction.insert(VarID);424425GlobalStats.TotalBytesCovered += TotalBytesCovered;426if (BytesInScope) {427GlobalStats.ScopeBytesCovered += ScopeBytesCovered;428GlobalStats.ScopeBytes += BytesInScope;429GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;430if (IsParam) {431GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;432GlobalStats.ParamScopeBytes += BytesInScope;433GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;434} else if (IsLocalVar) {435GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;436GlobalStats.LocalVarScopeBytes += BytesInScope;437GlobalStats.LocalVarScopeEntryValueBytesCovered +=438BytesEntryValuesCovered;439}440assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);441}442443if (IsConstantMember) {444FnStats.ConstantMembers++;445return;446}447448FnStats.TotalVarWithLoc += (unsigned)HasLoc;449450if (Die.find(dwarf::DW_AT_artificial)) {451FnStats.NumArtificial++;452return;453}454455if (IsParam) {456FnStats.NumParams++;457if (HasType)458FnStats.NumParamTypes++;459if (HasSrcLoc)460FnStats.NumParamSourceLocations++;461if (HasLoc)462FnStats.NumParamLocations++;463} else if (IsLocalVar) {464FnStats.NumLocalVars++;465if (HasType)466FnStats.NumLocalVarTypes++;467if (HasSrcLoc)468FnStats.NumLocalVarSourceLocations++;469if (HasLoc)470FnStats.NumLocalVarLocations++;471}472}473474/// Recursively collect variables from subprogram with DW_AT_inline attribute.475static void collectAbstractOriginFnInfo(476DWARFDie Die, uint64_t SPOffset,477AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,478AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {479DWARFDie Child = Die.getFirstChild();480while (Child) {481const dwarf::Tag ChildTag = Child.getTag();482if (ChildTag == dwarf::DW_TAG_formal_parameter ||483ChildTag == dwarf::DW_TAG_variable) {484GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());485LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());486} else if (ChildTag == dwarf::DW_TAG_lexical_block)487collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,488LocalAbstractOriginFnInfo);489Child = Child.getSibling();490}491}492493/// Recursively collect debug info quality metrics.494static void collectStatsRecursive(495DWARFDie Die, std::string FnPrefix, std::string VarPrefix,496uint64_t BytesInScope, uint32_t InlineDepth,497StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,498LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,499AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,500AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,501FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,502AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {503// Skip NULL nodes.504if (Die.isNULL())505return;506507const dwarf::Tag Tag = Die.getTag();508// Skip function types.509if (Tag == dwarf::DW_TAG_subroutine_type)510return;511512// Handle any kind of lexical scope.513const bool HasAbstractOrigin =514Die.find(dwarf::DW_AT_abstract_origin) != std::nullopt;515const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;516const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;517const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;518// We want to know how many variables (with abstract_origin) don't have519// location info.520const bool IsCandidateForZeroLocCovTracking =521(IsInlinedFunction || (IsFunction && HasAbstractOrigin));522523AbstractOriginVarsTy AbstractOriginVars;524525// Get the vars of the inlined fn, so the locstats526// reports the missing vars (with coverage 0%).527if (IsCandidateForZeroLocCovTracking) {528auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);529if (OffsetFn) {530uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();531if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {532AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];533AbstractOriginVarsPtr = &AbstractOriginVars;534} else {535// This means that the DW_AT_inline fn copy is out of order536// or that the abstract_origin references another CU,537// so this abstract origin instance will be processed later.538FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());539AbstractOriginVarsPtr = nullptr;540}541}542}543544if (IsFunction || IsInlinedFunction || IsBlock) {545// Reset VarPrefix when entering a new function.546if (IsFunction || IsInlinedFunction)547VarPrefix = "v";548549// Ignore forward declarations.550if (Die.find(dwarf::DW_AT_declaration))551return;552553// Check for call sites.554if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))555GlobalStats.CallSiteEntries++;556557// PC Ranges.558auto RangesOrError = Die.getAddressRanges();559if (!RangesOrError) {560llvm::consumeError(RangesOrError.takeError());561return;562}563564auto Ranges = RangesOrError.get();565uint64_t BytesInThisScope = 0;566for (auto Range : Ranges)567BytesInThisScope += Range.HighPC - Range.LowPC;568569// Count the function.570if (!IsBlock) {571// Skip over abstract origins, but collect variables572// from it so it can be used for location statistics573// for inlined instancies.574if (Die.find(dwarf::DW_AT_inline)) {575uint64_t SPOffset = Die.getOffset();576AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();577collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,578LocalAbstractOriginFnInfo);579return;580}581582std::string FnID = constructDieID(Die);583// We've seen an instance of this function.584auto &FnStats = FnStatMap[FnID];585FnStats.IsFunction = true;586if (IsInlinedFunction) {587FnStats.NumFnInlined++;588if (Die.findRecursively(dwarf::DW_AT_abstract_origin))589FnStats.NumAbstractOrigins++;590} else {591FnStats.NumFnOutOfLine++;592}593if (Die.findRecursively(dwarf::DW_AT_decl_file) &&594Die.findRecursively(dwarf::DW_AT_decl_line))595FnStats.HasSourceLocation = true;596// Update function prefix.597FnPrefix = FnID;598}599600if (BytesInThisScope) {601BytesInScope = BytesInThisScope;602if (IsFunction)603GlobalStats.FunctionSize += BytesInThisScope;604else if (IsInlinedFunction && InlineDepth == 0)605GlobalStats.InlineFunctionSize += BytesInThisScope;606}607} else {608// Not a scope, visit the Die itself. It could be a variable.609collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,610FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);611}612613// Set InlineDepth correctly for child recursion614if (IsFunction)615InlineDepth = 0;616else if (IsInlinedFunction)617++InlineDepth;618619// Traverse children.620unsigned LexicalBlockIndex = 0;621unsigned FormalParameterIndex = 0;622DWARFDie Child = Die.getFirstChild();623while (Child) {624std::string ChildVarPrefix = VarPrefix;625if (Child.getTag() == dwarf::DW_TAG_lexical_block)626ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';627if (Child.getTag() == dwarf::DW_TAG_formal_parameter)628ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';629630collectStatsRecursive(631Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,632GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,633LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,634AbstractOriginVarsPtr);635Child = Child.getSibling();636}637638if (!IsCandidateForZeroLocCovTracking)639return;640641// After we have processed all vars of the inlined function (or function with642// an abstract_origin), we want to know how many variables have no location.643for (auto Offset : AbstractOriginVars) {644LocStats.NumVarParam++;645LocStats.VarParamLocStats[ZeroCoverageBucket]++;646auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);647if (!FnDie)648continue;649auto Tag = FnDie.getTag();650if (Tag == dwarf::DW_TAG_formal_parameter) {651LocStats.NumParam++;652LocStats.ParamLocStats[ZeroCoverageBucket]++;653} else if (Tag == dwarf::DW_TAG_variable) {654LocStats.NumVar++;655LocStats.LocalVarLocStats[ZeroCoverageBucket]++;656}657}658}659660/// Print human-readable output.661/// \{662static void printDatum(json::OStream &J, const char *Key, json::Value Value) {663if (Value == OverflowValue)664J.attribute(Key, "overflowed");665else666J.attribute(Key, Value);667668LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');669}670671static void printLocationStats(json::OStream &J, const char *Key,672std::vector<SaturatingUINT64> &LocationStats) {673if (LocationStats[0].Value == OverflowValue)674J.attribute((Twine(Key) +675" with (0%,10%) of parent scope covered by DW_AT_location")676.str(),677"overflowed");678else679J.attribute(680(Twine(Key) + " with 0% of parent scope covered by DW_AT_location")681.str(),682LocationStats[0].Value);683LLVM_DEBUG(684llvm::dbgs() << Key685<< " with 0% of parent scope covered by DW_AT_location: \\"686<< LocationStats[0].Value << '\n');687688if (LocationStats[1].Value == OverflowValue)689J.attribute((Twine(Key) +690" with (0%,10%) of parent scope covered by DW_AT_location")691.str(),692"overflowed");693else694J.attribute((Twine(Key) +695" with (0%,10%) of parent scope covered by DW_AT_location")696.str(),697LocationStats[1].Value);698LLVM_DEBUG(llvm::dbgs()699<< Key700<< " with (0%,10%) of parent scope covered by DW_AT_location: "701<< LocationStats[1].Value << '\n');702703for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {704if (LocationStats[i].Value == OverflowValue)705J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +706Twine(i * 10) +707"%) of parent scope covered by DW_AT_location")708.str(),709"overflowed");710else711J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +712Twine(i * 10) +713"%) of parent scope covered by DW_AT_location")714.str(),715LocationStats[i].Value);716LLVM_DEBUG(llvm::dbgs()717<< Key << " with [" << (i - 1) * 10 << "%," << i * 10718<< "%) of parent scope covered by DW_AT_location: "719<< LocationStats[i].Value);720}721if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)722J.attribute(723(Twine(Key) + " with 100% of parent scope covered by DW_AT_location")724.str(),725"overflowed");726else727J.attribute(728(Twine(Key) + " with 100% of parent scope covered by DW_AT_location")729.str(),730LocationStats[NumOfCoverageCategories - 1].Value);731LLVM_DEBUG(732llvm::dbgs() << Key733<< " with 100% of parent scope covered by DW_AT_location: "734<< LocationStats[NumOfCoverageCategories - 1].Value);735}736737static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {738for (const auto &It : Sizes.DebugSectionSizes)739J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));740}741742/// Stop tracking variables that contain abstract_origin with a location.743/// This is used for out-of-order DW_AT_inline subprograms only.744static void updateVarsWithAbstractOriginLocCovInfo(745DWARFDie FnDieWithAbstractOrigin,746AbstractOriginVarsTy &AbstractOriginVars) {747DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();748while (Child) {749const dwarf::Tag ChildTag = Child.getTag();750if ((ChildTag == dwarf::DW_TAG_formal_parameter ||751ChildTag == dwarf::DW_TAG_variable) &&752(Child.find(dwarf::DW_AT_location) ||753Child.find(dwarf::DW_AT_const_value))) {754auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);755if (OffsetVar)756llvm::erase(AbstractOriginVars, (*OffsetVar).getRawUValue());757} else if (ChildTag == dwarf::DW_TAG_lexical_block)758updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);759Child = Child.getSibling();760}761}762763/// Collect zero location coverage for inlined variables which refer to764/// a DW_AT_inline copy of subprogram that is out of order in the DWARF.765/// Also cover the variables of a concrete function (represented with766/// the DW_TAG_subprogram) with an abstract_origin attribute.767static void collectZeroLocCovForVarsWithAbstractOrigin(768DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,769AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,770FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {771// The next variable is used to filter out functions that have been processed,772// leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.773FunctionsWithAbstractOriginTy ProcessedFns;774for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {775DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);776auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);777AbstractOriginVarsTy AbstractOriginVars;778if (!FnCopy)779continue;780uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();781// If there is no entry within LocalAbstractOriginFnInfo for the given782// FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have783// CrossCU referencing.784if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))785continue;786AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];787updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,788AbstractOriginVars);789790for (auto Offset : AbstractOriginVars) {791LocStats.NumVarParam++;792LocStats.VarParamLocStats[ZeroCoverageBucket]++;793auto Tag = DwUnit->getDIEForOffset(Offset).getTag();794if (Tag == dwarf::DW_TAG_formal_parameter) {795LocStats.NumParam++;796LocStats.ParamLocStats[ZeroCoverageBucket]++;797} else if (Tag == dwarf::DW_TAG_variable) {798LocStats.NumVar++;799LocStats.LocalVarLocStats[ZeroCoverageBucket]++;800}801}802ProcessedFns.push_back(FnOffset);803}804for (auto ProcessedFn : ProcessedFns)805llvm::erase(FnsWithAbstractOriginToBeProcessed, ProcessedFn);806}807808/// Collect zero location coverage for inlined variables which refer to809/// a DW_AT_inline copy of subprogram that is in a different CU.810static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(811LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,812AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,813CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {814for (const auto &CrossCUReferenceToBeResolved :815CrossCUReferencesToBeResolved) {816DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;817DWARFDie FnDIEWithCrossCUReferencing =818DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);819auto FnCopy =820FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);821if (!FnCopy)822continue;823uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();824AbstractOriginVarsTy AbstractOriginVars =825GlobalAbstractOriginFnInfo[FnCopyRawUValue];826updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,827AbstractOriginVars);828for (auto Offset : AbstractOriginVars) {829LocStats.NumVarParam++;830LocStats.VarParamLocStats[ZeroCoverageBucket]++;831auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])832->getDIEForOffset(Offset)833.getTag();834if (Tag == dwarf::DW_TAG_formal_parameter) {835LocStats.NumParam++;836LocStats.ParamLocStats[ZeroCoverageBucket]++;837} else if (Tag == dwarf::DW_TAG_variable) {838LocStats.NumVar++;839LocStats.LocalVarLocStats[ZeroCoverageBucket]++;840}841}842}843}844845/// \}846847/// Collect debug info quality metrics for an entire DIContext.848///849/// Do the impossible and reduce the quality of the debug info down to a few850/// numbers. The idea is to condense the data into numbers that can be tracked851/// over time to identify trends in newer compiler versions and gauge the effect852/// of particular optimizations. The raw numbers themselves are not particularly853/// useful, only the delta between compiling the same program with different854/// compilers is.855bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,856const Twine &Filename,857raw_ostream &OS) {858StringRef FormatName = Obj.getFileFormatName();859GlobalStats GlobalStats;860LocationStats LocStats;861LineStats LnStats;862StringMap<PerFunctionStats> Statistics;863// This variable holds variable information for functions with864// abstract_origin globally, across all CUs.865AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;866// This variable holds information about the CU of a function with867// abstract_origin.868FunctionDIECUTyMap AbstractOriginFnCUs;869CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;870// Tuple representing a single source code position in the line table. Fields871// are respectively: Line, Col, File, where 'File' is an index into the Files872// vector below.873using LineTuple = std::tuple<uint32_t, uint16_t, uint16_t>;874SmallVector<std::string> Files;875DenseSet<LineTuple> UniqueLines;876DenseSet<LineTuple> UniqueNonZeroLines;877878for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {879if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {880// This variable holds variable information for functions with881// abstract_origin, but just for the current CU.882AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;883FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;884885collectStatsRecursive(886CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,887AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,888LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);889890// collectZeroLocCovForVarsWithAbstractOrigin will filter out all891// out-of-order DWARF functions that have been processed within it,892// leaving FnsWithAbstractOriginToBeProcessed with only CrossCU893// references.894collectZeroLocCovForVarsWithAbstractOrigin(895CUDie.getDwarfUnit(), GlobalStats, LocStats,896LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);897898// Collect all CrossCU references into CrossCUReferencesToBeResolved.899for (auto CrossCUReferencingDIEOffset :900FnsWithAbstractOriginToBeProcessed)901CrossCUReferencesToBeResolved.push_back(902DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));903}904const auto *LineTable = DICtx.getLineTableForUnit(CU.get());905std::optional<uint64_t> LastFileIdxOpt;906if (LineTable)907LastFileIdxOpt = LineTable->getLastValidFileIndex();908if (LastFileIdxOpt) {909// Each CU has its own file index; in order to track unique line entries910// across CUs, we therefore need to map each CU file index to a global911// file index, which we store here.912DenseMap<uint64_t, uint16_t> CUFileMapping;913for (uint64_t FileIdx = 0; FileIdx <= *LastFileIdxOpt; ++FileIdx) {914std::string File;915if (LineTable->getFileNameByIndex(916FileIdx, CU->getCompilationDir(),917DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,918File)) {919auto ExistingFile = llvm::find(Files, File);920if (ExistingFile != Files.end()) {921CUFileMapping[FileIdx] = std::distance(Files.begin(), ExistingFile);922} else {923CUFileMapping[FileIdx] = Files.size();924Files.push_back(File);925}926}927}928for (const auto &Seq : LineTable->Sequences) {929LnStats.NumBytes += Seq.HighPC - Seq.LowPC;930// Ignore the `end_sequence` entry, since it's not interesting for us.931LnStats.NumEntries += Seq.LastRowIndex - Seq.FirstRowIndex - 1;932for (size_t RowIdx = Seq.FirstRowIndex; RowIdx < Seq.LastRowIndex - 1;933++RowIdx) {934auto Entry = LineTable->Rows[RowIdx];935if (Entry.IsStmt)936LnStats.NumIsStmtEntries += 1;937assert(CUFileMapping.contains(Entry.File) &&938"Should have been collected earlier!");939uint16_t MappedFile = CUFileMapping[Entry.File];940UniqueLines.insert({Entry.Line, Entry.Column, MappedFile});941if (Entry.Line != 0) {942UniqueNonZeroLines.insert({Entry.Line, Entry.Column, MappedFile});943} else {944auto EntryStartAddress = Entry.Address.Address;945auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address;946LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress;947}948}949}950}951}952953LnStats.NumUniqueEntries = UniqueLines.size();954LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size();955956/// Resolve CrossCU references.957collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(958LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,959CrossCUReferencesToBeResolved);960961/// Collect the sizes of debug sections.962SectionSizes Sizes;963calculateSectionSizes(Obj, Sizes, Filename);964965/// The version number should be increased every time the algorithm is changed966/// (including bug fixes). New metrics may be added without increasing the967/// version.968unsigned Version = 9;969SaturatingUINT64 VarParamTotal = 0;970SaturatingUINT64 VarParamUnique = 0;971SaturatingUINT64 VarParamWithLoc = 0;972SaturatingUINT64 NumFunctions = 0;973SaturatingUINT64 NumInlinedFunctions = 0;974SaturatingUINT64 NumFuncsWithSrcLoc = 0;975SaturatingUINT64 NumAbstractOrigins = 0;976SaturatingUINT64 ParamTotal = 0;977SaturatingUINT64 ParamWithType = 0;978SaturatingUINT64 ParamWithLoc = 0;979SaturatingUINT64 ParamWithSrcLoc = 0;980SaturatingUINT64 LocalVarTotal = 0;981SaturatingUINT64 LocalVarWithType = 0;982SaturatingUINT64 LocalVarWithSrcLoc = 0;983SaturatingUINT64 LocalVarWithLoc = 0;984for (auto &Entry : Statistics) {985PerFunctionStats &Stats = Entry.getValue();986uint64_t TotalVars = Stats.VarsInFunction.size() *987(Stats.NumFnInlined + Stats.NumFnOutOfLine);988// Count variables in global scope.989if (!Stats.IsFunction)990TotalVars =991Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;992uint64_t Constants = Stats.ConstantMembers;993VarParamWithLoc += Stats.TotalVarWithLoc + Constants;994VarParamTotal += TotalVars;995VarParamUnique += Stats.VarsInFunction.size();996LLVM_DEBUG(for (auto &V997: Stats.VarsInFunction) llvm::dbgs()998<< Entry.getKey() << ": " << V.getKey() << "\n");999NumFunctions += Stats.IsFunction;1000NumFuncsWithSrcLoc += Stats.HasSourceLocation;1001NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;1002NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;1003ParamTotal += Stats.NumParams;1004ParamWithType += Stats.NumParamTypes;1005ParamWithLoc += Stats.NumParamLocations;1006ParamWithSrcLoc += Stats.NumParamSourceLocations;1007LocalVarTotal += Stats.NumLocalVars;1008LocalVarWithType += Stats.NumLocalVarTypes;1009LocalVarWithLoc += Stats.NumLocalVarLocations;1010LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;1011}10121013// Print summary.1014OS.SetBufferSize(1024);1015json::OStream J(OS, 2);1016J.objectBegin();1017J.attribute("version", Version);1018LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";1019llvm::dbgs() << "---------------------------------\n");10201021printDatum(J, "file", Filename.str());1022printDatum(J, "format", FormatName);10231024printDatum(J, "#functions", NumFunctions.Value);1025printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);1026printDatum(J, "#inlined functions", NumInlinedFunctions.Value);1027printDatum(J, "#inlined functions with abstract origins",1028NumAbstractOrigins.Value);10291030// This includes local variables and formal parameters.1031printDatum(J, "#unique source variables", VarParamUnique.Value);1032printDatum(J, "#source variables", VarParamTotal.Value);1033printDatum(J, "#source variables with location", VarParamWithLoc.Value);10341035printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);1036printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);1037printDatum(J, "#call site parameter DIEs",1038GlobalStats.CallSiteParamDIEs.Value);10391040printDatum(J, "sum_all_variables(#bytes in parent scope)",1041GlobalStats.ScopeBytes.Value);1042printDatum(J,1043"sum_all_variables(#bytes in any scope covered by DW_AT_location)",1044GlobalStats.TotalBytesCovered.Value);1045printDatum(J,1046"sum_all_variables(#bytes in parent scope covered by "1047"DW_AT_location)",1048GlobalStats.ScopeBytesCovered.Value);1049printDatum(J,1050"sum_all_variables(#bytes in parent scope covered by "1051"DW_OP_entry_value)",1052GlobalStats.ScopeEntryValueBytesCovered.Value);10531054printDatum(J, "sum_all_params(#bytes in parent scope)",1055GlobalStats.ParamScopeBytes.Value);1056printDatum(J,1057"sum_all_params(#bytes in parent scope covered by DW_AT_location)",1058GlobalStats.ParamScopeBytesCovered.Value);1059printDatum(J,1060"sum_all_params(#bytes in parent scope covered by "1061"DW_OP_entry_value)",1062GlobalStats.ParamScopeEntryValueBytesCovered.Value);10631064printDatum(J, "sum_all_local_vars(#bytes in parent scope)",1065GlobalStats.LocalVarScopeBytes.Value);1066printDatum(J,1067"sum_all_local_vars(#bytes in parent scope covered by "1068"DW_AT_location)",1069GlobalStats.LocalVarScopeBytesCovered.Value);1070printDatum(J,1071"sum_all_local_vars(#bytes in parent scope covered by "1072"DW_OP_entry_value)",1073GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);10741075printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);1076printDatum(J, "#bytes within inlined functions",1077GlobalStats.InlineFunctionSize.Value);10781079// Print the summary for formal parameters.1080printDatum(J, "#params", ParamTotal.Value);1081printDatum(J, "#params with source location", ParamWithSrcLoc.Value);1082printDatum(J, "#params with type", ParamWithType.Value);1083printDatum(J, "#params with binary location", ParamWithLoc.Value);10841085// Print the summary for local variables.1086printDatum(J, "#local vars", LocalVarTotal.Value);1087printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);1088printDatum(J, "#local vars with type", LocalVarWithType.Value);1089printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);10901091// Print the debug section sizes.1092printSectionSizes(J, Sizes);10931094// Print the location statistics for variables (includes local variables1095// and formal parameters).1096printDatum(J, "#variables processed by location statistics",1097LocStats.NumVarParam.Value);1098printLocationStats(J, "#variables", LocStats.VarParamLocStats);1099printLocationStats(J, "#variables - entry values",1100LocStats.VarParamNonEntryValLocStats);11011102// Print the location statistics for formal parameters.1103printDatum(J, "#params processed by location statistics",1104LocStats.NumParam.Value);1105printLocationStats(J, "#params", LocStats.ParamLocStats);1106printLocationStats(J, "#params - entry values",1107LocStats.ParamNonEntryValLocStats);11081109// Print the location statistics for local variables.1110printDatum(J, "#local vars processed by location statistics",1111LocStats.NumVar.Value);1112printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);1113printLocationStats(J, "#local vars - entry values",1114LocStats.LocalVarNonEntryValLocStats);11151116// Print line statistics for the object file.1117printDatum(J, "#bytes with line information", LnStats.NumBytes.Value);1118printDatum(J, "#bytes with line-0 locations", LnStats.NumLineZeroBytes.Value);1119printDatum(J, "#line entries", LnStats.NumEntries.Value);1120printDatum(J, "#line entries (is_stmt)", LnStats.NumIsStmtEntries.Value);1121printDatum(J, "#line entries (unique)", LnStats.NumUniqueEntries.Value);1122printDatum(J, "#line entries (unique non-0)",1123LnStats.NumUniqueNonZeroEntries.Value);11241125J.objectEnd();1126OS << '\n';1127LLVM_DEBUG(1128llvm::dbgs() << "Total Availability: "1129<< (VarParamTotal.Value1130? (int)std::round((VarParamWithLoc.Value * 100.0) /1131VarParamTotal.Value)1132: 0)1133<< "%\n";1134llvm::dbgs() << "PC Ranges covered: "1135<< (GlobalStats.ScopeBytes.Value1136? (int)std::round(1137(GlobalStats.ScopeBytesCovered.Value * 100.0) /1138GlobalStats.ScopeBytes.Value)1139: 0)1140<< "%\n");1141return true;1142}114311441145