in folly/experimental/symbolizer/Symbolizer.cpp [139:270]
size_t Symbolizer::symbolize(
folly::Range<const uintptr_t*> addrs,
folly::Range<SymbolizedFrame*> frames) {
size_t addrCount = addrs.size();
size_t frameCount = frames.size();
FOLLY_SAFE_CHECK(addrCount <= frameCount, "Not enough frames.");
size_t remaining = addrCount;
auto const dbg = detail::get_r_debug();
if (dbg == nullptr) {
return 0;
}
if (dbg->r_version != 1) {
return 0;
}
char selfPath[PATH_MAX + 8];
ssize_t selfSize;
if ((selfSize = readlink(exePath_.c_str(), selfPath, PATH_MAX + 1)) == -1) {
// Something has gone terribly wrong.
return 0;
}
selfPath[selfSize] = '\0';
for (size_t i = 0; i < addrCount; i++) {
frames[i].addr = addrs[i];
}
// Find out how many frames were filled in.
auto countFrames = [](folly::Range<SymbolizedFrame*> framesRange) {
return std::distance(
framesRange.begin(),
std::find_if(framesRange.begin(), framesRange.end(), [&](auto frame) {
return !frame.found;
}));
};
for (auto lmap = dbg->r_map; lmap != nullptr && remaining != 0;
lmap = lmap->l_next) {
// The empty string is used in place of the filename for the link_map
// corresponding to the running executable. Additionally, the `l_addr' is
// 0 and the link_map appears to be first in the list---but none of this
// behavior appears to be documented, so checking for the empty string is
// as good as anything.
auto const objPath = lmap->l_name[0] != '\0' ? lmap->l_name : selfPath;
auto const elfFile = cache_->getFile(objPath);
if (!elfFile) {
continue;
}
for (size_t i = 0; i < addrCount && remaining != 0; ++i) {
auto& frame = frames[i];
if (frame.found) {
continue;
}
auto const addr = frame.addr;
if (symbolCache_) {
// Need a write lock, because EvictingCacheMap brings found item to
// front of eviction list.
auto lockedSymbolCache = symbolCache_->wlock();
auto const iter = lockedSymbolCache->find(addr);
if (iter != lockedSymbolCache->end()) {
size_t numCachedFrames = countFrames(folly::range(iter->second));
// 1 entry in cache is the non-inlined function call and that one
// already has space reserved at `frames[i]`
auto numInlineFrames = numCachedFrames - 1;
if (numInlineFrames <= frameCount - addrCount) {
// Move the rest of the frames to make space for inlined frames.
std::move_backward(
frames.begin() + i + 1,
frames.begin() + addrCount,
frames.begin() + addrCount + numInlineFrames);
// Overwrite frames[i] too (the non-inlined function call entry).
std::copy(
iter->second.begin(),
iter->second.begin() + numInlineFrames + 1,
frames.begin() + i);
i += numInlineFrames;
addrCount += numInlineFrames;
}
continue;
}
}
// Get the unrelocated, ELF-relative address by normalizing via the
// address at which the object is loaded.
auto const adjusted = addr - reinterpret_cast<uintptr_t>(lmap->l_addr);
size_t numInlined = 0;
if (elfFile->getSectionContainingAddress(adjusted)) {
if (mode_ == LocationInfoMode::FULL_WITH_INLINE &&
frameCount > addrCount) {
size_t maxInline = std::min<size_t>(
kMaxInlineLocationInfoPerFrame, frameCount - addrCount);
// First use the trailing empty frames (index starting from addrCount)
// to get the inline call stack, then rotate these inline functions
// before the caller at `frame[i]`.
folly::Range<SymbolizedFrame*> inlineFrameRange(
frames.begin() + addrCount,
frames.begin() + addrCount + maxInline);
setSymbolizedFrame(frame, elfFile, adjusted, mode_, inlineFrameRange);
numInlined = countFrames(inlineFrameRange);
// Rotate inline frames right before its caller frame.
std::rotate(
frames.begin() + i,
frames.begin() + addrCount,
frames.begin() + addrCount + numInlined);
addrCount += numInlined;
} else {
setSymbolizedFrame(frame, elfFile, adjusted, mode_);
}
--remaining;
if (symbolCache_) {
// frame may already have been set here. That's ok, we'll just
// overwrite, which doesn't cause a correctness problem.
CachedSymbolizedFrames cacheFrames;
std::copy(
frames.begin() + i,
frames.begin() + i + std::min(numInlined + 1, cacheFrames.size()),
cacheFrames.begin());
symbolCache_->wlock()->set(addr, cacheFrames);
}
// Skip over the newly added inlined items.
i += numInlined;
}
}
}
return addrCount;
}