• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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