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