Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "content/gpu/gpu_watchdog_thread.h" | 5 #include "content/gpu/gpu_watchdog_thread.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/compiler_specific.h" | 13 #include "base/compiler_specific.h" |
| 14 #include "base/debug/alias.h" | 14 #include "base/debug/alias.h" |
| 15 #include "base/files/file_path.h" | |
| 15 #include "base/files/file_util.h" | 16 #include "base/files/file_util.h" |
| 16 #include "base/location.h" | 17 #include "base/location.h" |
| 17 #include "base/macros.h" | 18 #include "base/macros.h" |
| 18 #include "base/power_monitor/power_monitor.h" | 19 #include "base/power_monitor/power_monitor.h" |
| 19 #include "base/process/process.h" | 20 #include "base/process/process.h" |
| 20 #include "base/single_thread_task_runner.h" | 21 #include "base/single_thread_task_runner.h" |
| 21 #include "base/threading/platform_thread.h" | 22 #include "base/threading/platform_thread.h" |
| 23 #include "base/timer/elapsed_timer.h" | |
| 22 #include "build/build_config.h" | 24 #include "build/build_config.h" |
| 23 #include "content/public/common/content_switches.h" | 25 #include "content/public/common/content_switches.h" |
| 24 #include "content/public/common/result_codes.h" | 26 #include "content/public/common/result_codes.h" |
| 25 | 27 |
| 26 #if defined(OS_WIN) | 28 #if defined(OS_WIN) |
| 27 #include <windows.h> | 29 #include <windows.h> |
| 28 #endif | 30 #endif |
| 29 | 31 |
| 30 namespace content { | 32 namespace content { |
| 31 namespace { | 33 namespace { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 DCHECK(result); | 75 DCHECK(result); |
| 74 #endif | 76 #endif |
| 75 | 77 |
| 76 #if defined(OS_CHROMEOS) | 78 #if defined(OS_CHROMEOS) |
| 77 tty_file_ = base::OpenFile(base::FilePath(kTtyFilePath), "r"); | 79 tty_file_ = base::OpenFile(base::FilePath(kTtyFilePath), "r"); |
| 78 #endif | 80 #endif |
| 79 #if defined(USE_X11) | 81 #if defined(USE_X11) |
| 80 SetupXServer(); | 82 SetupXServer(); |
| 81 #endif | 83 #endif |
| 82 watched_message_loop_->AddTaskObserver(&task_observer_); | 84 watched_message_loop_->AddTaskObserver(&task_observer_); |
| 85 | |
| 86 // Create a temp file for checking whether I/O is a bottleneck. | |
| 87 // This code runs on the main GPU thread before the sandbox is lowered. | |
| 88 base::FilePath temp_dir_path; | |
| 89 if (GetTempDir(&temp_dir_path)) { | |
| 90 base::FilePath temp_file_path = | |
| 91 temp_dir_path.Append(FILE_PATH_LITERAL("gpu-watchdog.tmp")); | |
|
Will Harris
2016/05/19 10:25:01
use a constant for "gpu-watchdog.tmp" as it's used
stanisc
2016/06/09 00:33:46
Done.
| |
| 92 // Please note that multiple instances of GPU process may reuse the same | |
| 93 // temporary file. That's OK because the file is written to only when | |
| 94 // the watchdog gets triggered and about to crash the process. The file | |
| 95 // should be deleted when the last handle is closed. | |
| 96 temp_file_for_io_checking_.Initialize( | |
| 97 temp_file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE | | |
| 98 base::File::FLAG_DELETE_ON_CLOSE | | |
| 99 base::File::FLAG_SHARE_DELETE); | |
|
Will Harris
2016/05/19 10:25:01
two chrome's running on the same machine as same u
stanisc
2016/05/19 18:47:26
It does work with two clients. I've tested that. T
| |
| 100 if (!temp_file_for_io_checking_.IsValid()) { | |
| 101 LOG(ERROR) << "Couldn't create " << temp_file_path.value().c_str() | |
| 102 << ", error: " << temp_file_for_io_checking_.error_details(); | |
| 103 } | |
| 104 } | |
| 83 } | 105 } |
| 84 | 106 |
| 85 void GpuWatchdogThread::PostAcknowledge() { | 107 void GpuWatchdogThread::PostAcknowledge() { |
| 86 // Called on the monitored thread. Responds with OnAcknowledge. Cannot use | 108 // Called on the monitored thread. Responds with OnAcknowledge. Cannot use |
| 87 // the method factory. Rely on reference counting instead. | 109 // the method factory. Rely on reference counting instead. |
| 88 task_runner()->PostTask(FROM_HERE, | 110 task_runner()->PostTask(FROM_HERE, |
| 89 base::Bind(&GpuWatchdogThread::OnAcknowledge, this)); | 111 base::Bind(&GpuWatchdogThread::OnAcknowledge, this)); |
| 90 } | 112 } |
| 91 | 113 |
| 92 void GpuWatchdogThread::CheckArmed() { | 114 void GpuWatchdogThread::CheckArmed() { |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 223 | 245 |
| 224 // Post a task to the monitored thread that does nothing but wake up the | 246 // Post a task to the monitored thread that does nothing but wake up the |
| 225 // TaskObserver. Any other tasks that are pending on the watched thread will | 247 // TaskObserver. Any other tasks that are pending on the watched thread will |
| 226 // also wake up the observer. This simply ensures there is at least one. | 248 // also wake up the observer. This simply ensures there is at least one. |
| 227 watched_message_loop_->task_runner()->PostTask(FROM_HERE, | 249 watched_message_loop_->task_runner()->PostTask(FROM_HERE, |
| 228 base::Bind(&base::DoNothing)); | 250 base::Bind(&base::DoNothing)); |
| 229 | 251 |
| 230 // Post a task to the watchdog thread to exit if the monitored thread does | 252 // Post a task to the watchdog thread to exit if the monitored thread does |
| 231 // not respond in time. | 253 // not respond in time. |
| 232 task_runner()->PostDelayedTask( | 254 task_runner()->PostDelayedTask( |
| 233 FROM_HERE, | 255 FROM_HERE, base::Bind(&GpuWatchdogThread::BeginTerminating, |
| 234 base::Bind(&GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang, | 256 weak_factory_.GetWeakPtr()), |
| 235 weak_factory_.GetWeakPtr()), | |
| 236 timeout); | 257 timeout); |
| 237 } | 258 } |
| 238 | 259 |
| 239 // Use the --disable-gpu-watchdog command line switch to disable this. | 260 void GpuWatchdogThread::BeginTerminating() { |
| 240 void GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang() { | |
| 241 // Should not get here while the system is suspended. | 261 // Should not get here while the system is suspended. |
| 242 DCHECK(!suspended_); | 262 DCHECK(!suspended_); |
| 243 | 263 |
| 244 #if defined(OS_WIN) | 264 #if defined(OS_WIN) |
| 245 // Defer termination until a certain amount of CPU time has elapsed on the | 265 // Defer termination until a certain amount of CPU time has elapsed on the |
| 246 // watched thread. | 266 // watched thread. |
| 247 base::ThreadTicks current_cpu_time = GetWatchedThreadTime(); | 267 base::ThreadTicks current_cpu_time = GetWatchedThreadTime(); |
| 248 base::TimeDelta time_since_arm = current_cpu_time - arm_cpu_time_; | 268 base::TimeDelta time_since_arm = current_cpu_time - arm_cpu_time_; |
| 249 if (use_thread_cpu_time_ && (time_since_arm < timeout_)) { | 269 if (use_thread_cpu_time_ && (time_since_arm < timeout_)) { |
| 250 message_loop()->PostDelayedTask( | 270 message_loop()->PostDelayedTask( |
| 251 FROM_HERE, | 271 FROM_HERE, base::Bind(&GpuWatchdogThread::BeginTerminating, |
| 252 base::Bind( | 272 weak_factory_.GetWeakPtr()), |
| 253 &GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang, | |
| 254 weak_factory_.GetWeakPtr()), | |
| 255 timeout_ - time_since_arm); | 273 timeout_ - time_since_arm); |
| 256 return; | 274 return; |
| 257 } | 275 } |
| 258 #endif | 276 #endif |
| 259 | 277 |
| 278 // If the machine is busy with heavy I/O activity this should defer | |
| 279 // termination until the I/O queue clears (on the drive that contains | |
| 280 // the temp directory). | |
| 281 if (temp_file_for_io_checking_.IsValid()) { | |
| 282 // Write a few bytes and wait for the write to flush. | |
| 283 const char temp_data[32] = {0}; | |
| 284 base::ElapsedTimer timer; | |
| 285 temp_file_for_io_checking_.Write(0, temp_data, sizeof(temp_data)); | |
| 286 temp_file_for_io_checking_.Flush(); | |
| 287 io_check_duration_ = timer.Elapsed(); | |
| 288 } | |
| 289 | |
| 290 // Post a new task to actually terminate unless an acknowledge from the | |
| 291 // watched thread arrives in between. | |
| 292 message_loop()->PostTask( | |
| 293 FROM_HERE, | |
| 294 base::Bind(&GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang, | |
| 295 weak_factory_.GetWeakPtr())); | |
| 296 } | |
| 297 | |
| 298 // Use the --disable-gpu-watchdog command line switch to disable this. | |
| 299 void GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang() { | |
| 300 // Should not get here while the system is suspended. | |
| 301 DCHECK(!suspended_); | |
| 302 | |
| 260 // If the watchdog woke up significantly behind schedule, disarm and reset | 303 // If the watchdog woke up significantly behind schedule, disarm and reset |
| 261 // the watchdog check. This is to prevent the watchdog thread from terminating | 304 // the watchdog check. This is to prevent the watchdog thread from terminating |
| 262 // when a machine wakes up from sleep or hibernation, which would otherwise | 305 // when a machine wakes up from sleep or hibernation, which would otherwise |
| 263 // appear to be a hang. | 306 // appear to be a hang. |
| 264 if (base::Time::Now() > suspension_timeout_) { | 307 if (base::Time::Now() > suspension_timeout_) { |
| 265 armed_ = false; | 308 armed_ = false; |
| 266 OnCheck(true); | 309 OnCheck(true); |
| 267 return; | 310 return; |
| 268 } | 311 } |
| 269 | 312 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 341 | 384 |
| 342 // Store variables so they're available in crash dumps to help determine the | 385 // Store variables so they're available in crash dumps to help determine the |
| 343 // cause of any hang. | 386 // cause of any hang. |
| 344 #if defined(OS_WIN) | 387 #if defined(OS_WIN) |
| 345 ULONGLONG fire_interrupt_time; | 388 ULONGLONG fire_interrupt_time; |
| 346 QueryUnbiasedInterruptTime(&fire_interrupt_time); | 389 QueryUnbiasedInterruptTime(&fire_interrupt_time); |
| 347 | 390 |
| 348 // This is the time since the watchdog was armed, in 100ns intervals, | 391 // This is the time since the watchdog was armed, in 100ns intervals, |
| 349 // ignoring time where the computer is suspended. | 392 // ignoring time where the computer is suspended. |
| 350 ULONGLONG interrupt_delay = fire_interrupt_time - arm_interrupt_time_; | 393 ULONGLONG interrupt_delay = fire_interrupt_time - arm_interrupt_time_; |
| 394 base::debug::Alias(&interrupt_delay); | |
| 351 | 395 |
| 352 base::debug::Alias(&interrupt_delay); | 396 base::ThreadTicks current_cpu_time = GetWatchedThreadTime(); |
| 353 base::debug::Alias(¤t_cpu_time); | 397 base::debug::Alias(¤t_cpu_time); |
| 398 | |
| 399 base::TimeDelta time_since_arm = current_cpu_time - arm_cpu_time_; | |
| 354 base::debug::Alias(&time_since_arm); | 400 base::debug::Alias(&time_since_arm); |
| 355 | 401 |
| 356 bool using_thread_ticks = base::ThreadTicks::IsSupported(); | 402 bool using_thread_ticks = base::ThreadTicks::IsSupported(); |
| 357 base::debug::Alias(&using_thread_ticks); | 403 base::debug::Alias(&using_thread_ticks); |
| 358 | 404 |
| 359 bool using_high_res_timer = base::Time::IsHighResolutionTimerInUse(); | 405 bool using_high_res_timer = base::Time::IsHighResolutionTimerInUse(); |
| 360 base::debug::Alias(&using_high_res_timer); | 406 base::debug::Alias(&using_high_res_timer); |
| 361 #endif | 407 #endif |
| 362 | 408 |
| 363 base::Time current_time = base::Time::Now(); | 409 base::Time current_time = base::Time::Now(); |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 457 // calls into kernel level repeatedly, giving up its quanta before it is | 503 // calls into kernel level repeatedly, giving up its quanta before it is |
| 458 // tracked, for example a loop that repeatedly Sleeps. | 504 // tracked, for example a loop that repeatedly Sleeps. |
| 459 return base::ThreadTicks() + | 505 return base::ThreadTicks() + |
| 460 base::TimeDelta::FromMilliseconds(static_cast<int64_t>( | 506 base::TimeDelta::FromMilliseconds(static_cast<int64_t>( |
| 461 (user_time64.QuadPart + kernel_time64.QuadPart) / 10000)); | 507 (user_time64.QuadPart + kernel_time64.QuadPart) / 10000)); |
| 462 } | 508 } |
| 463 } | 509 } |
| 464 #endif | 510 #endif |
| 465 | 511 |
| 466 } // namespace content | 512 } // namespace content |
| OLD | NEW |