in folly/experimental/symbolizer/DwarfImpl.cpp [567:722]
void DwarfImpl::findInlinedSubroutineDieForAddress(
const Die& die,
const DwarfLineNumberVM& lineVM,
uint64_t address,
folly::Optional<uint64_t> baseAddrCU,
folly::Range<CallLocation*> locations,
size_t& numFound) const {
if (numFound >= locations.size()) {
return;
}
forEachChild(die, [&](const Die& childDie) {
// Between a DW_TAG_subprogram and and DW_TAG_inlined_subroutine we might
// have arbitrary intermediary "nodes", including DW_TAG_common_block,
// DW_TAG_lexical_block, DW_TAG_try_block, DW_TAG_catch_block and
// DW_TAG_with_stmt, etc.
// We can't filter with locationhere since its range may be not specified.
// See section 2.6.2: A location list containing only an end of list entry
// describes an object that exists in the source code but not in the
// executable program.
if (childDie.abbr.tag == DW_TAG_try_block ||
childDie.abbr.tag == DW_TAG_catch_block ||
childDie.abbr.tag == DW_TAG_entry_point ||
childDie.abbr.tag == DW_TAG_common_block ||
childDie.abbr.tag == DW_TAG_lexical_block) {
findInlinedSubroutineDieForAddress(
childDie, lineVM, address, baseAddrCU, locations, numFound);
return true;
}
folly::Optional<uint64_t> lowPc;
folly::Optional<uint64_t> highPc;
folly::Optional<bool> isHighPcAddr;
folly::Optional<uint64_t> abstractOrigin;
folly::Optional<uint64_t> abstractOriginRefType;
folly::Optional<uint64_t> callFile;
folly::Optional<uint64_t> callLine;
folly::Optional<uint64_t> rangeOffset;
forEachAttribute(cu_, childDie, [&](const Attribute& attr) {
switch (attr.spec.name) {
case DW_AT_ranges:
rangeOffset = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_low_pc:
lowPc = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_high_pc:
// The value of the DW_AT_high_pc attribute can be
// an address (DW_FORM_addr*) or an offset (DW_FORM_data*).
isHighPcAddr = attr.spec.form == DW_FORM_addr || //
attr.spec.form == DW_FORM_addrx || //
attr.spec.form == DW_FORM_addrx1 || //
attr.spec.form == DW_FORM_addrx2 || //
attr.spec.form == DW_FORM_addrx3 || //
attr.spec.form == DW_FORM_addrx4;
highPc = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_abstract_origin:
abstractOriginRefType = attr.spec.form;
abstractOrigin = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_call_line:
callLine = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_call_file:
callFile = boost::get<uint64_t>(attr.attrValue);
break;
}
return true; // continue forEachAttribute
});
// 2.17 Code Addresses and Ranges
// Any debugging information entry describing an entity that has a
// machine code address or range of machine code addresses,
// which includes compilation units, module initialization, subroutines,
// ordinary blocks, try/catch blocks, labels and the like, may have
// - A DW_AT_low_pc attribute for a single address,
// - A DW_AT_low_pc and DW_AT_high_pc pair of attributes for a
// single contiguous range of addresses, or
// - A DW_AT_ranges attribute for a non-contiguous range of addresses.
// TODO: Support DW_TAG_entry_point and DW_TAG_common_block that don't
// have DW_AT_low_pc/DW_AT_high_pc pairs and DW_AT_ranges.
// TODO: Support relocated address which requires lookup in relocation map.
bool pcMatch = lowPc && highPc && isHighPcAddr && address >= *lowPc &&
(address < (*isHighPcAddr ? *highPc : (*lowPc + *highPc)));
bool rangeMatch =
rangeOffset &&
isAddrInRangeList(
address, baseAddrCU, rangeOffset.value(), cu_.addrSize);
if (!pcMatch && !rangeMatch) {
// Address doesn't match. Keep searching other children.
return true;
}
if (!abstractOrigin || !abstractOriginRefType || !callLine || !callFile) {
// We expect a single sibling DIE to match on addr, but it's missing
// required fields. Stop searching for other DIEs.
return false;
}
locations[numFound].file = lineVM.getFullFileName(*callFile);
locations[numFound].line = *callLine;
auto getFunctionName = [&](const CompilationUnit& srcu,
uint64_t dieOffset) {
auto declDie = getDieAtOffset(srcu, dieOffset);
// Jump to the actual function definition instead of declaration for name
// and line info.
auto defDie = findDefinitionDie(srcu, declDie);
folly::StringPiece name;
// The file and line will be set in the next inline subroutine based on
// its DW_AT_call_file and DW_AT_call_line.
forEachAttribute(srcu, defDie, [&](const Attribute& attr) {
switch (attr.spec.name) {
case DW_AT_linkage_name:
name = boost::get<folly::StringPiece>(attr.attrValue);
break;
case DW_AT_name:
// NOTE: when DW_AT_linkage_name and DW_AT_name match, dwarf
// emitters omit DW_AT_linkage_name (to save space). If present
// DW_AT_linkage_name should always be preferred (mangled C++ name
// vs just the function name).
if (name.empty()) {
name = boost::get<folly::StringPiece>(attr.attrValue);
}
break;
}
return true; // continue forEachAttribute
});
return name;
};
// DW_AT_abstract_origin is a reference. There a 3 types of references:
// - the reference can identify any debugging information entry within the
// compilation unit (DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4,
// DW_FORM_ref8, DW_FORM_ref_udata). This type of reference is an offset
// from the first byte of the compilation header for the compilation unit
// containing the reference.
// - the reference can identify any debugging information entry within a
// .debug_info section; in particular, it may refer to an entry in a
// different compilation unit (DW_FORM_ref_addr)
// - the reference can identify any debugging information type entry that
// has been placed in its own type unit.
// Not applicable for DW_AT_abstract_origin.
locations[numFound].name = (*abstractOriginRefType != DW_FORM_ref_addr)
? getFunctionName(cu_, cu_.offset + *abstractOrigin)
: getFunctionName(
findCompilationUnit(*abstractOrigin), *abstractOrigin);
findInlinedSubroutineDieForAddress(
childDie, lineVM, address, baseAddrCU, locations, ++numFound);
return false;
});
}