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

Change symbolizer cache to support frames with inline functions.

Summary:
The general idea is to change cache to a mapping between an address and array of frames instead of just one frame.

Also changed Dwarf to directly fill frames array with inline function calls, instead of allocating extra LocationInfo array.

Moved shared data structure like LocationInfo and SymbolizedFrame into a separate file so that it can used in both Dwarf.cpp and Symbolizer.cpp.

Reviewed By: luciang

Differential Revision: D18825831

fbshipit-source-id: fe963a1adbbc6f0055c466b91708eee732abc141
parent 616f02b2
...@@ -78,7 +78,7 @@ void printExceptionInfo( ...@@ -78,7 +78,7 @@ void printExceptionInfo(
Symbolizer symbolizer( Symbolizer symbolizer(
(options & SymbolizePrinter::NO_FILE_AND_LINE) (options & SymbolizePrinter::NO_FILE_AND_LINE)
? Dwarf::LocationInfoMode::DISABLED ? LocationInfoMode::DISABLED
: Symbolizer::kDefaultLocationInfoMode); : Symbolizer::kDefaultLocationInfoMode);
symbolizer.symbolize(addresses, frames.data(), frameCount); symbolizer.symbolize(addresses, frames.data(), frameCount);
......
...@@ -335,55 +335,6 @@ void readCompilationUnitAbbrs( ...@@ -335,55 +335,6 @@ void readCompilationUnitAbbrs(
} }
} }
// Simplify a path -- as much as we can while not moving data around...
void simplifyPath(folly::StringPiece& sp) {
// Strip leading slashes and useless patterns (./), leaving one initial
// slash.
for (;;) {
if (sp.empty()) {
return;
}
// Strip leading slashes, leaving one.
while (sp.startsWith("//")) {
sp.advance(1);
}
if (sp.startsWith("/./")) {
// Note 2, not 3, to keep it absolute
sp.advance(2);
continue;
}
if (sp.removePrefix("./")) {
// Also remove any subsequent slashes to avoid making this path absolute.
while (sp.startsWith('/')) {
sp.advance(1);
}
continue;
}
break;
}
// Strip trailing slashes and useless patterns (/.).
for (;;) {
if (sp.empty()) {
return;
}
// Strip trailing slashes, except when this is the root path.
while (sp.size() > 1 && sp.removeSuffix('/')) {
}
if (sp.removeSuffix("/.")) {
continue;
}
break;
}
}
} // namespace } // namespace
Dwarf::Dwarf(const ElfFile* elf) Dwarf::Dwarf(const ElfFile* elf)
...@@ -403,122 +354,6 @@ Dwarf::Dwarf(const ElfFile* elf) ...@@ -403,122 +354,6 @@ Dwarf::Dwarf(const ElfFile* elf)
Dwarf::Section::Section(folly::StringPiece d) : is64Bit_(false), data_(d) {} Dwarf::Section::Section(folly::StringPiece d) : is64Bit_(false), data_(d) {}
Dwarf::Path::Path(
folly::StringPiece baseDir,
folly::StringPiece subDir,
folly::StringPiece file)
: baseDir_(baseDir), subDir_(subDir), file_(file) {
using std::swap;
// Normalize
if (file_.empty()) {
baseDir_.clear();
subDir_.clear();
return;
}
if (file_[0] == '/') {
// file_ is absolute
baseDir_.clear();
subDir_.clear();
}
if (!subDir_.empty() && subDir_[0] == '/') {
baseDir_.clear(); // subDir_ is absolute
}
simplifyPath(baseDir_);
simplifyPath(subDir_);
simplifyPath(file_);
// Make sure it's never the case that baseDir_ is empty, but subDir_ isn't.
if (baseDir_.empty()) {
swap(baseDir_, subDir_);
}
}
size_t Dwarf::Path::size() const {
size_t size = 0;
bool needsSlash = false;
if (!baseDir_.empty()) {
size += baseDir_.size();
needsSlash = !baseDir_.endsWith('/');
}
if (!subDir_.empty()) {
size += needsSlash;
size += subDir_.size();
needsSlash = !subDir_.endsWith('/');
}
if (!file_.empty()) {
size += needsSlash;
size += file_.size();
}
return size;
}
size_t Dwarf::Path::toBuffer(char* buf, size_t bufSize) const {
size_t totalSize = 0;
bool needsSlash = false;
auto append = [&](folly::StringPiece sp) {
if (bufSize >= 2) {
size_t toCopy = std::min(sp.size(), bufSize - 1);
memcpy(buf, sp.data(), toCopy);
buf += toCopy;
bufSize -= toCopy;
}
totalSize += sp.size();
};
if (!baseDir_.empty()) {
append(baseDir_);
needsSlash = !baseDir_.endsWith('/');
}
if (!subDir_.empty()) {
if (needsSlash) {
append("/");
}
append(subDir_);
needsSlash = !subDir_.endsWith('/');
}
if (!file_.empty()) {
if (needsSlash) {
append("/");
}
append(file_);
}
if (bufSize) {
*buf = '\0';
}
assert(totalSize == size());
return totalSize;
}
void Dwarf::Path::toString(std::string& dest) const {
size_t initialSize = dest.size();
dest.reserve(initialSize + size());
if (!baseDir_.empty()) {
dest.append(baseDir_.begin(), baseDir_.end());
}
if (!subDir_.empty()) {
if (!dest.empty() && dest.back() != '/') {
dest.push_back('/');
}
dest.append(subDir_.begin(), subDir_.end());
}
if (!file_.empty()) {
if (!dest.empty() && dest.back() != '/') {
dest.push_back('/');
}
dest.append(file_.begin(), file_.end());
}
assert(dest.size() == initialSize + size());
}
// Next chunk in section // Next chunk in section
bool Dwarf::Section::next(folly::StringPiece& chunk) { bool Dwarf::Section::next(folly::StringPiece& chunk) {
chunk = data_; chunk = data_;
...@@ -619,7 +454,7 @@ bool Dwarf::findLocation( ...@@ -619,7 +454,7 @@ bool Dwarf::findLocation(
const LocationInfoMode mode, const LocationInfoMode mode,
detail::CompilationUnit& cu, detail::CompilationUnit& cu,
LocationInfo& locationInfo, LocationInfo& locationInfo,
folly::Range<Dwarf::LocationInfo*> inlineLocationInfo) const { folly::Range<SymbolizedFrame*> inlineFrames) const {
detail::Die die = getDieAtOffset(cu, cu.firstDie); detail::Die die = getDieAtOffset(cu, cu.firstDie);
FOLLY_SAFE_CHECK( FOLLY_SAFE_CHECK(
die.abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry"); die.abbr.tag == DW_TAG_compile_unit, "expecting compile unit entry");
...@@ -670,8 +505,8 @@ bool Dwarf::findLocation( ...@@ -670,8 +505,8 @@ bool Dwarf::findLocation(
lineVM.findAddress(address, locationInfo.file, locationInfo.line); lineVM.findAddress(address, locationInfo.file, locationInfo.line);
// Look up whether inline function. // Look up whether inline function.
if (mode == Dwarf::LocationInfoMode::FULL_WITH_INLINE && if (mode == LocationInfoMode::FULL_WITH_INLINE && !inlineFrames.empty() &&
!inlineLocationInfo.empty() && locationInfo.hasFileAndLine) { locationInfo.hasFileAndLine) {
// Re-get the compilation unit with abbreviation cached. // Re-get the compilation unit with abbreviation cached.
std::array<detail::DIEAbbreviation, kMaxAbbreviationEntries> abbrs; std::array<detail::DIEAbbreviation, kMaxAbbreviationEntries> abbrs;
cu.abbrCache = folly::range(abbrs); cu.abbrCache = folly::range(abbrs);
...@@ -681,24 +516,32 @@ bool Dwarf::findLocation( ...@@ -681,24 +516,32 @@ bool Dwarf::findLocation(
// Subprogram is the DIE of caller function. // Subprogram is the DIE of caller function.
if (subprogram.abbr.hasChildren) { if (subprogram.abbr.hasChildren) {
size_t size = std::min<size_t>( size_t size = std::min<size_t>(
Dwarf::kMaxInlineLocationInfoPerFrame, inlineLocationInfo.size()); Dwarf::kMaxInlineLocationInfoPerFrame, inlineFrames.size());
detail::CodeLocation isrLocs[size]; detail::CodeLocation inlineLocs[size];
auto isrLocsRange = folly::Range<detail::CodeLocation*>(isrLocs, size); auto inlineLocsRange =
findInlinedSubroutineDieForAddress(cu, subprogram, address, isrLocsRange); folly::Range<detail::CodeLocation*>(inlineLocs, size);
uint32_t numFound = isrLocsRange.data() - isrLocs; findInlinedSubroutineDieForAddress(
auto* prevLocation = &locationInfo; cu, subprogram, address, inlineLocsRange);
auto* nextLocation = inlineLocationInfo.data(); size_t numFound = inlineLocsRange.data() - inlineLocs;
auto* isrLoc = isrLocs;
// The line in locationInfo is the line in the deepest inline functions, // The line in locationInfo is the line in the deepest inline functions,
// and the lines in inlineLocationInfo are the line of callers in call // and the lines in inlineLocationInfo are the line of callers in call
// sequence. A shuffle is needed to match lines with their function name. // sequence. A shuffle is needed to match lines with their function name.
uint64_t callerLine = locationInfo.line; uint64_t callerLine = locationInfo.line;
while (numFound > 0) { inlineLocsRange =
prevLocation->line = isrLoc->line; folly::Range<detail::CodeLocation*>(inlineLocs, numFound);
// Put inline frames from inside to outside (callee is before caller).
std::reverse(inlineLocsRange.begin(), inlineLocsRange.end());
auto inlineFrameIter = inlineFrames.begin();
for (auto inlineLocIter = inlineLocsRange.begin();
inlineLocIter != inlineLocsRange.end();
inlineLocIter++, inlineFrameIter++) {
inlineFrameIter->location.line = callerLine;
callerLine = inlineLocIter->line;
folly::Optional<folly::StringPiece> name; folly::Optional<folly::StringPiece> name;
folly::Optional<uint64_t> file; folly::Optional<uint64_t> file;
forEachAttribute(
forEachAttribute(cu, isrLoc->die, [&](const detail::Attribute& attr) { cu, inlineLocIter->die, [&](const detail::Attribute& attr) {
switch (attr.spec.name) { switch (attr.spec.name) {
case DW_AT_name: case DW_AT_name:
name = boost::get<StringPiece>(attr.attrValue); name = boost::get<StringPiece>(attr.attrValue);
...@@ -713,15 +556,15 @@ bool Dwarf::findLocation( ...@@ -713,15 +556,15 @@ bool Dwarf::findLocation(
if (!name.has_value() || !file.has_value()) { if (!name.has_value() || !file.has_value()) {
break; break;
} }
nextLocation->hasFileAndLine = true;
nextLocation->name = name.value(); inlineFrameIter->found = true;
nextLocation->file = lineVM.getFullFileName(file.value()); inlineFrameIter->addr = address;
prevLocation = nextLocation; inlineFrameIter->name = name.value().data();
nextLocation++; inlineFrameIter->location.hasFileAndLine = true;
isrLoc++; inlineFrameIter->location.name = name.value();
numFound--; inlineFrameIter->location.file = lineVM.getFullFileName(file.value());
} }
prevLocation->line = callerLine; locationInfo.line = callerLine;
} }
} }
...@@ -732,7 +575,7 @@ bool Dwarf::findAddress( ...@@ -732,7 +575,7 @@ bool Dwarf::findAddress(
uintptr_t address, uintptr_t address,
LocationInfoMode mode, LocationInfoMode mode,
LocationInfo& locationInfo, LocationInfo& locationInfo,
folly::Range<Dwarf::LocationInfo*> inlineLocationInfo) const { folly::Range<SymbolizedFrame*> inlineFrames) const {
if (mode == LocationInfoMode::DISABLED) { if (mode == LocationInfoMode::DISABLED) {
return false; return false;
} }
...@@ -748,7 +591,7 @@ bool Dwarf::findAddress( ...@@ -748,7 +591,7 @@ bool Dwarf::findAddress(
if (findDebugInfoOffset(address, debugAranges_, offset)) { if (findDebugInfoOffset(address, debugAranges_, offset)) {
// Read compilation unit header from .debug_info // Read compilation unit header from .debug_info
auto unit = getCompilationUnit(debugInfo_, offset); auto unit = getCompilationUnit(debugInfo_, offset);
findLocation(address, mode, unit, locationInfo, inlineLocationInfo); findLocation(address, mode, unit, locationInfo, inlineFrames);
return locationInfo.hasFileAndLine; return locationInfo.hasFileAndLine;
} else if (mode == LocationInfoMode::FAST) { } else if (mode == LocationInfoMode::FAST) {
// NOTE: Clang (when using -gdwarf-aranges) doesn't generate entries // NOTE: Clang (when using -gdwarf-aranges) doesn't generate entries
...@@ -771,7 +614,7 @@ bool Dwarf::findAddress( ...@@ -771,7 +614,7 @@ bool Dwarf::findAddress(
while (offset < debugInfo_.size() && !locationInfo.hasFileAndLine) { while (offset < debugInfo_.size() && !locationInfo.hasFileAndLine) {
auto unit = getCompilationUnit(debugInfo_, offset); auto unit = getCompilationUnit(debugInfo_, offset);
offset += unit.size; offset += unit.size;
findLocation(address, mode, unit, locationInfo, inlineLocationInfo); findLocation(address, mode, unit, locationInfo, inlineFrames);
} }
return locationInfo.hasFileAndLine; return locationInfo.hasFileAndLine;
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <folly/Function.h> #include <folly/Function.h>
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/experimental/symbolizer/Elf.h> #include <folly/experimental/symbolizer/Elf.h>
#include <folly/experimental/symbolizer/SymbolizedFrame.h>
namespace folly { namespace folly {
namespace symbolizer { namespace symbolizer {
...@@ -80,39 +81,18 @@ class Dwarf { ...@@ -80,39 +81,18 @@ class Dwarf {
/** Create a DWARF parser around an ELF file. */ /** Create a DWARF parser around an ELF file. */
explicit Dwarf(const ElfFile* elf); explicit Dwarf(const ElfFile* elf);
/**
* Represent a file path a s collection of three parts (base directory,
* subdirectory, and file).
*/
class Path;
enum class LocationInfoMode {
// Don't resolve location info.
DISABLED,
// Perform CU lookup using .debug_aranges (might be incomplete).
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 * More than one location info may exist if current frame is an inline
* function call. * function call.
*/ */
static const uint32_t kMaxInlineLocationInfoPerFrame = 3; static const uint32_t kMaxInlineLocationInfoPerFrame = 3;
/** Contains location info like file name, line number, etc. */
struct LocationInfo;
/** Find the file and line number information corresponding to address. */ /** Find the file and line number information corresponding to address. */
bool findAddress( bool findAddress(
uintptr_t address, uintptr_t address,
LocationInfoMode mode, LocationInfoMode mode,
LocationInfo& info, LocationInfo& info,
folly::Range<Dwarf::LocationInfo*> inlineInfo = {}) const; folly::Range<SymbolizedFrame*> inlineFrames = {}) const;
private: private:
using AttributeValue = boost::variant<uint64_t, folly::StringPiece>; using AttributeValue = boost::variant<uint64_t, folly::StringPiece>;
...@@ -137,7 +117,7 @@ class Dwarf { ...@@ -137,7 +117,7 @@ class Dwarf {
const LocationInfoMode mode, const LocationInfoMode mode,
detail::CompilationUnit& unit, detail::CompilationUnit& unit,
LocationInfo& info, LocationInfo& info,
folly::Range<Dwarf::LocationInfo*> inlineInfo = {}) const; folly::Range<SymbolizedFrame*> inlineFrames = {}) const;
/** /**
* Finds a subprogram debugging info entry that contains a given address among * Finds a subprogram debugging info entry that contains a given address among
...@@ -227,61 +207,6 @@ class Dwarf::Section { ...@@ -227,61 +207,6 @@ class Dwarf::Section {
folly::StringPiece data_; folly::StringPiece data_;
}; };
class Dwarf::Path {
public:
Path() {}
Path(
folly::StringPiece baseDir,
folly::StringPiece subDir,
folly::StringPiece file);
folly::StringPiece baseDir() const {
return baseDir_;
}
folly::StringPiece subDir() const {
return subDir_;
}
folly::StringPiece file() const {
return file_;
}
size_t size() const;
/**
* Copy the Path to a buffer of size bufSize.
*
* toBuffer behaves like snprintf: It will always null-terminate the
* buffer (so it will copy at most bufSize-1 bytes), and it will return
* the number of bytes that would have been written if there had been
* enough room, so, if toBuffer returns a value >= bufSize, the output
* was truncated.
*/
size_t toBuffer(char* buf, size_t bufSize) const;
void toString(std::string& dest) const;
std::string toString() const {
std::string s;
toString(s);
return s;
}
private:
folly::StringPiece baseDir_;
folly::StringPiece subDir_;
folly::StringPiece file_;
};
struct Dwarf::LocationInfo {
bool hasFileAndLine = false;
bool hasMainFile = false;
// Function name in call stack.
folly::StringPiece name;
Path mainFile;
Path file;
uint64_t line = 0;
};
class Dwarf::LineNumberVM { class Dwarf::LineNumberVM {
public: public:
LineNumberVM( LineNumberVM(
...@@ -370,9 +295,5 @@ class Dwarf::LineNumberVM { ...@@ -370,9 +295,5 @@ class Dwarf::LineNumberVM {
uint64_t discriminator_; uint64_t discriminator_;
}; };
inline std::ostream& operator<<(std::ostream& out, const Dwarf::Path& path) {
return out << path.toString();
}
} // namespace symbolizer } // namespace symbolizer
} // namespace folly } // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#include <folly/experimental/symbolizer/SymbolizedFrame.h>
namespace folly {
namespace symbolizer {
namespace {
// Simplify a path -- as much as we can while not moving data around...
void simplifyPath(StringPiece& sp) {
// Strip leading slashes and useless patterns (./), leaving one initial
// slash.
for (;;) {
if (sp.empty()) {
return;
}
// Strip leading slashes, leaving one.
while (sp.startsWith("//")) {
sp.advance(1);
}
if (sp.startsWith("/./")) {
// Note 2, not 3, to keep it absolute
sp.advance(2);
continue;
}
if (sp.removePrefix("./")) {
// Also remove any subsequent slashes to avoid making this path absolute.
while (sp.startsWith('/')) {
sp.advance(1);
}
continue;
}
break;
}
// Strip trailing slashes and useless patterns (/.).
for (;;) {
if (sp.empty()) {
return;
}
// Strip trailing slashes, except when this is the root path.
while (sp.size() > 1 && sp.removeSuffix('/')) {
}
if (sp.removeSuffix("/.")) {
continue;
}
break;
}
}
} // namespace
Path::Path(StringPiece baseDir, StringPiece subDir, StringPiece file)
: baseDir_(baseDir), subDir_(subDir), file_(file) {
using std::swap;
// Normalize
if (file_.empty()) {
baseDir_.clear();
subDir_.clear();
return;
}
if (file_[0] == '/') {
// file_ is absolute
baseDir_.clear();
subDir_.clear();
}
if (!subDir_.empty() && subDir_[0] == '/') {
baseDir_.clear(); // subDir_ is absolute
}
simplifyPath(baseDir_);
simplifyPath(subDir_);
simplifyPath(file_);
// Make sure it's never the case that baseDir_ is empty, but subDir_ isn't.
if (baseDir_.empty()) {
swap(baseDir_, subDir_);
}
}
size_t Path::size() const {
size_t size = 0;
bool needsSlash = false;
if (!baseDir_.empty()) {
size += baseDir_.size();
needsSlash = !baseDir_.endsWith('/');
}
if (!subDir_.empty()) {
size += needsSlash;
size += subDir_.size();
needsSlash = !subDir_.endsWith('/');
}
if (!file_.empty()) {
size += needsSlash;
size += file_.size();
}
return size;
}
size_t Path::toBuffer(char* buf, size_t bufSize) const {
size_t totalSize = 0;
bool needsSlash = false;
auto append = [&](StringPiece sp) {
if (bufSize >= 2) {
size_t toCopy = std::min(sp.size(), bufSize - 1);
memcpy(buf, sp.data(), toCopy);
buf += toCopy;
bufSize -= toCopy;
}
totalSize += sp.size();
};
if (!baseDir_.empty()) {
append(baseDir_);
needsSlash = !baseDir_.endsWith('/');
}
if (!subDir_.empty()) {
if (needsSlash) {
append("/");
}
append(subDir_);
needsSlash = !subDir_.endsWith('/');
}
if (!file_.empty()) {
if (needsSlash) {
append("/");
}
append(file_);
}
if (bufSize) {
*buf = '\0';
}
assert(totalSize == size());
return totalSize;
}
void Path::toString(std::string& dest) const {
size_t initialSize = dest.size();
dest.reserve(initialSize + size());
if (!baseDir_.empty()) {
dest.append(baseDir_.begin(), baseDir_.end());
}
if (!subDir_.empty()) {
if (!dest.empty() && dest.back() != '/') {
dest.push_back('/');
}
dest.append(subDir_.begin(), subDir_.end());
}
if (!file_.empty()) {
if (!dest.empty() && dest.back() != '/') {
dest.push_back('/');
}
dest.append(file_.begin(), file_.end());
}
assert(dest.size() == initialSize + size());
}
} // namespace symbolizer
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 <array>
#include <cstdint>
#include <memory>
#include <string>
#include <folly/Range.h>
#include <folly/experimental/symbolizer/Elf.h>
namespace folly {
namespace symbolizer {
/**
* Represent a file path a s collection of three parts (base directory,
* subdirectory, and file).
*/
class Path {
public:
Path() {}
Path(
folly::StringPiece baseDir,
folly::StringPiece subDir,
folly::StringPiece file);
folly::StringPiece baseDir() const {
return baseDir_;
}
folly::StringPiece subDir() const {
return subDir_;
}
folly::StringPiece file() const {
return file_;
}
size_t size() const;
/**
* Copy the Path to a buffer of size bufSize.
*
* toBuffer behaves like snprintf: It will always null-terminate the
* buffer (so it will copy at most bufSize-1 bytes), and it will return
* the number of bytes that would have been written if there had been
* enough room, so, if toBuffer returns a value >= bufSize, the output
* was truncated.
*/
size_t toBuffer(char* buf, size_t bufSize) const;
void toString(std::string& dest) const;
std::string toString() const {
std::string s;
toString(s);
return s;
}
private:
folly::StringPiece baseDir_;
folly::StringPiece subDir_;
folly::StringPiece file_;
};
inline std::ostream& operator<<(std::ostream& out, const Path& path) {
return out << path.toString();
}
enum class LocationInfoMode {
// Don't resolve location info.
DISABLED,
// Perform CU lookup using .debug_aranges (might be incomplete).
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,
};
/**
* Contains location info like file name, line number, etc.
*/
struct LocationInfo {
bool hasFileAndLine = false;
bool hasMainFile = false;
// Function name in call stack.
folly::StringPiece name;
Path mainFile;
Path file;
uint64_t line = 0;
};
/**
* Frame information: symbol name and location.
*/
struct SymbolizedFrame {
bool found = false;
uintptr_t addr = 0;
// Mangled symbol name. Use `folly::demangle()` to demangle it.
const char* name = nullptr;
LocationInfo location;
std::shared_ptr<ElfFile> file;
void clear() {
*this = SymbolizedFrame();
}
};
template <size_t N>
struct FrameArray {
FrameArray() {}
size_t frameCount = 0;
uintptr_t addresses[N];
SymbolizedFrame frames[N];
};
} // namespace symbolizer
} // namespace folly
...@@ -60,30 +60,33 @@ ElfCache* defaultElfCache() { ...@@ -60,30 +60,33 @@ ElfCache* defaultElfCache() {
return cache; return cache;
} }
} // namespace void setSymbolizedFrame(
SymbolizedFrame& frame,
void SymbolizedFrame::set(
const std::shared_ptr<ElfFile>& file, const std::shared_ptr<ElfFile>& file,
uintptr_t address, uintptr_t address,
Dwarf::LocationInfoMode mode, LocationInfoMode mode,
folly::Range<Dwarf::LocationInfo*> inlineLocations) { folly::Range<SymbolizedFrame*> extraInlineFrames = {}) {
clear(); frame.clear();
found = true; frame.found = true;
auto sym = file->getDefinitionByAddress(address); auto sym = file->getDefinitionByAddress(address);
if (!sym.first) { if (!sym.first) {
return; return;
} }
file_ = file; frame.addr = address;
name = file->getSymbolName(sym); frame.file = file;
frame.name = file->getSymbolName(sym);
Dwarf(file.get()).findAddress(address, mode, location, inlineLocations); Dwarf(file.get())
.findAddress(address, mode, frame.location, extraInlineFrames);
} }
} // namespace
Symbolizer::Symbolizer( Symbolizer::Symbolizer(
ElfCacheBase* cache, ElfCacheBase* cache,
Dwarf::LocationInfoMode mode, LocationInfoMode mode,
size_t symbolCacheSize) size_t symbolCacheSize)
: cache_(cache ? cache : defaultElfCache()), mode_(mode) { : cache_(cache ? cache : defaultElfCache()), mode_(mode) {
if (symbolCacheSize > 0) { if (symbolCacheSize > 0) {
...@@ -125,6 +128,15 @@ void Symbolizer::symbolize( ...@@ -125,6 +128,15 @@ void Symbolizer::symbolize(
frames[i].addr = addrs[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 = _r_debug.r_map; lmap != nullptr && remaining != 0; for (auto lmap = _r_debug.r_map; lmap != nullptr && remaining != 0;
lmap = lmap->l_next) { lmap = lmap->l_next) {
// The empty string is used in place of the filename for the link_map // The empty string is used in place of the filename for the link_map
...@@ -146,16 +158,31 @@ void Symbolizer::symbolize( ...@@ -146,16 +158,31 @@ void Symbolizer::symbolize(
} }
auto const addr = frame.addr; auto const addr = frame.addr;
// Mode FULL_WITH_INLINE (may have multiple frames for an address) is not if (symbolCache_) {
// supported in symbolCache_.
if (symbolCache_ && mode_ != Dwarf::LocationInfoMode::FULL_WITH_INLINE) {
// Need a write lock, because EvictingCacheMap brings found item to // Need a write lock, because EvictingCacheMap brings found item to
// front of eviction list. // front of eviction list.
auto lockedSymbolCache = symbolCache_->wlock(); auto lockedSymbolCache = symbolCache_->wlock();
auto const iter = lockedSymbolCache->find(addr); auto const iter = lockedSymbolCache->find(addr);
if (iter != lockedSymbolCache->end()) { if (iter != lockedSymbolCache->end()) {
frame = iter->second; 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; continue;
} }
} }
...@@ -163,55 +190,43 @@ void Symbolizer::symbolize( ...@@ -163,55 +190,43 @@ void Symbolizer::symbolize(
// Get the unrelocated, ELF-relative address by normalizing via the // Get the unrelocated, ELF-relative address by normalizing via the
// address at which the object is loaded. // address at which the object is loaded.
auto const adjusted = addr - lmap->l_addr; auto const adjusted = addr - lmap->l_addr;
size_t numInlined = 0;
if (elfFile->getSectionContainingAddress(adjusted)) { if (elfFile->getSectionContainingAddress(adjusted)) {
if (mode_ == Dwarf::LocationInfoMode::FULL_WITH_INLINE && if (mode_ == LocationInfoMode::FULL_WITH_INLINE &&
frameCount > addrCount) { frameCount > addrCount) {
size_t maxInline = std::min<size_t>( size_t maxInline = std::min<size_t>(
Dwarf::kMaxInlineLocationInfoPerFrame, frameCount - addrCount); Dwarf::kMaxInlineLocationInfoPerFrame, frameCount - addrCount);
Dwarf::LocationInfo inlineLocations[maxInline]; // First use the trailing empty frames (index starting from addrCount)
folly::Range<Dwarf::LocationInfo*> inlineLocRange( // to get the inline call stack, then rotate these inline functions
inlineLocations, maxInline); // before the caller at `frame[i]`.
frame.set(elfFile, adjusted, mode_, inlineLocRange); folly::Range<SymbolizedFrame*> inlineFrameRange(
frames.begin() + addrCount,
// Find out how many LocationInfo were filled in. frames.begin() + addrCount + maxInline);
size_t numInlined = std::distance( setSymbolizedFrame(frame, elfFile, adjusted, mode_, inlineFrameRange);
inlineLocRange.begin(),
std::find_if(
inlineLocRange.begin(), inlineLocRange.end(), [&](auto loc) {
return !loc.hasFileAndLine;
}));
// Move the rest of the frames to make space for inlined location info numInlined = countFrames(inlineFrameRange);
std::move_backward( // Rotate inline frames right before its caller frame.
std::rotate(
frames.begin() + i, frames.begin() + i,
frames.begin() + addrCount, frames.begin() + addrCount,
frames.begin() + addrCount + numInlined); frames.begin() + addrCount + numInlined);
// Insert inlined location info in the space we opened up above. The
// inlineLocations contains inline functions in call sequence, and
// here we store them reversely in frames (caller in later frame).
for (size_t k = 0; k < numInlined; k++) {
frames[i + k].found = true;
frames[i + k].addr = addr;
frames[i + k].location = inlineLocations[numInlined - k - 1];
frames[i + k].name =
inlineLocations[numInlined - k - 1].name.data();
}
// Skip over the newly added inlined items.
i += numInlined;
addrCount += numInlined; addrCount += numInlined;
} else { } else {
frame.set(elfFile, adjusted, mode_); setSymbolizedFrame(frame, elfFile, adjusted, mode_);
} }
--remaining; --remaining;
if (symbolCache_ && if (symbolCache_) {
mode_ != Dwarf::LocationInfoMode::FULL_WITH_INLINE) {
// frame may already have been set here. That's ok, we'll just // frame may already have been set here. That's ok, we'll just
// overwrite, which doesn't cause a correctness problem. // overwrite, which doesn't cause a correctness problem.
symbolCache_->wlock()->set(addr, frame); 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;
} }
} }
} }
...@@ -466,7 +481,7 @@ void SafeStackTracePrinter::printSymbolizedStackTrace() { ...@@ -466,7 +481,7 @@ void SafeStackTracePrinter::printSymbolizedStackTrace() {
// Do our best to populate location info, process is going to terminate, // Do our best to populate location info, process is going to terminate,
// so performance isn't critical. // so performance isn't critical.
Symbolizer symbolizer(&elfCache_, Dwarf::LocationInfoMode::FULL); Symbolizer symbolizer(&elfCache_, LocationInfoMode::FULL);
symbolizer.symbolize(*addresses_); symbolizer.symbolize(*addresses_);
// Skip the top 2 frames captured by printStackTrace: // Skip the top 2 frames captured by printStackTrace:
...@@ -508,7 +523,7 @@ FastStackTracePrinter::FastStackTracePrinter( ...@@ -508,7 +523,7 @@ FastStackTracePrinter::FastStackTracePrinter(
printer_(std::move(printer)), printer_(std::move(printer)),
symbolizer_( symbolizer_(
elfCache_ ? elfCache_.get() : defaultElfCache(), elfCache_ ? elfCache_.get() : defaultElfCache(),
Dwarf::LocationInfoMode::FULL, LocationInfoMode::FULL,
symbolCacheSize) {} symbolCacheSize) {}
FastStackTracePrinter::~FastStackTracePrinter() {} FastStackTracePrinter::~FastStackTracePrinter() {}
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <folly/experimental/symbolizer/Elf.h> #include <folly/experimental/symbolizer/Elf.h>
#include <folly/experimental/symbolizer/ElfCache.h> #include <folly/experimental/symbolizer/ElfCache.h>
#include <folly/experimental/symbolizer/StackTrace.h> #include <folly/experimental/symbolizer/StackTrace.h>
#include <folly/experimental/symbolizer/SymbolizedFrame.h>
#include <folly/io/IOBuf.h> #include <folly/io/IOBuf.h>
namespace folly { namespace folly {
...@@ -38,47 +39,6 @@ namespace symbolizer { ...@@ -38,47 +39,6 @@ namespace symbolizer {
class Symbolizer; class Symbolizer;
/**
* Frame information: symbol name and location.
*/
struct SymbolizedFrame {
SymbolizedFrame() {}
void set(
const std::shared_ptr<ElfFile>& file,
uintptr_t address,
Dwarf::LocationInfoMode mode,
folly::Range<Dwarf::LocationInfo*> inlineLocations = {});
void clear() {
*this = SymbolizedFrame();
}
bool found = false;
uintptr_t addr = 0;
const char* name = nullptr;
Dwarf::LocationInfo location;
/**
* Demangle the name and return it. Not async-signal-safe; allocates memory.
*/
fbstring demangledName() const {
return name ? demangle(name) : fbstring();
}
private:
std::shared_ptr<ElfFile> file_;
};
template <size_t N>
struct FrameArray {
FrameArray() {}
size_t frameCount = 0;
uintptr_t addresses[N];
SymbolizedFrame frames[N];
};
/** /**
* Get stack trace into a given FrameArray, return true on success (and * Get stack trace into a given FrameArray, return true on success (and
* set frameCount to the actual frame count, which may be > N) and false * set frameCount to the actual frame count, which may be > N) and false
...@@ -119,15 +79,14 @@ inline bool getStackTraceSafe(FrameArray<N>& fa) { ...@@ -119,15 +79,14 @@ inline bool getStackTraceSafe(FrameArray<N>& fa) {
class Symbolizer { class Symbolizer {
public: public:
static constexpr Dwarf::LocationInfoMode kDefaultLocationInfoMode = static constexpr auto kDefaultLocationInfoMode = LocationInfoMode::FAST;
Dwarf::LocationInfoMode::FAST;
explicit Symbolizer(Dwarf::LocationInfoMode mode = kDefaultLocationInfoMode) explicit Symbolizer(LocationInfoMode mode = kDefaultLocationInfoMode)
: Symbolizer(nullptr, mode) {} : Symbolizer(nullptr, mode) {}
explicit Symbolizer( explicit Symbolizer(
ElfCacheBase* cache, ElfCacheBase* cache,
Dwarf::LocationInfoMode mode = kDefaultLocationInfoMode, LocationInfoMode mode = kDefaultLocationInfoMode,
size_t symbolCacheSize = 0); size_t symbolCacheSize = 0);
/** /**
...@@ -173,9 +132,14 @@ class Symbolizer { ...@@ -173,9 +132,14 @@ class Symbolizer {
private: private:
ElfCacheBase* const cache_; ElfCacheBase* const cache_;
const Dwarf::LocationInfoMode mode_; const LocationInfoMode mode_;
using SymbolCache = EvictingCacheMap<uintptr_t, SymbolizedFrame>; // SymbolCache contains mapping between an address and its frames. The first
// frame is the normal function call, and the following are stacked inline
// function calls if any.
using CachedSymbolizedFrames =
std::array<SymbolizedFrame, 1 + Dwarf::kMaxInlineLocationInfoPerFrame>;
using SymbolCache = EvictingCacheMap<uintptr_t, CachedSymbolizedFrames>;
folly::Optional<Synchronized<SymbolCache>> symbolCache_; folly::Optional<Synchronized<SymbolCache>> symbolCache_;
}; };
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <folly/Benchmark.h> #include <folly/Benchmark.h>
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/experimental/symbolizer/Dwarf.h> #include <folly/experimental/symbolizer/Dwarf.h>
#include <folly/experimental/symbolizer/SymbolizedFrame.h>
#include <folly/portability/GFlags.h> #include <folly/portability/GFlags.h>
void dummy() {} void dummy() {}
...@@ -25,7 +26,7 @@ namespace { ...@@ -25,7 +26,7 @@ namespace {
using namespace folly::symbolizer; using namespace folly::symbolizer;
void run(Dwarf::LocationInfoMode mode, size_t n) { void run(LocationInfoMode mode, size_t n) {
folly::BenchmarkSuspender suspender; folly::BenchmarkSuspender suspender;
// NOTE: Using '/proc/self/exe' only works if the code for @dummy is // NOTE: Using '/proc/self/exe' only works if the code for @dummy is
// statically linked into the binary. // statically linked into the binary.
...@@ -33,25 +34,26 @@ void run(Dwarf::LocationInfoMode mode, size_t n) { ...@@ -33,25 +34,26 @@ void run(Dwarf::LocationInfoMode mode, size_t n) {
Dwarf dwarf(&elf); Dwarf dwarf(&elf);
suspender.dismiss(); suspender.dismiss();
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
Dwarf::LocationInfo info; LocationInfo info;
auto inlineInfo = std:: auto inlineFrames =
array<Dwarf::LocationInfo, Dwarf::kMaxInlineLocationInfoPerFrame>(); std::array<SymbolizedFrame, Dwarf::kMaxInlineLocationInfoPerFrame>();
dwarf.findAddress(uintptr_t(&dummy), mode, info, folly::range(inlineInfo)); dwarf.findAddress(
uintptr_t(&dummy), mode, info, folly::range(inlineFrames));
} }
} }
} // namespace } // namespace
BENCHMARK(DwarfFindAddressFast, n) { BENCHMARK(DwarfFindAddressFast, n) {
run(folly::symbolizer::Dwarf::LocationInfoMode::FAST, n); run(folly::symbolizer::LocationInfoMode::FAST, n);
} }
BENCHMARK(DwarfFindAddressFull, n) { BENCHMARK(DwarfFindAddressFull, n) {
run(folly::symbolizer::Dwarf::LocationInfoMode::FULL, n); run(folly::symbolizer::LocationInfoMode::FULL, n);
} }
BENCHMARK(DwarfFindAddressFullWithInline, n) { BENCHMARK(DwarfFindAddressFullWithInline, n) {
run(folly::symbolizer::Dwarf::LocationInfoMode::FULL_WITH_INLINE, n); run(folly::symbolizer::LocationInfoMode::FULL_WITH_INLINE, n);
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
......
...@@ -14,13 +14,13 @@ ...@@ -14,13 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
#include <folly/experimental/symbolizer/Dwarf.h> #include <folly/experimental/symbolizer/SymbolizedFrame.h>
#include <glog/logging.h> #include <glog/logging.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
using folly::symbolizer::Dwarf; using namespace folly::symbolizer;
void checkPath( void checkPath(
std::string expectedPath, std::string expectedPath,
...@@ -30,7 +30,7 @@ void checkPath( ...@@ -30,7 +30,7 @@ void checkPath(
std::string rawBaseDir, std::string rawBaseDir,
std::string rawSubDir, std::string rawSubDir,
std::string rawFile) { std::string rawFile) {
Dwarf::Path path(rawBaseDir, rawSubDir, rawFile); Path path(rawBaseDir, rawSubDir, rawFile);
CHECK_EQ(expectedBaseDir, path.baseDir()) CHECK_EQ(expectedBaseDir, path.baseDir())
<< "Path(" << rawBaseDir << ", " << rawSubDir << ", " << rawFile << ")"; << "Path(" << rawBaseDir << ", " << rawSubDir << ", " << rawFile << ")";
...@@ -48,7 +48,7 @@ void checkPath( ...@@ -48,7 +48,7 @@ void checkPath(
CHECK_EQ(expectedPath, std::string(buf, len)); CHECK_EQ(expectedPath, std::string(buf, len));
} }
TEST(Dwarf, Path) { TEST(SymbolizedFrame, Path) {
checkPath("hello.cpp", "", "", "hello.cpp", "", "", "hello.cpp"); checkPath("hello.cpp", "", "", "hello.cpp", "", "", "hello.cpp");
checkPath("foo/hello.cpp", "foo", "", "hello.cpp", "foo", "", "hello.cpp"); checkPath("foo/hello.cpp", "foo", "", "hello.cpp", "foo", "", "hello.cpp");
checkPath("foo/hello.cpp", "foo", "", "hello.cpp", "", "foo", "hello.cpp"); checkPath("foo/hello.cpp", "foo", "", "hello.cpp", "", "foo", "hello.cpp");
......
...@@ -19,8 +19,10 @@ ...@@ -19,8 +19,10 @@
#include <array> #include <array>
#include <cstdlib> #include <cstdlib>
#include <folly/Demangle.h>
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/String.h> #include <folly/String.h>
#include <folly/experimental/symbolizer/SymbolizedFrame.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
namespace folly { namespace folly {
...@@ -33,10 +35,10 @@ TEST(Symbolizer, Single) { ...@@ -33,10 +35,10 @@ TEST(Symbolizer, Single) {
// It looks like we could only use .debug_aranges with "-g2", with // It looks like we could only use .debug_aranges with "-g2", with
// "-g1 -gdwarf-aranges", the code has to fallback to line-tables to // "-g1 -gdwarf-aranges", the code has to fallback to line-tables to
// get the file name. // get the file name.
Symbolizer symbolizer(Dwarf::LocationInfoMode::FULL); Symbolizer symbolizer(LocationInfoMode::FULL);
SymbolizedFrame a; SymbolizedFrame a;
ASSERT_TRUE(symbolizer.symbolize(reinterpret_cast<uintptr_t>(foo), a)); ASSERT_TRUE(symbolizer.symbolize(reinterpret_cast<uintptr_t>(foo), a));
EXPECT_EQ("folly::symbolizer::test::foo()", a.demangledName()); EXPECT_EQ("folly::symbolizer::test::foo()", folly::demangle(a.name));
auto path = a.location.file.toString(); auto path = a.location.file.toString();
folly::StringPiece basename(path); folly::StringPiece basename(path);
...@@ -115,7 +117,7 @@ TEST_F(ElfCacheTest, SignalSafeElfCache) { ...@@ -115,7 +117,7 @@ TEST_F(ElfCacheTest, SignalSafeElfCache) {
} }
TEST(SymbolizerTest, SymbolCache) { TEST(SymbolizerTest, SymbolCache) {
Symbolizer symbolizer(nullptr, Dwarf::LocationInfoMode::FULL, 100); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL, 100);
FrameArray<100> frames; FrameArray<100> frames;
bar(frames); bar(frames);
...@@ -131,40 +133,61 @@ TEST(SymbolizerTest, SymbolCache) { ...@@ -131,40 +133,61 @@ TEST(SymbolizerTest, SymbolCache) {
namespace { namespace {
size_t kQsortCallLineNo = 0;
size_t kFooCallLineNo = 0;
template <size_t kNumFrames = 100> template <size_t kNumFrames = 100>
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames) { FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames) {
framesToFill = &frames; framesToFill = &frames;
std::array<int, 2> a = {1, 2}; std::array<int, 2> a = {1, 2};
// Use qsort, which is in a different library // Use qsort, which is in a different library
kQsortCallLineNo = __LINE__ + 1;
qsort(a.data(), 2, sizeof(int), comparator<kNumFrames>); qsort(a.data(), 2, sizeof(int), comparator<kNumFrames>);
framesToFill = nullptr; framesToFill = nullptr;
} }
template <size_t kNumFrames = 100> template <size_t kNumFrames = 100>
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames) { FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames) {
kFooCallLineNo = __LINE__ + 1;
inlineFoo(frames); inlineFoo(frames);
} }
} // namespace } // namespace
TEST(SymbolizerTest, InlineFunctionBasic) { TEST(SymbolizerTest, InlineFunctionBasic) {
Symbolizer symbolizer( Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
nullptr, Dwarf::LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames; FrameArray<100> frames;
inlineBar<100>(frames); inlineBar<100>(frames);
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
// clang-fromat off
// Expected full stack trace:
// Frame:
// Frame: getStackTrace<100>
// Frame: _ZN5folly10symbolizer4test10comparatorILm100EEEiPKvS4_
// Frame: msort_with_tmp.part.0
// Frame: __GI___qsort_r
// Frame: inlineFoo<100>
// Frame: inlineBar<100>
// Frame: _ZN5folly10symbolizer4test43SymbolizerTest_InlineFunctionWithCache_Test8TestBodyEv
// Frame: _ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS_4TestEvEET0_PT_MS4_FS3_vEPKc
// Frame: _ZN7testing4Test3RunEv 2490 Frame: _ZN7testing8TestInfo3RunEv
// Frame: _ZN7testing8TestCase3RunEv
// Frame: _ZN7testing8internal12UnitTestImpl11RunAllTestsEv
// Frame: _ZN7testing8UnitTest3RunEv 2473 Frame: _Z13RUN_ALL_TESTSv
// clang-fromat on
EXPECT_EQ(15, frames.frameCount);
EXPECT_EQ("inlineFoo<100>", std::string(frames.frames[5].name)); EXPECT_EQ("inlineFoo<100>", std::string(frames.frames[5].name));
EXPECT_EQ( EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp", "folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(frames.frames[5].location.file.toString())); std::string(frames.frames[5].location.file.toString()));
EXPECT_EQ(139, frames.frames[5].location.line); EXPECT_EQ(kQsortCallLineNo, frames.frames[5].location.line);
EXPECT_EQ("inlineBar<100>", std::string(frames.frames[6].name)); EXPECT_EQ("inlineBar<100>", std::string(frames.frames[6].name));
EXPECT_EQ( EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp", "folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(frames.frames[6].location.file.toString())); std::string(frames.frames[6].location.file.toString()));
EXPECT_EQ(145, frames.frames[6].location.line); EXPECT_EQ(kFooCallLineNo, frames.frames[6].location.line);
FrameArray<100> frames2; FrameArray<100> frames2;
inlineBar<100>(frames2); inlineBar<100>(frames2);
...@@ -176,13 +199,12 @@ TEST(SymbolizerTest, InlineFunctionBasic) { ...@@ -176,13 +199,12 @@ TEST(SymbolizerTest, InlineFunctionBasic) {
// No inline frames should be filled because of no extra frames. // No inline frames should be filled because of no extra frames.
TEST(SymbolizerTest, InlineFunctionBasicNoExtraFrames) { TEST(SymbolizerTest, InlineFunctionBasicNoExtraFrames) {
Symbolizer symbolizer( Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 100);
nullptr, Dwarf::LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<8> frames; FrameArray<8> frames;
inlineBar<8>(frames); inlineBar<8>(frames);
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
Symbolizer symbolizer2(nullptr, Dwarf::LocationInfoMode::FULL, 100); Symbolizer symbolizer2(nullptr, LocationInfoMode::FULL, 100);
FrameArray<8> frames2; FrameArray<8> frames2;
inlineBar<8>(frames2); inlineBar<8>(frames2);
symbolizer2.symbolize(frames2); symbolizer2.symbolize(frames2);
...@@ -192,6 +214,33 @@ TEST(SymbolizerTest, InlineFunctionBasicNoExtraFrames) { ...@@ -192,6 +214,33 @@ TEST(SymbolizerTest, InlineFunctionBasicNoExtraFrames) {
} }
} }
TEST(SymbolizerTest, InlineFunctionWithCache) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames;
inlineBar<100>(frames);
symbolizer.symbolize(frames);
EXPECT_EQ(15, frames.frameCount);
EXPECT_EQ("inlineFoo<100>", std::string(frames.frames[5].name));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(frames.frames[5].location.file.toString()));
EXPECT_EQ(kQsortCallLineNo, frames.frames[5].location.line);
EXPECT_EQ("inlineBar<100>", std::string(frames.frames[6].name));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(frames.frames[6].location.file.toString()));
EXPECT_EQ(kFooCallLineNo, frames.frames[6].location.line);
FrameArray<100> frames2;
inlineBar<100>(frames2);
symbolizer.symbolize(frames2);
for (size_t i = 0; i < frames.frameCount; i++) {
EXPECT_STREQ(frames.frames[i].name, frames2.frames[i].name);
}
}
} // namespace test } // namespace test
} // namespace symbolizer } // namespace symbolizer
} // namespace folly } // 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