forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmodule_cache.cc
165 lines (138 loc) · 6.43 KB
/
module_cache.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/profiler/module_cache.h"
#include <iterator>
#include <utility>
#include "base/ranges/algorithm.h"
namespace base {
namespace {
// Supports heterogeneous comparisons on modules and addresses, for use in
// binary searching modules sorted by range for a contained address.
struct ModuleAddressCompare {
bool operator()(const std::unique_ptr<const ModuleCache::Module>& module,
uintptr_t address) const {
return module->GetBaseAddress() + module->GetSize() <= address;
}
bool operator()(
uintptr_t address,
const std::unique_ptr<const ModuleCache::Module>& module) const {
return address < module->GetBaseAddress();
}
};
} // namespace
ModuleCache::ModuleCache() = default;
ModuleCache::~ModuleCache() {
DCHECK_EQ(auxiliary_module_provider_, nullptr);
}
const ModuleCache::Module* ModuleCache::GetModuleForAddress(uintptr_t address) {
if (const ModuleCache::Module* module = GetExistingModuleForAddress(address))
return module;
std::unique_ptr<const Module> new_module = CreateModuleForAddress(address);
if (!new_module && auxiliary_module_provider_)
new_module = auxiliary_module_provider_->TryCreateModuleForAddress(address);
if (!new_module)
return nullptr;
const auto result = native_modules_.insert(std::move(new_module));
// TODO(https://crbug.com/1131769): Reintroduce DCHECK(result.second) after
// fixing the issue that is causing it to fail.
return result.first->get();
}
std::vector<const ModuleCache::Module*> ModuleCache::GetModules() const {
std::vector<const Module*> result;
result.reserve(native_modules_.size());
for (const std::unique_ptr<const Module>& module : native_modules_)
result.push_back(module.get());
for (const std::unique_ptr<const Module>& module : non_native_modules_)
result.push_back(module.get());
return result;
}
void ModuleCache::UpdateNonNativeModules(
const std::vector<const Module*>& defunct_modules,
std::vector<std::unique_ptr<const Module>> new_modules) {
// Insert the modules to remove into a set to support O(log(n)) lookup below.
flat_set<const Module*> defunct_modules_set(defunct_modules.begin(),
defunct_modules.end());
// Reorder the modules to be removed to the last slots in the set, then move
// them to the inactive modules, then erase the moved-from modules from the
// set. This is a variation on the standard erase-remove idiom, which is
// explicitly endorsed for implementing erase behavior on flat_sets.
//
// stable_partition is O(m*log(r)) where m is the number of current modules
// and r is the number of modules to remove. insert and erase are both O(r).
auto first_module_defunct_modules = ranges::stable_partition(
non_native_modules_,
[&defunct_modules_set](const std::unique_ptr<const Module>& module) {
return defunct_modules_set.find(module.get()) ==
defunct_modules_set.end();
});
// All modules requested to be removed should have been found.
DCHECK_EQ(
static_cast<ptrdiff_t>(defunct_modules.size()),
std::distance(first_module_defunct_modules, non_native_modules_.end()));
inactive_non_native_modules_.insert(
inactive_non_native_modules_.end(),
std::make_move_iterator(first_module_defunct_modules),
std::make_move_iterator(non_native_modules_.end()));
non_native_modules_.erase(first_module_defunct_modules,
non_native_modules_.end());
// Insert the modules to be added. This operation is O((m + a) + a*log(a))
// where m is the number of current modules and a is the number of modules to
// be added.
const size_t prior_non_native_modules_size = non_native_modules_.size();
non_native_modules_.insert(std::make_move_iterator(new_modules.begin()),
std::make_move_iterator(new_modules.end()));
// Every module in |new_modules| should have been moved into
// |non_native_modules_|. This guards against use-after-frees if |new_modules|
// were to contain any modules equivalent to what's already in
// |non_native_modules_|, in which case the module would remain in
// |new_modules| and be deleted on return from the function. While this
// scenario would be a violation of the API contract, it would present a
// difficult-to-track-down crash scenario.
CHECK_EQ(prior_non_native_modules_size + new_modules.size(),
non_native_modules_.size());
}
void ModuleCache::AddCustomNativeModule(std::unique_ptr<const Module> module) {
const bool was_inserted = native_modules_.insert(std::move(module)).second;
// |module| should have been inserted into |native_modules_|, indicating that
// there was no equivalent module already present. While this scenario would
// be a violation of the API contract, it would present a
// difficult-to-track-down crash scenario.
CHECK(was_inserted);
}
const ModuleCache::Module* ModuleCache::GetExistingModuleForAddress(
uintptr_t address) const {
const auto non_native_module_loc = non_native_modules_.find(address);
if (non_native_module_loc != non_native_modules_.end())
return non_native_module_loc->get();
const auto native_module_loc = native_modules_.find(address);
if (native_module_loc != native_modules_.end())
return native_module_loc->get();
return nullptr;
}
void ModuleCache::RegisterAuxiliaryModuleProvider(
AuxiliaryModuleProvider* auxiliary_module_provider) {
DCHECK(!auxiliary_module_provider_);
auxiliary_module_provider_ = auxiliary_module_provider;
}
void ModuleCache::UnregisterAuxiliaryModuleProvider(
AuxiliaryModuleProvider* auxiliary_module_provider) {
DCHECK_EQ(auxiliary_module_provider_, auxiliary_module_provider);
auxiliary_module_provider_ = nullptr;
}
bool ModuleCache::ModuleAndAddressCompare::operator()(
const std::unique_ptr<const Module>& m1,
const std::unique_ptr<const Module>& m2) const {
return m1->GetBaseAddress() < m2->GetBaseAddress();
}
bool ModuleCache::ModuleAndAddressCompare::operator()(
const std::unique_ptr<const Module>& m1,
uintptr_t address) const {
return m1->GetBaseAddress() + m1->GetSize() <= address;
}
bool ModuleCache::ModuleAndAddressCompare::operator()(
uintptr_t address,
const std::unique_ptr<const Module>& m2) const {
return address < m2->GetBaseAddress();
}
} // namespace base