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 | #pragma once |
18 | |
19 | #include <climits> // for PATH_MAX |
20 | #include <cstring> |
21 | #include <memory> |
22 | #include <mutex> |
23 | #include <string> |
24 | #include <unordered_map> |
25 | #include <vector> |
26 | |
27 | #include <boost/container/flat_map.hpp> |
28 | #include <boost/intrusive/list.hpp> |
29 | #include <boost/operators.hpp> |
30 | #include <glog/logging.h> |
31 | |
32 | #include <folly/Range.h> |
33 | #include <folly/experimental/symbolizer/Elf.h> |
34 | #include <folly/hash/Hash.h> |
35 | |
36 | namespace folly { |
37 | namespace symbolizer { |
38 | |
39 | /** |
40 | * Number of ELF files loaded by the dynamic loader. |
41 | */ |
42 | size_t countLoadedElfFiles(); |
43 | |
44 | class ElfCacheBase { |
45 | public: |
46 | virtual std::shared_ptr<ElfFile> getFile(StringPiece path) = 0; |
47 | virtual ~ElfCacheBase() {} |
48 | }; |
49 | |
50 | /** |
51 | * Cache ELF files. Async-signal-safe: does memory allocation upfront. |
52 | * |
53 | * Will not grow; once the capacity is reached, lookups for files that |
54 | * aren't already in the cache will fail (return nullptr). |
55 | * |
56 | * Not MT-safe. May not be used concurrently from multiple threads. |
57 | * |
58 | * NOTE that async-signal-safety is preserved only as long as the |
59 | * SignalSafeElfCache object exists; after the SignalSafeElfCache object |
60 | * is destroyed, destroying returned shared_ptr<ElfFile> objects may |
61 | * cause ElfFile objects to be destroyed, and that's not async-signal-safe. |
62 | */ |
63 | class SignalSafeElfCache : public ElfCacheBase { |
64 | public: |
65 | explicit SignalSafeElfCache(size_t capacity); |
66 | |
67 | std::shared_ptr<ElfFile> getFile(StringPiece path) override; |
68 | |
69 | private: |
70 | // We can't use std::string (allocating memory is bad!) so we roll our |
71 | // own wrapper around a fixed-size, null-terminated string. |
72 | class Path : private boost::totally_ordered<Path> { |
73 | public: |
74 | Path() { |
75 | assign(folly::StringPiece()); |
76 | } |
77 | |
78 | explicit Path(StringPiece s) { |
79 | assign(s); |
80 | } |
81 | |
82 | void assign(StringPiece s) { |
83 | DCHECK_LE(s.size(), kMaxSize); |
84 | if (!s.empty()) { |
85 | memcpy(data_, s.data(), s.size()); |
86 | } |
87 | data_[s.size()] = '\0'; |
88 | } |
89 | |
90 | bool operator<(const Path& other) const { |
91 | return strcmp(data_, other.data_) < 0; |
92 | } |
93 | |
94 | bool operator==(const Path& other) const { |
95 | return strcmp(data_, other.data_) == 0; |
96 | } |
97 | |
98 | const char* data() const { |
99 | return data_; |
100 | } |
101 | |
102 | static constexpr size_t kMaxSize = PATH_MAX - 1; |
103 | |
104 | private: |
105 | char data_[kMaxSize + 1]; |
106 | }; |
107 | |
108 | Path scratchpad_; // Preallocated key for map_ lookups. |
109 | boost::container::flat_map<Path, int> map_; |
110 | std::vector<std::shared_ptr<ElfFile>> slots_; |
111 | }; |
112 | |
113 | /** |
114 | * General-purpose ELF file cache. |
115 | * |
116 | * LRU of given capacity. MT-safe (uses locking). Not async-signal-safe. |
117 | */ |
118 | class ElfCache : public ElfCacheBase { |
119 | public: |
120 | explicit ElfCache(size_t capacity); |
121 | |
122 | std::shared_ptr<ElfFile> getFile(StringPiece path) override; |
123 | |
124 | private: |
125 | std::mutex mutex_; |
126 | |
127 | typedef boost::intrusive::list_member_hook<> LruLink; |
128 | |
129 | struct Entry { |
130 | std::string path; |
131 | ElfFile file; |
132 | LruLink lruLink; |
133 | }; |
134 | |
135 | static std::shared_ptr<ElfFile> filePtr(const std::shared_ptr<Entry>& e); |
136 | |
137 | size_t capacity_; |
138 | std::unordered_map<StringPiece, std::shared_ptr<Entry>, Hash> files_; |
139 | |
140 | typedef boost::intrusive::list< |
141 | Entry, |
142 | boost::intrusive::member_hook<Entry, LruLink, &Entry::lruLink>, |
143 | boost::intrusive::constant_time_size<false>> |
144 | LruList; |
145 | LruList lruList_; |
146 | }; |
147 | } // namespace symbolizer |
148 | } // namespace folly |
149 | |