Commit 35ff8b26 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

Let ElfCache be unbounded

Summary:
[Folly] Let `ElfCache` be unbounded to permit binaries with many dynamically-linked libraries from continuously missing the cache when symbolizing.

Some applications symbolize stacktraces relatively frequently. When the build is using dynamic linking, such as might be done when developing the application or running the unit-tests, the overhead from re-loading `ElfFile` instances can appear. Address this by making the cache be unboundedly large.

This is unlikely to affect applications built using mostly-static linking, such as might be done for production builds, even when they are large applications built from many libraries, since the capacity parameter is usually much larger than the total number of dynamically loaded libraries.

Currently, no substantial work is done when initializing `ElfFile` instances. If substantial work were to be done, especially in an unoptimized build such as might be used when developing the application or running the unit-tests, and instances do not stay in cache, then that substantial work would be repeated whenever symbolizing stacktraces rather than being amortized.

Reviewed By: luciang

Differential Revision: D18819555

fbshipit-source-id: 0e75326133e901e7bd49ea3cefe9b318e6d4466f
parent 4479aa0c
...@@ -36,8 +36,7 @@ std::string getSingletonStackTrace() { ...@@ -36,8 +36,7 @@ std::string getSingletonStackTrace() {
if (!getStackTraceSafe(*addresses)) { if (!getStackTraceSafe(*addresses)) {
return ""; return "";
} else { } else {
constexpr size_t kDefaultCapacity = 500; symbolizer::ElfCache elfCache;
symbolizer::ElfCache elfCache(kDefaultCapacity);
symbolizer::Symbolizer symbolizer(&elfCache); symbolizer::Symbolizer symbolizer(&elfCache);
symbolizer.symbolize(*addresses); symbolizer.symbolize(*addresses);
......
...@@ -94,17 +94,13 @@ std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) { ...@@ -94,17 +94,13 @@ std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) {
return pos->file; return pos->file;
} }
ElfCache::ElfCache(size_t capacity) : capacity_(capacity) {}
std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) { std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
auto pos = files_.find(p); auto pos = files_.find(p);
if (pos != files_.end()) { if (pos != files_.end()) {
// Found, move to back (MRU) // Found
auto& entry = pos->second; auto& entry = pos->second;
lruList_.erase(lruList_.iterator_to(*entry));
lruList_.push_back(*entry);
return filePtr(entry); return filePtr(entry);
} }
...@@ -118,16 +114,9 @@ std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) { ...@@ -118,16 +114,9 @@ std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {
return nullptr; return nullptr;
} }
if (files_.size() == capacity_) { pos = files_.emplace(path, std::move(entry)).first;
auto& e = lruList_.front();
lruList_.pop_front();
files_.erase(e.path);
}
files_.emplace(entry->path, entry);
lruList_.push_back(*entry);
return filePtr(entry); return filePtr(pos->second);
} }
std::shared_ptr<ElfFile> ElfCache::filePtr(const std::shared_ptr<Entry>& e) { std::shared_ptr<ElfFile> ElfCache::filePtr(const std::shared_ptr<Entry>& e) {
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <unordered_map> #include <unordered_map>
#include <boost/intrusive/avl_set.hpp> #include <boost/intrusive/avl_set.hpp>
#include <boost/intrusive/list.hpp>
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/experimental/symbolizer/Elf.h> #include <folly/experimental/symbolizer/Elf.h>
...@@ -91,32 +90,19 @@ class SignalSafeElfCache : public ElfCacheBase { ...@@ -91,32 +90,19 @@ class SignalSafeElfCache : public ElfCacheBase {
*/ */
class ElfCache : public ElfCacheBase { class ElfCache : public ElfCacheBase {
public: public:
explicit ElfCache(size_t capacity);
std::shared_ptr<ElfFile> getFile(StringPiece path) override; std::shared_ptr<ElfFile> getFile(StringPiece path) override;
private: private:
std::mutex mutex_; std::mutex mutex_;
typedef boost::intrusive::list_member_hook<> LruLink;
struct Entry { struct Entry {
std::string path; std::string path;
ElfFile file; ElfFile file;
LruLink lruLink;
}; };
static std::shared_ptr<ElfFile> filePtr(const std::shared_ptr<Entry>& e); static std::shared_ptr<ElfFile> filePtr(const std::shared_ptr<Entry>& e);
size_t capacity_;
std::unordered_map<StringPiece, std::shared_ptr<Entry>, Hash> files_; std::unordered_map<StringPiece, std::shared_ptr<Entry>, Hash> files_;
typedef boost::intrusive::list<
Entry,
boost::intrusive::member_hook<Entry, LruLink, &Entry::lruLink>,
boost::intrusive::constant_time_size<false>>
LruList;
LruList lruList_;
}; };
} // namespace symbolizer } // namespace symbolizer
} // namespace folly } // namespace folly
...@@ -55,8 +55,7 @@ namespace symbolizer { ...@@ -55,8 +55,7 @@ namespace symbolizer {
namespace { namespace {
ElfCache* defaultElfCache() { ElfCache* defaultElfCache() {
static constexpr size_t defaultCapacity = 500; static auto cache = new ElfCache();
static auto cache = new ElfCache(defaultCapacity);
return cache; return cache;
} }
...@@ -512,17 +511,9 @@ void SafeStackTracePrinter::printStackTrace(bool symbolize) { ...@@ -512,17 +511,9 @@ void SafeStackTracePrinter::printStackTrace(bool symbolize) {
FastStackTracePrinter::FastStackTracePrinter( FastStackTracePrinter::FastStackTracePrinter(
std::unique_ptr<SymbolizePrinter> printer, std::unique_ptr<SymbolizePrinter> printer,
size_t elfCacheSize,
size_t symbolCacheSize) size_t symbolCacheSize)
: elfCache_( : printer_(std::move(printer)),
elfCacheSize == 0 symbolizer_(defaultElfCache(), LocationInfoMode::FULL, symbolCacheSize) {}
? nullptr
: new ElfCache{std::max(countLoadedElfFiles(), elfCacheSize)}),
printer_(std::move(printer)),
symbolizer_(
elfCache_ ? elfCache_.get() : defaultElfCache(),
LocationInfoMode::FULL,
symbolCacheSize) {}
FastStackTracePrinter::~FastStackTracePrinter() = default; FastStackTracePrinter::~FastStackTracePrinter() = default;
......
...@@ -390,7 +390,6 @@ class FastStackTracePrinter { ...@@ -390,7 +390,6 @@ class FastStackTracePrinter {
explicit FastStackTracePrinter( explicit FastStackTracePrinter(
std::unique_ptr<SymbolizePrinter> printer, std::unique_ptr<SymbolizePrinter> printer,
size_t elfCacheSize = 0, // 0 means "use the default elf cache instance."
size_t symbolCacheSize = kDefaultSymbolCacheSize); size_t symbolCacheSize = kDefaultSymbolCacheSize);
~FastStackTracePrinter(); ~FastStackTracePrinter();
...@@ -406,7 +405,6 @@ class FastStackTracePrinter { ...@@ -406,7 +405,6 @@ class FastStackTracePrinter {
private: private:
static constexpr size_t kMaxStackTraceDepth = 100; static constexpr size_t kMaxStackTraceDepth = 100;
const std::unique_ptr<ElfCache> elfCache_;
const std::unique_ptr<SymbolizePrinter> printer_; const std::unique_ptr<SymbolizePrinter> printer_;
Symbolizer symbolizer_; Symbolizer symbolizer_;
}; };
......
...@@ -89,10 +89,9 @@ void runElfCacheTest(Symbolizer& symbolizer) { ...@@ -89,10 +89,9 @@ void runElfCacheTest(Symbolizer& symbolizer) {
} }
} }
TEST_F(ElfCacheTest, TinyElfCache) { TEST_F(ElfCacheTest, ElfCache) {
ElfCache cache(1); ElfCache cache;
Symbolizer symbolizer(&cache); Symbolizer symbolizer(&cache);
// Run twice, in case the wrong stuff gets evicted?
for (size_t i = 0; i < 2; ++i) { for (size_t i = 0; i < 2; ++i) {
runElfCacheTest(symbolizer); runElfCacheTest(symbolizer);
} }
......
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