2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/experimental/symbolizer/ElfCache.h>
22 * This is declared in `link.h' on Linux platforms, but apparently not on the
23 * Mac version of the file. It's harmless to declare again, in any case.
25 * Note that declaring it with `extern "C"` results in linkage conflicts.
27 extern struct r_debug _r_debug;
29 namespace folly { namespace symbolizer {
31 size_t countLoadedElfFiles() {
32 // _r_debug synchronization is... lacking to say the least. It's
33 // meant as an aid for debuggers and synchrnization is done by
34 // calling dl_debug_state() which debuggers are supposed to
35 // intercept by setting a breakpoint on.
37 // Can't really do that here, so we apply the hope-and-pray strategy.
38 if (_r_debug.r_version != 1 || _r_debug.r_state != r_debug::RT_CONSISTENT) {
43 // r_map -> head of a linked list of 'link_map_t' entries,
44 // one per ELF 'binary' in the process address space.
46 for (auto lmap = _r_debug.r_map; lmap != nullptr; lmap = lmap->l_next) {
52 SignalSafeElfCache::SignalSafeElfCache(size_t capacity) {
53 map_.reserve(capacity);
54 slots_.reserve(capacity);
57 for (size_t i = 0; i < capacity; ++i) {
58 slots_.push_back(std::make_shared<ElfFile>());
62 std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) {
63 if (p.size() > Path::kMaxSize) {
68 auto pos = map_.find(path);
69 if (pos != map_.end()) {
70 return slots_[pos->second];
73 size_t n = map_.size();
74 if (n >= slots_.size()) {
75 DCHECK_EQ(map_.size(), slots_.size());
82 int r = f->openAndFollow(path.data(), true, &msg);
83 if (r != ElfFile::kSuccess) {
91 ElfCache::ElfCache(size_t capacity) : capacity_(capacity) { }
93 std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {
94 std::lock_guard<std::mutex> lock(mutex_);
96 auto pos = files_.find(p);
97 if (pos != files_.end()) {
98 // Found, move to back (MRU)
99 auto& entry = pos->second;
100 lruList_.erase(lruList_.iterator_to(*entry));
101 lruList_.push_back(*entry);
102 return filePtr(entry);
105 auto entry = std::make_shared<Entry>();
106 entry->path = p.str();
107 auto& path = entry->path;
109 // No negative caching
110 const char* msg = "";
111 int r = entry->file.openAndFollow(path.c_str(), true, &msg);
112 if (r != ElfFile::kSuccess) {
116 if (files_.size() == capacity_) {
117 auto& e = lruList_.front();
118 lruList_.pop_front();
119 files_.erase(e.path);
122 files_.emplace(entry->path, entry);
123 lruList_.push_back(*entry);
125 return filePtr(entry);
128 std::shared_ptr<ElfFile> ElfCache::filePtr(const std::shared_ptr<Entry>& e) {
130 return std::shared_ptr<ElfFile>(e, &e->file);