1 // Copyright 2018 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_PROFILER_MODULE_CACHE_H_ 6 #define BASE_PROFILER_MODULE_CACHE_H_ 7 8 #include <memory> 9 #include <set> 10 #include <string> 11 #include <vector> 12 13 #include "base/base_export.h" 14 #include "base/containers/flat_set.h" 15 #include "base/files/file_path.h" 16 #include "base/memory/raw_ptr.h" 17 #include "base/strings/string_piece.h" 18 #include "build/build_config.h" 19 20 #if BUILDFLAG(IS_WIN) 21 #include "base/win/windows_types.h" 22 #endif 23 24 namespace base { 25 26 // Converts module id to match the id that the Google-internal symbol server 27 // expects. 28 BASE_EXPORT std::string TransformModuleIDToSymbolServerFormat( 29 StringPiece module_id); 30 31 // Supports cached lookup of modules by address, with caching based on module 32 // address ranges. 33 // 34 // Cached lookup is necessary on Mac for performance, due to an inefficient 35 // dladdr implementation. See https://crrev.com/487092. 36 // 37 // Cached lookup is beneficial on Windows to minimize use of the loader 38 // lock. Note however that the cache retains a handle to looked-up modules for 39 // its lifetime, which may result in pinning modules in memory that were 40 // transiently loaded by the OS. 41 class BASE_EXPORT ModuleCache { 42 public: 43 // Module represents a binary module (executable or library) and its 44 // associated state. 45 class BASE_EXPORT Module { 46 public: 47 Module() = default; 48 virtual ~Module() = default; 49 50 Module(const Module&) = delete; 51 Module& operator=(const Module&) = delete; 52 53 // Gets the base address of the module. 54 virtual uintptr_t GetBaseAddress() const = 0; 55 56 // Gets the opaque binary string that uniquely identifies a particular 57 // program version with high probability. This is parsed from headers of the 58 // loaded module. 59 // For binaries generated by GNU tools: 60 // Contents of the .note.gnu.build-id field. 61 // On Windows: 62 // GUID + AGE in the debug image headers of a module. 63 virtual std::string GetId() const = 0; 64 65 // Gets the debug basename of the module. This is the basename of the PDB 66 // file on Windows and the basename of the binary on other platforms. 67 virtual FilePath GetDebugBasename() const = 0; 68 69 // Gets the size of the module. 70 virtual size_t GetSize() const = 0; 71 72 // True if this is a native module. 73 virtual bool IsNative() const = 0; 74 }; 75 76 // Interface for lazily creating a native module for a given |address|. The 77 // provider is registered with RegisterAuxiliaryModuleProvider(). 78 class AuxiliaryModuleProvider { 79 public: 80 AuxiliaryModuleProvider() = default; 81 AuxiliaryModuleProvider(const AuxiliaryModuleProvider&) = delete; 82 AuxiliaryModuleProvider& operator=(const AuxiliaryModuleProvider&) = delete; 83 84 virtual std::unique_ptr<const Module> TryCreateModuleForAddress( 85 uintptr_t address) = 0; 86 87 protected: 88 ~AuxiliaryModuleProvider() = default; 89 }; 90 91 ModuleCache(); 92 ~ModuleCache(); 93 94 // Gets the module containing |address| or nullptr if |address| is not within 95 // a module. The returned module remains owned by and has the same lifetime as 96 // the ModuleCache object. 97 const Module* GetModuleForAddress(uintptr_t address); 98 std::vector<const Module*> GetModules() const; 99 100 // Updates the set of non-native modules maintained by the 101 // ModuleCache. Non-native modules represent regions of non-native executable 102 // code such as V8 generated code. 103 // 104 // Note that non-native modules may be embedded within native modules, as in 105 // the case of V8 builtin code compiled within Chrome. In that case 106 // GetModuleForAddress() will return the non-native module rather than the 107 // native module for the memory region it occupies. 108 // 109 // Modules in |defunct_modules| are removed from the set of active modules; 110 // specifically they no longer participate in the GetModuleForAddress() 111 // lookup. They continue to exist for the lifetime of the ModuleCache, 112 // however, so that existing references to them remain valid. Modules in 113 // |new_modules| are added to the set of active non-native modules. Modules in 114 // |new_modules| may not overlap with any non-native Modules already present 115 // in ModuleCache, unless those modules are provided in |defunct_modules| in 116 // the same call. 117 void UpdateNonNativeModules( 118 const std::vector<const Module*>& defunct_modules, 119 std::vector<std::unique_ptr<const Module>> new_modules); 120 121 // Adds a custom native module to the cache. This is intended to support 122 // native modules that require custom handling. In general, native modules 123 // will be found and added automatically when invoking GetModuleForAddress(). 124 // |module| may not overlap with any native Modules already present in 125 // ModuleCache. 126 void AddCustomNativeModule(std::unique_ptr<const Module> module); 127 128 // Registers a custom module provider for lazily creating native modules. At 129 // most one provider can be registered at any time, and the provider must be 130 // unregistered before being destroyed. This is intended to support native 131 // modules that require custom handling. In general, native modules will be 132 // found and added automatically when invoking GetModuleForAddress(). If no 133 // module is found, this provider will be used as fallback. 134 void RegisterAuxiliaryModuleProvider( 135 AuxiliaryModuleProvider* auxiliary_module_provider); 136 137 // Unregisters the custom module provider. 138 void UnregisterAuxiliaryModuleProvider( 139 AuxiliaryModuleProvider* auxiliary_module_provider); 140 141 // Gets the module containing |address| if one already exists, or nullptr 142 // otherwise. The returned module remains owned by and has the same lifetime 143 // as the ModuleCache object. 144 // NOTE: Only users that create their own modules and need control over native 145 // module creation should use this function. Everyone else should use 146 // GetModuleForAddress(). 147 const Module* GetExistingModuleForAddress(uintptr_t address) const; 148 149 private: 150 // Heterogenously compares modules by base address, and modules and 151 // addresses. The module/address comparison considers the address equivalent 152 // to the module if the address is within the extent of the module. Combined 153 // with is_transparent this allows modules to be looked up by address in the 154 // using containers. 155 struct ModuleAndAddressCompare { 156 using is_transparent = void; 157 bool operator()(const std::unique_ptr<const Module>& m1, 158 const std::unique_ptr<const Module>& m2) const; 159 bool operator()(const std::unique_ptr<const Module>& m1, 160 uintptr_t address) const; 161 bool operator()(uintptr_t address, 162 const std::unique_ptr<const Module>& m2) const; 163 }; 164 165 // Creates a Module object for the specified memory address. Returns null if 166 // the address does not belong to a module. 167 static std::unique_ptr<const Module> CreateModuleForAddress( 168 uintptr_t address); 169 170 // Set of native modules sorted by base address. We use set rather than 171 // flat_set because the latter type has O(n^2) runtime for adding modules 172 // one-at-a-time, which is how modules are added on Windows and Mac. 173 std::set<std::unique_ptr<const Module>, ModuleAndAddressCompare> 174 native_modules_; 175 176 // Set of non-native modules currently mapped into the address space, sorted 177 // by base address. Represented as flat_set because std::set does not support 178 // extracting move-only element types prior to C++17's 179 // std::set<>::extract(). The non-native module insertion/removal patterns -- 180 // initial bulk insertion, then infrequent inserts/removals -- should work 181 // reasonably well with the flat_set complexity guarantees. Separate from 182 // native_modules_ to support preferential lookup of non-native modules 183 // embedded in native modules; see comment on UpdateNonNativeModules(). 184 base::flat_set<std::unique_ptr<const Module>, ModuleAndAddressCompare> 185 non_native_modules_; 186 187 // Unsorted vector of inactive non-native modules. Inactive modules are no 188 // longer mapped in the address space and don't participate in address lookup, 189 // but are retained by the cache so that existing references to the them 190 // remain valid. Note that this cannot be represented as a set/flat_set 191 // because it can contain multiple modules that were loaded (then subsequently 192 // unloaded) at the same base address. 193 std::vector<std::unique_ptr<const Module>> inactive_non_native_modules_; 194 195 // Auxiliary module provider, for lazily creating native modules. 196 raw_ptr<AuxiliaryModuleProvider> auxiliary_module_provider_ = nullptr; 197 }; 198 199 } // namespace base 200 201 #endif // BASE_PROFILER_MODULE_CACHE_H_ 202