Cache open ELF files in Symbolizer
[folly.git] / folly / experimental / symbolizer / ElfCache.cpp
1 /*
2  * Copyright 2014 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 namespace folly { namespace symbolizer {
20
21 SignalSafeElfCache::SignalSafeElfCache(size_t capacity) {
22   map_.reserve(capacity);
23   slots_.reserve(capacity);
24
25   // Preallocate
26   for (size_t i = 0; i < capacity; ++i) {
27     slots_.push_back(std::make_shared<ElfFile>());
28   }
29 }
30
31 std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) {
32   if (p.size() > Path::kMaxSize) {
33     return nullptr;
34   }
35
36   Path path(p);
37   auto pos = map_.find(path);
38   if (pos != map_.end()) {
39     return slots_[pos->second];
40   }
41
42   size_t n = map_.size();
43   if (n >= slots_.size()) {
44     DCHECK_EQ(map_.size(), slots_.size());
45     return nullptr;
46   }
47
48   auto& f = slots_[n];
49   if (f->openNoThrow(path.data()) == -1) {
50     return nullptr;
51   }
52
53   map_[path] = n;
54   return f;
55 }
56
57 ElfCache::ElfCache(size_t capacity) : capacity_(capacity) { }
58
59 std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {
60   auto path = p.str();
61
62   std::lock_guard<std::mutex> lock(mutex_);
63
64   auto pos = files_.find(path);
65   if (pos != files_.end()) {
66     // Found, move to back (MRU)
67     auto& entry = pos->second;
68     lruList_.erase(lruList_.iterator_to(*entry));
69     lruList_.push_back(*entry);
70     return filePtr(entry);
71   }
72
73   auto entry = std::make_shared<Entry>();
74
75   // No negative caching
76   if (entry->file.openNoThrow(path.c_str()) == -1) {
77     return nullptr;
78   }
79
80   if (files_.size() == capacity_) {
81     // Evict LRU
82     lruList_.pop_front();
83   }
84
85   files_.emplace(std::move(path), entry);
86   lruList_.push_back(*entry);
87
88   return filePtr(entry);
89 }
90
91 std::shared_ptr<ElfFile> ElfCache::filePtr(const std::shared_ptr<Entry>& e) {
92   // share ownership
93   return std::shared_ptr<ElfFile>(e, &e->file);
94 }
95
96 }}  // namespaces
97