/* * Copyright 2014-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include // for PATH_MAX #include #include #include #include #include #include #include #include #include #include #include #include #include namespace folly { namespace symbolizer { /** * Number of ELF files loaded by the dynamic loader. */ size_t countLoadedElfFiles(); class ElfCacheBase { public: virtual std::shared_ptr getFile(StringPiece path) = 0; virtual ~ElfCacheBase() {} }; /** * Cache ELF files. Async-signal-safe: does memory allocation upfront. * * Will not grow; once the capacity is reached, lookups for files that * aren't already in the cache will fail (return nullptr). * * Not MT-safe. May not be used concurrently from multiple threads. * * NOTE that async-signal-safety is preserved only as long as the * SignalSafeElfCache object exists; after the SignalSafeElfCache object * is destroyed, destroying returned shared_ptr objects may * cause ElfFile objects to be destroyed, and that's not async-signal-safe. */ class SignalSafeElfCache : public ElfCacheBase { public: explicit SignalSafeElfCache(size_t capacity); std::shared_ptr getFile(StringPiece path) override; private: // We can't use std::string (allocating memory is bad!) so we roll our // own wrapper around a fixed-size, null-terminated string. class Path : private boost::totally_ordered { public: Path() { assign(folly::StringPiece()); } explicit Path(StringPiece s) { assign(s); } void assign(StringPiece s) { DCHECK_LE(s.size(), kMaxSize); if (!s.empty()) { memcpy(data_, s.data(), s.size()); } data_[s.size()] = '\0'; } bool operator<(const Path& other) const { return strcmp(data_, other.data_) < 0; } bool operator==(const Path& other) const { return strcmp(data_, other.data_) == 0; } const char* data() const { return data_; } static constexpr size_t kMaxSize = PATH_MAX - 1; private: char data_[kMaxSize + 1]; }; Path scratchpad_; // Preallocated key for map_ lookups. boost::container::flat_map map_; std::vector> slots_; }; /** * General-purpose ELF file cache. * * LRU of given capacity. MT-safe (uses locking). Not async-signal-safe. */ class ElfCache : public ElfCacheBase { public: explicit ElfCache(size_t capacity); std::shared_ptr getFile(StringPiece path) override; private: std::mutex mutex_; typedef boost::intrusive::list_member_hook<> LruLink; struct Entry { std::string path; ElfFile file; LruLink lruLink; }; static std::shared_ptr filePtr(const std::shared_ptr& e); size_t capacity_; std::unordered_map, Hash> files_; typedef boost::intrusive::list< Entry, boost::intrusive::member_hook, boost::intrusive::constant_time_size> LruList; LruList lruList_; }; } // namespace symbolizer } // namespace folly