Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(211)

Side by Side Diff: components/browser_watcher/window_hang_monitor_win.cc

Issue 1543803005: Added an integration test for kasko hang reports (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Using components for integration test. Rewrote unittests Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 #include "components/browser_watcher/window_hang_monitor_win.h" 4 #include "components/browser_watcher/window_hang_monitor_win.h"
5 5
6 #include "base/callback.h" 6 #include "base/callback.h"
7 #include "base/files/file_util.h"
7 #include "base/location.h" 8 #include "base/location.h"
8 #include "base/logging.h" 9 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h" 10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/string_util.h"
10 #include "base/win/message_window.h" 13 #include "base/win/message_window.h"
11 14
12 namespace browser_watcher { 15 namespace browser_watcher {
13 16
14 namespace { 17 namespace {
15 18
16 HWND FindNamedWindowForProcess(const base::string16 name, base::ProcessId pid) { 19 // Returns true if the class name for |window| equals |str|.
17 HWND candidate = base::win::MessageWindow::FindWindow(name); 20 bool WindowClassNameEqualsString(HWND window, base::StringPiece16 str) {
18 if (candidate) { 21 wchar_t class_name[MAX_PATH];
22 int str_length = ::GetClassName(window, class_name, MAX_PATH);
23 return str_length && str.compare(class_name) == 0;
24 }
25
26 // Returns true if the window text is an existing directory. Ensures that
27 // |window| is the right Chrome message window to ping.
28 bool WindowNameIsExistingDirectory(HWND window) {
Sigurður Ásgeirsson 2016/01/20 18:21:13 nice - I think this heuristic is fine. Maybe leave
Patrick Monette 2016/01/21 00:05:10 Done.
29 base::string16 window_name;
30 int str_length = ::GetWindowText(
31 window, base::WriteInto(&window_name, MAX_PATH), MAX_PATH);
32 window_name.resize(str_length);
33 return base::DirectoryExists(base::FilePath(window_name));
34 }
35
36 // Returns the Chrome message window handle for the specified |pid| or nullptr
37 // if not found.
38 HWND FindChromeMessageWindow(base::ProcessId pid) {
39 HWND candidate = ::FindWindowEx(HWND_MESSAGE, nullptr, nullptr, nullptr);
40 while (candidate) {
19 DWORD actual_process_id = 0; 41 DWORD actual_process_id = 0;
20 ::GetWindowThreadProcessId(candidate, &actual_process_id); 42 ::GetWindowThreadProcessId(candidate, &actual_process_id);
21 if (actual_process_id == pid) 43 if (WindowClassNameEqualsString(candidate, L"Chrome_MessageWindow") &&
44 WindowNameIsExistingDirectory(candidate) && actual_process_id == pid) {
22 return candidate; 45 return candidate;
46 }
47 candidate = ::GetNextWindow(candidate, GW_HWNDNEXT);
23 } 48 }
24 return nullptr; 49 return nullptr;
25 } 50 }
26 51
27 } // namespace 52 } // namespace
28 53
29 WindowHangMonitor::WindowHangMonitor(base::TimeDelta ping_interval, 54 WindowHangMonitor::WindowHangMonitor(base::TimeDelta ping_interval,
30 base::TimeDelta timeout, 55 base::TimeDelta timeout,
31 const WindowEventCallback& callback) 56 const WindowEventCallback& callback)
32 : callback_(callback), 57 : callback_(callback),
33 ping_interval_(ping_interval), 58 ping_interval_(ping_interval),
34 hang_timeout_(timeout), 59 hang_timeout_(timeout),
35 timer_(false /* don't retain user task */, false /* don't repeat */), 60 timer_(false /* don't retain user task */, false /* don't repeat */),
36 outstanding_ping_(nullptr) { 61 outstanding_ping_(nullptr) {
37 } 62 }
38 63
39 WindowHangMonitor::~WindowHangMonitor() { 64 WindowHangMonitor::~WindowHangMonitor() {
40 if (outstanding_ping_) { 65 if (outstanding_ping_) {
41 // We have an outstanding ping, disable it and leak it intentionally as 66 // We have an outstanding ping, disable it and leak it intentionally as
42 // if the callback arrives eventually, it'll cause a use-after-free. 67 // if the callback arrives eventually, it'll cause a use-after-free.
43 outstanding_ping_->monitor = nullptr; 68 outstanding_ping_->monitor = nullptr;
44 outstanding_ping_ = nullptr; 69 outstanding_ping_ = nullptr;
45 } 70 }
46 } 71 }
47 72
48 void WindowHangMonitor::Initialize(base::Process process, 73 void WindowHangMonitor::Initialize(base::Process process) {
49 const base::string16& window_name) {
50 window_name_ = window_name;
51 window_process_ = process.Pass(); 74 window_process_ = process.Pass();
52 timer_.SetTaskRunner(base::MessageLoop::current()->task_runner()); 75 timer_.SetTaskRunner(base::MessageLoop::current()->task_runner());
53 76
54 ScheduleFindWindow(); 77 ScheduleFindWindow();
55 } 78 }
56 79
57 void WindowHangMonitor::ScheduleFindWindow() { 80 void WindowHangMonitor::ScheduleFindWindow() {
58 // TODO(erikwright): We could reduce the polling by using WaitForInputIdle, 81 // TODO(erikwright): We could reduce the polling by using WaitForInputIdle,
59 // but it is hard to test (requiring a non-Console executable). 82 // but it is hard to test (requiring a non-Console executable).
60 timer_.Start( 83 timer_.Start(
61 FROM_HERE, ping_interval_, 84 FROM_HERE, ping_interval_,
62 base::Bind(&WindowHangMonitor::PollForWindow, base::Unretained(this))); 85 base::Bind(&WindowHangMonitor::PollForWindow, base::Unretained(this)));
63 } 86 }
64 87
65 void WindowHangMonitor::PollForWindow() { 88 void WindowHangMonitor::PollForWindow() {
66 int exit_code = 0; 89 int exit_code = 0;
67 if (window_process_.WaitForExitWithTimeout(base::TimeDelta(), &exit_code)) { 90 if (window_process_.WaitForExitWithTimeout(base::TimeDelta(), &exit_code)) {
68 callback_.Run(WINDOW_NOT_FOUND); 91 callback_.Run(WINDOW_NOT_FOUND);
69 return; 92 return;
70 } 93 }
71 94
72 HWND hwnd = FindNamedWindowForProcess(window_name_, window_process_.Pid()); 95 HWND hwnd = FindChromeMessageWindow(window_process_.Pid());
73 if (hwnd) { 96 if (hwnd) {
74 // Sends a ping and schedules a timeout task. Upon receiving a ping response 97 // Sends a ping and schedules a timeout task. Upon receiving a ping response
75 // further pings will be scheduled ad infinitum. Will signal any failure now 98 // further pings will be scheduled ad infinitum. Will signal any failure now
76 // or later via the callback. 99 // or later via the callback.
77 SendPing(hwnd); 100 SendPing(hwnd);
78 } else { 101 } else {
79 ScheduleFindWindow(); 102 ScheduleFindWindow();
80 } 103 }
81 } 104 }
82 105
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
114 } 137 }
115 138
116 // Issue the count-out callback. 139 // Issue the count-out callback.
117 timer_.Start(FROM_HERE, hang_timeout_, 140 timer_.Start(FROM_HERE, hang_timeout_,
118 base::Bind(&WindowHangMonitor::OnHangTimeout, 141 base::Bind(&WindowHangMonitor::OnHangTimeout,
119 base::Unretained(this), hwnd)); 142 base::Unretained(this), hwnd));
120 } 143 }
121 144
122 void WindowHangMonitor::OnHangTimeout(HWND hwnd) { 145 void WindowHangMonitor::OnHangTimeout(HWND hwnd) {
123 DCHECK(window_process_.IsValid()); 146 DCHECK(window_process_.IsValid());
124
125 if (outstanding_ping_) { 147 if (outstanding_ping_) {
126 // The ping is still outstanding, the window is hung or has vanished. 148 // The ping is still outstanding, the window is hung or has vanished.
127 // Orphan the outstanding ping. If the callback arrives late, it will 149 // Orphan the outstanding ping. If the callback arrives late, it will
128 // delete it, or if the callback never arrives it'll leak. 150 // delete it, or if the callback never arrives it'll leak.
129 outstanding_ping_->monitor = NULL; 151 outstanding_ping_->monitor = NULL;
130 outstanding_ping_ = NULL; 152 outstanding_ping_ = NULL;
131 153
132 if (hwnd != 154 if (hwnd != FindChromeMessageWindow(window_process_.Pid())) {
133 FindNamedWindowForProcess(window_name_, window_process_.Pid())) {
134 // The window vanished. 155 // The window vanished.
135 callback_.Run(WINDOW_VANISHED); 156 callback_.Run(WINDOW_VANISHED);
136 } else { 157 } else {
137 // The window hung. 158 // The window hung.
138 callback_.Run(WINDOW_HUNG); 159 callback_.Run(WINDOW_HUNG);
139 } 160 }
140 } else { 161 } else {
141 // No ping outstanding, window is not yet hung. Schedule the next retry. 162 // No ping outstanding, window is not yet hung. Schedule the next retry.
142 timer_.Start( 163 timer_.Start(
143 FROM_HERE, hang_timeout_ - ping_interval_, 164 FROM_HERE, hang_timeout_ - ping_interval_,
144 base::Bind(&WindowHangMonitor::OnRetryTimeout, base::Unretained(this))); 165 base::Bind(&WindowHangMonitor::OnRetryTimeout, base::Unretained(this)));
145 } 166 }
146 } 167 }
147 168
148 void WindowHangMonitor::OnRetryTimeout() { 169 void WindowHangMonitor::OnRetryTimeout() {
149 DCHECK(window_process_.IsValid()); 170 DCHECK(window_process_.IsValid());
171 DCHECK(window_process_.IsValid());
150 DCHECK(!outstanding_ping_); 172 DCHECK(!outstanding_ping_);
151 // We can't simply hold onto the previously located HWND due to potential 173 // We can't simply hold onto the previously located HWND due to potential
152 // aliasing. 174 // aliasing.
153 // 1. The window handle might have been re-assigned to a different window 175 // 1. The window handle might have been re-assigned to a different window
154 // from the time we found it to the point where we query for its owning 176 // from the time we found it to the point where we query for its owning
155 // process. 177 // process.
156 // 2. The window handle might have been re-assigned to a different process 178 // 2. The window handle might have been re-assigned to a different process
157 // at any point after we found it. 179 // at any point after we found it.
158 HWND hwnd = FindNamedWindowForProcess(window_name_, window_process_.Pid()); 180 HWND hwnd = FindChromeMessageWindow(window_process_.Pid());
159 if (hwnd) 181 if (hwnd) {
160 SendPing(hwnd); 182 SendPing(hwnd);
161 else 183 } else {
162 callback_.Run(WINDOW_VANISHED); 184 callback_.Run(WINDOW_VANISHED);
185 }
163 } 186 }
164 187
165 } // namespace browser_watcher 188 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698