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