| Index: tools/memory_watcher/memory_hook.cc
|
| diff --git a/tools/memory_watcher/memory_hook.cc b/tools/memory_watcher/memory_hook.cc
|
| deleted file mode 100644
|
| index 64df93d204150978ae7046a6f71821893266c3f3..0000000000000000000000000000000000000000
|
| --- a/tools/memory_watcher/memory_hook.cc
|
| +++ /dev/null
|
| @@ -1,562 +0,0 @@
|
| -// Copyright (c) 2006-2008 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.
|
| -
|
| -// Static class for hooking Win32 API routines.
|
| -
|
| -// Some notes about how to hook Memory Allocation Routines in Windows.
|
| -//
|
| -// For our purposes we do not hook the libc routines. There are two
|
| -// reasons for this. First, the libc routines all go through HeapAlloc
|
| -// anyway. So, it's redundant to log both HeapAlloc and malloc.
|
| -// Second, it can be tricky to hook in both static and dynamic linkages
|
| -// of libc.
|
| -
|
| -#include <windows.h>
|
| -
|
| -#include "memory_hook.h"
|
| -#include "memory_watcher.h"
|
| -#include "preamble_patcher.h"
|
| -
|
| -// Calls GetProcAddress, but casts to the correct type.
|
| -#define GET_PROC_ADDRESS(hmodule, name) \
|
| - ( (Type_##name)(::GetProcAddress(hmodule, #name)) )
|
| -
|
| -// Macro to declare Patch functions.
|
| -#define DECLARE_PATCH(name) Patch<Type_##name> patch_##name
|
| -
|
| -// Macro to install Patch functions.
|
| -#define INSTALL_PATCH(name) do { \
|
| - patch_##name.set_original(GET_PROC_ADDRESS(hkernel32, ##name)); \
|
| - patch_##name.Install(&Perftools_##name); \
|
| -} while (0)
|
| -
|
| -// Macro to install Patch functions.
|
| -#define INSTALL_NTDLLPATCH(name) do { \
|
| - patch_##name.set_original(GET_PROC_ADDRESS(hntdll, ##name)); \
|
| - patch_##name.Install(&Perftools_##name); \
|
| -} while (0)
|
| -
|
| -// Macro to uninstall Patch functions.
|
| -#define UNINSTALL_PATCH(name) patch_##name.Uninstall();
|
| -
|
| -
|
| -
|
| -// Windows APIs to be hooked
|
| -
|
| -// HeapAlloc routines
|
| -typedef HANDLE (WINAPI *Type_HeapCreate)(DWORD flOptions,
|
| - SIZE_T dwInitialSize,
|
| - SIZE_T dwMaximumSize);
|
| -typedef BOOL (WINAPI *Type_HeapDestroy)(HANDLE hHeap);
|
| -typedef LPVOID (WINAPI *Type_HeapAlloc)(HANDLE hHeap, DWORD dwFlags,
|
| - DWORD_PTR dwBytes);
|
| -typedef LPVOID (WINAPI *Type_HeapReAlloc)(HANDLE hHeap, DWORD dwFlags,
|
| - LPVOID lpMem, SIZE_T dwBytes);
|
| -typedef BOOL (WINAPI *Type_HeapFree)(HANDLE hHeap, DWORD dwFlags,
|
| - LPVOID lpMem);
|
| -
|
| -// GlobalAlloc routines
|
| -typedef HGLOBAL (WINAPI *Type_GlobalAlloc)(UINT uFlags, SIZE_T dwBytes);
|
| -typedef HGLOBAL (WINAPI *Type_GlobalReAlloc)(HGLOBAL hMem, SIZE_T dwBytes,
|
| - UINT uFlags);
|
| -typedef HGLOBAL (WINAPI *Type_GlobalFree)(HGLOBAL hMem);
|
| -
|
| -// LocalAlloc routines
|
| -typedef HLOCAL (WINAPI *Type_LocalAlloc)(UINT uFlags, SIZE_T uBytes);
|
| -typedef HLOCAL (WINAPI *Type_LocalReAlloc)(HLOCAL hMem, SIZE_T uBytes,
|
| - UINT uFlags);
|
| -typedef HLOCAL (WINAPI *Type_LocalFree)(HLOCAL hMem);
|
| -
|
| -// A Windows-API equivalent of mmap and munmap, for "anonymous regions"
|
| -typedef LPVOID (WINAPI *Type_VirtualAllocEx)(HANDLE process, LPVOID address,
|
| - SIZE_T size, DWORD type,
|
| - DWORD protect);
|
| -typedef BOOL (WINAPI *Type_VirtualFreeEx)(HANDLE process, LPVOID address,
|
| - SIZE_T size, DWORD type);
|
| -
|
| -// A Windows-API equivalent of mmap and munmap, for actual files
|
| -typedef LPVOID (WINAPI *Type_MapViewOfFile)(HANDLE hFileMappingObject,
|
| - DWORD dwDesiredAccess,
|
| - DWORD dwFileOffsetHigh,
|
| - DWORD dwFileOffsetLow,
|
| - SIZE_T dwNumberOfBytesToMap);
|
| -typedef LPVOID (WINAPI *Type_MapViewOfFileEx)(HANDLE hFileMappingObject,
|
| - DWORD dwDesiredAccess,
|
| - DWORD dwFileOffsetHigh,
|
| - DWORD dwFileOffsetLow,
|
| - SIZE_T dwNumberOfBytesToMap,
|
| - LPVOID lpBaseAddress);
|
| -typedef BOOL (WINAPI *Type_UnmapViewOfFile)(LPVOID lpBaseAddress);
|
| -
|
| -typedef DWORD (WINAPI *Type_NtUnmapViewOfSection)(HANDLE process,
|
| - LPVOID lpBaseAddress);
|
| -
|
| -
|
| -// Patch is a template for keeping the pointer to the original
|
| -// hooked routine, the function to call when hooked, and the
|
| -// stub routine which is patched.
|
| -template<class T>
|
| -class Patch {
|
| - public:
|
| - // Constructor. Does not hook the function yet.
|
| - Patch<T>()
|
| - : original_function_(NULL),
|
| - patch_function_(NULL),
|
| - stub_function_(NULL) {
|
| - }
|
| -
|
| - // Destructor. Unhooks the function if it has been hooked.
|
| - ~Patch<T>() {
|
| - Uninstall();
|
| - }
|
| -
|
| - // Patches original function with func.
|
| - // Must have called set_original to set the original function.
|
| - void Install(T func) {
|
| - patch_function_ = func;
|
| - CHECK(patch_function_ != NULL);
|
| - CHECK(original_function_ != NULL);
|
| - CHECK(stub_function_ == NULL);
|
| - CHECK(sidestep::SIDESTEP_SUCCESS ==
|
| - sidestep::PreamblePatcher::Patch(original_function_,
|
| - patch_function_, &stub_function_));
|
| - }
|
| -
|
| - // Un-patches the function.
|
| - void Uninstall() {
|
| - if (stub_function_)
|
| - sidestep::PreamblePatcher::Unpatch(original_function_,
|
| - patch_function_, stub_function_);
|
| - stub_function_ = NULL;
|
| - }
|
| -
|
| - // Set the function to be patched.
|
| - void set_original(T original) { original_function_ = original; }
|
| -
|
| - // Get the original function being patched.
|
| - T original() { return original_function_; }
|
| -
|
| - // Get the patched function. (e.g. the replacement function)
|
| - T patched() { return patch_function_; }
|
| -
|
| - // Access to the stub for calling the original function
|
| - // while it is patched.
|
| - T operator()() {
|
| - DCHECK(stub_function_);
|
| - return stub_function_;
|
| - }
|
| -
|
| - private:
|
| - // The function that we plan to patch.
|
| - T original_function_;
|
| - // The function to replace the original with.
|
| - T patch_function_;
|
| - // To unpatch, we also need to keep around a "stub" that points to the
|
| - // pre-patched Windows function.
|
| - T stub_function_;
|
| -};
|
| -
|
| -
|
| -// All Windows memory-allocation routines call through to one of these.
|
| -DECLARE_PATCH(HeapCreate);
|
| -DECLARE_PATCH(HeapDestroy);
|
| -DECLARE_PATCH(HeapAlloc);
|
| -DECLARE_PATCH(HeapReAlloc);
|
| -DECLARE_PATCH(HeapFree);
|
| -DECLARE_PATCH(VirtualAllocEx);
|
| -DECLARE_PATCH(VirtualFreeEx);
|
| -DECLARE_PATCH(MapViewOfFile);
|
| -DECLARE_PATCH(MapViewOfFileEx);
|
| -DECLARE_PATCH(UnmapViewOfFile);
|
| -DECLARE_PATCH(GlobalAlloc);
|
| -DECLARE_PATCH(GlobalReAlloc);
|
| -DECLARE_PATCH(GlobalFree);
|
| -DECLARE_PATCH(LocalAlloc);
|
| -DECLARE_PATCH(LocalReAlloc);
|
| -DECLARE_PATCH(LocalFree);
|
| -DECLARE_PATCH(NtUnmapViewOfSection);
|
| -
|
| -// Our replacement functions.
|
| -
|
| -static HANDLE WINAPI Perftools_HeapCreate(DWORD flOptions,
|
| - SIZE_T dwInitialSize,
|
| - SIZE_T dwMaximumSize) {
|
| - if (dwInitialSize > 4096)
|
| - dwInitialSize = 4096;
|
| - return patch_HeapCreate()(flOptions, dwInitialSize, dwMaximumSize);
|
| -}
|
| -
|
| -static BOOL WINAPI Perftools_HeapDestroy(HANDLE hHeap) {
|
| - return patch_HeapDestroy()(hHeap);
|
| -}
|
| -
|
| -static LPVOID WINAPI Perftools_HeapAlloc(HANDLE hHeap, DWORD dwFlags,
|
| - DWORD_PTR dwBytes) {
|
| - LPVOID rv = patch_HeapAlloc()(hHeap, dwFlags, dwBytes);
|
| - MemoryHook::hook()->OnTrack(hHeap, reinterpret_cast<int32>(rv), dwBytes);
|
| - return rv;
|
| -}
|
| -
|
| -static BOOL WINAPI Perftools_HeapFree(HANDLE hHeap, DWORD dwFlags,
|
| - LPVOID lpMem) {
|
| - size_t size = 0;
|
| - if (lpMem != 0) {
|
| - size = HeapSize(hHeap, 0, lpMem); // Will crash if lpMem is 0.
|
| - // Note: size could be 0; HeapAlloc does allocate 0 length buffers.
|
| - }
|
| - MemoryHook::hook()->OnUntrack(hHeap, reinterpret_cast<int32>(lpMem), size);
|
| - return patch_HeapFree()(hHeap, dwFlags, lpMem);
|
| -}
|
| -
|
| -static LPVOID WINAPI Perftools_HeapReAlloc(HANDLE hHeap, DWORD dwFlags,
|
| - LPVOID lpMem, SIZE_T dwBytes) {
|
| - // Don't call realloc, but instead do a free/malloc. The problem is that
|
| - // the builtin realloc may either expand a buffer, or it may simply
|
| - // just call free/malloc. If so, we will already have tracked the new
|
| - // block via Perftools_HeapAlloc.
|
| -
|
| - LPVOID rv = Perftools_HeapAlloc(hHeap, dwFlags, dwBytes);
|
| - DCHECK_EQ((HEAP_REALLOC_IN_PLACE_ONLY & dwFlags), 0u);
|
| -
|
| - // If there was an old buffer, now copy the data to the new buffer.
|
| - if (lpMem != 0) {
|
| - size_t size = HeapSize(hHeap, 0, lpMem);
|
| - if (size > dwBytes)
|
| - size = dwBytes;
|
| - // Note: size could be 0; HeapAlloc does allocate 0 length buffers.
|
| - memcpy(rv, lpMem, size);
|
| - Perftools_HeapFree(hHeap, dwFlags, lpMem);
|
| - }
|
| - return rv;
|
| -}
|
| -
|
| -static LPVOID WINAPI Perftools_VirtualAllocEx(HANDLE process, LPVOID address,
|
| - SIZE_T size, DWORD type,
|
| - DWORD protect) {
|
| - bool already_committed = false;
|
| - if (address != NULL) {
|
| - MEMORY_BASIC_INFORMATION info;
|
| - CHECK(VirtualQuery(address, &info, sizeof(info)));
|
| - if (info.State & MEM_COMMIT) {
|
| - already_committed = true;
|
| - CHECK(size >= info.RegionSize);
|
| - }
|
| - }
|
| - bool reserving = (address == NULL) || (type & MEM_RESERVE);
|
| - bool committing = !already_committed && (type & MEM_COMMIT);
|
| -
|
| -
|
| - LPVOID result = patch_VirtualAllocEx()(process, address, size, type,
|
| - protect);
|
| - MEMORY_BASIC_INFORMATION info;
|
| - CHECK(VirtualQuery(result, &info, sizeof(info)));
|
| - size = info.RegionSize;
|
| -
|
| - if (committing)
|
| - MemoryHook::hook()->OnTrack(0, reinterpret_cast<int32>(result), size);
|
| -
|
| - return result;
|
| -}
|
| -
|
| -static BOOL WINAPI Perftools_VirtualFreeEx(HANDLE process, LPVOID address,
|
| - SIZE_T size, DWORD type) {
|
| - int chunk_size = size;
|
| - MEMORY_BASIC_INFORMATION info;
|
| - CHECK(VirtualQuery(address, &info, sizeof(info)));
|
| - if (chunk_size == 0)
|
| - chunk_size = info.RegionSize;
|
| - bool decommit = (info.State & MEM_COMMIT) != 0;
|
| -
|
| - if (decommit)
|
| - MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(address),
|
| - chunk_size);
|
| -
|
| - return patch_VirtualFreeEx()(process, address, size, type);
|
| -}
|
| -
|
| -static base::Lock known_maps_lock;
|
| -static std::map<void*, int> known_maps;
|
| -
|
| -static LPVOID WINAPI Perftools_MapViewOfFileEx(HANDLE hFileMappingObject,
|
| - DWORD dwDesiredAccess,
|
| - DWORD dwFileOffsetHigh,
|
| - DWORD dwFileOffsetLow,
|
| - SIZE_T dwNumberOfBytesToMap,
|
| - LPVOID lpBaseAddress) {
|
| - // For this function pair, you always deallocate the full block of
|
| - // data that you allocate, so NewHook/DeleteHook is the right API.
|
| - LPVOID result = patch_MapViewOfFileEx()(hFileMappingObject, dwDesiredAccess,
|
| - dwFileOffsetHigh, dwFileOffsetLow,
|
| - dwNumberOfBytesToMap, lpBaseAddress);
|
| - {
|
| - base::AutoLock lock(known_maps_lock);
|
| - MEMORY_BASIC_INFORMATION info;
|
| - if (known_maps.find(result) == known_maps.end()) {
|
| - CHECK(VirtualQuery(result, &info, sizeof(info)));
|
| - // TODO(mbelshe): THIS map uses the standard heap!!!!
|
| - known_maps[result] = 1;
|
| - MemoryHook::hook()->OnTrack(0, reinterpret_cast<int32>(result),
|
| - info.RegionSize);
|
| - } else {
|
| - known_maps[result] = known_maps[result] + 1;
|
| - }
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -static LPVOID WINAPI Perftools_MapViewOfFile(HANDLE hFileMappingObject,
|
| - DWORD dwDesiredAccess,
|
| - DWORD dwFileOffsetHigh,
|
| - DWORD dwFileOffsetLow,
|
| - SIZE_T dwNumberOfBytesToMap) {
|
| - return Perftools_MapViewOfFileEx(hFileMappingObject, dwDesiredAccess,
|
| - dwFileOffsetHigh, dwFileOffsetLow,
|
| - dwNumberOfBytesToMap, 0);
|
| -}
|
| -
|
| -static BOOL WINAPI Perftools_UnmapViewOfFile(LPVOID lpBaseAddress) {
|
| - // This will call into NtUnmapViewOfSection().
|
| - return patch_UnmapViewOfFile()(lpBaseAddress);
|
| -}
|
| -
|
| -static DWORD WINAPI Perftools_NtUnmapViewOfSection(HANDLE process,
|
| - LPVOID lpBaseAddress) {
|
| - // Some windows APIs call directly into this routine rather
|
| - // than calling UnmapViewOfFile. If we didn't trap this function,
|
| - // then we appear to have bogus leaks.
|
| - {
|
| - base::AutoLock lock(known_maps_lock);
|
| - MEMORY_BASIC_INFORMATION info;
|
| - CHECK(VirtualQuery(lpBaseAddress, &info, sizeof(info)));
|
| - if (known_maps.find(lpBaseAddress) != known_maps.end()) {
|
| - if (known_maps[lpBaseAddress] == 1) {
|
| - MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(lpBaseAddress),
|
| - info.RegionSize);
|
| - known_maps.erase(lpBaseAddress);
|
| - } else {
|
| - known_maps[lpBaseAddress] = known_maps[lpBaseAddress] - 1;
|
| - }
|
| - }
|
| - }
|
| - return patch_NtUnmapViewOfSection()(process, lpBaseAddress);
|
| -}
|
| -
|
| -static HGLOBAL WINAPI Perftools_GlobalAlloc(UINT uFlags, SIZE_T dwBytes) {
|
| - // GlobalAlloc is built atop HeapAlloc anyway. So we don't track these.
|
| - // GlobalAlloc will internally call into HeapAlloc and we track there.
|
| -
|
| - // Force all memory to be fixed.
|
| - uFlags &= ~GMEM_MOVEABLE;
|
| - HGLOBAL rv = patch_GlobalAlloc()(uFlags, dwBytes);
|
| - return rv;
|
| -}
|
| -
|
| -static HGLOBAL WINAPI Perftools_GlobalFree(HGLOBAL hMem) {
|
| - return patch_GlobalFree()(hMem);
|
| -}
|
| -
|
| -static HGLOBAL WINAPI Perftools_GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes,
|
| - UINT uFlags) {
|
| - // TODO(jar): [The following looks like a copy/paste typo from LocalRealloc.]
|
| - // GlobalDiscard is a macro which calls LocalReAlloc with size 0.
|
| - if (dwBytes == 0) {
|
| - return patch_GlobalReAlloc()(hMem, dwBytes, uFlags);
|
| - }
|
| -
|
| - HGLOBAL rv = Perftools_GlobalAlloc(uFlags, dwBytes);
|
| - if (hMem != 0) {
|
| - size_t size = GlobalSize(hMem);
|
| - if (size > dwBytes)
|
| - size = dwBytes;
|
| - // Note: size could be 0; HeapAlloc does allocate 0 length buffers.
|
| - memcpy(rv, hMem, size);
|
| - Perftools_GlobalFree(hMem);
|
| - }
|
| -
|
| - return rv;
|
| -}
|
| -
|
| -static HLOCAL WINAPI Perftools_LocalAlloc(UINT uFlags, SIZE_T dwBytes) {
|
| - // LocalAlloc is built atop HeapAlloc anyway. So we don't track these.
|
| - // LocalAlloc will internally call into HeapAlloc and we track there.
|
| -
|
| - // Force all memory to be fixed.
|
| - uFlags &= ~LMEM_MOVEABLE;
|
| - HLOCAL rv = patch_LocalAlloc()(uFlags, dwBytes);
|
| - return rv;
|
| -}
|
| -
|
| -static HLOCAL WINAPI Perftools_LocalFree(HLOCAL hMem) {
|
| - return patch_LocalFree()(hMem);
|
| -}
|
| -
|
| -static HLOCAL WINAPI Perftools_LocalReAlloc(HLOCAL hMem, SIZE_T dwBytes,
|
| - UINT uFlags) {
|
| - // LocalDiscard is a macro which calls LocalReAlloc with size 0.
|
| - if (dwBytes == 0) {
|
| - return patch_LocalReAlloc()(hMem, dwBytes, uFlags);
|
| - }
|
| -
|
| - HGLOBAL rv = Perftools_LocalAlloc(uFlags, dwBytes);
|
| - if (hMem != 0) {
|
| - size_t size = LocalSize(hMem);
|
| - if (size > dwBytes)
|
| - size = dwBytes;
|
| - // Note: size could be 0; HeapAlloc does allocate 0 length buffers.
|
| - memcpy(rv, hMem, size);
|
| - Perftools_LocalFree(hMem);
|
| - }
|
| -
|
| - return rv;
|
| -}
|
| -
|
| -bool MemoryHook::hooked_ = false;
|
| -MemoryHook* MemoryHook::global_hook_ = NULL;
|
| -
|
| -MemoryHook::MemoryHook()
|
| - : watcher_(NULL),
|
| - heap_(NULL) {
|
| - CreateHeap();
|
| -}
|
| -
|
| -MemoryHook::~MemoryHook() {
|
| - // It's a bit dangerous to ever close this heap; MemoryWatchers may have
|
| - // used this heap for their tracking data. Closing the heap while any
|
| - // MemoryWatchers still exist is pretty dangerous.
|
| - CloseHeap();
|
| -}
|
| -
|
| -bool MemoryHook::Initialize() {
|
| - if (global_hook_ == NULL)
|
| - global_hook_ = new MemoryHook();
|
| - return true;
|
| -}
|
| -
|
| -bool MemoryHook::Hook() {
|
| - DCHECK(!hooked_);
|
| - if (!hooked_) {
|
| - DCHECK(global_hook_);
|
| -
|
| - // Luckily, Patch() doesn't call malloc or windows alloc routines
|
| - // itself -- though it does call new (we can use PatchWithStub to
|
| - // get around that, and will need to if we need to patch new).
|
| -
|
| - HMODULE hkernel32 = ::GetModuleHandle(L"kernel32");
|
| - CHECK(hkernel32 != NULL);
|
| -
|
| - HMODULE hntdll = ::GetModuleHandle(L"ntdll");
|
| - CHECK(hntdll != NULL);
|
| -
|
| - // Now that we've found all the functions, patch them
|
| - INSTALL_PATCH(HeapCreate);
|
| - INSTALL_PATCH(HeapDestroy);
|
| - INSTALL_PATCH(HeapAlloc);
|
| - INSTALL_PATCH(HeapReAlloc);
|
| - INSTALL_PATCH(HeapFree);
|
| - INSTALL_PATCH(VirtualAllocEx);
|
| - INSTALL_PATCH(VirtualFreeEx);
|
| - INSTALL_PATCH(MapViewOfFileEx);
|
| - INSTALL_PATCH(MapViewOfFile);
|
| - INSTALL_PATCH(UnmapViewOfFile);
|
| - INSTALL_NTDLLPATCH(NtUnmapViewOfSection);
|
| - INSTALL_PATCH(GlobalAlloc);
|
| - INSTALL_PATCH(GlobalReAlloc);
|
| - INSTALL_PATCH(GlobalFree);
|
| - INSTALL_PATCH(LocalAlloc);
|
| - INSTALL_PATCH(LocalReAlloc);
|
| - INSTALL_PATCH(LocalFree);
|
| -
|
| - // We are finally completely hooked.
|
| - hooked_ = true;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool MemoryHook::Unhook() {
|
| - if (hooked_) {
|
| - // We need to go back to the system malloc/etc at global destruct time,
|
| - // so objects that were constructed before tcmalloc, using the system
|
| - // malloc, can destroy themselves using the system free. This depends
|
| - // on DLLs unloading in the reverse order in which they load!
|
| - //
|
| - // We also go back to the default HeapAlloc/etc, just for consistency.
|
| - // Who knows, it may help avoid weird bugs in some situations.
|
| - UNINSTALL_PATCH(HeapCreate);
|
| - UNINSTALL_PATCH(HeapDestroy);
|
| - UNINSTALL_PATCH(HeapAlloc);
|
| - UNINSTALL_PATCH(HeapReAlloc);
|
| - UNINSTALL_PATCH(HeapFree);
|
| - UNINSTALL_PATCH(VirtualAllocEx);
|
| - UNINSTALL_PATCH(VirtualFreeEx);
|
| - UNINSTALL_PATCH(MapViewOfFile);
|
| - UNINSTALL_PATCH(MapViewOfFileEx);
|
| - UNINSTALL_PATCH(UnmapViewOfFile);
|
| - UNINSTALL_PATCH(NtUnmapViewOfSection);
|
| - UNINSTALL_PATCH(GlobalAlloc);
|
| - UNINSTALL_PATCH(GlobalReAlloc);
|
| - UNINSTALL_PATCH(GlobalFree);
|
| - UNINSTALL_PATCH(LocalAlloc);
|
| - UNINSTALL_PATCH(LocalReAlloc);
|
| - UNINSTALL_PATCH(LocalFree);
|
| -
|
| - hooked_ = false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool MemoryHook::RegisterWatcher(MemoryObserver* watcher) {
|
| - DCHECK(global_hook_->watcher_ == NULL);
|
| -
|
| - if (!hooked_)
|
| - Hook();
|
| -
|
| - DCHECK(global_hook_);
|
| - global_hook_->watcher_ = watcher;
|
| - return true;
|
| -}
|
| -
|
| -bool MemoryHook::UnregisterWatcher(MemoryObserver* watcher) {
|
| - DCHECK(hooked_);
|
| - DCHECK(global_hook_->watcher_ == watcher);
|
| - // TODO(jar): changing watcher_ here is very racy. Other threads may (without
|
| - // a lock) testing, and then calling through this value. We probably can't
|
| - // remove this until we are single threaded.
|
| - global_hook_->watcher_ = NULL;
|
| -
|
| - // For now, since there are no more watchers, unhook memory.
|
| - return Unhook();
|
| -}
|
| -
|
| -bool MemoryHook::CreateHeap() {
|
| - // Create a heap for our own memory.
|
| - DCHECK(heap_ == NULL);
|
| - heap_ = HeapCreate(0, 0, 0);
|
| - DCHECK(heap_ != NULL);
|
| - return heap_ != NULL;
|
| -}
|
| -
|
| -bool MemoryHook::CloseHeap() {
|
| - DCHECK(heap_ != NULL);
|
| - HeapDestroy(heap_);
|
| - heap_ = NULL;
|
| - return true;
|
| -}
|
| -
|
| -void MemoryHook::OnTrack(HANDLE heap, int32 id, int32 size) {
|
| - // Don't notify about allocations to our internal heap.
|
| - if (heap == heap_)
|
| - return;
|
| -
|
| - if (watcher_)
|
| - watcher_->OnTrack(heap, id, size);
|
| -}
|
| -
|
| -void MemoryHook::OnUntrack(HANDLE heap, int32 id, int32 size) {
|
| - // Don't notify about allocations to our internal heap.
|
| - if (heap == heap_)
|
| - return;
|
| -
|
| - if (watcher_)
|
| - watcher_->OnUntrack(heap, id, size);
|
| -}
|
|
|