• 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 #include "base/profiler/module_cache.h"
6 
7 #include <iterator>
8 #include <utility>
9 
10 #include "base/check_op.h"
11 #include "base/ranges/algorithm.h"
12 #include "base/strings/strcat.h"
13 
14 namespace base {
15 
16 namespace {
17 
18 // Supports heterogeneous comparisons on modules and addresses, for use in
19 // binary searching modules sorted by range for a contained address.
20 struct ModuleAddressCompare {
operator ()base::__anon47a454790111::ModuleAddressCompare21   bool operator()(const std::unique_ptr<const ModuleCache::Module>& module,
22                   uintptr_t address) const {
23     return module->GetBaseAddress() + module->GetSize() <= address;
24   }
25 
operator ()base::__anon47a454790111::ModuleAddressCompare26   bool operator()(
27       uintptr_t address,
28       const std::unique_ptr<const ModuleCache::Module>& module) const {
29     return address < module->GetBaseAddress();
30   }
31 };
32 
33 }  // namespace
34 
TransformModuleIDToSymbolServerFormat(StringPiece module_id)35 std::string TransformModuleIDToSymbolServerFormat(StringPiece module_id) {
36   std::string mangled_id(module_id);
37   // Android and Linux Chrome builds use the "breakpad" format to index their
38   // build id, so we transform the build id for these platforms. All other
39   // platforms keep their symbols indexed by the original build ID.
40 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
41   // Linux ELF module IDs are 160bit integers, which we need to mangle
42   // down to 128bit integers to match the id that Breakpad outputs.
43   // Example on version '66.0.3359.170' x64:
44   //   Build-ID: "7f0715c2 86f8 b16c 10e4ad349cda3b9b 56c7a773
45   //   Debug-ID  "C215077F F886 6CB1 10E4AD349CDA3B9B 0"
46 
47   if (mangled_id.size() < 32) {
48     mangled_id.resize(32, '0');
49   }
50 
51   mangled_id = base::StrCat({mangled_id.substr(6, 2), mangled_id.substr(4, 2),
52                              mangled_id.substr(2, 2), mangled_id.substr(0, 2),
53                              mangled_id.substr(10, 2), mangled_id.substr(8, 2),
54                              mangled_id.substr(14, 2), mangled_id.substr(12, 2),
55                              mangled_id.substr(16, 16), "0"});
56 #endif
57   return mangled_id;
58 }
59 
60 ModuleCache::ModuleCache() = default;
61 
~ModuleCache()62 ModuleCache::~ModuleCache() {
63   DCHECK_EQ(auxiliary_module_provider_, nullptr);
64 }
65 
GetModuleForAddress(uintptr_t address)66 const ModuleCache::Module* ModuleCache::GetModuleForAddress(uintptr_t address) {
67   if (const ModuleCache::Module* module = GetExistingModuleForAddress(address))
68     return module;
69 
70   std::unique_ptr<const Module> new_module = CreateModuleForAddress(address);
71   if (!new_module && auxiliary_module_provider_)
72     new_module = auxiliary_module_provider_->TryCreateModuleForAddress(address);
73   if (!new_module)
74     return nullptr;
75 
76   const auto result = native_modules_.insert(std::move(new_module));
77   // TODO(https://crbug.com/1131769): Reintroduce DCHECK(result.second) after
78   // fixing the issue that is causing it to fail.
79   return result.first->get();
80 }
81 
GetModules() const82 std::vector<const ModuleCache::Module*> ModuleCache::GetModules() const {
83   std::vector<const Module*> result;
84   result.reserve(native_modules_.size());
85   for (const std::unique_ptr<const Module>& module : native_modules_)
86     result.push_back(module.get());
87   for (const std::unique_ptr<const Module>& module : non_native_modules_)
88     result.push_back(module.get());
89   return result;
90 }
91 
UpdateNonNativeModules(const std::vector<const Module * > & defunct_modules,std::vector<std::unique_ptr<const Module>> new_modules)92 void ModuleCache::UpdateNonNativeModules(
93     const std::vector<const Module*>& defunct_modules,
94     std::vector<std::unique_ptr<const Module>> new_modules) {
95   // Insert the modules to remove into a set to support O(log(n)) lookup below.
96   flat_set<const Module*> defunct_modules_set(defunct_modules.begin(),
97                                               defunct_modules.end());
98 
99   // Reorder the modules to be removed to the last slots in the set, then move
100   // them to the inactive modules, then erase the moved-from modules from the
101   // set. This is a variation on the standard erase-remove idiom, which is
102   // explicitly endorsed for implementing erase behavior on flat_sets.
103   //
104   // stable_partition is O(m*log(r)) where m is the number of current modules
105   // and r is the number of modules to remove. insert and erase are both O(r).
106   auto first_module_defunct_modules = ranges::stable_partition(
107       non_native_modules_,
108       [&defunct_modules_set](const std::unique_ptr<const Module>& module) {
109         return defunct_modules_set.find(module.get()) ==
110                defunct_modules_set.end();
111       });
112   // All modules requested to be removed should have been found.
113   DCHECK_EQ(
114       static_cast<ptrdiff_t>(defunct_modules.size()),
115       std::distance(first_module_defunct_modules, non_native_modules_.end()));
116   inactive_non_native_modules_.insert(
117       inactive_non_native_modules_.end(),
118       std::make_move_iterator(first_module_defunct_modules),
119       std::make_move_iterator(non_native_modules_.end()));
120   non_native_modules_.erase(first_module_defunct_modules,
121                             non_native_modules_.end());
122 
123   // Insert the modules to be added. This operation is O((m + a) + a*log(a))
124   // where m is the number of current modules and a is the number of modules to
125   // be added.
126   const size_t prior_non_native_modules_size = non_native_modules_.size();
127   non_native_modules_.insert(std::make_move_iterator(new_modules.begin()),
128                              std::make_move_iterator(new_modules.end()));
129   // Every module in |new_modules| should have been moved into
130   // |non_native_modules_|. This guards against use-after-frees if |new_modules|
131   // were to contain any modules equivalent to what's already in
132   // |non_native_modules_|, in which case the module would remain in
133   // |new_modules| and be deleted on return from the function. While this
134   // scenario would be a violation of the API contract, it would present a
135   // difficult-to-track-down crash scenario.
136   CHECK_EQ(prior_non_native_modules_size + new_modules.size(),
137            non_native_modules_.size());
138 }
139 
AddCustomNativeModule(std::unique_ptr<const Module> module)140 void ModuleCache::AddCustomNativeModule(std::unique_ptr<const Module> module) {
141   const bool was_inserted = native_modules_.insert(std::move(module)).second;
142   // |module| should have been inserted into |native_modules_|, indicating that
143   // there was no equivalent module already present. While this scenario would
144   // be a violation of the API contract, it would present a
145   // difficult-to-track-down crash scenario.
146   CHECK(was_inserted);
147 }
148 
GetExistingModuleForAddress(uintptr_t address) const149 const ModuleCache::Module* ModuleCache::GetExistingModuleForAddress(
150     uintptr_t address) const {
151   const auto non_native_module_loc = non_native_modules_.find(address);
152   if (non_native_module_loc != non_native_modules_.end())
153     return non_native_module_loc->get();
154 
155   const auto native_module_loc = native_modules_.find(address);
156   if (native_module_loc != native_modules_.end())
157     return native_module_loc->get();
158 
159   return nullptr;
160 }
161 
RegisterAuxiliaryModuleProvider(AuxiliaryModuleProvider * auxiliary_module_provider)162 void ModuleCache::RegisterAuxiliaryModuleProvider(
163     AuxiliaryModuleProvider* auxiliary_module_provider) {
164   DCHECK(!auxiliary_module_provider_);
165   auxiliary_module_provider_ = auxiliary_module_provider;
166 }
167 
UnregisterAuxiliaryModuleProvider(AuxiliaryModuleProvider * auxiliary_module_provider)168 void ModuleCache::UnregisterAuxiliaryModuleProvider(
169     AuxiliaryModuleProvider* auxiliary_module_provider) {
170   DCHECK_EQ(auxiliary_module_provider_, auxiliary_module_provider);
171   auxiliary_module_provider_ = nullptr;
172 }
173 
operator ()(const std::unique_ptr<const Module> & m1,const std::unique_ptr<const Module> & m2) const174 bool ModuleCache::ModuleAndAddressCompare::operator()(
175     const std::unique_ptr<const Module>& m1,
176     const std::unique_ptr<const Module>& m2) const {
177   return m1->GetBaseAddress() < m2->GetBaseAddress();
178 }
179 
operator ()(const std::unique_ptr<const Module> & m1,uintptr_t address) const180 bool ModuleCache::ModuleAndAddressCompare::operator()(
181     const std::unique_ptr<const Module>& m1,
182     uintptr_t address) const {
183   return m1->GetBaseAddress() + m1->GetSize() <= address;
184 }
185 
operator ()(uintptr_t address,const std::unique_ptr<const Module> & m2) const186 bool ModuleCache::ModuleAndAddressCompare::operator()(
187     uintptr_t address,
188     const std::unique_ptr<const Module>& m2) const {
189   return address < m2->GetBaseAddress();
190 }
191 
192 }  // namespace base
193