Apply clang-format to folly/experimental/symbolizer/
[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 {
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