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