#include "llvm-dwarfdump.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/JSON.h"
#define DEBUG_TYPE "dwarfdump"
using namespace llvm;
using namespace llvm::dwarfdump;
using namespace llvm::object;
namespace {
constexpr int NumOfCoverageCategories = 12;
constexpr unsigned ZeroCoverageBucket = 0;
constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
struct SaturatingUINT64 {
uint64_t Value;
SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
void operator++(int) { return *this += 1; }
void operator+=(uint64_t Value_) {
if (Value != OverflowValue) {
if (Value < OverflowValue - Value_)
Value += Value_;
else
Value = OverflowValue;
}
}
};
struct DIELocation {
DWARFUnit *DwUnit;
uint64_t DIEOffset;
DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
: DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
};
using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
struct PerFunctionStats {
uint64_t NumFnInlined = 0;
uint64_t NumFnOutOfLine = 0;
uint64_t NumAbstractOrigins = 0;
uint64_t TotalVarWithLoc = 0;
uint64_t ConstantMembers = 0;
uint64_t NumArtificial = 0;
StringSet<> VarsInFunction;
bool IsFunction = false;
bool HasSourceLocation = false;
uint64_t NumParams = 0;
uint64_t NumParamSourceLocations = 0;
uint64_t NumParamTypes = 0;
uint64_t NumParamLocations = 0;
uint64_t NumLocalVars = 0;
uint64_t NumLocalVarSourceLocations = 0;
uint64_t NumLocalVarTypes = 0;
uint64_t NumLocalVarLocations = 0;
};
struct GlobalStats {
SaturatingUINT64 TotalBytesCovered = 0;
SaturatingUINT64 ScopeBytesCovered = 0;
SaturatingUINT64 ScopeBytes = 0;
SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
SaturatingUINT64 ParamScopeBytesCovered = 0;
SaturatingUINT64 ParamScopeBytes = 0;
SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
SaturatingUINT64 LocalVarScopeBytesCovered = 0;
SaturatingUINT64 LocalVarScopeBytes = 0;
SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
SaturatingUINT64 CallSiteEntries = 0;
SaturatingUINT64 CallSiteDIEs = 0;
SaturatingUINT64 CallSiteParamDIEs = 0;
SaturatingUINT64 FunctionSize = 0;
SaturatingUINT64 InlineFunctionSize = 0;
};
struct LocationStats {
std::vector<SaturatingUINT64> VarParamLocStats{
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
std::vector<SaturatingUINT64> ParamLocStats{
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
std::vector<SaturatingUINT64> LocalVarLocStats{
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
SaturatingUINT64 NumVarParam = 0;
SaturatingUINT64 NumParam = 0;
SaturatingUINT64 NumVar = 0;
};
}
static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
std::vector<SaturatingUINT64> &VarParamLocStats,
std::vector<SaturatingUINT64> &ParamLocStats,
std::vector<SaturatingUINT64> &LocalVarLocStats,
bool IsParam, bool IsLocalVar) {
auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
if (ScopeBytesCovered == 0)
return 0;
if (ScopeBytesCovered >= BytesInScope)
return NumOfCoverageCategories - 1;
unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
LocBucket /= 10;
return LocBucket + 1;
};
unsigned CoverageBucket = getCoverageBucket();
VarParamLocStats[CoverageBucket].Value++;
if (IsParam)
ParamLocStats[CoverageBucket].Value++;
else if (IsLocalVar)
LocalVarLocStats[CoverageBucket].Value++;
}
static std::string constructDieID(DWARFDie Die,
StringRef Prefix = StringRef()) {
std::string IDStr;
llvm::raw_string_ostream ID(IDStr);
ID << Prefix
<< Die.getName(DINameKind::LinkageName);
if (!Prefix.empty() && !Prefix.equals("g"))
return ID.str();
auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
std::string File;
if (DeclFile) {
DWARFUnit *U = Die.getDwarfUnit();
if (const auto *LT = U->getContext().getLineTableForUnit(U))
if (LT->getFileNameByIndex(
dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
File = std::string(sys::path::filename(File));
}
ID << ":" << (File.empty() ? "/" : File);
ID << ":"
<< dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
return ID.str();
}
static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
uint64_t Lower = std::max(A.LowPC, B.LowPC);
uint64_t Upper = std::min(A.HighPC, B.HighPC);
if (Lower >= Upper)
return 0;
return Upper - Lower;
}
static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
const std::string &VarPrefix,
uint64_t BytesInScope, uint32_t InlineDepth,
StringMap<PerFunctionStats> &FnStatMap,
GlobalStats &GlobalStats,
LocationStats &LocStats,
AbstractOriginVarsTy *AbstractOriginVariables) {
const dwarf::Tag Tag = Die.getTag();
if (Tag == dwarf::DW_TAG_compile_unit)
return;
bool HasLoc = false;
bool HasSrcLoc = false;
bool HasType = false;
uint64_t TotalBytesCovered = 0;
uint64_t ScopeBytesCovered = 0;
uint64_t BytesEntryValuesCovered = 0;
auto &FnStats = FnStatMap[FnPrefix];
bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
Die.find(dwarf::DW_AT_const_value);
bool DeferLocStats = false;
if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
GlobalStats.CallSiteDIEs++;
return;
}
if (Tag == dwarf::DW_TAG_call_site_parameter ||
Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
GlobalStats.CallSiteParamDIEs++;
return;
}
if (!IsParam && !IsLocalVar && !IsConstantMember) {
return;
}
if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
return;
if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
Die.findRecursively(dwarf::DW_AT_decl_line))
HasSrcLoc = true;
if (Die.findRecursively(dwarf::DW_AT_type))
HasType = true;
if (Die.find(dwarf::DW_AT_abstract_origin)) {
if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
if (AbstractOriginVariables) {
auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue());
}
} else {
DeferLocStats = true;
}
}
auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
DWARFUnit *U = Die.getDwarfUnit();
DataExtractor Data(toStringRef(D),
Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
DWARFExpression Expression(Data, U->getAddressByteSize(),
U->getFormParams().Format);
return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
return Op.getCode() == dwarf::DW_OP_entry_value ||
Op.getCode() == dwarf::DW_OP_GNU_entry_value;
});
};
if (Die.find(dwarf::DW_AT_const_value)) {
HasLoc = true;
ScopeBytesCovered = BytesInScope;
TotalBytesCovered = BytesInScope;
} else {
Expected<std::vector<DWARFLocationExpression>> Loc =
Die.getLocations(dwarf::DW_AT_location);
if (!Loc) {
consumeError(Loc.takeError());
} else {
HasLoc = true;
auto Default = find_if(
*Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
if (Default != Loc->end()) {
ScopeBytesCovered = BytesInScope;
TotalBytesCovered = BytesInScope;
} else {
auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
for (auto Entry : *Loc) {
TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
uint64_t ScopeBytesCoveredByEntry = 0;
for (DWARFAddressRange R : ScopeRanges) {
ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
}
ScopeBytesCovered += ScopeBytesCoveredByEntry;
if (IsEntryValue(Entry.Expr))
BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
}
}
}
}
if (BytesInScope && !DeferLocStats) {
LocStats.NumVarParam.Value++;
if (IsParam)
LocStats.NumParam.Value++;
else if (IsLocalVar)
LocStats.NumVar.Value++;
collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
IsLocalVar);
collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
LocStats.VarParamNonEntryValLocStats,
LocStats.ParamNonEntryValLocStats,
LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
}
if (DWARFDie D =
Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
Die = D;
std::string VarID = constructDieID(Die, VarPrefix);
FnStats.VarsInFunction.insert(VarID);
GlobalStats.TotalBytesCovered += TotalBytesCovered;
if (BytesInScope) {
GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
GlobalStats.ScopeBytes += BytesInScope;
GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
if (IsParam) {
GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
GlobalStats.ParamScopeBytes += BytesInScope;
GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
} else if (IsLocalVar) {
GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
GlobalStats.LocalVarScopeBytes += BytesInScope;
GlobalStats.LocalVarScopeEntryValueBytesCovered +=
BytesEntryValuesCovered;
}
assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
}
if (IsConstantMember) {
FnStats.ConstantMembers++;
return;
}
FnStats.TotalVarWithLoc += (unsigned)HasLoc;
if (Die.find(dwarf::DW_AT_artificial)) {
FnStats.NumArtificial++;
return;
}
if (IsParam) {
FnStats.NumParams++;
if (HasType)
FnStats.NumParamTypes++;
if (HasSrcLoc)
FnStats.NumParamSourceLocations++;
if (HasLoc)
FnStats.NumParamLocations++;
} else if (IsLocalVar) {
FnStats.NumLocalVars++;
if (HasType)
FnStats.NumLocalVarTypes++;
if (HasSrcLoc)
FnStats.NumLocalVarSourceLocations++;
if (HasLoc)
FnStats.NumLocalVarLocations++;
}
}
static void collectAbstractOriginFnInfo(
DWARFDie Die, uint64_t SPOffset,
AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
DWARFDie Child = Die.getFirstChild();
while (Child) {
const dwarf::Tag ChildTag = Child.getTag();
if (ChildTag == dwarf::DW_TAG_formal_parameter ||
ChildTag == dwarf::DW_TAG_variable) {
GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
} else if (ChildTag == dwarf::DW_TAG_lexical_block)
collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
LocalAbstractOriginFnInfo);
Child = Child.getSibling();
}
}
static void collectStatsRecursive(
DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
uint64_t BytesInScope, uint32_t InlineDepth,
StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
if (Die.isNULL())
return;
const dwarf::Tag Tag = Die.getTag();
if (Tag == dwarf::DW_TAG_subroutine_type)
return;
const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None;
const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
const bool IsCandidateForZeroLocCovTracking =
(IsInlinedFunction || (IsFunction && HasAbstractOrigin));
AbstractOriginVarsTy AbstractOriginVars;
if (IsCandidateForZeroLocCovTracking) {
auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
if (OffsetFn) {
uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
AbstractOriginVarsPtr = &AbstractOriginVars;
} else {
FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
AbstractOriginVarsPtr = nullptr;
}
}
}
if (IsFunction || IsInlinedFunction || IsBlock) {
if (IsFunction || IsInlinedFunction)
VarPrefix = "v";
if (Die.find(dwarf::DW_AT_declaration))
return;
if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
GlobalStats.CallSiteEntries++;
auto RangesOrError = Die.getAddressRanges();
if (!RangesOrError) {
llvm::consumeError(RangesOrError.takeError());
return;
}
auto Ranges = RangesOrError.get();
uint64_t BytesInThisScope = 0;
for (auto Range : Ranges)
BytesInThisScope += Range.HighPC - Range.LowPC;
if (!IsBlock) {
if (Die.find(dwarf::DW_AT_inline)) {
uint64_t SPOffset = Die.getOffset();
AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
LocalAbstractOriginFnInfo);
return;
}
std::string FnID = constructDieID(Die);
auto &FnStats = FnStatMap[FnID];
FnStats.IsFunction = true;
if (IsInlinedFunction) {
FnStats.NumFnInlined++;
if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
FnStats.NumAbstractOrigins++;
} else {
FnStats.NumFnOutOfLine++;
}
if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
Die.findRecursively(dwarf::DW_AT_decl_line))
FnStats.HasSourceLocation = true;
FnPrefix = FnID;
}
if (BytesInThisScope) {
BytesInScope = BytesInThisScope;
if (IsFunction)
GlobalStats.FunctionSize += BytesInThisScope;
else if (IsInlinedFunction && InlineDepth == 0)
GlobalStats.InlineFunctionSize += BytesInThisScope;
}
} else {
collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
}
if (IsFunction)
InlineDepth = 0;
else if (IsInlinedFunction)
++InlineDepth;
unsigned LexicalBlockIndex = 0;
unsigned FormalParameterIndex = 0;
DWARFDie Child = Die.getFirstChild();
while (Child) {
std::string ChildVarPrefix = VarPrefix;
if (Child.getTag() == dwarf::DW_TAG_lexical_block)
ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
collectStatsRecursive(
Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
AbstractOriginVarsPtr);
Child = Child.getSibling();
}
if (!IsCandidateForZeroLocCovTracking)
return;
for (auto Offset : AbstractOriginVars) {
LocStats.NumVarParam++;
LocStats.VarParamLocStats[ZeroCoverageBucket]++;
auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
if (!FnDie)
continue;
auto Tag = FnDie.getTag();
if (Tag == dwarf::DW_TAG_formal_parameter) {
LocStats.NumParam++;
LocStats.ParamLocStats[ZeroCoverageBucket]++;
} else if (Tag == dwarf::DW_TAG_variable) {
LocStats.NumVar++;
LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
}
}
}
static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
if (Value == OverflowValue)
J.attribute(Key, "overflowed");
else
J.attribute(Key, Value);
LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
}
static void printLocationStats(json::OStream &J, const char *Key,
std::vector<SaturatingUINT64> &LocationStats) {
if (LocationStats[0].Value == OverflowValue)
J.attribute((Twine(Key) +
" with (0%,10%) of parent scope covered by DW_AT_location")
.str(),
"overflowed");
else
J.attribute(
(Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
.str(),
LocationStats[0].Value);
LLVM_DEBUG(
llvm::dbgs() << Key
<< " with 0% of parent scope covered by DW_AT_location: \\"
<< LocationStats[0].Value << '\n');
if (LocationStats[1].Value == OverflowValue)
J.attribute((Twine(Key) +
" with (0%,10%) of parent scope covered by DW_AT_location")
.str(),
"overflowed");
else
J.attribute((Twine(Key) +
" with (0%,10%) of parent scope covered by DW_AT_location")
.str(),
LocationStats[1].Value);
LLVM_DEBUG(llvm::dbgs()
<< Key
<< " with (0%,10%) of parent scope covered by DW_AT_location: "
<< LocationStats[1].Value << '\n');
for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
if (LocationStats[i].Value == OverflowValue)
J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
Twine(i * 10) +
"%) of parent scope covered by DW_AT_location")
.str(),
"overflowed");
else
J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
Twine(i * 10) +
"%) of parent scope covered by DW_AT_location")
.str(),
LocationStats[i].Value);
LLVM_DEBUG(llvm::dbgs()
<< Key << " with [" << (i - 1) * 10 << "%," << i * 10
<< "%) of parent scope covered by DW_AT_location: "
<< LocationStats[i].Value);
}
if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
J.attribute(
(Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
.str(),
"overflowed");
else
J.attribute(
(Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
.str(),
LocationStats[NumOfCoverageCategories - 1].Value);
LLVM_DEBUG(
llvm::dbgs() << Key
<< " with 100% of parent scope covered by DW_AT_location: "
<< LocationStats[NumOfCoverageCategories - 1].Value);
}
static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
for (const auto &It : Sizes.DebugSectionSizes)
J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
}
static void updateVarsWithAbstractOriginLocCovInfo(
DWARFDie FnDieWithAbstractOrigin,
AbstractOriginVarsTy &AbstractOriginVars) {
DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
while (Child) {
const dwarf::Tag ChildTag = Child.getTag();
if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
ChildTag == dwarf::DW_TAG_variable) &&
(Child.find(dwarf::DW_AT_location) ||
Child.find(dwarf::DW_AT_const_value))) {
auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
if (OffsetVar)
llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue());
} else if (ChildTag == dwarf::DW_TAG_lexical_block)
updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
Child = Child.getSibling();
}
}
static void collectZeroLocCovForVarsWithAbstractOrigin(
DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
FunctionsWithAbstractOriginTy ProcessedFns;
for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
AbstractOriginVarsTy AbstractOriginVars;
if (!FnCopy)
continue;
uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
continue;
AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
AbstractOriginVars);
for (auto Offset : AbstractOriginVars) {
LocStats.NumVarParam++;
LocStats.VarParamLocStats[ZeroCoverageBucket]++;
auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
if (Tag == dwarf::DW_TAG_formal_parameter) {
LocStats.NumParam++;
LocStats.ParamLocStats[ZeroCoverageBucket]++;
} else if (Tag == dwarf::DW_TAG_variable) {
LocStats.NumVar++;
LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
}
}
ProcessedFns.push_back(FnOffset);
}
for (auto ProcessedFn : ProcessedFns)
llvm::erase_value(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
}
static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
for (const auto &CrossCUReferenceToBeResolved :
CrossCUReferencesToBeResolved) {
DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
DWARFDie FnDIEWithCrossCUReferencing =
DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
auto FnCopy =
FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
if (!FnCopy)
continue;
uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
AbstractOriginVarsTy AbstractOriginVars =
GlobalAbstractOriginFnInfo[FnCopyRawUValue];
updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
AbstractOriginVars);
for (auto Offset : AbstractOriginVars) {
LocStats.NumVarParam++;
LocStats.VarParamLocStats[ZeroCoverageBucket]++;
auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
->getDIEForOffset(Offset)
.getTag();
if (Tag == dwarf::DW_TAG_formal_parameter) {
LocStats.NumParam++;
LocStats.ParamLocStats[ZeroCoverageBucket]++;
} else if (Tag == dwarf::DW_TAG_variable) {
LocStats.NumVar++;
LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
}
}
}
}
bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
const Twine &Filename,
raw_ostream &OS) {
StringRef FormatName = Obj.getFileFormatName();
GlobalStats GlobalStats;
LocationStats LocStats;
StringMap<PerFunctionStats> Statistics;
AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
FunctionDIECUTyMap AbstractOriginFnCUs;
CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
collectStatsRecursive(
CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
collectZeroLocCovForVarsWithAbstractOrigin(
CUDie.getDwarfUnit(), GlobalStats, LocStats,
LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
for (auto CrossCUReferencingDIEOffset :
FnsWithAbstractOriginToBeProcessed)
CrossCUReferencesToBeResolved.push_back(
DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
}
}
collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
CrossCUReferencesToBeResolved);
SectionSizes Sizes;
calculateSectionSizes(Obj, Sizes, Filename);
unsigned Version = 9;
SaturatingUINT64 VarParamTotal = 0;
SaturatingUINT64 VarParamUnique = 0;
SaturatingUINT64 VarParamWithLoc = 0;
SaturatingUINT64 NumFunctions = 0;
SaturatingUINT64 NumInlinedFunctions = 0;
SaturatingUINT64 NumFuncsWithSrcLoc = 0;
SaturatingUINT64 NumAbstractOrigins = 0;
SaturatingUINT64 ParamTotal = 0;
SaturatingUINT64 ParamWithType = 0;
SaturatingUINT64 ParamWithLoc = 0;
SaturatingUINT64 ParamWithSrcLoc = 0;
SaturatingUINT64 LocalVarTotal = 0;
SaturatingUINT64 LocalVarWithType = 0;
SaturatingUINT64 LocalVarWithSrcLoc = 0;
SaturatingUINT64 LocalVarWithLoc = 0;
for (auto &Entry : Statistics) {
PerFunctionStats &Stats = Entry.getValue();
uint64_t TotalVars = Stats.VarsInFunction.size() *
(Stats.NumFnInlined + Stats.NumFnOutOfLine);
if (!Stats.IsFunction)
TotalVars =
Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
uint64_t Constants = Stats.ConstantMembers;
VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
VarParamTotal += TotalVars;
VarParamUnique += Stats.VarsInFunction.size();
LLVM_DEBUG(for (auto &V
: Stats.VarsInFunction) llvm::dbgs()
<< Entry.getKey() << ": " << V.getKey() << "\n");
NumFunctions += Stats.IsFunction;
NumFuncsWithSrcLoc += Stats.HasSourceLocation;
NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
ParamTotal += Stats.NumParams;
ParamWithType += Stats.NumParamTypes;
ParamWithLoc += Stats.NumParamLocations;
ParamWithSrcLoc += Stats.NumParamSourceLocations;
LocalVarTotal += Stats.NumLocalVars;
LocalVarWithType += Stats.NumLocalVarTypes;
LocalVarWithLoc += Stats.NumLocalVarLocations;
LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
}
OS.SetBufferSize(1024);
json::OStream J(OS, 2);
J.objectBegin();
J.attribute("version", Version);
LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
llvm::dbgs() << "---------------------------------\n");
printDatum(J, "file", Filename.str());
printDatum(J, "format", FormatName);
printDatum(J, "#functions", NumFunctions.Value);
printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
printDatum(J, "#inlined functions with abstract origins",
NumAbstractOrigins.Value);
printDatum(J, "#unique source variables", VarParamUnique.Value);
printDatum(J, "#source variables", VarParamTotal.Value);
printDatum(J, "#source variables with location", VarParamWithLoc.Value);
printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
printDatum(J, "#call site parameter DIEs",
GlobalStats.CallSiteParamDIEs.Value);
printDatum(J, "sum_all_variables(#bytes in parent scope)",
GlobalStats.ScopeBytes.Value);
printDatum(J,
"sum_all_variables(#bytes in any scope covered by DW_AT_location)",
GlobalStats.TotalBytesCovered.Value);
printDatum(J,
"sum_all_variables(#bytes in parent scope covered by "
"DW_AT_location)",
GlobalStats.ScopeBytesCovered.Value);
printDatum(J,
"sum_all_variables(#bytes in parent scope covered by "
"DW_OP_entry_value)",
GlobalStats.ScopeEntryValueBytesCovered.Value);
printDatum(J, "sum_all_params(#bytes in parent scope)",
GlobalStats.ParamScopeBytes.Value);
printDatum(J,
"sum_all_params(#bytes in parent scope covered by DW_AT_location)",
GlobalStats.ParamScopeBytesCovered.Value);
printDatum(J,
"sum_all_params(#bytes in parent scope covered by "
"DW_OP_entry_value)",
GlobalStats.ParamScopeEntryValueBytesCovered.Value);
printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
GlobalStats.LocalVarScopeBytes.Value);
printDatum(J,
"sum_all_local_vars(#bytes in parent scope covered by "
"DW_AT_location)",
GlobalStats.LocalVarScopeBytesCovered.Value);
printDatum(J,
"sum_all_local_vars(#bytes in parent scope covered by "
"DW_OP_entry_value)",
GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
printDatum(J, "#bytes within inlined functions",
GlobalStats.InlineFunctionSize.Value);
printDatum(J, "#params", ParamTotal.Value);
printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
printDatum(J, "#params with type", ParamWithType.Value);
printDatum(J, "#params with binary location", ParamWithLoc.Value);
printDatum(J, "#local vars", LocalVarTotal.Value);
printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
printDatum(J, "#local vars with type", LocalVarWithType.Value);
printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
printSectionSizes(J, Sizes);
printDatum(J, "#variables processed by location statistics",
LocStats.NumVarParam.Value);
printLocationStats(J, "#variables", LocStats.VarParamLocStats);
printLocationStats(J, "#variables - entry values",
LocStats.VarParamNonEntryValLocStats);
printDatum(J, "#params processed by location statistics",
LocStats.NumParam.Value);
printLocationStats(J, "#params", LocStats.ParamLocStats);
printLocationStats(J, "#params - entry values",
LocStats.ParamNonEntryValLocStats);
printDatum(J, "#local vars processed by location statistics",
LocStats.NumVar.Value);
printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
printLocationStats(J, "#local vars - entry values",
LocStats.LocalVarNonEntryValLocStats);
J.objectEnd();
OS << '\n';
LLVM_DEBUG(
llvm::dbgs() << "Total Availability: "
<< (VarParamTotal.Value
? (int)std::round((VarParamWithLoc.Value * 100.0) /
VarParamTotal.Value)
: 0)
<< "%\n";
llvm::dbgs() << "PC Ranges covered: "
<< (GlobalStats.ScopeBytes.Value
? (int)std::round(
(GlobalStats.ScopeBytesCovered.Value * 100.0) /
GlobalStats.ScopeBytes.Value)
: 0)
<< "%\n");
return true;
}