Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 | 4 |
| 5 #include "components/browser_watcher/window_hang_monitor_win.h" | 5 #include "components/browser_watcher/window_hang_monitor_win.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include "base/base_paths.h" |
| 8 | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/base_switches.h" | 8 #include "base/base_switches.h" |
| 12 #include "base/bind.h" | |
| 13 #include "base/bind_helpers.h" | |
| 14 #include "base/callback.h" | |
| 15 #include "base/callback_helpers.h" | |
| 16 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 17 #include "base/location.h" | 10 #include "base/path_service.h" |
| 18 #include "base/macros.h" | |
| 19 #include "base/memory/scoped_ptr.h" | |
| 20 #include "base/message_loop/message_loop.h" | |
| 21 #include "base/process/launch.h" | 11 #include "base/process/launch.h" |
| 22 #include "base/process/process.h" | 12 #include "base/process/process.h" |
| 23 #include "base/process/process_handle.h" | |
| 24 #include "base/run_loop.h" | |
| 25 #include "base/strings/string16.h" | |
| 26 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
| 27 #include "base/strings/stringprintf.h" | |
| 28 #include "base/strings/utf_string_conversions.h" | |
| 29 #include "base/synchronization/waitable_event.h" | 14 #include "base/synchronization/waitable_event.h" |
| 30 #include "base/test/multiprocess_test.h" | 15 #include "base/test/multiprocess_test.h" |
| 31 #include "base/threading/thread.h" | 16 #include "base/threading/thread.h" |
| 32 #include "base/win/message_window.h" | 17 #include "base/win/message_window.h" |
| 18 #include "base/win/win_util.h" | |
| 33 #include "testing/gtest/include/gtest/gtest.h" | 19 #include "testing/gtest/include/gtest/gtest.h" |
| 34 #include "testing/multiprocess_func_list.h" | 20 #include "testing/multiprocess_func_list.h" |
| 35 | 21 |
| 36 namespace browser_watcher { | 22 namespace browser_watcher { |
| 37 | 23 |
| 38 namespace { | 24 namespace { |
| 39 | 25 |
| 40 // Simulates a process that never opens a window. | 26 const char kChildReadPipeSwitch[] = "child_read_pipe"; |
| 41 MULTIPROCESS_TEST_MAIN(NoWindowChild) { | 27 const char kChildWritePipeSwitch[] = "child_write_pipe"; |
| 42 ::Sleep(INFINITE); | 28 |
| 29 // Signals used for IPC between the monitor process and the monitor. | |
| 30 enum IPCSignal { | |
| 31 IPC_SIGNAL_INVALID, | |
| 32 IPC_SIGNAL_READY, | |
| 33 IPC_SIGNAL_TERMINATE_PROCESS, | |
| 34 IPC_SIGNAL_CREATE_MESSAGE_WINDOW, | |
| 35 IPC_SIGNAL_DELETE_MESSAGE_WINDOW, | |
| 36 IPC_SIGNAL_HANG_MESSAGE_WINDOW, | |
| 37 }; | |
| 38 | |
| 39 // Sends |ipc_signal| through the |write_pipe|. | |
| 40 bool SendPipeSignal(HANDLE write_pipe, IPCSignal ipc_signal) { | |
| 41 DWORD bytes_written = 0; | |
| 42 if (!WriteFile(write_pipe, &ipc_signal, sizeof(ipc_signal), &bytes_written, | |
| 43 nullptr)) | |
| 44 return false; | |
| 45 | |
| 46 return bytes_written == sizeof(ipc_signal); | |
| 47 } | |
| 48 | |
| 49 // Blocks on |read_pipe| until a signal is received into |ipc_signal|. | |
| 50 bool WaitForPipeSignal(HANDLE read_pipe, IPCSignal* ipc_signal) { | |
| 51 CHECK(ipc_signal); | |
| 52 DWORD bytes_read = 0; | |
| 53 if (!ReadFile(read_pipe, ipc_signal, sizeof(*ipc_signal), &bytes_read, | |
| 54 nullptr)) | |
| 55 return false; | |
| 56 | |
| 57 return bytes_read == sizeof(*ipc_signal); | |
| 58 } | |
| 59 | |
| 60 // Blocks on |read_pipe| until a signal is received and returns true if it | |
| 61 // matches |expected_ipc_signal|. | |
| 62 bool WaitForSpecificPipeSignal(HANDLE read_pipe, | |
| 63 IPCSignal expected_ipc_signal) { | |
| 64 IPCSignal received_signal = IPC_SIGNAL_INVALID; | |
| 65 return WaitForPipeSignal(read_pipe, &received_signal) && | |
| 66 received_signal == expected_ipc_signal; | |
| 67 } | |
| 68 | |
| 69 // Appends |handle| as a command line switch. | |
| 70 void AppendSwitchHandle(base::CommandLine* command_line, | |
| 71 std::string switch_name, | |
| 72 HANDLE handle) { | |
| 73 command_line->AppendSwitchASCII( | |
| 74 switch_name, base::UintToString(base::win::HandleToUint32(handle))); | |
| 75 } | |
| 76 | |
| 77 // Retrieves the |handle| associated to |switch_name| from the command line. | |
| 78 HANDLE GetSwitchValueHandle(base::CommandLine* command_line, | |
| 79 std::string switch_name) { | |
| 80 std::string switch_string = command_line->GetSwitchValueASCII(switch_name); | |
| 81 unsigned int switch_uint = 0; | |
| 82 if (switch_string.empty() || | |
| 83 !base::StringToUint(switch_string, &switch_uint)) { | |
| 84 DLOG(ERROR) << "Missing or invalid " << switch_name << " argument."; | |
| 85 return nullptr; | |
| 86 } | |
| 87 return reinterpret_cast<HANDLE>(switch_uint); | |
| 88 } | |
| 89 | |
| 90 // An instance of this class lives in the monitored process and receives signals | |
| 91 // and executes their associated function. | |
| 92 class MonitoredProcessClient { | |
|
Sigurður Ásgeirsson
2016/01/20 18:21:13
wow - what a rigamarole to get some IPC going :/.
Patrick Monette
2016/01/21 00:05:10
Haha yes.
| |
| 93 public: | |
| 94 MonitoredProcessClient() | |
| 95 : message_window_thread_("Message window thread"), | |
| 96 hang_event_(true, false) { | |
| 97 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
| 98 | |
| 99 read_pipe_ = GetSwitchValueHandle(command_line, kChildReadPipeSwitch); | |
| 100 write_pipe_ = GetSwitchValueHandle(command_line, kChildWritePipeSwitch); | |
| 101 } | |
| 102 | |
| 103 ~MonitoredProcessClient() { | |
| 104 if (message_window_thread_.IsRunning()) { | |
| 105 DeleteMessageWindow(); | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 void RunEventLoop() { | |
| 110 bool running = true; | |
| 111 IPCSignal ipc_signal = IPC_SIGNAL_INVALID; | |
| 112 while (running) { | |
| 113 CHECK(WaitForPipeSignal(read_pipe_, &ipc_signal)); | |
| 114 switch (ipc_signal) { | |
| 115 // The parent process should never send those. | |
| 116 case IPC_SIGNAL_INVALID: | |
| 117 case IPC_SIGNAL_READY: | |
| 118 CHECK(false); | |
| 119 break; | |
| 120 case IPC_SIGNAL_TERMINATE_PROCESS: | |
| 121 running = false; | |
| 122 break; | |
| 123 case IPC_SIGNAL_CREATE_MESSAGE_WINDOW: | |
| 124 CreateMessageWindow(); | |
| 125 break; | |
| 126 case IPC_SIGNAL_DELETE_MESSAGE_WINDOW: | |
| 127 DeleteMessageWindow(); | |
| 128 break; | |
| 129 case IPC_SIGNAL_HANG_MESSAGE_WINDOW: | |
| 130 HangMessageWindow(); | |
| 131 break; | |
| 132 } | |
| 133 SendSignalToParent(IPC_SIGNAL_READY); | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 // Creates a thread then creates the message window on it. | |
| 138 void CreateMessageWindow() { | |
| 139 ASSERT_TRUE(message_window_thread_.StartWithOptions( | |
| 140 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))); | |
| 141 | |
| 142 bool succeeded = false; | |
| 143 base::WaitableEvent created(true, false); | |
| 144 ASSERT_TRUE(message_window_thread_.task_runner()->PostTask( | |
| 145 FROM_HERE, | |
| 146 base::Bind(&MonitoredProcessClient::CreateMessageWindowInWorkerThread, | |
| 147 base::Unretained(this), &succeeded, &created))); | |
| 148 created.Wait(); | |
| 149 ASSERT_TRUE(succeeded); | |
| 150 } | |
| 151 | |
| 152 // Creates a thread then creates the message window on it. | |
| 153 void HangMessageWindow() { | |
| 154 message_window_thread_.task_runner()->PostTask( | |
| 155 FROM_HERE, | |
| 156 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang_event_))); | |
| 157 } | |
| 158 | |
| 159 bool SendSignalToParent(IPCSignal ipc_signal) { | |
| 160 return SendPipeSignal(write_pipe_, ipc_signal); | |
| 161 } | |
| 162 | |
| 163 private: | |
| 164 bool EmptyMessageCallback(UINT message, | |
| 165 WPARAM wparam, | |
| 166 LPARAM lparam, | |
| 167 LRESULT* result) { | |
| 168 EXPECT_EQ(message_window_thread_.message_loop(), | |
| 169 base::MessageLoop::current()); | |
| 170 return false; // Pass through to DefWindowProc. | |
| 171 } | |
| 172 | |
| 173 void CreateMessageWindowInWorkerThread(bool* success, | |
| 174 base::WaitableEvent* created) { | |
| 175 CHECK(created); | |
| 176 | |
| 177 // As an alternative to checking if the name of the message window is the | |
| 178 // user data directory, the hang watcher verifies that the window name is an | |
| 179 // existing directory. DIR_CURRENT is used to meet this constraint. | |
| 180 base::FilePath existing_dir; | |
| 181 CHECK(PathService::Get(base::DIR_CURRENT, &existing_dir)); | |
| 182 | |
| 183 message_window_.reset(new base::win::MessageWindow); | |
| 184 *success = message_window_->CreateNamed( | |
| 185 base::Bind(&MonitoredProcessClient::EmptyMessageCallback, | |
| 186 base::Unretained(this)), | |
| 187 existing_dir.value().c_str()); | |
| 188 created->Signal(); | |
| 189 } | |
| 190 | |
| 191 void DeleteMessageWindow() { | |
| 192 base::WaitableEvent deleted(true, false); | |
| 193 message_window_thread_.task_runner()->PostTask( | |
| 194 FROM_HERE, | |
| 195 base::Bind(&MonitoredProcessClient::DeleteMessageWindowInWorkerThread, | |
| 196 base::Unretained(this), &deleted)); | |
| 197 deleted.Wait(); | |
| 198 | |
| 199 message_window_thread_.Stop(); | |
| 200 } | |
| 201 | |
| 202 void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) { | |
| 203 CHECK(deleted); | |
| 204 message_window_.reset(); | |
| 205 deleted->Signal(); | |
| 206 } | |
| 207 | |
| 208 // The thread that holds the message window. | |
| 209 base::Thread message_window_thread_; | |
| 210 scoped_ptr<base::win::MessageWindow> message_window_; | |
| 211 | |
| 212 // Event used to hang the message window. | |
| 213 base::WaitableEvent hang_event_; | |
| 214 | |
| 215 // Anonymous pipe handles for IPC with the parent process. | |
| 216 HANDLE read_pipe_; | |
| 217 HANDLE write_pipe_; | |
| 218 | |
| 219 DISALLOW_COPY_AND_ASSIGN(MonitoredProcessClient); | |
| 220 }; | |
| 221 | |
| 222 // The monitored process main function. | |
| 223 MULTIPROCESS_TEST_MAIN(MonitoredProcess) { | |
| 224 MonitoredProcessClient monitored_process_client; | |
| 225 CHECK(monitored_process_client.SendSignalToParent(IPC_SIGNAL_READY)); | |
| 226 | |
| 227 monitored_process_client.RunEventLoop(); | |
| 228 | |
| 43 return 0; | 229 return 0; |
| 44 } | 230 } |
| 45 | 231 |
| 46 // Manages a WindowHangMonitor that lives on a background thread. | 232 // Manages a WindowHangMonitor that lives on a background thread. |
| 47 class HangMonitorThread { | 233 class HangMonitorThread { |
| 48 public: | 234 public: |
| 49 // Instantiates the background thread. | 235 // Instantiates the background thread. |
| 50 HangMonitorThread() | 236 HangMonitorThread() |
| 51 : event_(WindowHangMonitor::WINDOW_NOT_FOUND), | 237 : event_(WindowHangMonitor::WINDOW_NOT_FOUND), |
| 52 event_received_(false, false), | 238 event_received_(false, false), |
| 53 thread_("HangMonitorThread") {} | 239 thread_("Hang monitor thread") {} |
| 54 | 240 |
| 55 ~HangMonitorThread() { | 241 ~HangMonitorThread() { |
| 56 if (hang_monitor_.get()) | 242 if (hang_monitor_.get()) |
| 57 DestroyWatcher(); | 243 DestroyWatcher(); |
| 58 } | 244 } |
| 59 | 245 |
| 60 // Starts the background thread and the monitor to observe the window named | 246 // Starts the background thread and the monitor to observe Chrome message |
| 61 // |window_name| in |process|. Blocks until the monitor has been initialized. | 247 // window for |process|. Blocks until the monitor has been initialized. |
| 62 bool Start(base::Process process, const base::string16& window_name) { | 248 bool Start(base::Process process) { |
| 63 if (!thread_.StartWithOptions( | 249 if (!thread_.StartWithOptions( |
| 64 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))) { | 250 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))) { |
| 65 return false; | 251 return false; |
| 66 } | 252 } |
| 67 | 253 |
| 68 base::WaitableEvent complete(false, false); | 254 base::WaitableEvent complete(false, false); |
| 69 if (!thread_.task_runner()->PostTask( | 255 if (!thread_.task_runner()->PostTask( |
| 70 FROM_HERE, base::Bind(&HangMonitorThread::StartupOnThread, | 256 FROM_HERE, |
| 71 base::Unretained(this), window_name, | 257 base::Bind(&HangMonitorThread::StartupOnThread, |
| 72 base::Passed(process.Pass()), | 258 base::Unretained(this), base::Passed(std::move(process)), |
| 73 base::Unretained(&complete)))) { | 259 base::Unretained(&complete)))) { |
| 74 return false; | 260 return false; |
| 75 } | 261 } |
| 76 | 262 |
| 77 complete.Wait(); | 263 complete.Wait(); |
| 78 | 264 |
| 79 return true; | 265 return true; |
| 80 } | 266 } |
| 81 | 267 |
| 82 // Returns true if a window event is detected within |timeout|. | 268 // Returns true if a window event is detected within |timeout|. |
| 83 bool TimedWaitForEvent(base::TimeDelta timeout) { | 269 bool TimedWaitForEvent(base::TimeDelta timeout) { |
| 84 return event_received_.TimedWait(timeout); | 270 return event_received_.TimedWait(timeout); |
| 85 } | 271 } |
| 86 | 272 |
| 87 // Blocks indefinitely for a window event and returns it. | 273 // Blocks indefinitely for a window event and returns it. |
| 88 WindowHangMonitor::WindowEvent WaitForEvent() { | 274 WindowHangMonitor::WindowEvent WaitForEvent() { |
| 89 event_received_.Wait(); | 275 event_received_.Wait(); |
| 90 return event_; | 276 return event_; |
| 91 } | 277 } |
| 92 | 278 |
| 279 private: | |
| 93 // Destroys the monitor and stops the background thread. Blocks until the | 280 // Destroys the monitor and stops the background thread. Blocks until the |
| 94 // operation completes. | 281 // operation completes. |
| 95 void DestroyWatcher() { | 282 void DestroyWatcher() { |
| 96 thread_.task_runner()->PostTask( | 283 thread_.task_runner()->PostTask( |
| 97 FROM_HERE, base::Bind(&HangMonitorThread::ShutdownOnThread, | 284 FROM_HERE, base::Bind(&HangMonitorThread::ShutdownOnThread, |
| 98 base::Unretained(this))); | 285 base::Unretained(this))); |
| 99 // This will block until the above-posted task completes. | 286 // This will block until the above-posted task completes. |
| 100 thread_.Stop(); | 287 thread_.Stop(); |
| 101 } | 288 } |
| 102 | 289 |
| 103 private: | |
| 104 // Invoked when the monitor signals an event. Unblocks a call to | 290 // Invoked when the monitor signals an event. Unblocks a call to |
| 105 // TimedWaitForEvent or WaitForEvent. | 291 // TimedWaitForEvent or WaitForEvent. |
| 106 void EventCallback(WindowHangMonitor::WindowEvent event) { | 292 void EventCallback(WindowHangMonitor::WindowEvent event) { |
| 107 if (event_received_.IsSignaled()) | 293 if (event_received_.IsSignaled()) |
| 108 ADD_FAILURE() << "Multiple calls to EventCallback."; | 294 ADD_FAILURE() << "Multiple calls to EventCallback."; |
| 109 event_ = event; | 295 event_ = event; |
| 110 event_received_.Signal(); | 296 event_received_.Signal(); |
| 111 } | 297 } |
| 112 | 298 |
| 113 // Initializes the WindowHangMonitor to observe the window named |window_name| | 299 // Initializes the WindowHangMonitor to observe the Chrome message window for |
| 114 // in |process|. Signals |complete| when done. | 300 // |process|. Signals |complete| when done. |
| 115 void StartupOnThread(const base::string16& window_name, | 301 void StartupOnThread(base::Process process, base::WaitableEvent* complete) { |
| 116 base::Process process, | |
| 117 base::WaitableEvent* complete) { | |
| 118 hang_monitor_.reset(new WindowHangMonitor( | 302 hang_monitor_.reset(new WindowHangMonitor( |
| 119 base::TimeDelta::FromMilliseconds(100), | 303 base::TimeDelta::FromMilliseconds(100), |
| 120 base::TimeDelta::FromMilliseconds(100), | 304 base::TimeDelta::FromMilliseconds(100), |
| 121 base::Bind(&HangMonitorThread::EventCallback, base::Unretained(this)))); | 305 base::Bind(&HangMonitorThread::EventCallback, base::Unretained(this)))); |
| 122 hang_monitor_->Initialize(process.Pass(), window_name); | 306 hang_monitor_->Initialize(std::move(process)); |
| 123 complete->Signal(); | 307 complete->Signal(); |
| 124 } | 308 } |
| 125 | 309 |
| 126 // Destroys the WindowHangMonitor. | 310 // Destroys the WindowHangMonitor. |
| 127 void ShutdownOnThread() { hang_monitor_.reset(); } | 311 void ShutdownOnThread() { hang_monitor_.reset(); } |
| 128 | 312 |
| 129 // The detected event. Invalid if |event_received_| has not been signaled. | 313 // The detected event. Invalid if |event_received_| has not been signaled. |
| 130 WindowHangMonitor::WindowEvent event_; | 314 WindowHangMonitor::WindowEvent event_; |
| 131 // Indicates that |event_| has been assigned in response to a callback from | 315 // Indicates that |event_| has been assigned in response to a callback from |
| 132 // the WindowHangMonitor. | 316 // the WindowHangMonitor. |
| 133 base::WaitableEvent event_received_; | 317 base::WaitableEvent event_received_; |
| 134 // The WindowHangMonitor under test. | 318 // The WindowHangMonitor under test. |
| 135 scoped_ptr<WindowHangMonitor> hang_monitor_; | 319 scoped_ptr<WindowHangMonitor> hang_monitor_; |
| 136 // The background thread. | 320 // The background thread. |
| 137 base::Thread thread_; | 321 base::Thread thread_; |
| 138 | 322 |
| 139 DISALLOW_COPY_AND_ASSIGN(HangMonitorThread); | 323 DISALLOW_COPY_AND_ASSIGN(HangMonitorThread); |
| 140 }; | 324 }; |
| 141 | 325 |
| 142 class WindowHangMonitorTest : public testing::Test { | 326 class WindowHangMonitorTest : public testing::Test { |
| 143 public: | 327 public: |
| 144 WindowHangMonitorTest() | 328 WindowHangMonitorTest() {} |
| 145 : ping_event_(false, false), | |
| 146 pings_(0), | |
| 147 window_thread_("WindowHangMonitorTest window_thread") {} | |
| 148 | 329 |
| 149 void SetUp() override { | 330 ~WindowHangMonitorTest() { |
| 150 // Pick a window name unique to this process. | 331 // Close process if running. |
| 151 window_name_ = base::StringPrintf(L"WindowHanMonitorTest-%d", | 332 monitored_process_.Terminate(1, false); |
| 152 base::GetCurrentProcId()); | |
| 153 ASSERT_TRUE(window_thread_.StartWithOptions( | |
| 154 base::Thread::Options(base::MessageLoop::TYPE_UI, 0))); | |
| 155 } | 333 } |
| 156 | 334 |
| 157 void TearDown() override { | 335 // Starts a child process that will be monitored. Handles to anonymous pipes |
| 158 DeleteMessageWindow(); | 336 // are passed to the command line to provide a way to communicate with the |
| 159 window_thread_.Stop(); | 337 // child process. This function blocks until IPC_SIGNAL_READY is received. |
| 338 bool StartMonitoredProcess() { | |
| 339 HANDLE child_read_pipe = nullptr; | |
| 340 HANDLE child_write_pipe = nullptr; | |
| 341 if (!CreatePipes(&child_read_pipe, &child_write_pipe)) | |
| 342 return false; | |
| 343 | |
| 344 base::CommandLine command_line = | |
| 345 base::GetMultiProcessTestChildBaseCommandLine(); | |
| 346 command_line.AppendSwitchASCII(switches::kTestChildProcess, | |
| 347 "MonitoredProcess"); | |
| 348 | |
| 349 AppendSwitchHandle(&command_line, kChildReadPipeSwitch, child_read_pipe); | |
| 350 AppendSwitchHandle(&command_line, kChildWritePipeSwitch, child_write_pipe); | |
| 351 | |
| 352 base::LaunchOptions options = {}; | |
| 353 options.inherit_handles = true; | |
| 354 monitored_process_ = base::LaunchProcess(command_line, options); | |
| 355 if (!monitored_process_.IsValid()) | |
| 356 return false; | |
| 357 | |
| 358 return WaitForSignal(IPC_SIGNAL_READY); | |
| 160 } | 359 } |
| 161 | 360 |
| 162 void CreateMessageWindow() { | 361 void StartHangMonitor() { |
| 163 bool succeeded = false; | 362 monitor_thread_.Start(monitored_process_.Duplicate()); |
| 164 base::WaitableEvent created(true, false); | |
| 165 ASSERT_TRUE(window_thread_.task_runner()->PostTask( | |
| 166 FROM_HERE, | |
| 167 base::Bind(&WindowHangMonitorTest::CreateMessageWindowInWorkerThread, | |
| 168 base::Unretained(this), window_name_, &succeeded, | |
| 169 &created))); | |
| 170 created.Wait(); | |
| 171 ASSERT_TRUE(succeeded); | |
| 172 } | 363 } |
| 173 | 364 |
| 174 void DeleteMessageWindow() { | 365 // Sends the |ipc_signal| to the child process and wait for a IPC_SIGNAL_READY |
| 175 base::WaitableEvent deleted(true, false); | 366 // response. |
| 176 window_thread_.task_runner()->PostTask( | 367 bool SendSignal(IPCSignal ipc_signal) { |
| 177 FROM_HERE, | 368 if (!SendPipeSignal(write_pipe_, ipc_signal)) |
| 178 base::Bind(&WindowHangMonitorTest::DeleteMessageWindowInWorkerThread, | 369 return false; |
| 179 base::Unretained(this), &deleted)); | 370 |
| 180 deleted.Wait(); | 371 return WaitForSignal(IPC_SIGNAL_READY); |
| 181 } | 372 } |
| 182 | 373 |
| 183 void WaitForPing() { | 374 // Blocks until |ipc_signal| is received from the child process. |
| 184 while (true) { | 375 bool WaitForSignal(IPCSignal ipc_signal) { |
| 185 { | 376 return WaitForSpecificPipeSignal(read_pipe_, ipc_signal); |
| 186 base::AutoLock auto_lock(ping_lock_); | |
| 187 if (pings_) { | |
| 188 ping_event_.Reset(); | |
| 189 --pings_; | |
| 190 return; | |
| 191 } | |
| 192 } | |
| 193 ping_event_.Wait(); | |
| 194 } | |
| 195 } | 377 } |
| 196 | 378 |
| 197 HangMonitorThread& monitor_thread() { return monitor_thread_; } | 379 HangMonitorThread& monitor_thread() { return monitor_thread_; } |
| 198 | 380 |
| 199 const base::win::MessageWindow* message_window() const { | 381 private: |
| 200 return message_window_.get(); | 382 // Creates pipes for IPC with the child process. |
| 383 bool CreatePipes(HANDLE* child_read_pipe, HANDLE* child_write_pipe) { | |
| 384 CHECK(child_read_pipe); | |
| 385 CHECK(child_write_pipe); | |
| 386 SECURITY_ATTRIBUTES security_attributes = { | |
| 387 sizeof(SECURITY_ATTRIBUTES), nullptr, true /* inherit handles */}; | |
| 388 | |
| 389 if (!CreatePipe(&read_pipe_, child_write_pipe, &security_attributes, 0)) { | |
| 390 return false; | |
| 391 } | |
| 392 if (!CreatePipe(child_read_pipe, &write_pipe_, &security_attributes, 0)) { | |
| 393 return false; | |
| 394 } | |
| 395 return true; | |
| 201 } | 396 } |
| 202 | 397 |
| 203 const base::string16& window_name() const { return window_name_; } | 398 // The thread that monitors the child process. |
| 399 HangMonitorThread monitor_thread_; | |
| 400 // The process that is monitored. | |
| 401 base::Process monitored_process_; | |
| 204 | 402 |
| 205 base::Thread* window_thread() { return &window_thread_; } | 403 // Anonymous pipe handles for IPC with the monitored process. |
| 206 | 404 HANDLE read_pipe_; |
|
Sigurður Ásgeirsson
2016/01/20 18:21:13
do these perhaps want to be base::win::ScopedHandl
Patrick Monette
2016/01/21 00:05:10
Done.
| |
| 207 private: | 405 HANDLE write_pipe_; |
| 208 bool MessageCallback(UINT message, | |
| 209 WPARAM wparam, | |
| 210 LPARAM lparam, | |
| 211 LRESULT* result) { | |
| 212 EXPECT_EQ(window_thread_.message_loop(), base::MessageLoop::current()); | |
| 213 if (message == WM_NULL) { | |
| 214 base::AutoLock auto_lock(ping_lock_); | |
| 215 ++pings_; | |
| 216 ping_event_.Signal(); | |
| 217 } | |
| 218 | |
| 219 return false; // Pass through to DefWindowProc. | |
| 220 } | |
| 221 | |
| 222 void CreateMessageWindowInWorkerThread(const base::string16& name, | |
| 223 bool* success, | |
| 224 base::WaitableEvent* created) { | |
| 225 message_window_.reset(new base::win::MessageWindow); | |
| 226 *success = message_window_->CreateNamed( | |
| 227 base::Bind(&WindowHangMonitorTest::MessageCallback, | |
| 228 base::Unretained(this)), | |
| 229 name); | |
| 230 created->Signal(); | |
| 231 } | |
| 232 | |
| 233 void DeleteMessageWindowInWorkerThread(base::WaitableEvent* deleted) { | |
| 234 message_window_.reset(); | |
| 235 if (deleted) | |
| 236 deleted->Signal(); | |
| 237 } | |
| 238 | |
| 239 HangMonitorThread monitor_thread_; | |
| 240 scoped_ptr<base::win::MessageWindow> message_window_; | |
| 241 base::string16 window_name_; | |
| 242 base::Lock ping_lock_; | |
| 243 base::WaitableEvent ping_event_; | |
| 244 size_t pings_; | |
| 245 base::Thread window_thread_; | |
| 246 | 406 |
| 247 DISALLOW_COPY_AND_ASSIGN(WindowHangMonitorTest); | 407 DISALLOW_COPY_AND_ASSIGN(WindowHangMonitorTest); |
| 248 }; | 408 }; |
| 249 | 409 |
| 250 } // namespace | 410 } // namespace |
| 251 | 411 |
| 252 TEST_F(WindowHangMonitorTest, NoWindow) { | 412 TEST_F(WindowHangMonitorTest, WindowNotFound) { |
| 253 base::CommandLine child_command_line = | 413 ASSERT_TRUE(StartMonitoredProcess()); |
| 254 base::GetMultiProcessTestChildBaseCommandLine(); | |
| 255 child_command_line.AppendSwitchASCII(switches::kTestChildProcess, | |
| 256 "NoWindowChild"); | |
| 257 base::Process process = | |
| 258 base::LaunchProcess(child_command_line, base::LaunchOptions()); | |
| 259 ASSERT_TRUE(process.IsValid()); | |
| 260 | 414 |
| 261 base::ScopedClosureRunner terminate_process_runner( | 415 StartHangMonitor(); |
| 262 base::Bind(base::IgnoreResult(&base::Process::Terminate), | |
| 263 base::Unretained(&process), 1, true)); | |
| 264 | 416 |
| 265 monitor_thread().Start(process.Duplicate(), window_name()); | 417 ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS)); |
| 266 | 418 |
| 267 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 419 EXPECT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND, |
| 268 base::TimeDelta::FromMilliseconds(150))); | |
| 269 | |
| 270 terminate_process_runner.Reset(); | |
| 271 | |
| 272 ASSERT_EQ(WindowHangMonitor::WINDOW_NOT_FOUND, | |
| 273 monitor_thread().WaitForEvent()); | |
| 274 | |
| 275 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 276 base::TimeDelta::FromMilliseconds(150))); | |
| 277 } | |
| 278 | |
| 279 TEST_F(WindowHangMonitorTest, WindowBeforeWatcher) { | |
| 280 CreateMessageWindow(); | |
| 281 | |
| 282 monitor_thread().Start(base::Process::Current(), window_name()); | |
| 283 | |
| 284 WaitForPing(); | |
| 285 | |
| 286 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 287 base::TimeDelta::FromMilliseconds(150))); | |
| 288 } | |
| 289 | |
| 290 TEST_F(WindowHangMonitorTest, WindowBeforeDestroy) { | |
| 291 CreateMessageWindow(); | |
| 292 | |
| 293 monitor_thread().Start(base::Process::Current(), window_name()); | |
| 294 | |
| 295 WaitForPing(); | |
| 296 | |
| 297 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 298 base::TimeDelta::FromMilliseconds(150))); | |
| 299 | |
| 300 monitor_thread().DestroyWatcher(); | |
| 301 | |
| 302 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta())); | |
| 303 } | |
| 304 | |
| 305 TEST_F(WindowHangMonitorTest, NoWindowBeforeDestroy) { | |
| 306 monitor_thread().Start(base::Process::Current(), window_name()); | |
| 307 | |
| 308 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 309 base::TimeDelta::FromMilliseconds(150))); | |
| 310 monitor_thread().DestroyWatcher(); | |
| 311 | |
| 312 ASSERT_FALSE(monitor_thread().TimedWaitForEvent(base::TimeDelta())); | |
| 313 } | |
| 314 | |
| 315 TEST_F(WindowHangMonitorTest, WatcherBeforeWindow) { | |
| 316 monitor_thread().Start(base::Process::Current(), window_name()); | |
| 317 | |
| 318 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 319 base::TimeDelta::FromMilliseconds(150))); | |
| 320 | |
| 321 CreateMessageWindow(); | |
| 322 | |
| 323 WaitForPing(); | |
| 324 | |
| 325 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 326 base::TimeDelta::FromMilliseconds(150))); | |
| 327 } | |
| 328 | |
| 329 TEST_F(WindowHangMonitorTest, DetectsWindowDisappearance) { | |
| 330 CreateMessageWindow(); | |
| 331 | |
| 332 monitor_thread().Start(base::Process::Current(), window_name()); | |
| 333 | |
| 334 WaitForPing(); | |
| 335 | |
| 336 DeleteMessageWindow(); | |
| 337 | |
| 338 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED, | |
| 339 monitor_thread().WaitForEvent()); | |
| 340 | |
| 341 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 342 base::TimeDelta::FromMilliseconds(150))); | |
| 343 } | |
| 344 | |
| 345 TEST_F(WindowHangMonitorTest, DetectsWindowNameChange) { | |
| 346 // This test changes the title of the message window as a proxy for what | |
| 347 // happens if the window handle is reused for a different purpose. The latter | |
| 348 // is impossible to test in a deterministic fashion. | |
| 349 CreateMessageWindow(); | |
| 350 | |
| 351 monitor_thread().Start(base::Process::Current(), window_name()); | |
| 352 | |
| 353 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 354 base::TimeDelta::FromMilliseconds(150))); | |
| 355 | |
| 356 ASSERT_TRUE(::SetWindowText(message_window()->hwnd(), L"Gonsky")); | |
| 357 | |
| 358 ASSERT_EQ(WindowHangMonitor::WINDOW_VANISHED, | |
| 359 monitor_thread().WaitForEvent()); | 420 monitor_thread().WaitForEvent()); |
| 360 } | 421 } |
| 361 | 422 |
| 362 TEST_F(WindowHangMonitorTest, DetectsWindowHang) { | 423 TEST_F(WindowHangMonitorTest, WindowVanished) { |
| 363 CreateMessageWindow(); | 424 ASSERT_TRUE(StartMonitoredProcess()); |
| 364 | 425 |
| 365 monitor_thread().Start(base::Process::Current(), window_name()); | 426 ASSERT_TRUE(SendSignal(IPC_SIGNAL_CREATE_MESSAGE_WINDOW)); |
| 427 | |
| 428 StartHangMonitor(); | |
| 366 | 429 |
| 367 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | 430 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( |
| 368 base::TimeDelta::FromMilliseconds(150))); | 431 base::TimeDelta::FromMilliseconds(250))); |
| 369 | 432 |
| 370 // Block the worker thread. | 433 ASSERT_TRUE(SendSignal(IPC_SIGNAL_DELETE_MESSAGE_WINDOW)); |
| 371 base::WaitableEvent hang(true, false); | |
| 372 | 434 |
| 373 window_thread()->task_runner()->PostTask( | 435 EXPECT_EQ(WindowHangMonitor::WINDOW_VANISHED, |
| 374 FROM_HERE, | 436 monitor_thread().WaitForEvent()); |
| 375 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&hang))); | 437 |
| 438 ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS)); | |
| 439 } | |
| 440 | |
| 441 TEST_F(WindowHangMonitorTest, WindowHang) { | |
| 442 ASSERT_TRUE(StartMonitoredProcess()); | |
| 443 | |
| 444 ASSERT_TRUE(SendSignal(IPC_SIGNAL_CREATE_MESSAGE_WINDOW)); | |
| 445 | |
| 446 StartHangMonitor(); | |
| 447 | |
| 448 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 449 base::TimeDelta::FromMilliseconds(250))); | |
| 450 | |
| 451 ASSERT_TRUE(SendSignal(IPC_SIGNAL_HANG_MESSAGE_WINDOW)); | |
| 376 | 452 |
| 377 EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG, | 453 EXPECT_EQ(WindowHangMonitor::WINDOW_HUNG, |
| 378 monitor_thread().WaitForEvent()); | 454 monitor_thread().WaitForEvent()); |
| 379 | 455 |
| 380 // Unblock the worker thread. | 456 ASSERT_TRUE(SendSignal(IPC_SIGNAL_TERMINATE_PROCESS)); |
| 381 hang.Signal(); | |
| 382 | |
| 383 ASSERT_FALSE(monitor_thread().TimedWaitForEvent( | |
| 384 base::TimeDelta::FromMilliseconds(150))); | |
| 385 } | 457 } |
| 386 | 458 |
| 387 } // namespace browser_watcher | 459 } // namespace browser_watcher |
| OLD | NEW |