Home | History | Annotate | Download | only in memory_watcher
      1 // Copyright (c) 2012 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 // The memory_watcher.dll is hooked by simply linking it.  When we get the
      6 // windows notification that this DLL is loaded, we do a few things:
      7 //    1) Register a Hot Key.
      8 //       Only one process can hook the Hot Key, so one will get it, and the
      9 //       others will silently fail.
     10 //    2) Create a thread to wait on an event.
     11 //       Since only one process will get the HotKey, it will be responsible for
     12 //       notifying all process when it's time to do something.  Each process
     13 //       will have a thread waiting for communication from the master to dump
     14 //       the callstacks.
     15 
     16 #include <windows.h>
     17 
     18 #include "base/at_exit.h"
     19 #include "tools/memory_watcher/memory_watcher.h"
     20 #include "tools/memory_watcher/hotkey.h"
     21 
     22 class MemoryWatcherDumpKey;  // Defined below.
     23 
     24 static wchar_t* kDumpEvent = L"MemWatcher.DumpEvent";
     25 static base::AtExitManager* g_memory_watcher_exit_manager = NULL;
     26 static MemoryWatcher* g_memory_watcher = NULL;
     27 static MemoryWatcherDumpKey* g_hotkey_handler = NULL;
     28 static HANDLE g_dump_event = INVALID_HANDLE_VALUE;
     29 static HANDLE g_quit_event = INVALID_HANDLE_VALUE;
     30 static HANDLE g_watcher_thread = INVALID_HANDLE_VALUE;
     31 
     32 // A HotKey to dump the memory statistics.
     33 class MemoryWatcherDumpKey : public HotKeyHandler {
     34  public:
     35   MemoryWatcherDumpKey(UINT modifiers, UINT vkey)
     36     : HotKeyHandler(modifiers, vkey) {}
     37 
     38   virtual LRESULT OnHotKey(UINT, WPARAM, LPARAM, BOOL& bHandled) {
     39     SetEvent(g_dump_event);
     40     return 1;
     41   }
     42 };
     43 
     44 // Creates the global memory watcher.
     45 void CreateMemoryWatcher() {
     46   g_memory_watcher_exit_manager = new base::AtExitManager();
     47   g_memory_watcher = new MemoryWatcher();
     48   // Register ALT-CONTROL-D to Dump Memory stats.
     49   g_hotkey_handler = new MemoryWatcherDumpKey(MOD_ALT|MOD_CONTROL, 0x44);
     50 }
     51 
     52 // Deletes the global memory watcher.
     53 void DeleteMemoryWatcher() {
     54   if (g_hotkey_handler)
     55     delete g_hotkey_handler;
     56   g_hotkey_handler = NULL;
     57   if (g_memory_watcher)
     58     delete g_memory_watcher;
     59   g_memory_watcher = NULL;
     60 
     61   // Intentionly leak g_memory_watcher_exit_manager.
     62 }
     63 
     64 // Thread for watching for key events.
     65 DWORD WINAPI ThreadMain(LPVOID) {
     66   bool stopping = false;
     67   HANDLE events[2] =  { g_dump_event, g_quit_event };
     68   while (!stopping) {
     69     DWORD rv = WaitForMultipleObjects(2, events, FALSE, INFINITE);
     70     switch (rv) {
     71       case WAIT_OBJECT_0:
     72         if (g_memory_watcher) {
     73           g_memory_watcher->DumpLeaks();
     74         }
     75         stopping = true;
     76         break;
     77       case WAIT_OBJECT_0 + 1:
     78         stopping = true;
     79         break;
     80       default:
     81         NOTREACHED();
     82         break;
     83     }
     84   }
     85   return 0;
     86 }
     87 
     88 // Creates the background thread
     89 void CreateBackgroundThread() {
     90   // Create a named event which can be used to notify
     91   // all watched processes.
     92   g_dump_event = CreateEvent(0, TRUE, FALSE, kDumpEvent);
     93   DCHECK(g_dump_event != NULL);
     94 
     95   // Create a local event which can be used to kill our
     96   // background thread.
     97   g_quit_event = CreateEvent(0, TRUE, FALSE, NULL);
     98   DCHECK(g_quit_event != NULL);
     99 
    100   // Create the background thread.
    101   g_watcher_thread = CreateThread(0,
    102                                 0,
    103                                 ThreadMain,
    104                                 0,
    105                                 0,
    106                                 0);
    107   DCHECK(g_watcher_thread != NULL);
    108 }
    109 
    110 // Tell the background thread to stop.
    111 void StopBackgroundThread() {
    112   // Send notification to our background thread.
    113   SetEvent(g_quit_event);
    114 
    115   // Wait for our background thread to die.
    116   DWORD rv = WaitForSingleObject(g_watcher_thread, INFINITE);
    117   DCHECK(rv == WAIT_OBJECT_0);
    118 
    119   // Cleanup our global handles.
    120   CloseHandle(g_quit_event);
    121   CloseHandle(g_dump_event);
    122   CloseHandle(g_watcher_thread);
    123 }
    124 
    125 bool IsChromeExe() {
    126   return GetModuleHandleA("chrome.exe") != NULL;
    127 }
    128 
    129 extern "C" {
    130 // DllMain is the windows entry point to this DLL.
    131 // We use the entry point as the mechanism for starting and stopping
    132 // the MemoryWatcher.
    133 BOOL WINAPI DllMain(HINSTANCE dll_instance, DWORD reason,
    134                               LPVOID reserved) {
    135   if (!IsChromeExe())
    136     return FALSE;
    137 
    138   switch (reason) {
    139     case DLL_PROCESS_ATTACH:
    140       CreateMemoryWatcher();
    141       CreateBackgroundThread();
    142       break;
    143     case DLL_PROCESS_DETACH:
    144       DeleteMemoryWatcher();
    145       StopBackgroundThread();
    146       break;
    147   }
    148   return TRUE;
    149 }
    150 
    151 __declspec(dllexport) void __cdecl SetLogName(char* name) {
    152   g_memory_watcher->SetLogName(name);
    153 }
    154 
    155 }  // extern "C"
    156