Home | History | Annotate | Download | only in memory_watcher
      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 }
    563