Adds writer test case for RCU
[folly.git] / folly / experimental / symbolizer / ElfCache.cpp
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