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"
14 const wchar_t HungWindowDetector::kHungChildWindowTimeout
[] =
15 L
"Chrome_HungChildWindowTimeout";
17 HungWindowDetector::HungWindowDetector(HungWindowNotification
* notification
)
18 : notification_(notification
),
19 top_level_window_(NULL
),
20 message_response_timeout_(0),
22 DCHECK(NULL
!= notification_
);
24 // NOTE: It is the caller's responsibility to make sure that
25 // callbacks on this object have been stopped before
26 // destroying this object
27 HungWindowDetector::~HungWindowDetector() {
30 bool HungWindowDetector::Initialize(HWND top_level_window
,
31 int message_response_timeout
) {
32 if (NULL
== notification_
) {
35 if (NULL
== top_level_window
) {
38 // It is OK to call Initialize on this object repeatedly
39 // with different top lebel HWNDs and timeout values each time.
40 // And we do not need a lock for this because we are just
42 top_level_window_
= top_level_window
;
43 message_response_timeout_
= message_response_timeout
;
47 void HungWindowDetector::OnTick() {
49 base::AutoLock
lock(hang_detection_lock_
);
50 // If we already are checking for hung windows on another thread,
51 // don't do this again.
56 } while (false); // To scope the AutoLock
58 EnumChildWindows(top_level_window_
, ChildWndEnumProc
,
59 reinterpret_cast<LPARAM
>(this));
61 // The window shouldn't be disabled unless we're showing a modal dialog.
62 // If we're not, then reenable the window.
63 if (!::IsWindowEnabled(top_level_window_
) &&
64 !::GetWindow(top_level_window_
, GW_ENABLEDPOPUP
)) {
65 ::EnableWindow(top_level_window_
, TRUE
);
71 bool HungWindowDetector::CheckChildWindow(HWND child_window
) {
72 // It can happen that the window is DOA. It specifically happens
73 // when we have just killed a plugin process and the enum is still
74 // enumerating windows from that process.
75 if (!IsWindow(child_window
)) {
79 DWORD top_level_window_thread_id
=
80 GetWindowThreadProcessId(top_level_window_
, NULL
);
82 DWORD child_window_process_id
= 0;
83 DWORD child_window_thread_id
=
84 GetWindowThreadProcessId(child_window
, &child_window_process_id
);
85 bool continue_hang_detection
= true;
87 if (top_level_window_thread_id
!= child_window_thread_id
) {
88 // The message timeout for a child window starts of with a default
89 // value specified by the message_response_timeout_ member. It is
90 // tracked by a property on the child window.
91 #pragma warning(disable:4311)
92 int child_window_message_timeout
=
93 reinterpret_cast<int>(GetProp(child_window
, kHungChildWindowTimeout
));
94 #pragma warning(default:4311)
95 if (!child_window_message_timeout
) {
96 child_window_message_timeout
= message_response_timeout_
;
100 if (0 == SendMessageTimeout(child_window
,
105 child_window_message_timeout
,
107 HungWindowNotification::ActionOnHungWindow action
=
108 HungWindowNotification::HUNG_WINDOW_IGNORE
;
109 #pragma warning(disable:4312)
110 SetProp(child_window
, kHungChildWindowTimeout
,
111 reinterpret_cast<HANDLE
>(child_window_message_timeout
));
112 #pragma warning(default:4312)
113 continue_hang_detection
=
114 notification_
->OnHungWindowDetected(child_window
, top_level_window_
,
116 // Make sure this window still a child of our top-level parent
117 if (!IsChild(top_level_window_
, child_window
)) {
118 return continue_hang_detection
;
121 if (action
== HungWindowNotification::HUNG_WINDOW_TERMINATE_PROCESS
) {
122 RemoveProp(child_window
, kHungChildWindowTimeout
);
123 CHandle
child_process(OpenProcess(PROCESS_ALL_ACCESS
,
125 child_window_process_id
));
127 if (NULL
== child_process
.m_h
) {
128 return continue_hang_detection
;
130 // Before swinging the axe, do some sanity checks to make
131 // sure this window still belongs to the same process
132 DWORD process_id_check
= 0;
133 GetWindowThreadProcessId(child_window
, &process_id_check
);
134 if (process_id_check
!= child_window_process_id
) {
135 return continue_hang_detection
;
138 // Before terminating the process we try collecting a dump. Which
139 // a transient thread in the child process will do for us.
140 CrashDumpAndTerminateHungChildProcess(child_process
);
141 child_process
.Close();
144 RemoveProp(child_window
, kHungChildWindowTimeout
);
148 return continue_hang_detection
;
151 BOOL CALLBACK
HungWindowDetector::ChildWndEnumProc(HWND child_window
,
153 HungWindowDetector
* detector_instance
=
154 reinterpret_cast<HungWindowDetector
*>(param
);
155 if (NULL
== detector_instance
) {
160 return detector_instance
->CheckChildWindow(child_window
);