| OLD | NEW |
| (Empty) |
| 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 #include "chrome/browser/hang_monitor/hung_window_detector.h" | |
| 6 | |
| 7 #include <windows.h> | |
| 8 #include <atlbase.h> | |
| 9 | |
| 10 #include "base/logging.h" | |
| 11 #include "base/win/win_util.h" | |
| 12 #include "chrome/browser/hang_monitor/hang_crash_dump_win.h" | |
| 13 #include "content/public/common/result_codes.h" | |
| 14 | |
| 15 const wchar_t HungWindowDetector::kHungChildWindowTimeout[] = | |
| 16 L"Chrome_HungChildWindowTimeout"; | |
| 17 | |
| 18 HungWindowDetector::HungWindowDetector(HungWindowNotification* notification) | |
| 19 : notification_(notification), | |
| 20 top_level_window_(NULL), | |
| 21 message_response_timeout_(0), | |
| 22 enumerating_(false) { | |
| 23 DCHECK(NULL != notification_); | |
| 24 } | |
| 25 // NOTE: It is the caller's responsibility to make sure that | |
| 26 // callbacks on this object have been stopped before | |
| 27 // destroying this object | |
| 28 HungWindowDetector::~HungWindowDetector() { | |
| 29 } | |
| 30 | |
| 31 bool HungWindowDetector::Initialize(HWND top_level_window, | |
| 32 int message_response_timeout) { | |
| 33 if (NULL == notification_) { | |
| 34 return false; | |
| 35 } | |
| 36 if (NULL == top_level_window) { | |
| 37 return false; | |
| 38 } | |
| 39 // It is OK to call Initialize on this object repeatedly | |
| 40 // with different top lebel HWNDs and timeout values each time. | |
| 41 // And we do not need a lock for this because we are just | |
| 42 // swapping DWORDs. | |
| 43 top_level_window_ = top_level_window; | |
| 44 message_response_timeout_ = message_response_timeout; | |
| 45 return true; | |
| 46 } | |
| 47 | |
| 48 void HungWindowDetector::OnTick() { | |
| 49 do { | |
| 50 base::AutoLock lock(hang_detection_lock_); | |
| 51 // If we already are checking for hung windows on another thread, | |
| 52 // don't do this again. | |
| 53 if (enumerating_) { | |
| 54 return; | |
| 55 } | |
| 56 enumerating_ = true; | |
| 57 } while (false); // To scope the AutoLock | |
| 58 | |
| 59 EnumChildWindows(top_level_window_, ChildWndEnumProc, | |
| 60 reinterpret_cast<LPARAM>(this)); | |
| 61 | |
| 62 // The window shouldn't be disabled unless we're showing a modal dialog. | |
| 63 // If we're not, then reenable the window. | |
| 64 if (!::IsWindowEnabled(top_level_window_) && | |
| 65 !::GetWindow(top_level_window_, GW_ENABLEDPOPUP)) { | |
| 66 ::EnableWindow(top_level_window_, TRUE); | |
| 67 } | |
| 68 | |
| 69 enumerating_ = false; | |
| 70 } | |
| 71 | |
| 72 bool HungWindowDetector::CheckChildWindow(HWND child_window) { | |
| 73 // It can happen that the window is DOA. It specifically happens | |
| 74 // when we have just killed a plugin process and the enum is still | |
| 75 // enumerating windows from that process. | |
| 76 if (!IsWindow(child_window)) { | |
| 77 return true; | |
| 78 } | |
| 79 | |
| 80 DWORD top_level_window_thread_id = | |
| 81 GetWindowThreadProcessId(top_level_window_, NULL); | |
| 82 | |
| 83 DWORD child_window_process_id = 0; | |
| 84 DWORD child_window_thread_id = | |
| 85 GetWindowThreadProcessId(child_window, &child_window_process_id); | |
| 86 bool continue_hang_detection = true; | |
| 87 | |
| 88 if (top_level_window_thread_id != child_window_thread_id) { | |
| 89 // The message timeout for a child window starts of with a default | |
| 90 // value specified by the message_response_timeout_ member. It is | |
| 91 // tracked by a property on the child window. | |
| 92 int child_window_message_timeout = base::win::HandleToUint32( | |
| 93 GetProp(child_window, kHungChildWindowTimeout)); | |
| 94 if (!child_window_message_timeout) { | |
| 95 child_window_message_timeout = message_response_timeout_; | |
| 96 } | |
| 97 | |
| 98 DWORD_PTR result = 0; | |
| 99 if (0 == SendMessageTimeout(child_window, | |
| 100 WM_NULL, | |
| 101 0, | |
| 102 0, | |
| 103 SMTO_BLOCK, | |
| 104 child_window_message_timeout, | |
| 105 &result)) { | |
| 106 HungWindowNotification::ActionOnHungWindow action = | |
| 107 HungWindowNotification::HUNG_WINDOW_IGNORE; | |
| 108 #pragma warning(disable:4312) | |
| 109 SetProp(child_window, kHungChildWindowTimeout, | |
| 110 reinterpret_cast<HANDLE>(child_window_message_timeout)); | |
| 111 #pragma warning(default:4312) | |
| 112 continue_hang_detection = | |
| 113 notification_->OnHungWindowDetected(child_window, top_level_window_, | |
| 114 &action); | |
| 115 // Make sure this window still a child of our top-level parent | |
| 116 if (!IsChild(top_level_window_, child_window)) { | |
| 117 return continue_hang_detection; | |
| 118 } | |
| 119 | |
| 120 if (action == HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS) { | |
| 121 RemoveProp(child_window, kHungChildWindowTimeout); | |
| 122 CHandle child_process(OpenProcess(PROCESS_ALL_ACCESS, | |
| 123 FALSE, | |
| 124 child_window_process_id)); | |
| 125 | |
| 126 if (NULL == child_process.m_h) { | |
| 127 return continue_hang_detection; | |
| 128 } | |
| 129 // Before swinging the axe, do some sanity checks to make | |
| 130 // sure this window still belongs to the same process | |
| 131 DWORD process_id_check = 0; | |
| 132 GetWindowThreadProcessId(child_window, &process_id_check); | |
| 133 if (process_id_check != child_window_process_id) { | |
| 134 return continue_hang_detection; | |
| 135 } | |
| 136 | |
| 137 // Before terminating the process we try collecting a dump. Which | |
| 138 // a transient thread in the child process will do for us. | |
| 139 CrashDumpAndTerminateHungChildProcess(child_process); | |
| 140 child_process.Close(); | |
| 141 } | |
| 142 } else { | |
| 143 RemoveProp(child_window, kHungChildWindowTimeout); | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 return continue_hang_detection; | |
| 148 } | |
| 149 | |
| 150 BOOL CALLBACK HungWindowDetector::ChildWndEnumProc(HWND child_window, | |
| 151 LPARAM param) { | |
| 152 HungWindowDetector* detector_instance = | |
| 153 reinterpret_cast<HungWindowDetector*>(param); | |
| 154 if (NULL == detector_instance) { | |
| 155 NOTREACHED(); | |
| 156 return FALSE; | |
| 157 } | |
| 158 | |
| 159 return detector_instance->CheckChildWindow(child_window); | |
| 160 } | |
| OLD | NEW |