Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: chrome/browser/conflicts/module_database_win.cc

Issue 2576843002: [win] Create ModuleDatabase and ModuleEventSinkImpl. (Closed)
Patch Set: Moar comments, fix typos. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
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 "chrome/browser/conflicts/module_database_win.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10
11 namespace {
12
13 // Document the assumptions made on the ProcessType enum in order to convert
14 // them to bits.
15 static_assert(content::PROCESS_TYPE_UNKNOWN == 1,
16 "assumes unknown process type has value 1");
17 static_assert(content::PROCESS_TYPE_BROWSER == 2,
18 "assumes browser process type has value 2");
19 constexpr uint32_t kMinProcessType = content::PROCESS_TYPE_BROWSER;
20
21 } // namespace
22
23 ModuleDatabase::ModuleDatabase(
24 scoped_refptr<base::SequencedTaskRunner> task_runner)
25 : task_runner_(std::move(task_runner)), weak_ptr_factory_(this) {}
26
27 ModuleDatabase::~ModuleDatabase() = default;
28
29 void ModuleDatabase::OnProcessStarted(uint32_t process_id,
30 uint64_t creation_time,
31 content::ProcessType process_type) {
32 DCHECK(task_runner_->RunsTasksOnCurrentThread());
33 CreateProcessInfo(process_id, creation_time, process_type);
34 }
35
36 void ModuleDatabase::OnModuleLoad(uint32_t process_id,
37 uint64_t creation_time,
38 const base::FilePath& module_path,
39 uint32_t module_size,
40 uint32_t module_time_date_stamp,
41 uintptr_t module_load_address) {
42 // Messages can arrive from any thread (UI thread for calls over IPC, and
43 // anywhere at all for calls from ModuleWatcher), so bounce if necessary.
44 if (!task_runner_->RunsTasksOnCurrentThread()) {
45 task_runner_->PostTask(
46 FROM_HERE, base::Bind(&ModuleDatabase::OnModuleLoad,
47 weak_ptr_factory_.GetWeakPtr(), process_id,
48 creation_time, module_path, module_size,
49 module_time_date_stamp, module_load_address));
50 return;
51 }
52
53 // In theory this should always succeed. However, it is possible for a client
54 // to misbehave and send out-of-order messages. It is easy to be tolerant of
55 // this by simply not updating the process info in this case. It's not worth
56 // crashing if this data is slightly out of sync as this is purely
57 // informational.
58 auto* process_info = GetProcessInfo(process_id, creation_time);
59 if (!process_info)
60 return;
61
62 auto* module_info =
63 FindOrCreateModuleInfo(module_path, module_size, module_time_date_stamp);
64
65 // Update the list of process types that this module has been seen in.
66 module_info->process_types |= ProcessTypeToBit(process_info->process_type);
67
68 // Update the load address maps.
69 InsertLoadAddress(module_info->module_id, module_load_address,
70 &process_info->loaded_modules);
71 RemoveLoadAddressById(module_info->module_id,
72 &process_info->unloaded_modules);
73 }
74
75 void ModuleDatabase::OnModuleUnload(uint32_t process_id,
76 uint64_t creation_time,
77 uintptr_t module_load_address) {
78 // Messages can arrive from any thread (UI thread for calls over IPC, and
79 // anywhere at all for calls from ModuleWatcher), so bounce if necessary.
80 if (!task_runner_->RunsTasksOnCurrentThread()) {
81 task_runner_->PostTask(
82 FROM_HERE, base::Bind(&ModuleDatabase::OnModuleUnload,
83 weak_ptr_factory_.GetWeakPtr(), process_id,
84 creation_time, module_load_address));
85 return;
86 }
87
88 // See the long-winded comment in OnModuleLoad about reasons why this can
89 // fail (but shouldn't normally).
90 auto* process_info = GetProcessInfo(process_id, creation_time);
91 if (!process_info)
92 return;
93
94 // Find the module corresponding to this load address. This is O(1) in the
95 // common case of removing a recently removed module, but O(n) worst case.
96 // Thankfully, unload events occur far less often and n is quite small.
97 size_t i = FindLoadAddressIndexByAddress(module_load_address,
98 process_info->loaded_modules);
99
100 // No such module found. This shouldn't happen either, unless messages are
101 // malformed or out of order. Gracefully fail in this case.
102 if (i == kInvalidIndex)
103 return;
104
105 ModuleId module_id = process_info->loaded_modules[i].first;
106
107 // Remove from the loaded module list and insert into the unloaded module
108 // list.
109 RemoveLoadAddressByIndex(i, &process_info->loaded_modules);
110 InsertLoadAddress(module_id, module_load_address,
111 &process_info->unloaded_modules);
112 }
113
114 void ModuleDatabase::OnProcessEnded(uint32_t process_id,
115 uint64_t creation_time) {
116 // Messages can arrive from any thread (UI thread for calls over IPC, and
117 // anywhere at all for calls from ModuleWatcher), so bounce if necessary.
118 if (!task_runner_->RunsTasksOnCurrentThread()) {
119 task_runner_->PostTask(
120 FROM_HERE,
121 base::Bind(&ModuleDatabase::OnProcessEnded,
122 weak_ptr_factory_.GetWeakPtr(), process_id, creation_time));
123 return;
124 }
125
126 DeleteProcessInfo(process_id, creation_time);
127 }
128
129 // static
130 uint32_t ModuleDatabase::ProcessTypeToBit(content::ProcessType process_type) {
131 uint32_t bit_index = static_cast<uint32_t>(process_type) - kMinProcessType;
132 DCHECK_LE(0u, bit_index);
133 DCHECK_GE(31u, bit_index);
134 uint32_t bit = (1 << bit_index);
135 return bit;
136 }
137
138 // static
139 content::ProcessType ModuleDatabase::BitIndexToProcessType(uint32_t bit_index) {
140 DCHECK_LE(0u, bit_index);
141 DCHECK_GE(31u, bit_index);
142 return static_cast<content::ProcessType>(bit_index + kMinProcessType);
143 }
144
145 // static
146 size_t ModuleDatabase::FindLoadAddressIndexById(
147 ModuleId module_id,
148 const ModuleLoadAddresses& load_addresses) {
149 // Process elements in reverse order so that RemoveLoadAddressById can handle
150 // the more common case of removing the maximum element in O(1).
151 for (size_t i = load_addresses.size() - 1; i < load_addresses.size(); --i) {
152 if (load_addresses[i].first == module_id)
153 return i;
154 }
155 return kInvalidIndex;
156 }
157
158 // static
159 size_t ModuleDatabase::FindLoadAddressIndexByAddress(
160 uintptr_t load_address,
161 const ModuleLoadAddresses& load_addresses) {
162 for (size_t i = 0; i < load_addresses.size(); ++i) {
163 if (load_addresses[i].second == load_address)
164 return i;
165 }
166 return kInvalidIndex;
167 }
168
169 // static
170 void ModuleDatabase::InsertLoadAddress(ModuleId module_id,
171 uintptr_t load_address,
172 ModuleLoadAddresses* load_addresses) {
173 // A very small optimization: the largest module_id is always placed at the
174 // end of the array. This is the most common case, and allows O(1)
175 // determination that a |module_id| isn't present when it's bigger than the
176 // maximum already in the array. This keeps insertions to O(1) in the usual
177 // case.
178 if (load_addresses->empty() || module_id > load_addresses->back().first) {
179 load_addresses->emplace_back(module_id, load_address);
180 return;
181 }
182
183 // If the module exists in the collection then update the load address and
184 // return. This should never really occur, unless the client is deliberately
185 // misbehaving or a race causes a reload event (at a different address) to be
186 // processed before the corresponding unload. This is very unlikely.
187 size_t i = FindLoadAddressIndexById(module_id, *load_addresses);
188 if (i != kInvalidIndex) {
189 (*load_addresses)[i].second = load_address;
190 return;
191 }
192
193 // The module does not exist, and by definition is smaller in value than
194 // the largest module ID already present. Add it, ensuring that the largest
195 // module ID stays at the end.
196 load_addresses->emplace(--load_addresses->end(), module_id, load_address);
197
198 return;
grt (UTC plus 2) 2016/12/22 14:19:06 nit: omit this and previous line
chrisha 2017/01/03 21:34:47 Done.
199 }
200
201 // static
202 void ModuleDatabase::RemoveLoadAddressById(
203 ModuleId module_id,
204 ModuleLoadAddresses* load_addresses) {
205 if (load_addresses->empty())
206 return;
207
208 // This handles the special case of removing the max element in O(1), as
209 // FindLoadAddressIndexById processes the elements in reverse order.
210 size_t i = FindLoadAddressIndexById(module_id, *load_addresses);
211 RemoveLoadAddressByIndex(i, load_addresses);
212 }
213
214 // static
215 void ModuleDatabase::RemoveLoadAddressByIndex(
216 size_t index,
217 ModuleLoadAddresses* load_addresses) {
218 DCHECK_LE(0u, index);
grt (UTC plus 2) 2016/12/22 14:19:06 index is unsigned, so isn't this unconditionally t
chrisha 2017/01/03 21:34:47 Oops, left over from when it was as int. Removed.
219 DCHECK_GT(load_addresses->size(), index);
grt (UTC plus 2) 2016/12/22 14:19:06 i think this is more natural the other way: index
chrisha 2017/01/03 21:34:47 Done.
220
221 // Special case: removing the last module (with maximum id). Need to find the
222 // new maximum element and ensure it goes to the end.
223 if (load_addresses->size() > 2 && index + 1 == load_addresses->size()) {
224 // Find the index of the new maximum element.
225 ModuleId max_id = -1; // These start at zero.
226 int max_index = kInvalidIndex;
227 for (size_t i = 0; i < load_addresses->size() - 1; ++i) {
228 if ((*load_addresses)[i].first > max_id) {
229 max_id = (*load_addresses)[i].first;
230 max_index = i;
231 }
232 }
233
234 // Remove the max element.
235 load_addresses->resize(load_addresses->size() - 1);
grt (UTC plus 2) 2016/12/22 14:19:05 nit: it's been established on line 223 that index
chrisha 2017/01/03 21:34:47 Done. Also made the comment more verbose.
236
237 // If the new max element isn't in the last position, then swap it so it is.
238 int last_index = load_addresses->size() - 1;
239 if (max_index != last_index)
240 std::swap((*load_addresses)[max_index], (*load_addresses)[last_index]);
241
242 return;
243 }
244
245 // If the element to be removed is second last then a single copy is
246 // sufficient.
247 if (index + 2 == load_addresses->size()) {
248 (*load_addresses)[index] = (*load_addresses)[index + 1];
249 } else {
250 // In the general case two copies are necessary.
251 int max_index = load_addresses->size() - 1;
252 (*load_addresses)[index] = (*load_addresses)[max_index - 1];
253 (*load_addresses)[max_index - 1] = (*load_addresses)[max_index];
254 }
255
256 // Remove the last element, which is now duplicated.
257 load_addresses->resize(load_addresses->size() - 1);
258 }
259
260 ModuleDatabase::ModuleInfo* ModuleDatabase::FindOrCreateModuleInfo(
261 const base::FilePath& module_path,
262 uint32_t module_size,
263 uint32_t module_time_date_stamp) {
264 ModuleInfo key(module_path, module_size, module_time_date_stamp,
265 modules_.size());
266 auto result = modules_.emplace(key);
grt (UTC plus 2) 2016/12/22 14:19:05 nit: get rid of |key| so that only one instance is
chrisha 2017/01/03 21:34:47 Done.
267 // Cast away constness so that the non-key portions of the object can be
268 // modified. The key portions of the object are themselves marked const, so
269 // this causes no trouble with std::set.
270 return const_cast<ModuleInfo*>(&(*result.first));
271 }
272
273 ModuleDatabase::ProcessInfo* ModuleDatabase::GetProcessInfo(
274 uint32_t process_id,
275 uint64_t creation_time) {
276 ProcessInfo key(process_id, creation_time, content::PROCESS_TYPE_UNKNOWN);
277 auto it = processes_.find(key);
278 if (it == processes_.end())
279 return nullptr;
280 // Cast away constness so that the non-key portions of the object can be
281 // modified. The key portions of the object are themselves marked const, so
282 // this causes no trouble with std::set.
283 return const_cast<ProcessInfo*>(&(*it));
284 }
285
286 void ModuleDatabase::CreateProcessInfo(uint32_t process_id,
287 uint64_t creation_time,
288 content::ProcessType process_type) {
289 ProcessInfo key(process_id, creation_time, process_type);
290 processes_.emplace(key);
grt (UTC plus 2) 2016/12/22 14:19:06 nit: get rid of |key| so that only one instance is
chrisha 2017/01/03 21:34:47 Done.
291 }
292
293 void ModuleDatabase::DeleteProcessInfo(uint32_t process_id,
294 uint64_t creation_time) {
295 ProcessInfo key(process_id, creation_time, content::PROCESS_TYPE_UNKNOWN);
296 auto it = processes_.find(key);
297 if (it != processes_.end())
298 processes_.erase(it);
299 }
300
301 // ModuleDatabase::ModuleInfo --------------------------------------------------
302
303 ModuleDatabase::ModuleInfo::ModuleInfo(const base::FilePath& module_path,
304 uint32_t module_size,
305 uint32_t module_time_date_stamp,
306 uint32_t module_id)
307 : module_path(module_path),
308 module_size(module_size),
309 module_time_date_stamp(module_time_date_stamp),
310 module_id(module_id),
311 process_types(0) {}
312
313 bool ModuleDatabase::ModuleInfo::operator<(const ModuleInfo& mi) const {
314 // The key consists of the triplet of
grt (UTC plus 2) 2016/12/22 14:19:06 i think std::tuple implements operator< for lexico
chrisha 2017/01/03 21:34:47 Great idea! Done.
315 // (module_path, module_size, module_time_date_stamp).
316 if (module_path < mi.module_path)
317 return true;
318 if (mi.module_path < module_path) // FilePath has no operator>.
319 return false;
320 if (module_size < mi.module_size)
321 return true;
322 if (module_size > mi.module_size)
323 return false;
324 return module_time_date_stamp < mi.module_time_date_stamp;
325 }
326
327 // ModuleDatabase::ProcessInfo -------------------------------------------------
328
329 ModuleDatabase::ProcessInfo::ProcessInfo(uint32_t process_id,
330 uint64_t creation_time,
331 content::ProcessType process_type)
332 : process_id(process_id),
333 creation_time(creation_time),
334 process_type(process_type) {}
335
336 bool ModuleDatabase::ProcessInfo::operator<(const ProcessInfo& pi) const {
337 // The key consists of the pair of (process_id, creation_time).
grt (UTC plus 2) 2016/12/22 14:19:06 maybe use tuple or pair here, too?
chrisha 2017/01/03 21:34:47 Done.
338 if (process_id < pi.process_id)
339 return true;
340 if (process_id > pi.process_id)
341 return false;
342 return creation_time < pi.creation_time;
343 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698