| 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 |