2 * Copyright 2017 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;
30 namespace symbolizer {
32 size_t countLoadedElfFiles() {
33 // _r_debug synchronization is... lacking to say the least. It's
34 // meant as an aid for debuggers and synchronization is done by
35 // calling dl_debug_state() which debuggers are supposed to
36 // intercept by setting a breakpoint on.
38 // Can't really do that here, so we apply the hope-and-pray strategy.
39 if (_r_debug.r_version != 1 || _r_debug.r_state != r_debug::RT_CONSISTENT) {
44 // r_map -> head of a linked list of 'link_map_t' entries,
45 // one per ELF 'binary' in the process address space.
47 for (auto lmap = _r_debug.r_map; lmap != nullptr; lmap = lmap->l_next) {
53 SignalSafeElfCache::SignalSafeElfCache(size_t capacity) {
54 map_.reserve(capacity);
55 slots_.reserve(capacity);
58 for (size_t i = 0; i < capacity; ++i) {
59 slots_.push_back(std::make_shared<ElfFile>());
63 std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) {
64 if (p.size() > Path::kMaxSize) {
68 scratchpad_.assign(p);
69 auto pos = map_.find(scratchpad_);
70 if (pos != map_.end()) {
71 return slots_[pos->second];
74 size_t n = map_.size();
75 if (n >= slots_.size()) {
76 DCHECK_EQ(map_.size(), slots_.size());
83 int r = f->openAndFollow(scratchpad_.data(), true, &msg);
84 if (r != ElfFile::kSuccess) {
88 map_[scratchpad_] = n;
92 ElfCache::ElfCache(size_t capacity) : capacity_(capacity) {}
94 std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {
95 std::lock_guard<std::mutex> lock(mutex_);
97 auto pos = files_.find(p);
98 if (pos != files_.end()) {
99 // Found, move to back (MRU)
100 auto& entry = pos->second;
101 lruList_.erase(lruList_.iterator_to(*entry));
102 lruList_.push_back(*entry);
103 return filePtr(entry);
106 auto entry = std::make_shared<Entry>();
107 entry->path = p.str();
108 auto& path = entry->path;
110 // No negative caching
111 const char* msg = "";
112 int r = entry->file.openAndFollow(path.c_str(), true, &msg);
113 if (r != ElfFile::kSuccess) {
117 if (files_.size() == capacity_) {
118 auto& e = lruList_.front();
119 lruList_.pop_front();
120 files_.erase(e.path);
123 files_.emplace(entry->path, entry);
124 lruList_.push_back(*entry);
126 return filePtr(entry);
129 std::shared_ptr<ElfFile> ElfCache::filePtr(const std::shared_ptr<Entry>& e) {
131 return std::shared_ptr<ElfFile>(e, &e->file);
133 } // namespace symbolizer