OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 // Static class for hooking Win32 API routines. | |
6 | |
7 // Some notes about how to hook Memory Allocation Routines in Windows. | |
8 // | |
9 // For our purposes we do not hook the libc routines. There are two | |
10 // reasons for this. First, the libc routines all go through HeapAlloc | |
11 // anyway. So, it's redundant to log both HeapAlloc and malloc. | |
12 // Second, it can be tricky to hook in both static and dynamic linkages | |
13 // of libc. | |
14 | |
15 #include <windows.h> | |
16 | |
17 #include "memory_hook.h" | |
18 #include "memory_watcher.h" | |
19 #include "preamble_patcher.h" | |
20 | |
21 // Calls GetProcAddress, but casts to the correct type. | |
22 #define GET_PROC_ADDRESS(hmodule, name) \ | |
23 ( (Type_##name)(::GetProcAddress(hmodule, #name)) ) | |
24 | |
25 // Macro to declare Patch functions. | |
26 #define DECLARE_PATCH(name) Patch<Type_##name> patch_##name | |
27 | |
28 // Macro to install Patch functions. | |
29 #define INSTALL_PATCH(name) do { \ | |
30 patch_##name.set_original(GET_PROC_ADDRESS(hkernel32, ##name)); \ | |
31 patch_##name.Install(&Perftools_##name); \ | |
32 } while (0) | |
33 | |
34 // Macro to install Patch functions. | |
35 #define INSTALL_NTDLLPATCH(name) do { \ | |
36 patch_##name.set_original(GET_PROC_ADDRESS(hntdll, ##name)); \ | |
37 patch_##name.Install(&Perftools_##name); \ | |
38 } while (0) | |
39 | |
40 // Macro to uninstall Patch functions. | |
41 #define UNINSTALL_PATCH(name) patch_##name.Uninstall(); | |
42 | |
43 | |
44 | |
45 // Windows APIs to be hooked | |
46 | |
47 // HeapAlloc routines | |
48 typedef HANDLE (WINAPI *Type_HeapCreate)(DWORD flOptions, | |
49 SIZE_T dwInitialSize, | |
50 SIZE_T dwMaximumSize); | |
51 typedef BOOL (WINAPI *Type_HeapDestroy)(HANDLE hHeap); | |
52 typedef LPVOID (WINAPI *Type_HeapAlloc)(HANDLE hHeap, DWORD dwFlags, | |
53 DWORD_PTR dwBytes); | |
54 typedef LPVOID (WINAPI *Type_HeapReAlloc)(HANDLE hHeap, DWORD dwFlags, | |
55 LPVOID lpMem, SIZE_T dwBytes); | |
56 typedef BOOL (WINAPI *Type_HeapFree)(HANDLE hHeap, DWORD dwFlags, | |
57 LPVOID lpMem); | |
58 | |
59 // GlobalAlloc routines | |
60 typedef HGLOBAL (WINAPI *Type_GlobalAlloc)(UINT uFlags, SIZE_T dwBytes); | |
61 typedef HGLOBAL (WINAPI *Type_GlobalReAlloc)(HGLOBAL hMem, SIZE_T dwBytes, | |
62 UINT uFlags); | |
63 typedef HGLOBAL (WINAPI *Type_GlobalFree)(HGLOBAL hMem); | |
64 | |
65 // LocalAlloc routines | |
66 typedef HLOCAL (WINAPI *Type_LocalAlloc)(UINT uFlags, SIZE_T uBytes); | |
67 typedef HLOCAL (WINAPI *Type_LocalReAlloc)(HLOCAL hMem, SIZE_T uBytes, | |
68 UINT uFlags); | |
69 typedef HLOCAL (WINAPI *Type_LocalFree)(HLOCAL hMem); | |
70 | |
71 // A Windows-API equivalent of mmap and munmap, for "anonymous regions" | |
72 typedef LPVOID (WINAPI *Type_VirtualAllocEx)(HANDLE process, LPVOID address, | |
73 SIZE_T size, DWORD type, | |
74 DWORD protect); | |
75 typedef BOOL (WINAPI *Type_VirtualFreeEx)(HANDLE process, LPVOID address, | |
76 SIZE_T size, DWORD type); | |
77 | |
78 // A Windows-API equivalent of mmap and munmap, for actual files | |
79 typedef LPVOID (WINAPI *Type_MapViewOfFile)(HANDLE hFileMappingObject, | |
80 DWORD dwDesiredAccess, | |
81 DWORD dwFileOffsetHigh, | |
82 DWORD dwFileOffsetLow, | |
83 SIZE_T dwNumberOfBytesToMap); | |
84 typedef LPVOID (WINAPI *Type_MapViewOfFileEx)(HANDLE hFileMappingObject, | |
85 DWORD dwDesiredAccess, | |
86 DWORD dwFileOffsetHigh, | |
87 DWORD dwFileOffsetLow, | |
88 SIZE_T dwNumberOfBytesToMap, | |
89 LPVOID lpBaseAddress); | |
90 typedef BOOL (WINAPI *Type_UnmapViewOfFile)(LPVOID lpBaseAddress); | |
91 | |
92 typedef DWORD (WINAPI *Type_NtUnmapViewOfSection)(HANDLE process, | |
93 LPVOID lpBaseAddress); | |
94 | |
95 | |
96 // Patch is a template for keeping the pointer to the original | |
97 // hooked routine, the function to call when hooked, and the | |
98 // stub routine which is patched. | |
99 template<class T> | |
100 class Patch { | |
101 public: | |
102 // Constructor. Does not hook the function yet. | |
103 Patch<T>() | |
104 : original_function_(NULL), | |
105 patch_function_(NULL), | |
106 stub_function_(NULL) { | |
107 } | |
108 | |
109 // Destructor. Unhooks the function if it has been hooked. | |
110 ~Patch<T>() { | |
111 Uninstall(); | |
112 } | |
113 | |
114 // Patches original function with func. | |
115 // Must have called set_original to set the original function. | |
116 void Install(T func) { | |
117 patch_function_ = func; | |
118 CHECK(patch_function_ != NULL); | |
119 CHECK(original_function_ != NULL); | |
120 CHECK(stub_function_ == NULL); | |
121 CHECK(sidestep::SIDESTEP_SUCCESS == | |
122 sidestep::PreamblePatcher::Patch(original_function_, | |
123 patch_function_, &stub_function_)); | |
124 } | |
125 | |
126 // Un-patches the function. | |
127 void Uninstall() { | |
128 if (stub_function_) | |
129 sidestep::PreamblePatcher::Unpatch(original_function_, | |
130 patch_function_, stub_function_); | |
131 stub_function_ = NULL; | |
132 } | |
133 | |
134 // Set the function to be patched. | |
135 void set_original(T original) { original_function_ = original; } | |
136 | |
137 // Get the original function being patched. | |
138 T original() { return original_function_; } | |
139 | |
140 // Get the patched function. (e.g. the replacement function) | |
141 T patched() { return patch_function_; } | |
142 | |
143 // Access to the stub for calling the original function | |
144 // while it is patched. | |
145 T operator()() { | |
146 DCHECK(stub_function_); | |
147 return stub_function_; | |
148 } | |
149 | |
150 private: | |
151 // The function that we plan to patch. | |
152 T original_function_; | |
153 // The function to replace the original with. | |
154 T patch_function_; | |
155 // To unpatch, we also need to keep around a "stub" that points to the | |
156 // pre-patched Windows function. | |
157 T stub_function_; | |
158 }; | |
159 | |
160 | |
161 // All Windows memory-allocation routines call through to one of these. | |
162 DECLARE_PATCH(HeapCreate); | |
163 DECLARE_PATCH(HeapDestroy); | |
164 DECLARE_PATCH(HeapAlloc); | |
165 DECLARE_PATCH(HeapReAlloc); | |
166 DECLARE_PATCH(HeapFree); | |
167 DECLARE_PATCH(VirtualAllocEx); | |
168 DECLARE_PATCH(VirtualFreeEx); | |
169 DECLARE_PATCH(MapViewOfFile); | |
170 DECLARE_PATCH(MapViewOfFileEx); | |
171 DECLARE_PATCH(UnmapViewOfFile); | |
172 DECLARE_PATCH(GlobalAlloc); | |
173 DECLARE_PATCH(GlobalReAlloc); | |
174 DECLARE_PATCH(GlobalFree); | |
175 DECLARE_PATCH(LocalAlloc); | |
176 DECLARE_PATCH(LocalReAlloc); | |
177 DECLARE_PATCH(LocalFree); | |
178 DECLARE_PATCH(NtUnmapViewOfSection); | |
179 | |
180 // Our replacement functions. | |
181 | |
182 static HANDLE WINAPI Perftools_HeapCreate(DWORD flOptions, | |
183 SIZE_T dwInitialSize, | |
184 SIZE_T dwMaximumSize) { | |
185 if (dwInitialSize > 4096) | |
186 dwInitialSize = 4096; | |
187 return patch_HeapCreate()(flOptions, dwInitialSize, dwMaximumSize); | |
188 } | |
189 | |
190 static BOOL WINAPI Perftools_HeapDestroy(HANDLE hHeap) { | |
191 return patch_HeapDestroy()(hHeap); | |
192 } | |
193 | |
194 static LPVOID WINAPI Perftools_HeapAlloc(HANDLE hHeap, DWORD dwFlags, | |
195 DWORD_PTR dwBytes) { | |
196 LPVOID rv = patch_HeapAlloc()(hHeap, dwFlags, dwBytes); | |
197 MemoryHook::hook()->OnTrack(hHeap, reinterpret_cast<int32>(rv), dwBytes); | |
198 return rv; | |
199 } | |
200 | |
201 static BOOL WINAPI Perftools_HeapFree(HANDLE hHeap, DWORD dwFlags, | |
202 LPVOID lpMem) { | |
203 size_t size = 0; | |
204 if (lpMem != 0) { | |
205 size = HeapSize(hHeap, 0, lpMem); // Will crash if lpMem is 0. | |
206 // Note: size could be 0; HeapAlloc does allocate 0 length buffers. | |
207 } | |
208 MemoryHook::hook()->OnUntrack(hHeap, reinterpret_cast<int32>(lpMem), size); | |
209 return patch_HeapFree()(hHeap, dwFlags, lpMem); | |
210 } | |
211 | |
212 static LPVOID WINAPI Perftools_HeapReAlloc(HANDLE hHeap, DWORD dwFlags, | |
213 LPVOID lpMem, SIZE_T dwBytes) { | |
214 // Don't call realloc, but instead do a free/malloc. The problem is that | |
215 // the builtin realloc may either expand a buffer, or it may simply | |
216 // just call free/malloc. If so, we will already have tracked the new | |
217 // block via Perftools_HeapAlloc. | |
218 | |
219 LPVOID rv = Perftools_HeapAlloc(hHeap, dwFlags, dwBytes); | |
220 DCHECK_EQ((HEAP_REALLOC_IN_PLACE_ONLY & dwFlags), 0u); | |
221 | |
222 // If there was an old buffer, now copy the data to the new buffer. | |
223 if (lpMem != 0) { | |
224 size_t size = HeapSize(hHeap, 0, lpMem); | |
225 if (size > dwBytes) | |
226 size = dwBytes; | |
227 // Note: size could be 0; HeapAlloc does allocate 0 length buffers. | |
228 memcpy(rv, lpMem, size); | |
229 Perftools_HeapFree(hHeap, dwFlags, lpMem); | |
230 } | |
231 return rv; | |
232 } | |
233 | |
234 static LPVOID WINAPI Perftools_VirtualAllocEx(HANDLE process, LPVOID address, | |
235 SIZE_T size, DWORD type, | |
236 DWORD protect) { | |
237 bool already_committed = false; | |
238 if (address != NULL) { | |
239 MEMORY_BASIC_INFORMATION info; | |
240 CHECK(VirtualQuery(address, &info, sizeof(info))); | |
241 if (info.State & MEM_COMMIT) { | |
242 already_committed = true; | |
243 CHECK(size >= info.RegionSize); | |
244 } | |
245 } | |
246 bool reserving = (address == NULL) || (type & MEM_RESERVE); | |
247 bool committing = !already_committed && (type & MEM_COMMIT); | |
248 | |
249 | |
250 LPVOID result = patch_VirtualAllocEx()(process, address, size, type, | |
251 protect); | |
252 MEMORY_BASIC_INFORMATION info; | |
253 CHECK(VirtualQuery(result, &info, sizeof(info))); | |
254 size = info.RegionSize; | |
255 | |
256 if (committing) | |
257 MemoryHook::hook()->OnTrack(0, reinterpret_cast<int32>(result), size); | |
258 | |
259 return result; | |
260 } | |
261 | |
262 static BOOL WINAPI Perftools_VirtualFreeEx(HANDLE process, LPVOID address, | |
263 SIZE_T size, DWORD type) { | |
264 int chunk_size = size; | |
265 MEMORY_BASIC_INFORMATION info; | |
266 CHECK(VirtualQuery(address, &info, sizeof(info))); | |
267 if (chunk_size == 0) | |
268 chunk_size = info.RegionSize; | |
269 bool decommit = (info.State & MEM_COMMIT) != 0; | |
270 | |
271 if (decommit) | |
272 MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(address), | |
273 chunk_size); | |
274 | |
275 return patch_VirtualFreeEx()(process, address, size, type); | |
276 } | |
277 | |
278 static base::Lock known_maps_lock; | |
279 static std::map<void*, int> known_maps; | |
280 | |
281 static LPVOID WINAPI Perftools_MapViewOfFileEx(HANDLE hFileMappingObject, | |
282 DWORD dwDesiredAccess, | |
283 DWORD dwFileOffsetHigh, | |
284 DWORD dwFileOffsetLow, | |
285 SIZE_T dwNumberOfBytesToMap, | |
286 LPVOID lpBaseAddress) { | |
287 // For this function pair, you always deallocate the full block of | |
288 // data that you allocate, so NewHook/DeleteHook is the right API. | |
289 LPVOID result = patch_MapViewOfFileEx()(hFileMappingObject, dwDesiredAccess, | |
290 dwFileOffsetHigh, dwFileOffsetLow, | |
291 dwNumberOfBytesToMap, lpBaseAddress); | |
292 { | |
293 base::AutoLock lock(known_maps_lock); | |
294 MEMORY_BASIC_INFORMATION info; | |
295 if (known_maps.find(result) == known_maps.end()) { | |
296 CHECK(VirtualQuery(result, &info, sizeof(info))); | |
297 // TODO(mbelshe): THIS map uses the standard heap!!!! | |
298 known_maps[result] = 1; | |
299 MemoryHook::hook()->OnTrack(0, reinterpret_cast<int32>(result), | |
300 info.RegionSize); | |
301 } else { | |
302 known_maps[result] = known_maps[result] + 1; | |
303 } | |
304 } | |
305 return result; | |
306 } | |
307 | |
308 static LPVOID WINAPI Perftools_MapViewOfFile(HANDLE hFileMappingObject, | |
309 DWORD dwDesiredAccess, | |
310 DWORD dwFileOffsetHigh, | |
311 DWORD dwFileOffsetLow, | |
312 SIZE_T dwNumberOfBytesToMap) { | |
313 return Perftools_MapViewOfFileEx(hFileMappingObject, dwDesiredAccess, | |
314 dwFileOffsetHigh, dwFileOffsetLow, | |
315 dwNumberOfBytesToMap, 0); | |
316 } | |
317 | |
318 static BOOL WINAPI Perftools_UnmapViewOfFile(LPVOID lpBaseAddress) { | |
319 // This will call into NtUnmapViewOfSection(). | |
320 return patch_UnmapViewOfFile()(lpBaseAddress); | |
321 } | |
322 | |
323 static DWORD WINAPI Perftools_NtUnmapViewOfSection(HANDLE process, | |
324 LPVOID lpBaseAddress) { | |
325 // Some windows APIs call directly into this routine rather | |
326 // than calling UnmapViewOfFile. If we didn't trap this function, | |
327 // then we appear to have bogus leaks. | |
328 { | |
329 base::AutoLock lock(known_maps_lock); | |
330 MEMORY_BASIC_INFORMATION info; | |
331 CHECK(VirtualQuery(lpBaseAddress, &info, sizeof(info))); | |
332 if (known_maps.find(lpBaseAddress) != known_maps.end()) { | |
333 if (known_maps[lpBaseAddress] == 1) { | |
334 MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(lpBaseAddress), | |
335 info.RegionSize); | |
336 known_maps.erase(lpBaseAddress); | |
337 } else { | |
338 known_maps[lpBaseAddress] = known_maps[lpBaseAddress] - 1; | |
339 } | |
340 } | |
341 } | |
342 return patch_NtUnmapViewOfSection()(process, lpBaseAddress); | |
343 } | |
344 | |
345 static HGLOBAL WINAPI Perftools_GlobalAlloc(UINT uFlags, SIZE_T dwBytes) { | |
346 // GlobalAlloc is built atop HeapAlloc anyway. So we don't track these. | |
347 // GlobalAlloc will internally call into HeapAlloc and we track there. | |
348 | |
349 // Force all memory to be fixed. | |
350 uFlags &= ~GMEM_MOVEABLE; | |
351 HGLOBAL rv = patch_GlobalAlloc()(uFlags, dwBytes); | |
352 return rv; | |
353 } | |
354 | |
355 static HGLOBAL WINAPI Perftools_GlobalFree(HGLOBAL hMem) { | |
356 return patch_GlobalFree()(hMem); | |
357 } | |
358 | |
359 static HGLOBAL WINAPI Perftools_GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, | |
360 UINT uFlags) { | |
361 // TODO(jar): [The following looks like a copy/paste typo from LocalRealloc.] | |
362 // GlobalDiscard is a macro which calls LocalReAlloc with size 0. | |
363 if (dwBytes == 0) { | |
364 return patch_GlobalReAlloc()(hMem, dwBytes, uFlags); | |
365 } | |
366 | |
367 HGLOBAL rv = Perftools_GlobalAlloc(uFlags, dwBytes); | |
368 if (hMem != 0) { | |
369 size_t size = GlobalSize(hMem); | |
370 if (size > dwBytes) | |
371 size = dwBytes; | |
372 // Note: size could be 0; HeapAlloc does allocate 0 length buffers. | |
373 memcpy(rv, hMem, size); | |
374 Perftools_GlobalFree(hMem); | |
375 } | |
376 | |
377 return rv; | |
378 } | |
379 | |
380 static HLOCAL WINAPI Perftools_LocalAlloc(UINT uFlags, SIZE_T dwBytes) { | |
381 // LocalAlloc is built atop HeapAlloc anyway. So we don't track these. | |
382 // LocalAlloc will internally call into HeapAlloc and we track there. | |
383 | |
384 // Force all memory to be fixed. | |
385 uFlags &= ~LMEM_MOVEABLE; | |
386 HLOCAL rv = patch_LocalAlloc()(uFlags, dwBytes); | |
387 return rv; | |
388 } | |
389 | |
390 static HLOCAL WINAPI Perftools_LocalFree(HLOCAL hMem) { | |
391 return patch_LocalFree()(hMem); | |
392 } | |
393 | |
394 static HLOCAL WINAPI Perftools_LocalReAlloc(HLOCAL hMem, SIZE_T dwBytes, | |
395 UINT uFlags) { | |
396 // LocalDiscard is a macro which calls LocalReAlloc with size 0. | |
397 if (dwBytes == 0) { | |
398 return patch_LocalReAlloc()(hMem, dwBytes, uFlags); | |
399 } | |
400 | |
401 HGLOBAL rv = Perftools_LocalAlloc(uFlags, dwBytes); | |
402 if (hMem != 0) { | |
403 size_t size = LocalSize(hMem); | |
404 if (size > dwBytes) | |
405 size = dwBytes; | |
406 // Note: size could be 0; HeapAlloc does allocate 0 length buffers. | |
407 memcpy(rv, hMem, size); | |
408 Perftools_LocalFree(hMem); | |
409 } | |
410 | |
411 return rv; | |
412 } | |
413 | |
414 bool MemoryHook::hooked_ = false; | |
415 MemoryHook* MemoryHook::global_hook_ = NULL; | |
416 | |
417 MemoryHook::MemoryHook() | |
418 : watcher_(NULL), | |
419 heap_(NULL) { | |
420 CreateHeap(); | |
421 } | |
422 | |
423 MemoryHook::~MemoryHook() { | |
424 // It's a bit dangerous to ever close this heap; MemoryWatchers may have | |
425 // used this heap for their tracking data. Closing the heap while any | |
426 // MemoryWatchers still exist is pretty dangerous. | |
427 CloseHeap(); | |
428 } | |
429 | |
430 bool MemoryHook::Initialize() { | |
431 if (global_hook_ == NULL) | |
432 global_hook_ = new MemoryHook(); | |
433 return true; | |
434 } | |
435 | |
436 bool MemoryHook::Hook() { | |
437 DCHECK(!hooked_); | |
438 if (!hooked_) { | |
439 DCHECK(global_hook_); | |
440 | |
441 // Luckily, Patch() doesn't call malloc or windows alloc routines | |
442 // itself -- though it does call new (we can use PatchWithStub to | |
443 // get around that, and will need to if we need to patch new). | |
444 | |
445 HMODULE hkernel32 = ::GetModuleHandle(L"kernel32"); | |
446 CHECK(hkernel32 != NULL); | |
447 | |
448 HMODULE hntdll = ::GetModuleHandle(L"ntdll"); | |
449 CHECK(hntdll != NULL); | |
450 | |
451 // Now that we've found all the functions, patch them | |
452 INSTALL_PATCH(HeapCreate); | |
453 INSTALL_PATCH(HeapDestroy); | |
454 INSTALL_PATCH(HeapAlloc); | |
455 INSTALL_PATCH(HeapReAlloc); | |
456 INSTALL_PATCH(HeapFree); | |
457 INSTALL_PATCH(VirtualAllocEx); | |
458 INSTALL_PATCH(VirtualFreeEx); | |
459 INSTALL_PATCH(MapViewOfFileEx); | |
460 INSTALL_PATCH(MapViewOfFile); | |
461 INSTALL_PATCH(UnmapViewOfFile); | |
462 INSTALL_NTDLLPATCH(NtUnmapViewOfSection); | |
463 INSTALL_PATCH(GlobalAlloc); | |
464 INSTALL_PATCH(GlobalReAlloc); | |
465 INSTALL_PATCH(GlobalFree); | |
466 INSTALL_PATCH(LocalAlloc); | |
467 INSTALL_PATCH(LocalReAlloc); | |
468 INSTALL_PATCH(LocalFree); | |
469 | |
470 // We are finally completely hooked. | |
471 hooked_ = true; | |
472 } | |
473 return true; | |
474 } | |
475 | |
476 bool MemoryHook::Unhook() { | |
477 if (hooked_) { | |
478 // We need to go back to the system malloc/etc at global destruct time, | |
479 // so objects that were constructed before tcmalloc, using the system | |
480 // malloc, can destroy themselves using the system free. This depends | |
481 // on DLLs unloading in the reverse order in which they load! | |
482 // | |
483 // We also go back to the default HeapAlloc/etc, just for consistency. | |
484 // Who knows, it may help avoid weird bugs in some situations. | |
485 UNINSTALL_PATCH(HeapCreate); | |
486 UNINSTALL_PATCH(HeapDestroy); | |
487 UNINSTALL_PATCH(HeapAlloc); | |
488 UNINSTALL_PATCH(HeapReAlloc); | |
489 UNINSTALL_PATCH(HeapFree); | |
490 UNINSTALL_PATCH(VirtualAllocEx); | |
491 UNINSTALL_PATCH(VirtualFreeEx); | |
492 UNINSTALL_PATCH(MapViewOfFile); | |
493 UNINSTALL_PATCH(MapViewOfFileEx); | |
494 UNINSTALL_PATCH(UnmapViewOfFile); | |
495 UNINSTALL_PATCH(NtUnmapViewOfSection); | |
496 UNINSTALL_PATCH(GlobalAlloc); | |
497 UNINSTALL_PATCH(GlobalReAlloc); | |
498 UNINSTALL_PATCH(GlobalFree); | |
499 UNINSTALL_PATCH(LocalAlloc); | |
500 UNINSTALL_PATCH(LocalReAlloc); | |
501 UNINSTALL_PATCH(LocalFree); | |
502 | |
503 hooked_ = false; | |
504 } | |
505 return true; | |
506 } | |
507 | |
508 bool MemoryHook::RegisterWatcher(MemoryObserver* watcher) { | |
509 DCHECK(global_hook_->watcher_ == NULL); | |
510 | |
511 if (!hooked_) | |
512 Hook(); | |
513 | |
514 DCHECK(global_hook_); | |
515 global_hook_->watcher_ = watcher; | |
516 return true; | |
517 } | |
518 | |
519 bool MemoryHook::UnregisterWatcher(MemoryObserver* watcher) { | |
520 DCHECK(hooked_); | |
521 DCHECK(global_hook_->watcher_ == watcher); | |
522 // TODO(jar): changing watcher_ here is very racy. Other threads may (without | |
523 // a lock) testing, and then calling through this value. We probably can't | |
524 // remove this until we are single threaded. | |
525 global_hook_->watcher_ = NULL; | |
526 | |
527 // For now, since there are no more watchers, unhook memory. | |
528 return Unhook(); | |
529 } | |
530 | |
531 bool MemoryHook::CreateHeap() { | |
532 // Create a heap for our own memory. | |
533 DCHECK(heap_ == NULL); | |
534 heap_ = HeapCreate(0, 0, 0); | |
535 DCHECK(heap_ != NULL); | |
536 return heap_ != NULL; | |
537 } | |
538 | |
539 bool MemoryHook::CloseHeap() { | |
540 DCHECK(heap_ != NULL); | |
541 HeapDestroy(heap_); | |
542 heap_ = NULL; | |
543 return true; | |
544 } | |
545 | |
546 void MemoryHook::OnTrack(HANDLE heap, int32 id, int32 size) { | |
547 // Don't notify about allocations to our internal heap. | |
548 if (heap == heap_) | |
549 return; | |
550 | |
551 if (watcher_) | |
552 watcher_->OnTrack(heap, id, size); | |
553 } | |
554 | |
555 void MemoryHook::OnUntrack(HANDLE heap, int32 id, int32 size) { | |
556 // Don't notify about allocations to our internal heap. | |
557 if (heap == heap_) | |
558 return; | |
559 | |
560 if (watcher_) | |
561 watcher_->OnUntrack(heap, id, size); | |
562 } | |
OLD | NEW |