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.
5 #include "chrome/browser/hang_monitor/hung_window_detector.h"
10 #include "base/logging.h"
11 #include "chrome/browser/hang_monitor/hang_crash_dump_win.h"
12 #include "content/public/common/result_codes.h"
16 // How long do we wait for the terminated thread or process to die (in ms)
17 static const int kTerminateTimeout
= 2000;
21 const wchar_t HungWindowDetector::kHungChildWindowTimeout
[] =
22 L
"Chrome_HungChildWindowTimeout";
24 HungWindowDetector::HungWindowDetector(HungWindowNotification
* notification
)
25 : notification_(notification
),
26 top_level_window_(NULL
),
27 message_response_timeout_(0),
29 DCHECK(NULL
!= notification_
);
31 // NOTE: It is the caller's responsibility to make sure that
32 // callbacks on this object have been stopped before
33 // destroying this object
34 HungWindowDetector::~HungWindowDetector() {
37 bool HungWindowDetector::Initialize(HWND top_level_window
,
38 int message_response_timeout
) {
39 if (NULL
== notification_
) {
42 if (NULL
== top_level_window
) {
45 // It is OK to call Initialize on this object repeatedly
46 // with different top lebel HWNDs and timeout values each time.
47 // And we do not need a lock for this because we are just
49 top_level_window_
= top_level_window
;
50 message_response_timeout_
= message_response_timeout
;
54 void HungWindowDetector::OnTick() {
56 base::AutoLock
lock(hang_detection_lock_
);
57 // If we already are checking for hung windows on another thread,
58 // don't do this again.
63 } while (false); // To scope the AutoLock
65 EnumChildWindows(top_level_window_
, ChildWndEnumProc
,
66 reinterpret_cast<LPARAM
>(this));
68 // The window shouldn't be disabled unless we're showing a modal dialog.
69 // If we're not, then reenable the window.
70 if (!::IsWindowEnabled(top_level_window_
) &&
71 !::GetWindow(top_level_window_
, GW_ENABLEDPOPUP
)) {
72 ::EnableWindow(top_level_window_
, TRUE
);
78 bool HungWindowDetector::CheckChildWindow(HWND child_window
) {
79 // It can happen that the window is DOA. It specifically happens
80 // when we have just killed a plugin process and the enum is still
81 // enumerating windows from that process.
82 if (!IsWindow(child_window
)) {
86 DWORD top_level_window_thread_id
=
87 GetWindowThreadProcessId(top_level_window_
, NULL
);
89 DWORD child_window_process_id
= 0;
90 DWORD child_window_thread_id
=
91 GetWindowThreadProcessId(child_window
, &child_window_process_id
);
92 bool continue_hang_detection
= true;
94 if (top_level_window_thread_id
!= child_window_thread_id
) {
95 // The message timeout for a child window starts of with a default
96 // value specified by the message_response_timeout_ member. It is
97 // tracked by a property on the child window.
98 #pragma warning(disable:4311)
99 int child_window_message_timeout
=
100 reinterpret_cast<int>(GetProp(child_window
, kHungChildWindowTimeout
));
101 #pragma warning(default:4311)
102 if (!child_window_message_timeout
) {
103 child_window_message_timeout
= message_response_timeout_
;
106 DWORD_PTR result
= 0;
107 if (0 == SendMessageTimeout(child_window
,
112 child_window_message_timeout
,
114 HungWindowNotification::ActionOnHungWindow action
=
115 HungWindowNotification::HUNG_WINDOW_IGNORE
;
116 #pragma warning(disable:4312)
117 SetProp(child_window
, kHungChildWindowTimeout
,
118 reinterpret_cast<HANDLE
>(child_window_message_timeout
));
119 #pragma warning(default:4312)
120 continue_hang_detection
=
121 notification_
->OnHungWindowDetected(child_window
, top_level_window_
,
123 // Make sure this window still a child of our top-level parent
124 if (!IsChild(top_level_window_
, child_window
)) {
125 return continue_hang_detection
;
128 if (action
== HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS
) {
129 RemoveProp(child_window
, kHungChildWindowTimeout
);
130 CHandle
child_process(OpenProcess(PROCESS_ALL_ACCESS
,
132 child_window_process_id
));
134 if (NULL
== child_process
.m_h
) {
135 return continue_hang_detection
;
137 // Before swinging the axe, do some sanity checks to make
138 // sure this window still belongs to the same process
139 DWORD process_id_check
= 0;
140 GetWindowThreadProcessId(child_window
, &process_id_check
);
141 if (process_id_check
!= child_window_process_id
) {
142 return continue_hang_detection
;
145 // Before terminating the process we try collecting a dump. Which
146 // a transient thread in the child process will do for us.
147 CrashDumpAndTerminateHungChildProcess(child_process
);
148 child_process
.Close();
151 RemoveProp(child_window
, kHungChildWindowTimeout
);
155 return continue_hang_detection
;
158 BOOL CALLBACK
HungWindowDetector::ChildWndEnumProc(HWND child_window
,
160 HungWindowDetector
* detector_instance
=
161 reinterpret_cast<HungWindowDetector
*>(param
);
162 if (NULL
== detector_instance
) {
167 return detector_instance
->CheckChildWindow(child_window
);