in folly/experimental/symbolizer/DwarfImpl.cpp [57:296]
bool DwarfImpl::findLocation(
uintptr_t address,
const LocationInfoMode mode,
LocationInfo& locationInfo,
folly::Range<SymbolizedFrame*> inlineFrames,
folly::FunctionRef<void(folly::StringPiece)> eachParameterName,
bool checkAddress) const {
Die die = getDieAtOffset(cu_, cu_.firstDie);
// Partial compilation unit (DW_TAG_partial_unit) is not supported.
FOLLY_SAFE_CHECK(
die.abbr.tag == DW_TAG_compile_unit ||
die.abbr.tag == DW_TAG_skeleton_unit,
die.abbr.tag);
// Offset in .debug_line for the line number VM program for this CU
folly::Optional<uint64_t> lineOffset;
folly::StringPiece compilationDirectory;
folly::Optional<folly::StringPiece> mainFileName;
folly::Optional<uint64_t> baseAddrCU;
folly::Optional<uint64_t> rangesOffset;
bool seenLowPC = false;
bool seenHighPC = false;
enum : unsigned {
kStmtList = 1U << 0,
kCompDir = 1U << 1,
kName = 1U << 2,
kLowPC = 1U << 3,
kHighPCOrRanges = 1U << 4,
};
unsigned expectedAttributes = kStmtList | kCompDir | kName | kLowPC;
bool foundAddress = !checkAddress;
if (!foundAddress) {
expectedAttributes |= kHighPCOrRanges;
}
forEachAttribute(cu_, die, [&](const Attribute& attr) {
switch (attr.spec.name) {
case DW_AT_stmt_list:
expectedAttributes &= ~kStmtList;
// Offset in .debug_line for the line number VM program for this
// compilation unit
lineOffset = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_comp_dir:
expectedAttributes &= ~kCompDir;
// Compilation directory
compilationDirectory = boost::get<folly::StringPiece>(attr.attrValue);
break;
case DW_AT_name:
expectedAttributes &= ~kName;
// File name of main file being compiled
mainFileName = boost::get<folly::StringPiece>(attr.attrValue);
break;
case DW_AT_low_pc:
expectedAttributes &= ~kLowPC;
baseAddrCU = boost::get<uint64_t>(attr.attrValue);
if (!foundAddress) {
if (address < *baseAddrCU) {
return false;
}
seenLowPC = true;
if (seenHighPC) {
foundAddress = true;
} else if (rangesOffset) {
if (!isAddrInRangeList(
address, baseAddrCU, *rangesOffset, cu_.addrSize)) {
return false;
}
foundAddress = true;
}
}
break;
case DW_AT_high_pc:
expectedAttributes &= ~kHighPCOrRanges;
if (!foundAddress) {
if (address >= boost::get<uint64_t>(attr.attrValue)) {
return false;
}
seenHighPC = true;
foundAddress = seenLowPC;
}
break;
case DW_AT_ranges:
// 3.1.1: CU entries have:
// - either DW_AT_low_pc and DW_AT_high_pc
// OR
// - DW_AT_ranges and optional DW_AT_low_pc
expectedAttributes &= ~kHighPCOrRanges;
if (!foundAddress) {
rangesOffset = boost::get<uint64_t>(attr.attrValue);
if (seenLowPC) {
if (!isAddrInRangeList(
address, baseAddrCU, *rangesOffset, cu_.addrSize)) {
return false;
}
foundAddress = true;
}
}
break;
}
return (expectedAttributes != 0); // continue forEachAttribute
});
if (!foundAddress || !lineOffset) {
return false;
}
if (mainFileName) {
locationInfo.hasMainFile = true;
locationInfo.mainFile = Path(compilationDirectory, "", *mainFileName);
}
folly::StringPiece lineSection(cu_.debugSections.debugLine);
lineSection.advance(*lineOffset);
DwarfLineNumberVM lineVM(
lineSection, compilationDirectory, cu_.debugSections);
// Execute line number VM program to find file and line
locationInfo.hasFileAndLine =
lineVM.findAddress(address, locationInfo.file, locationInfo.line);
if (!locationInfo.hasFileAndLine) {
return false;
}
// NOTE: locationInfo was found, so findLocation returns success bellow.
// Missing inline function / parameter name is not a failure (best effort).
bool checkInline =
(mode == LocationInfoMode::FULL_WITH_INLINE && !inlineFrames.empty());
if (!checkInline && !eachParameterName) {
return true;
}
// Cache abbreviation.
std::array<DIEAbbreviation, kMaxAbbreviationEntries> abbrs;
cu_.abbrCache = folly::range(abbrs);
folly::StringPiece abbrev = cu_.debugSections.debugAbbrev;
abbrev.advance(cu_.abbrevOffset);
DIEAbbreviation abbr;
while (readAbbreviation(abbrev, abbr)) {
// Abbreviation code 0 is reserved for null debugging information entries.
if (abbr.code != 0 && abbr.code <= kMaxAbbreviationEntries) {
cu_.abbrCache.data()[abbr.code - 1] = abbr;
}
}
// Find the subprogram that matches the given address.
Die subprogram;
if (!findSubProgramDieForAddress(die, address, baseAddrCU, subprogram)) {
// Even though @cu contains @address, it's possible
// that the corresponding DW_TAG_subprogram DIE is missing.
return true;
}
if (eachParameterName) {
forEachChild(subprogram, [&](const Die& child) {
if (child.abbr.tag == DW_TAG_formal_parameter) {
if (auto name = getAttribute<folly::StringPiece>(child, DW_AT_name)) {
eachParameterName(*name);
}
}
return true; // continue forEachChild
});
}
if (!checkInline || !subprogram.abbr.hasChildren) {
return true;
}
// NOTE: @subprogram is the DIE of caller function.
// Use an extra location and get its call file and call line, so that
// they can be used for the second last location when we don't have
// enough inline frames for all inline functions call stack.
size_t size =
std::min<size_t>(kMaxInlineLocationInfoPerFrame, inlineFrames.size()) + 1;
CallLocation callLocations[kMaxInlineLocationInfoPerFrame + 1];
size_t numFound = 0;
findInlinedSubroutineDieForAddress(
subprogram,
lineVM,
address,
baseAddrCU,
folly::Range<CallLocation*>(callLocations, size),
numFound);
if (numFound == 0) {
return true;
}
folly::Range<CallLocation*> inlineLocations(callLocations, numFound);
const auto innerMostFile = locationInfo.file;
const auto innerMostLine = locationInfo.line;
// Earlier we filled in locationInfo:
// - mainFile: the path to the CU -- the file where the non-inlined
// call is made from.
// - file + line: the location of the inner-most inlined call.
// Here we already find inlined info so mainFile would be redundant.
locationInfo.hasMainFile = false;
locationInfo.mainFile = Path{};
// @findInlinedSubroutineDieForAddress fills inlineLocations[0] with the
// file+line of the non-inlined outer function making the call.
// locationInfo.name is already set by the caller by looking up the
// non-inlined function @address belongs to.
locationInfo.hasFileAndLine = true;
locationInfo.file = inlineLocations[0].file;
locationInfo.line = inlineLocations[0].line;
// The next inlined subroutine's call file and call line is the current
// caller's location.
for (size_t i = 0; i < numFound - 1; i++) {
inlineLocations[i].file = inlineLocations[i + 1].file;
inlineLocations[i].line = inlineLocations[i + 1].line;
}
// CallLocation for the inner-most inlined function:
// - will be computed if enough space was available in the passed
// buffer.
// - will have a .name, but no !.file && !.line
// - its corresponding file+line is the one returned by LineVM based
// on @address.
// Use the inner-most inlined file+line info we got from the LineVM.
inlineLocations[numFound - 1].file = innerMostFile;
inlineLocations[numFound - 1].line = innerMostLine;
// Skip the extra location when actual inline function calls are more
// than provided frames.
inlineLocations =
inlineLocations.subpiece(0, std::min(numFound, inlineFrames.size()));
// Fill in inline frames in reverse order (as
// expected by the caller).
std::reverse(inlineLocations.begin(), inlineLocations.end());
for (size_t i = 0; i < inlineLocations.size(); i++) {
inlineFrames[i].found = true;
inlineFrames[i].addr = address;
inlineFrames[i].name = inlineLocations[i].name.data();
inlineFrames[i].location.hasFileAndLine = true;
inlineFrames[i].location.file = inlineLocations[i].file;
inlineFrames[i].location.line = inlineLocations[i].line;
}
return true;
}