Commit 88e03cf4 authored by Matt Ma's avatar Matt Ma Committed by Facebook Github Bot

Back out "Support inline functions in folly stack trace symbolizer"

Summary: broken zinc/test and tao/coordinator/test: in dev/nosan mode.

Reviewed By: luciang

Differential Revision: D17310046

fbshipit-source-id: 0a98d7cea24438a42932466394e63db4f64591cf
parent 32701064
......@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/experimental/symbolizer/Dwarf.h>
#include <dwarf.h>
#include <chrono>
#include <type_traits>
#include <folly/container/Array.h>
#include <dwarf.h>
namespace folly {
namespace symbolizer {
......@@ -84,7 +83,7 @@ uint64_t readOffset(folly::StringPiece& sp, bool is64Bit) {
// Read "len" bytes
folly::StringPiece readBytes(folly::StringPiece& sp, uint64_t len) {
FOLLY_SAFE_CHECK(len <= sp.size(), "invalid string length");
FOLLY_SAFE_CHECK(len >= sp.size(), "invalid string length");
folly::StringPiece ret(sp.data(), len);
sp.advance(len);
return ret;
......@@ -250,7 +249,7 @@ size_t Dwarf::Path::toBuffer(char* buf, size_t bufSize) const {
if (bufSize) {
*buf = '\0';
}
FOLLY_SAFE_CHECK(totalSize == size(), "Invalid total size value.");
assert(totalSize == size());
return totalSize;
}
......@@ -272,7 +271,7 @@ void Dwarf::Path::toString(std::string& dest) const {
}
dest.append(file_.begin(), file_.end());
}
FOLLY_SAFE_CHECK(dest.size() == initialSize + size(), "Invalid path size.");
assert(dest.size() == initialSize + size());
}
// Next chunk in section
......@@ -326,13 +325,13 @@ void Dwarf::init() {
bool Dwarf::readAbbreviation(
folly::StringPiece& section,
DIEAbbreviation& abbr) {
// Abbreviation code
// abbreviation code
abbr.code = readULEB(section);
if (abbr.code == 0) {
return false;
}
// Abbreviation tag
// abbreviation tag
abbr.tag = readULEB(section);
// does this entry have children?
......@@ -342,15 +341,20 @@ bool Dwarf::readAbbreviation(
const char* attributeBegin = section.data();
for (;;) {
FOLLY_SAFE_CHECK(!section.empty(), "invalid attribute section");
auto spec = readAttributeSpec(section);
if (!spec) {
auto attr = readAttribute(section);
if (attr.name == 0 && attr.form == 0) {
break;
}
}
abbr.attributes.assign(attributeBegin, section.data());
return true;
}
Dwarf::DIEAbbreviation::Attribute Dwarf::readAttribute(folly::StringPiece& sp) {
return {readULEB(sp), readULEB(sp)};
}
Dwarf::DIEAbbreviation Dwarf::getAbbreviation(uint64_t code, uint64_t offset)
const {
// Linear search in the .debug_abbrev section, starting at offset
......@@ -367,64 +371,55 @@ Dwarf::DIEAbbreviation Dwarf::getAbbreviation(uint64_t code, uint64_t offset)
FOLLY_SAFE_CHECK(false, "could not find abbreviation code");
}
Dwarf::AttributeSpec Dwarf::readAttributeSpec(folly::StringPiece& sp) {
return {readULEB(sp), readULEB(sp)};
}
Dwarf::Attribute Dwarf::readAttribute(
Dwarf::Die& die,
AttributeSpec& spec,
folly::StringPiece& sp) const {
switch (spec.form) {
Dwarf::AttributeValue Dwarf::readAttributeValue(
folly::StringPiece& sp,
uint64_t form,
bool is64Bit) const {
switch (form) {
case DW_FORM_addr:
return {spec, &die, read<uintptr_t>(sp)};
return read<uintptr_t>(sp);
case DW_FORM_block1:
return {spec, &die, readBytes(sp, read<uint8_t>(sp))};
return readBytes(sp, read<uint8_t>(sp));
case DW_FORM_block2:
return {spec, &die, readBytes(sp, read<uint16_t>(sp))};
return readBytes(sp, read<uint16_t>(sp));
case DW_FORM_block4:
return {spec, &die, readBytes(sp, read<uint32_t>(sp))};
return readBytes(sp, read<uint32_t>(sp));
case DW_FORM_block: // fallthrough
case DW_FORM_exprloc:
return {spec, &die, readBytes(sp, readULEB(sp))};
return readBytes(sp, readULEB(sp));
case DW_FORM_data1: // fallthrough
case DW_FORM_ref1:
return {spec, &die, read<uint8_t>(sp)};
return read<uint8_t>(sp);
case DW_FORM_data2: // fallthrough
case DW_FORM_ref2:
return {spec, &die, read<uint16_t>(sp)};
return read<uint16_t>(sp);
case DW_FORM_data4: // fallthrough
case DW_FORM_ref4:
return {spec, &die, read<uint32_t>(sp)};
return read<uint32_t>(sp);
case DW_FORM_data8: // fallthrough
case DW_FORM_ref8:
case DW_FORM_ref_sig8:
return {spec, &die, read<uint64_t>(sp)};
return read<uint64_t>(sp);
case DW_FORM_sdata:
return {spec, &die, readSLEB(sp)};
return readSLEB(sp);
case DW_FORM_udata: // fallthrough
case DW_FORM_ref_udata:
return {spec, &die, readULEB(sp)};
return readULEB(sp);
case DW_FORM_flag:
return {spec, &die, read<uint8_t>(sp)};
return read<uint8_t>(sp);
case DW_FORM_flag_present:
return {spec, &die, 1};
return 1;
case DW_FORM_sec_offset: // fallthrough
case DW_FORM_ref_addr:
return {spec, &die, readOffset(sp, die.is64Bit)};
return readOffset(sp, is64Bit);
case DW_FORM_string:
return {spec, &die, readNullTerminated(sp)};
return readNullTerminated(sp);
case DW_FORM_strp:
return {
spec, &die, getStringFromStringSection(readOffset(sp, die.is64Bit))};
return getStringFromStringSection(readOffset(sp, is64Bit));
case DW_FORM_indirect: // form is explicitly specified
// Update spec with the actual FORM.
spec.form = readULEB(sp);
return readAttribute(die, spec, sp);
return readAttributeValue(sp, readULEB(sp), is64Bit);
default:
FOLLY_SAFE_CHECK(false, "invalid attribute form");
}
return {spec, &die, 0};
}
folly::StringPiece Dwarf::getStringFromStringSection(uint64_t offset) const {
......@@ -483,41 +478,74 @@ bool Dwarf::findDebugInfoOffset(
*/
bool Dwarf::findLocation(
uintptr_t address,
const LocationInfoMode mode,
const Dwarf::CompilationUnit& unit,
LocationInfo& locationInfo,
LocationInfo inlineLocationInfo[],
uint32_t maxNumInline) const {
Die die = getDieAtOffset(unit, unit.firstDie);
StringPiece& infoEntry,
LocationInfo& locationInfo) const {
// For each compilation unit compiled with a DWARF producer, a
// contribution is made to the .debug_info section of the object
// file. Each such contribution consists of a compilation unit
// header (see Section 7.5.1.1) followed by a single
// DW_TAG_compile_unit or DW_TAG_partial_unit debugging information
// entry, together with its children.
// 7.5.1.1 Compilation Unit Header
// 1. unit_length (4B or 12B): read by Section::next
// 2. version (2B)
// 3. debug_abbrev_offset (4B or 8B): offset into the .debug_abbrev section
// 4. address_size (1B)
Section debugInfoSection(infoEntry);
folly::StringPiece chunk;
FOLLY_SAFE_CHECK(debugInfoSection.next(chunk), "invalid debug info");
auto version = read<uint16_t>(chunk);
FOLLY_SAFE_CHECK(version >= 2 && version <= 4, "invalid info version");
uint64_t abbrevOffset = readOffset(chunk, debugInfoSection.is64Bit());
auto addressSize = read<uint8_t>(chunk);
FOLLY_SAFE_CHECK(addressSize == sizeof(uintptr_t), "invalid address size");
// We survived so far. The first (and only) DIE should be DW_TAG_compile_unit
// NOTE: - binutils <= 2.25 does not issue DW_TAG_partial_unit.
// - dwarf compression tools like `dwz` may generate it.
// TODO(tudorb): Handle DW_TAG_partial_unit?
auto code = readULEB(chunk);
FOLLY_SAFE_CHECK(code != 0, "invalid code");
auto abbr = getAbbreviation(code, abbrevOffset);
FOLLY_SAFE_CHECK(
die.abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry");
abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry");
// Skip children entries, advance to the next compilation unit entry.
infoEntry.advance(chunk.end() - infoEntry.begin());
// Offset in .debug_line for the line number VM program for this
// compilation unit
// Read attributes, extracting the few we care about
bool foundLineOffset = false;
uint64_t lineOffset = 0;
folly::StringPiece compilationDirectory;
folly::StringPiece mainFileName;
forEachAttribute(die, [&](const Dwarf::Attribute& attr) {
switch (attr.spec.name) {
DIEAbbreviation::Attribute attr;
folly::StringPiece attributes = abbr.attributes;
for (;;) {
attr = readAttribute(attributes);
if (attr.name == 0 && attr.form == 0) {
break;
}
auto val = readAttributeValue(chunk, attr.form, debugInfoSection.is64Bit());
switch (attr.name) {
case DW_AT_stmt_list:
// Offset in .debug_line for the line number VM program for this
// compilation unit
lineOffset = boost::get<uint64_t>(attr.attrValue);
lineOffset = boost::get<uint64_t>(val);
foundLineOffset = true;
break;
case DW_AT_comp_dir:
// Compilation directory
compilationDirectory = boost::get<folly::StringPiece>(attr.attrValue);
compilationDirectory = boost::get<folly::StringPiece>(val);
break;
case DW_AT_name:
// File name of main file being compiled
mainFileName = boost::get<folly::StringPiece>(attr.attrValue);
mainFileName = boost::get<folly::StringPiece>(val);
break;
}
return true;
});
}
if (!mainFileName.empty()) {
locationInfo.hasMainFile = true;
......@@ -535,75 +563,15 @@ bool Dwarf::findLocation(
// Execute line number VM program to find file and line
locationInfo.hasFileAndLine =
lineVM.findAddress(address, locationInfo.file, locationInfo.line);
// Look up whether inline function.
if (mode == Dwarf::LocationInfoMode::FULL_WITH_INLINE &&
locationInfo.hasFileAndLine) {
// Re-get the compilation unit with abbreviation cached.
CompilationUnit cu;
std::array<Dwarf::DIEAbbreviation, kMaxAbbreviationEntries> abbrs;
cu.abbrCache = folly::Range<Dwarf::DIEAbbreviation*>(
abbrs.data(), kMaxAbbreviationEntries);
getCompilationUnit(cu, unit.offset, cu.abbrCache.data());
die.cu = &cu;
Die subprogram;
findSubProgramDieForAddress(die, address, subprogram);
// Subprogram is the DIE of caller function.
if (subprogram.abbr.hasChildren) {
auto isrLocs =
std::array<Dwarf::CodeLocation, kMaxLocationInfoPerFrame>();
uint32_t numFound = 0;
findInlinedSubroutineDieForAddress(
cu, subprogram, address, isrLocs.data(), numFound);
numFound = std::min(numFound, maxNumInline);
LocationInfo* prevLocation = &locationInfo;
LocationInfo* nextLocation = inlineLocationInfo;
Dwarf::CodeLocation* isrLoc = isrLocs.data();
// The line of code in inline function.
uint64_t callerLine = locationInfo.line;
while (numFound > 0) {
// linePtr points to the caller line.
prevLocation->line = isrLoc->line;
folly::Optional<Dwarf::AttributeValue> name;
folly::Optional<Dwarf::AttributeValue> file;
forEachAttribute(isrLoc->die, [&](const Dwarf::Attribute& attr) {
switch (attr.spec.name) {
case DW_AT_name:
name = attr.attrValue;
break;
case DW_AT_decl_file:
file = attr.attrValue;
break;
}
return !file.has_value() || !name.has_value();
});
if (!name.has_value() || !file.has_value()) {
break;
}
nextLocation->empty = false;
nextLocation->hasFileAndLine = true;
nextLocation->name = boost::get<StringPiece>(name.value());
nextLocation->file =
lineVM.getFullFileName(boost::get<uint64_t>(file.value()));
prevLocation = nextLocation;
nextLocation++;
isrLoc++;
numFound--;
}
prevLocation->line = callerLine;
}
}
return locationInfo.hasFileAndLine;
}
bool Dwarf::findAddress(
uintptr_t address,
LocationInfoMode mode,
LocationInfo& locationInfo,
LocationInfo inlineLocationInfo[],
uint32_t maxNumInline) const {
LocationInfoMode mode) const {
locationInfo = LocationInfo();
if (mode == LocationInfoMode::DISABLED) {
return false;
}
......@@ -618,10 +586,9 @@ bool Dwarf::findAddress(
uint64_t offset = 0;
if (findDebugInfoOffset(address, aranges_, offset)) {
// Read compilation unit header from .debug_info
Dwarf::CompilationUnit unit;
getCompilationUnit(unit, offset, nullptr);
findLocation(
address, mode, unit, locationInfo, inlineLocationInfo, maxNumInline);
folly::StringPiece infoEntry(info_);
infoEntry.advance(offset);
findLocation(address, infoEntry, locationInfo);
return locationInfo.hasFileAndLine;
} else if (mode == LocationInfoMode::FAST) {
// NOTE: Clang (when using -gdwarf-aranges) doesn't generate entries
......@@ -630,288 +597,20 @@ bool Dwarf::findAddress(
// it only if such behavior is requested via LocationInfoMode.
return false;
} else {
FOLLY_SAFE_DCHECK(
mode == LocationInfoMode::FULL ||
mode == LocationInfoMode::FULL_WITH_INLINE,
"unexpected mode");
FOLLY_SAFE_DCHECK(mode == LocationInfoMode::FULL, "unexpected mode");
// Fall back to the linear scan.
}
}
// Slow path (linear scan): Iterate over all .debug_info entries
// and look for the address in each compilation unit.
uint64_t offset = 0;
while (offset < info_.size() && !locationInfo.hasFileAndLine) {
Dwarf::CompilationUnit unit;
getCompilationUnit(unit, offset, nullptr);
offset += unit.size;
findLocation(
address, mode, unit, locationInfo, inlineLocationInfo, maxNumInline);
folly::StringPiece infoEntry(info_);
while (!infoEntry.empty() && !locationInfo.hasFileAndLine) {
findLocation(address, infoEntry, locationInfo);
}
return locationInfo.hasFileAndLine;
}
void Dwarf::getCompilationUnit(
Dwarf::CompilationUnit& cu,
uint64_t offset,
Dwarf::DIEAbbreviation* abbrs) const {
FOLLY_SAFE_DCHECK(offset < info_.size(), "unexpected offset");
folly::StringPiece chunk(info_);
cu.offset = offset;
chunk.advance(offset);
auto initialLength = read<uint32_t>(chunk);
cu.is64Bit = (initialLength == (uint32_t)-1);
cu.size = cu.is64Bit ? read<uint64_t>(chunk) : initialLength;
FOLLY_SAFE_CHECK(cu.size <= chunk.size(), "invalid chunk size");
cu.size += cu.is64Bit ? 12 : 4;
cu.version = read<uint16_t>(chunk);
FOLLY_SAFE_CHECK(cu.version >= 2 && cu.version <= 4, "invalid info version");
cu.abbrevOffset = readOffset(chunk, cu.is64Bit);
cu.addrSize = read<uint8_t>(chunk);
FOLLY_SAFE_CHECK(cu.addrSize == sizeof(uintptr_t), "invalid address size");
if (abbrs != nullptr) {
folly::StringPiece abbrev = abbrev_;
abbrev.advance(cu.abbrevOffset);
Dwarf::DIEAbbreviation abbr;
while (readAbbreviation(abbrev, abbr) &&
abbr.code <= kMaxAbbreviationEntries) {
// Abbreviation code 0 is reserved for null debugging information entries.
abbrs[abbr.code - 1] = abbr;
}
}
cu.firstDie = chunk.data() - info_.data();
}
Dwarf::CompilationUnit Dwarf::findCompilationUnit(uint64_t offset) const {
FOLLY_SAFE_DCHECK(offset < info_.size(), "unexpected offset");
Dwarf::CompilationUnit unit;
uint64_t cur = 0;
while (cur < info_.size()) {
if (cur > offset) {
return unit;
}
getCompilationUnit(unit, cur, nullptr);
cur += unit.size;
}
return unit;
}
Dwarf::Die Dwarf::getDieAtOffset(
const Dwarf::CompilationUnit& cu,
uint64_t offset) const {
FOLLY_SAFE_DCHECK(offset < info_.size(), "unexpected offset");
Dwarf::Die die;
die.cu = &cu;
folly::StringPiece sp = folly::StringPiece{
info_.data() + offset, info_.data() + cu.offset + cu.size};
die.offset = offset;
die.is64Bit = cu.is64Bit;
die.siblingDelta = 0;
die.nextDieDelta = 0;
auto code = readULEB(sp);
die.code = code;
if (code == 0) {
return die;
}
die.attrOffset = sp.data() - info_.data() - offset;
die.abbr = !cu.abbrCache.empty() && die.code < kMaxAbbreviationEntries
? cu.abbrCache[die.code - 1]
: getAbbreviation(die.code, cu.abbrevOffset);
return die;
}
Dwarf::Die Dwarf::getNextSibling(Dwarf::Die& die) const {
folly::StringPiece sp = {info_.data() + die.offset,
info_.data() + die.cu->offset + die.cu->size};
if (die.siblingDelta != 0) {
sp.advance(die.siblingDelta);
} else {
if (die.nextDieDelta == 0) {
moveToNextDie(die);
FOLLY_SAFE_CHECK(die.nextDieDelta != 0, "Invalid delta value.");
}
if (die.siblingDelta != 0) {
sp.advance(die.siblingDelta);
} else if (die.abbr.hasChildren) {
forEachChild(die, [](const Dwarf::Die&) { return true; });
sp.advance(die.siblingDelta);
} else {
FOLLY_SAFE_CHECK(die.nextDieDelta != 0, "Invalid delta value.");
sp.advance(die.nextDieDelta);
}
}
return getDieAtOffset(*(die.cu), sp.data() - info_.data());
}
template <typename F>
void Dwarf::forEachChild(Dwarf::Die& die, F&& f) const {
if (!die.abbr.hasChildren) {
return;
}
if (die.nextDieDelta == 0) {
moveToNextDie(die);
FOLLY_SAFE_CHECK(die.nextDieDelta, "Invalid delta value.");
}
auto sibling = getDieAtOffset(*(die.cu), die.offset + die.nextDieDelta);
while (sibling.code) {
if (!f(sibling)) {
return;
}
sibling = getNextSibling(sibling);
}
// Sibling is a dummy die whose offset is to the code 0 marking the
// end of the children. Need to add one to get the offset of the next die.
die.siblingDelta = sibling.offset + 1 - die.offset;
}
/*
* Iterate over all attributes of the given DIE, calling the given callable
* for each. Iteration is stopped early if any of the calls return false.
*/
template <typename F>
void Dwarf::forEachAttribute(Dwarf::Die& die, F&& f) const {
auto attrs = die.abbr.attributes;
auto values =
folly::StringPiece{info_.data() + die.offset + die.attrOffset,
info_.data() + die.cu->offset + die.cu->size};
while (auto spec = readAttributeSpec(attrs)) {
auto attr = readAttribute(die, spec, values);
if (!f(attr)) {
return;
}
}
}
void Dwarf::moveToNextDie(Dwarf::Die& die) const {
auto attrs = die.abbr.attributes;
auto values =
folly::StringPiece{info_.data() + die.offset + die.attrOffset,
info_.data() + die.cu->offset + die.cu->size};
while (auto spec = readAttributeSpec(attrs)) {
readAttribute(die, spec, values);
}
die.nextDieDelta = values.data() - info_.data() - die.offset;
if (!die.abbr.hasChildren) {
die.siblingDelta = die.nextDieDelta;
}
}
void Dwarf::findSubProgramDieForAddress(
Dwarf::Die& die,
uint64_t address,
Dwarf::Die& subprogram) const {
forEachChild(die, [&](Dwarf::Die& childDie) {
if (childDie.abbr.tag == DW_TAG_subprogram) {
uint64_t lowPc = 0;
uint64_t highPc = 0;
bool isHighPcAddr = false;
StringPiece name;
forEachAttribute(childDie, [&](const Dwarf::Attribute& attr) {
switch (attr.spec.name) {
// Here DW_AT_ranges is not supported since it requires looking up
// in a different section (.debug_ranges).
case DW_AT_low_pc:
lowPc = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_high_pc:
// Value of 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);
highPc = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_name:
name = boost::get<StringPiece>(attr.attrValue);
break;
}
return true;
});
if (address > lowPc &&
address < (isHighPcAddr ? highPc : lowPc + highPc)) {
subprogram = childDie;
return false;
}
} else if (
childDie.abbr.tag == DW_TAG_namespace ||
childDie.abbr.tag == DW_TAG_class_type) {
findSubProgramDieForAddress(childDie, address, subprogram);
}
return true;
});
}
void Dwarf::findInlinedSubroutineDieForAddress(
const CompilationUnit& cu,
Dwarf::Die& die,
uint64_t address,
Dwarf::CodeLocation* isrLoc,
uint32_t& numFound) const {
if (numFound >= kMaxLocationInfoPerFrame) {
return;
}
forEachChild(die, [&](Dwarf::Die& childDie) {
if (childDie.abbr.tag == DW_TAG_inlined_subroutine) {
uint64_t lowPc = 0;
uint64_t highPc = 0;
bool isHighPcAddr = false;
uint64_t origin = 0;
uint64_t originRefType = 0;
uint64_t callLine = 0;
forEachAttribute(childDie, [&](const Dwarf::Attribute& attr) {
switch (attr.spec.name) {
// Here DW_AT_ranges is not supported since it requires looking up
// in a different section (.debug_ranges).
case DW_AT_low_pc:
lowPc = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_high_pc:
// Value of 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);
highPc = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_abstract_origin:
originRefType = attr.spec.form;
origin = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_call_line:
callLine = boost::get<uint64_t>(attr.attrValue);
break;
}
return true;
});
if (address > lowPc &&
address < (isHighPcAddr ? highPc : lowPc + highPc)) {
if (originRefType == DW_FORM_ref1 || originRefType == DW_FORM_ref2 ||
originRefType == DW_FORM_ref4 || originRefType == DW_FORM_ref8 ||
originRefType == DW_FORM_ref_udata) {
isrLoc->cu = cu;
isrLoc->die = getDieAtOffset(cu, cu.offset + origin);
} else if (originRefType == DW_FORM_ref_addr) {
isrLoc->cu = findCompilationUnit(origin);
isrLoc->die = getDieAtOffset(cu, origin);
}
isrLoc->line = callLine;
numFound++;
const Dwarf::CompilationUnit& srcu = isrLoc->cu;
findInlinedSubroutineDieForAddress(
srcu, childDie, address, ++isrLoc, numFound);
return false;
}
}
return true;
});
}
Dwarf::LineNumberVM::LineNumberVM(
folly::StringPiece data,
folly::StringPiece compilationDirectory)
......@@ -1108,8 +807,8 @@ Dwarf::LineNumberVM::StepResult Dwarf::LineNumberVM::step(
}
if (opcode != 0) { // standard opcode
// Only interpret opcodes that are recognized by the version we're
// parsing; the others are vendor extensions and we should ignore them.
// Only interpret opcodes that are recognized by the version we're parsing;
// the others are vendor extensions and we should ignore them.
switch (opcode) {
case DW_LNS_copy:
basicBlock_ = false;
......
......@@ -20,7 +20,6 @@
#include <boost/variant.hpp>
#include <folly/Optional.h>
#include <folly/Range.h>
#include <folly/experimental/symbolizer/Elf.h>
......@@ -116,21 +115,9 @@ class Dwarf {
FAST,
// Scan all CU in .debug_info (slow!) on .debug_aranges lookup failure.
FULL,
// Scan .debug_info (super slower, use with caution) for inline functions in
// addition to FULL.
FULL_WITH_INLINE,
};
// More than one location info may exist if current frame is an inline
// function call.
static const uint32_t kMaxLocationInfoPerFrame = 5;
struct LocationInfo {
// Whether the location info is filled.
bool empty = true;
// Function name in call stack.
folly::StringPiece name;
bool hasMainFile = false;
Path mainFile;
......@@ -139,19 +126,21 @@ class Dwarf {
uint64_t line = 0;
};
// Find the file and line number information corresponding to address.
bool findAddress(
uintptr_t address,
LocationInfoMode mode,
LocationInfo& info,
LocationInfo inlineInfo[],
uint32_t maxNumInline = kMaxLocationInfoPerFrame) const;
/**
* Find the file and line number information corresponding to address.
*/
bool findAddress(uintptr_t address, LocationInfo& info, LocationInfoMode mode)
const;
private:
static bool
findDebugInfoOffset(uintptr_t address, StringPiece aranges, uint64_t& offset);
void init();
bool findLocation(
uintptr_t address,
StringPiece& infoEntry,
LocationInfo& info) const;
const ElfFile* elf_;
......@@ -180,78 +169,18 @@ class Dwarf {
folly::StringPiece data_;
};
struct AttributeSpec {
uint64_t name = 0;
uint64_t form = 0;
explicit operator bool() const {
return name != 0 || form != 0;
}
};
// Abbreviation for a Debugging Information Entry.
struct DIEAbbreviation {
uint64_t code;
uint64_t tag;
bool hasChildren = false;
folly::StringPiece attributes;
};
// Maximum number of DIEAbbreviation to cache in a compilation unit. Used to
// speed up inline function lookup.
static const uint32_t kMaxAbbreviationEntries = 1000;
// A top level chunk in the .debug_info that contains a compilation unit.
struct CompilationUnit {
// Offset in .debug_info of this compilation unit.
uint64_t offset;
uint64_t size;
bool is64Bit;
uint8_t version;
uint8_t addrSize;
uint64_t abbrevOffset;
// Offset in .debug_info for the first DIE in this compilation unit.
uint64_t firstDie;
// Only the CompilationUnit that contains the caller functions needs this
// cache.
// Indexed by (abbr.code - 1) if (abbr.code - 1) < abbrCache.size();
folly::Range<DIEAbbreviation*> abbrCache;
};
// Debugging information entry to define a low-level representation of a
// source program. Each debugging information entry consists of an identifying
// tag and a series of attributes. An entry, or group of entries together,
// provide a description of a corresponding entity in the source program.
struct Die {
const CompilationUnit* cu;
// Offset within debug info.
uint64_t offset;
bool is64Bit;
uint64_t code;
// Offset from start to first attribute
uint8_t attrOffset;
// If we know where the next sibling is (eg via DW_AT_sibling), and
// it fits in uint32_t, the delta we need to add to offset to get
// there; otherwise zero.
uint32_t siblingDelta;
// If we know where the next die is, and it fits in uint32_t, this
// is the delta we need to add to offset to get there; otherwise zero.
// if there are no children, this will be the same as sibling.
uint32_t nextDieDelta;
DIEAbbreviation abbr;
};
bool hasChildren;
struct Attribute {
AttributeSpec spec;
Die* die;
boost::variant<uint64_t, folly::StringPiece> attrValue;
uint64_t name;
uint64_t form;
};
struct CodeLocation {
CompilationUnit cu;
Die die;
uint64_t line;
folly::StringPiece attributes;
};
// Interpreter for the line number bytecode VM
......@@ -263,12 +192,6 @@ class Dwarf {
bool findAddress(uintptr_t address, Path& file, uint64_t& line);
// Gets full file name at given index including directory.
Path getFullFileName(uint64_t index) const {
auto fn = getFileName(index);
return Path({}, getIncludeDirectory(fn.directoryIndex), fn.relativeName);
}
private:
void init();
void reset();
......@@ -338,82 +261,27 @@ class Dwarf {
uint64_t discriminator_;
};
using AttributeValue = boost::variant<uint64_t, folly::StringPiece>;
// Finds the Compilation Unit starting at offset.
void getCompilationUnit(
Dwarf::CompilationUnit& cu,
uint64_t offset,
Dwarf::DIEAbbreviation* abbrs) const;
// Finds the Compilation Unit that contains offset.
CompilationUnit findCompilationUnit(uint64_t offset) const;
// cu must exist during the life cycle of created Die.
Die getDieAtOffset(const CompilationUnit& cu, uint64_t offset) const;
Die getNextSibling(Die& die) const;
// Reads an abbreviation from a StringPiece, return true if at end; advance sp
// Read an abbreviation from a StringPiece, return true if at end; advance sp
static bool readAbbreviation(folly::StringPiece& sp, DIEAbbreviation& abbr);
// Gets abbreviation corresponding to a code, in the chunk starting at
// Get abbreviation corresponding to a code, in the chunk starting at
// offset in the .debug_abbrev section
DIEAbbreviation getAbbreviation(uint64_t code, uint64_t offset) const;
// Reads attribute name and form from sp.
static AttributeSpec readAttributeSpec(folly::StringPiece& sp);
// Reads attribute value of given DIE
Attribute readAttribute(Die& die, AttributeSpec& spec, folly::StringPiece& sp)
const;
// Read one attribute <name, form> pair, advance sp; returns <0, 0> at end.
static DIEAbbreviation::Attribute readAttribute(folly::StringPiece& sp);
// Finds a subprogram debugging info entry that contains a given address among
// children of given die. Depth first search.
void findSubProgramDieForAddress(
Dwarf::Die& die,
uint64_t address,
Dwarf::Die& subprogram) const;
// Finds inlined subroutine DIEs and their caller lines that contains a given
// address among children of given die. Depth first search.
void findInlinedSubroutineDieForAddress(
const CompilationUnit& cu,
Dwarf::Die& die,
uint64_t address,
CodeLocation* isrLoc,
uint32_t& numFound) const;
// Iterates over all children of a debugging info entry, calling the given
// callable for each. Iteration is stopped early if any of the calls return
// false.
template <typename F>
void forEachChild(Die& die, F&& f) const;
// Iterates over all attributes of a debugging info entry, calling the given
// callable for each. Iteration is stopped early if any of the calls return
// false.
template <typename F>
void forEachAttribute(Die& die, F&& f) const;
// Iterate over all attributes of the given DIE, and move to its first child
// if any or its next sibling.
void moveToNextDie(Dwarf::Die& die) const;
// Read one attribute value, advance sp
typedef boost::variant<uint64_t, folly::StringPiece> AttributeValue;
AttributeValue
readAttributeValue(folly::StringPiece& sp, uint64_t form, bool is64Bit) const;
// Get an ELF section by name, return true if found
bool getSection(const char* name, folly::StringPiece* section) const;
// Gets a string from the .debug_str section
// Get a string from the .debug_str section
folly::StringPiece getStringFromStringSection(uint64_t offset) const;
// Finds location info (file and line) for a given address in the given
// compilation unit.
bool findLocation(
uintptr_t address,
const LocationInfoMode mode,
const CompilationUnit& unit,
LocationInfo& info,
LocationInfo inlineInfo[],
uint32_t maxNumInline = kMaxLocationInfoPerFrame) const;
folly::StringPiece info_; // .debug_info
folly::StringPiece abbrev_; // .debug_abbrev
folly::StringPiece aranges_; // .debug_aranges
......
......@@ -76,15 +76,8 @@ void SymbolizedFrame::set(
file_ = file;
name = file->getSymbolName(sym);
location.name = name;
Dwarf(file.get())
.findAddress(
address,
mode,
location,
inlineLocations,
Dwarf::kMaxLocationInfoPerFrame);
Dwarf(file.get()).findAddress(address, location, mode);
}
Symbolizer::Symbolizer(
......@@ -222,19 +215,52 @@ void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
doPrint(formatter.format(address));
}
const char padBuf[] = " ";
folly::StringPiece pad(
padBuf, sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
color(kFunctionColor);
if (!frame.found) {
doPrint(" (not found)");
return;
}
printLocationInfo(frame.location, false);
for (const Dwarf::LocationInfo& location : frame.inlineLocations) {
if (location.empty) {
break;
if (!frame.name || frame.name[0] == '\0') {
doPrint(" (unknown)");
} else {
char demangledBuf[2048];
demangle(frame.name, demangledBuf, sizeof(demangledBuf));
doPrint(" ");
doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
}
if (!(options_ & NO_FILE_AND_LINE)) {
color(kFileColor);
char fileBuf[PATH_MAX];
fileBuf[0] = '\0';
if (frame.location.hasFileAndLine) {
frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
doPrint("\n");
printLocationInfo(location, true);
doPrint(pad);
doPrint(fileBuf);
char buf[22];
uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
doPrint(":");
doPrint(StringPiece(buf, n));
}
if (frame.location.hasMainFile) {
char mainFileBuf[PATH_MAX];
mainFileBuf[0] = '\0';
frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
doPrint("\n");
doPrint(pad);
doPrint("-> ");
doPrint(mainFileBuf);
}
}
}
}
......@@ -286,60 +312,6 @@ void SymbolizePrinter::println(
}
}
void SymbolizePrinter::printLocationInfo(
const Dwarf::LocationInfo& location,
bool isInline) {
const char padBuf[] = " ";
folly::StringPiece pad(
padBuf, sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
// Inline function has no address in stack trace.
if (isInline) {
doPrint(pad);
}
color(kFunctionColor);
if (location.name.empty()) {
doPrint(" (unknown)");
} else if (isInline) {
doPrint(location.name);
} else {
char demangledBuf[2048];
demangle(location.name.data(), demangledBuf, sizeof(demangledBuf));
doPrint(" ");
doPrint(demangledBuf[0] == '\0' ? location.name : demangledBuf);
}
if (!(options_ & NO_FILE_AND_LINE)) {
color(kFileColor);
char fileBuf[PATH_MAX];
fileBuf[0] = '\0';
if (location.hasFileAndLine) {
location.file.toBuffer(fileBuf, sizeof(fileBuf));
doPrint("\n");
doPrint(pad);
doPrint(fileBuf);
char buf[22];
uint32_t n = uint64ToBufferUnsafe(location.line, buf);
doPrint(":");
doPrint(StringPiece(buf, n));
}
if (location.hasMainFile) {
char mainFileBuf[PATH_MAX];
mainFileBuf[0] = '\0';
location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
if (!location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
doPrint("\n");
doPrint(pad);
doPrint("-> ");
doPrint(mainFileBuf);
}
}
}
}
namespace {
int getFD(const std::ios& stream) {
......
......@@ -56,12 +56,6 @@ struct SymbolizedFrame {
bool found = false;
const char* name = nullptr;
Dwarf::LocationInfo location;
// If stack trace happens on an inline function call, more than one locations
// need to be printed. First element is always the location of caller
// function, and the subsequent locations are inline function locations if
// applicable. std::vector is not used here in favor of signal-safe and memory
// constrained situations like std::bad_alloc.
Dwarf::LocationInfo inlineLocations[Dwarf::kMaxLocationInfoPerFrame];
/**
* Demangle the name and return it. Not async-signal-safe; allocates memory.
......@@ -259,9 +253,6 @@ class SymbolizePrinter {
private:
void printTerse(uintptr_t address, const SymbolizedFrame& frame);
void printLocationInfo(
const Dwarf::LocationInfo& locationInfo,
bool isInline);
virtual void doPrint(StringPiece sp) = 0;
static constexpr std::array<const char*, Color::NUM> kColorMap = {{
......
......@@ -33,9 +33,7 @@ void run(Dwarf::LocationInfoMode mode, size_t n) {
suspender.dismiss();
for (size_t i = 0; i < n; i++) {
Dwarf::LocationInfo info;
auto inlineInfo =
std::array<Dwarf::LocationInfo, Dwarf::kMaxLocationInfoPerFrame>();
dwarf.findAddress(uintptr_t(&dummy), mode, info, inlineInfo.data());
dwarf.findAddress(uintptr_t(&dummy), info, mode);
}
}
......@@ -49,10 +47,6 @@ BENCHMARK(DwarfFindAddressFull, n) {
run(folly::symbolizer::Dwarf::LocationInfoMode::FULL, n);
}
BENCHMARK(DwarfFindAddressFullWithInline, n) {
run(folly::symbolizer::Dwarf::LocationInfoMode::FULL_WITH_INLINE, n);
}
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
......
......@@ -14,7 +14,6 @@
* limitations under the License.
*/
#include <folly/experimental/symbolizer/test/SymbolizerTest.h>
#include <folly/experimental/symbolizer/Symbolizer.h>
#include <cstdlib>
......@@ -47,6 +46,16 @@ TEST(Symbolizer, Single) {
EXPECT_EQ("SymbolizerTest.cpp", basename.str());
}
FrameArray<100>* framesToFill{nullptr};
int comparator(const void* ap, const void* bp) {
getStackTrace(*framesToFill);
int a = *static_cast<const int*>(ap);
int b = *static_cast<const int*>(bp);
return a < b ? -1 : a > b ? 1 : 0;
}
// Test stack frames...
FOLLY_NOINLINE void bar();
......@@ -118,78 +127,6 @@ TEST(SymbolizerTest, SymbolCache) {
}
}
namespace {
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<100>& frames) {
inlineFoo(frames);
}
} // namespace
TEST(SymbolizerTest, InlineFunctionBasic) {
Symbolizer symbolizer(
nullptr, Dwarf::LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames;
inlineBar(frames);
symbolizer.symbolize(frames);
StringSymbolizePrinter printer;
// Stack trace for SymbolizerTest_InlineFunctionBasic_Test function call.
printer.print(4, frames.frames[4]);
EXPECT_STREQ(
printer.str().c_str(),
R"( @ 0000000000000004 folly::symbolizer::test::SymbolizerTest_InlineFunctionBasic_Test::TestBody()
./folly/experimental/symbolizer/test/SymbolizerTest.h:134
-> ./folly/experimental/symbolizer/test/SymbolizerTest.cpp
inlineBar
folly/experimental/symbolizer/test/SymbolizerTest.cpp:124
inlineFoo
folly/experimental/symbolizer/test/SymbolizerTest.h:39)");
}
TEST(SymbolizerTest, InlineFunctionInSeparateFile) {
Symbolizer symbolizer(
nullptr, Dwarf::LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames;
inlineFunctionInSeparateFile(frames);
symbolizer.symbolize(frames);
StringSymbolizePrinter printer;
// Stack trace for SymbolizerTest_InlineFunctionInSeparateFile_Test function
// call.
printer.print(4, frames.frames[4]);
EXPECT_STREQ(
printer.str().c_str(),
R"( @ 0000000000000004 folly::symbolizer::test::SymbolizerTest_InlineFunctionInSeparateFile_Test::TestBody()
./folly/experimental/symbolizer/test/SymbolizerTest.h:155
-> ./folly/experimental/symbolizer/test/SymbolizerTest.cpp
inlineFunctionInSeparateFile
folly/experimental/symbolizer/test/SymbolizerTest.h:44
inlineFoo
folly/experimental/symbolizer/test/SymbolizerTest.h:39)");
}
// Not supported yet.
TEST(SymbolizerTest, InlineFunctionInClass) {
Symbolizer symbolizer(
nullptr, Dwarf::LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames;
ClassWithInlineFunction cif;
cif.inlineFunctionInClass(frames);
symbolizer.symbolize(frames);
StringSymbolizePrinter printer;
// Stack trace for SymbolizerTest_InlineFunctionInClass_Test function call.
printer.print(4, frames.frames[4]);
EXPECT_STREQ(
printer.str().c_str(),
R"( @ 0000000000000004 folly::symbolizer::test::SymbolizerTest_InlineFunctionInClass_Test::TestBody()
./folly/experimental/symbolizer/test/SymbolizerTest.h:39
-> ./folly/experimental/symbolizer/test/SymbolizerTest.cpp)");
}
} // namespace test
} // namespace symbolizer
} // namespace folly
......
/*
* Copyright 2019-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/container/Array.h>
#include <folly/experimental/symbolizer/Symbolizer.h>
namespace folly {
namespace symbolizer {
namespace test {
FrameArray<100>* framesToFill{nullptr};
int comparator(const void* ap, const void* bp) {
getStackTrace(*framesToFill);
int a = *static_cast<const int*>(ap);
int b = *static_cast<const int*>(bp);
return a < b ? -1 : a > b ? 1 : 0;
}
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<100>& frames) {
framesToFill = &frames;
int a[2] = {1, 2};
// Use qsort, which is in a different library
qsort(a, 2, sizeof(int), comparator);
framesToFill = nullptr;
}
FOLLY_ALWAYS_INLINE void inlineFunctionInSeparateFile(FrameArray<100>& frames) {
inlineFoo(frames);
}
class ClassWithInlineFunction {
public:
FOLLY_ALWAYS_INLINE void inlineFunctionInClass(FrameArray<100>& frames) {
inlineFoo(frames);
}
};
} // namespace test
} // namespace symbolizer
} // namespace folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment