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