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