summaryrefslogtreecommitdiffstats
path: root/tools/memory_watcher/memory_hook.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:12:16 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:12:16 +0000
commit920c091ac3ee15079194c82ae8a7a18215f3f23c (patch)
treed28515d1e7732e2b6d077df1b4855ace3f4ac84f /tools/memory_watcher/memory_hook.cc
parentae2c20f398933a9e86c387dcc465ec0f71065ffc (diff)
downloadchromium_src-920c091ac3ee15079194c82ae8a7a18215f3f23c.zip
chromium_src-920c091ac3ee15079194c82ae8a7a18215f3f23c.tar.gz
chromium_src-920c091ac3ee15079194c82ae8a7a18215f3f23c.tar.bz2
Add tools to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/memory_watcher/memory_hook.cc')
-rw-r--r--tools/memory_watcher/memory_hook.cc580
1 files changed, 580 insertions, 0 deletions
diff --git a/tools/memory_watcher/memory_hook.cc b/tools/memory_watcher/memory_hook.cc
new file mode 100644
index 0000000..2997fc0
--- /dev/null
+++ b/tools/memory_watcher/memory_hook.cc
@@ -0,0 +1,580 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// 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);
+
+ // 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;
+ }
+ 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);
+
+ if (decommit)
+ MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(address),
+ chunk_size);
+
+ return patch_VirtualFreeEx()(process, address, size, type);
+}
+
+static 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);
+ {
+ 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.
+ {
+ 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) {
+ // 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);
+ 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);
+}