1 | /* |
2 | * Copyright 2014-present Facebook, Inc. |
3 | * |
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 |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
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. |
15 | */ |
16 | |
17 | #include <folly/experimental/symbolizer/ElfCache.h> |
18 | |
19 | #include <link.h> |
20 | |
21 | /* |
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. |
24 | * |
25 | * Note that declaring it with `extern "C"` results in linkage conflicts. |
26 | */ |
27 | extern struct r_debug _r_debug; |
28 | |
29 | namespace folly { |
30 | namespace symbolizer { |
31 | |
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. |
37 | |
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) { |
40 | // computo ergo sum |
41 | return 1; |
42 | } |
43 | |
44 | // r_map -> head of a linked list of 'link_map_t' entries, |
45 | // one per ELF 'binary' in the process address space. |
46 | size_t count = 0; |
47 | for (auto lmap = _r_debug.r_map; lmap != nullptr; lmap = lmap->l_next) { |
48 | ++count; |
49 | } |
50 | return count; |
51 | } |
52 | |
53 | SignalSafeElfCache::SignalSafeElfCache(size_t capacity) { |
54 | map_.reserve(capacity); |
55 | slots_.reserve(capacity); |
56 | |
57 | // Preallocate |
58 | for (size_t i = 0; i < capacity; ++i) { |
59 | slots_.push_back(std::make_shared<ElfFile>()); |
60 | } |
61 | } |
62 | |
63 | std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) { |
64 | if (p.size() > Path::kMaxSize) { |
65 | return nullptr; |
66 | } |
67 | |
68 | scratchpad_.assign(p); |
69 | auto pos = map_.find(scratchpad_); |
70 | if (pos != map_.end()) { |
71 | return slots_[pos->second]; |
72 | } |
73 | |
74 | size_t n = map_.size(); |
75 | if (n >= slots_.size()) { |
76 | DCHECK_EQ(map_.size(), slots_.size()); |
77 | return nullptr; |
78 | } |
79 | |
80 | auto& f = slots_[n]; |
81 | |
82 | const char* msg = "" ; |
83 | int r = f->openAndFollow(scratchpad_.data(), true, &msg); |
84 | if (r != ElfFile::kSuccess) { |
85 | return nullptr; |
86 | } |
87 | |
88 | map_[scratchpad_] = n; |
89 | return f; |
90 | } |
91 | |
92 | ElfCache::ElfCache(size_t capacity) : capacity_(capacity) {} |
93 | |
94 | std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) { |
95 | std::lock_guard<std::mutex> lock(mutex_); |
96 | |
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); |
104 | } |
105 | |
106 | auto entry = std::make_shared<Entry>(); |
107 | entry->path = p.str(); |
108 | auto& path = entry->path; |
109 | |
110 | // No negative caching |
111 | const char* msg = "" ; |
112 | int r = entry->file.openAndFollow(path.c_str(), true, &msg); |
113 | if (r != ElfFile::kSuccess) { |
114 | return nullptr; |
115 | } |
116 | |
117 | if (files_.size() == capacity_) { |
118 | auto& e = lruList_.front(); |
119 | lruList_.pop_front(); |
120 | files_.erase(e.path); |
121 | } |
122 | |
123 | files_.emplace(entry->path, entry); |
124 | lruList_.push_back(*entry); |
125 | |
126 | return filePtr(entry); |
127 | } |
128 | |
129 | std::shared_ptr<ElfFile> ElfCache::filePtr(const std::shared_ptr<Entry>& e) { |
130 | // share ownership |
131 | return std::shared_ptr<ElfFile>(e, &e->file); |
132 | } |
133 | } // namespace symbolizer |
134 | } // namespace folly |
135 | |