diff options
Diffstat (limited to 'chrome/gpu')
-rw-r--r-- | chrome/gpu/gpu_main.cc | 85 | ||||
-rw-r--r-- | chrome/gpu/gpu_thread.cc | 71 | ||||
-rw-r--r-- | chrome/gpu/gpu_thread.h | 7 | ||||
-rw-r--r-- | chrome/gpu/gpu_watchdog_thread.cc | 110 | ||||
-rw-r--r-- | chrome/gpu/gpu_watchdog_thread.h | 41 |
5 files changed, 270 insertions, 44 deletions
diff --git a/chrome/gpu/gpu_main.cc b/chrome/gpu/gpu_main.cc index 0c0d3c51..51a75d5 100644 --- a/chrome/gpu/gpu_main.cc +++ b/chrome/gpu/gpu_main.cc @@ -2,15 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stdlib.h> + +#include "app/gfx/gl/gl_context.h" #include "app/gfx/gl/gl_implementation.h" +#include "base/environment.h" #include "base/message_loop.h" +#include "base/metrics/field_trial.h" +#include "base/stringprintf.h" #include "build/build_config.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/env_vars.h" #include "chrome/common/main_function_params.h" #include "chrome/gpu/gpu_config.h" #include "chrome/gpu/gpu_process.h" #include "chrome/gpu/gpu_thread.h" +#include "chrome/gpu/gpu_watchdog_thread.h" #if defined(USE_LINUX_BREAKPAD) #include "chrome/app/breakpad_linux.h" @@ -25,9 +33,17 @@ #include "app/x11_util_internal.h" #endif -#if defined(USE_X11) + namespace { +// 1% per watchdog trial group. +const int kFieldTrialSize = 1; + +// 5 - 20 seconds timeout. +const int kMinGpuTimeout = 5; +const int kMaxGpuTimeout = 20; + +#if defined(USE_X11) int GpuX11ErrorHandler(Display* d, XErrorEvent* error) { LOG(ERROR) << x11_util::GetErrorEventDescription(d, error); return 0; @@ -37,12 +53,14 @@ void SetGpuX11ErrorHandlers() { // Set up the error handlers so that only general errors aren't fatal. x11_util::SetX11ErrorHandlers(GpuX11ErrorHandler, NULL); } +#endif } -#endif // Main function for starting the Gpu process. int GpuMain(const MainFunctionParams& parameters) { + base::Time start_time = base::Time::Now(); + #if defined(USE_LINUX_BREAKPAD) // Needs to be called after we have chrome::DIR_USER_DATA. InitCrashReporter(); @@ -65,18 +83,75 @@ int GpuMain(const MainFunctionParams& parameters) { #if defined(OS_WIN) win_util::ScopedCOMInitializer com_initializer; -#elif defined(GPU_USE_GLX) - gfx::InitializeGLBindings(gfx::kGLImplementationDesktopGL); #endif + // Load the GL implementation and locate the bindings before starting as + // this can take a lot of time and the GPU watchdog might terminate the GPU + // process. + if (!gfx::GLContext::InitializeOneOff()) + return EXIT_FAILURE; + GpuProcess gpu_process; - gpu_process.set_main_thread(new GpuThread()); + GpuThread* gpu_thread = new GpuThread; + gpu_process.set_main_thread(gpu_thread); + + // Only enable this experimental feaure for a subset of users. + scoped_refptr<base::FieldTrial> watchdog_trial( + new base::FieldTrial("GpuWatchdogTrial", 100)); + int watchdog_timeout = 0; + for (int i = kMinGpuTimeout; i <= kMaxGpuTimeout; ++i) { + int group = watchdog_trial->AppendGroup(StringPrintf("%dsecs", i), + kFieldTrialSize); + if (group == watchdog_trial->group()) { + watchdog_timeout = i; + break; + } + } + + scoped_ptr<base::Environment> env(base::Environment::Create()); + + // In addition to disabling the watchdog if the command line switch is + // present, disable it in two other cases. OSMesa is expected to run very + // slowly. Also disable the watchdog on valgrind because the code is expected + // to run slowly in that case. + bool enable_watchdog = + watchdog_timeout != 0 && + !command_line.HasSwitch(switches::kDisableGpuWatchdog) && + gfx::GetGLImplementation() != gfx::kGLImplementationOSMesaGL && + !RunningOnValgrind(); + + // Disable the watchdog in debug builds because they tend to only be run by + // developers who will not appreciate the watchdog killing the GPU process. +#ifndef NDEBUG + enable_watchdog = false; +#endif + +// TODO(apatrick): Disable for this commit. I want to enable this feature with +// a simple single file change that can easily be reverted if need be without +// losing all the other features of the patch. +#if 1 + enable_watchdog = false; +#endif + + scoped_refptr<GpuWatchdogThread> watchdog_thread; + if (enable_watchdog) { + watchdog_thread = new GpuWatchdogThread(MessageLoop::current(), + watchdog_timeout * 1000); + watchdog_thread->Start(); + } #if defined(USE_X11) SetGpuX11ErrorHandlers(); #endif + // Do this immediately before running the message loop so the correct + // initialization time is recorded in the GPU info. + gpu_thread->Init(start_time); + main_message_loop.Run(); + if (enable_watchdog) + watchdog_thread->Stop(); + return 0; } diff --git a/chrome/gpu/gpu_thread.cc b/chrome/gpu/gpu_thread.cc index 8c1059f..3a1dc7b 100644 --- a/chrome/gpu/gpu_thread.cc +++ b/chrome/gpu/gpu_thread.cc @@ -12,7 +12,6 @@ #include "build/build_config.h" #include "chrome/common/child_process.h" #include "chrome/common/child_process_logging.h" -#include "chrome/common/gpu_info.h" #include "chrome/common/gpu_messages.h" #include "chrome/gpu/gpu_info_collector.h" #include "ipc/ipc_channel_handle.h" @@ -67,6 +66,15 @@ GpuThread::GpuThread() { GpuThread::~GpuThread() { } +void GpuThread::Init(const base::Time& process_start_time) { + gpu_info_collector::CollectGraphicsInfo(&gpu_info_); + child_process_logging::SetGpuInfo(gpu_info_); + + // Record initialization only after collecting the GPU info because that can + // take a significant amount of time. + gpu_info_.SetInitializationTime(base::Time::Now() - process_start_time); +} + #if defined(GPU_USE_GLX) GpuBackingStoreGLXContext* GpuThread::GetGLXContext() { if (!glx_context_.get()) @@ -102,40 +110,32 @@ void GpuThread::OnEstablishChannel(int renderer_id) { IPC::ChannelHandle channel_handle; GPUInfo gpu_info; - // Fail to establish a channel if some implementation of GL cannot be - // initialized. - if (gfx::GLContext::InitializeOneOff()) { - // Fail to establish channel if GPU stats cannot be retreived. - if (gpu_info_collector::CollectGraphicsInfo(&gpu_info)) { - child_process_logging::SetGpuInfo(gpu_info); - GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id); - if (iter == gpu_channels_.end()) { - channel = new GpuChannel(renderer_id); - } else { - channel = iter->second; - } - - DCHECK(channel != NULL); - - if (channel->Init()) { - gpu_channels_[renderer_id] = channel; - } else { - channel = NULL; - } - - if (channel.get()) { - channel_handle.name = channel->GetChannelName(); + GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id); + if (iter == gpu_channels_.end()) { + channel = new GpuChannel(renderer_id); + } else { + channel = iter->second; + } + + DCHECK(channel != NULL); + + if (channel->Init()) { + gpu_channels_[renderer_id] = channel; + } else { + channel = NULL; + } + + if (channel.get()) { + channel_handle.name = channel->GetChannelName(); #if defined(OS_POSIX) - // On POSIX, pass the renderer-side FD. Also mark it as auto-close so - // that it gets closed after it has been sent. - int renderer_fd = channel->DisownRendererFd(); - channel_handle.socket = base::FileDescriptor(renderer_fd, true); + // On POSIX, pass the renderer-side FD. Also mark it as auto-close so + // that it gets closed after it has been sent. + int renderer_fd = channel->DisownRendererFd(); + channel_handle.socket = base::FileDescriptor(renderer_fd, true); #endif - } - } } - Send(new GpuHostMsg_ChannelEstablished(channel_handle, gpu_info)); + Send(new GpuHostMsg_ChannelEstablished(channel_handle, gpu_info_)); } void GpuThread::OnSynchronize() { @@ -157,14 +157,7 @@ void GpuThread::OnNewRenderWidgetHostView(GpuNativeWindowHandle parent_window, } void GpuThread::OnCollectGraphicsInfo() { - // Fail to establish a channel if some implementation of GL cannot be - // initialized. - GPUInfo gpu_info; - if (gfx::GLContext::InitializeOneOff()) { - gpu_info_collector::CollectGraphicsInfo(&gpu_info); - } - - Send(new GpuHostMsg_GraphicsInfoCollected(gpu_info)); + Send(new GpuHostMsg_GraphicsInfoCollected(gpu_info_)); } void GpuThread::OnCrash() { diff --git a/chrome/gpu/gpu_thread.h b/chrome/gpu/gpu_thread.h index b896360..6d20422 100644 --- a/chrome/gpu/gpu_thread.h +++ b/chrome/gpu/gpu_thread.h @@ -8,8 +8,10 @@ #include "base/basictypes.h" #include "base/scoped_ptr.h" +#include "base/time.h" #include "build/build_config.h" #include "chrome/common/child_thread.h" +#include "chrome/common/gpu_info.h" #include "chrome/common/gpu_native_window_handle.h" #include "chrome/gpu/gpu_channel.h" #include "chrome/gpu/gpu_config.h" @@ -25,6 +27,8 @@ class GpuThread : public ChildThread { GpuThread(); ~GpuThread(); + void Init(const base::Time& process_start_time); + #if defined(GPU_USE_GLX) GpuBackingStoreGLXContext* GetGLXContext(); @@ -55,6 +59,9 @@ class GpuThread : public ChildThread { scoped_ptr<GpuBackingStoreGLXContext> glx_context_; #endif + // Information about the GPU, such as device and vendor ID. + GPUInfo gpu_info_; + DISALLOW_COPY_AND_ASSIGN(GpuThread); }; diff --git a/chrome/gpu/gpu_watchdog_thread.cc b/chrome/gpu/gpu_watchdog_thread.cc new file mode 100644 index 0000000..e262c79 --- /dev/null +++ b/chrome/gpu/gpu_watchdog_thread.cc @@ -0,0 +1,110 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if defined(OS_WIN) +#include <windows.h> +#endif + +#include "chrome/gpu/gpu_watchdog_thread.h" + +#include "base/compiler_specific.h" +#include "build/build_config.h" + +namespace { +const int64 kCheckPeriod = 2000; +} + +GpuWatchdogThread::GpuWatchdogThread(MessageLoop* watched_message_loop, + int timeout) + : base::Thread("Watchdog"), + watched_message_loop_(watched_message_loop), + timeout_(timeout) { + DCHECK(watched_message_loop); + DCHECK(timeout >= 0); +} + +GpuWatchdogThread::~GpuWatchdogThread() { + // Verify that the thread was explicitly stopped. If the thread is stopped + // implicitly by the destructor, CleanUp() will not be called. + DCHECK(!method_factory_.get()); +} + +void GpuWatchdogThread::Init() { + // The method factory must be created on the watchdog thread. + method_factory_.reset(new MethodFactory(this)); + + // Schedule the first check. + OnCheck(); +} + +void GpuWatchdogThread::CleanUp() { + // The method factory must be destroyed on the watchdog thread. + method_factory_->RevokeAll(); + method_factory_.reset(); + + // Prevent any more delayed tasks from being posted. + watched_message_loop_ = NULL; +} + +void GpuWatchdogThread::OnAcknowledge() { + // Revoke any pending OnExit. + method_factory_->RevokeAll(); + + // The monitored thread has responded. Post a task to check it again. + if (watched_message_loop_) { + message_loop()->PostDelayedTask( + FROM_HERE, + method_factory_->NewRunnableMethod(&GpuWatchdogThread::OnCheck), + kCheckPeriod); + } +} + +void GpuWatchdogThread::OnCheck() { + if (watched_message_loop_) { + // Post a task to the monitored thread that simply responds with a task that + // calls OnAcknowldge. + watched_message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &GpuWatchdogThread::PostAcknowledge)); + + // Post a task to the watchdog thread to exit if the nmonitored thread does + // not respond in time. + message_loop()->PostDelayedTask( + FROM_HERE, + method_factory_->NewRunnableMethod(&GpuWatchdogThread::OnExit), + timeout_); + } +} + +void GpuWatchdogThread::PostAcknowledge() { + // Called on the monitored thread. Responds with OnAcknowledge. Cannot use + // the method factory. Rely on reference counting instead. + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &GpuWatchdogThread::OnAcknowledge)); +} + +// Use the --disable-gpu-watchdog command line switch to disable this. +void GpuWatchdogThread::OnExit() { + // Make sure the timeout period is on the stack before crashing. + volatile int timeout = timeout_; + + // For minimal developer annoyance, don't keep crashing. + static bool crashed = false; + if (crashed) + return; + +#if defined(OS_WIN) + if (IsDebuggerPresent()) + return; +#endif + + LOG(ERROR) << "The GPU process hung. Restarting after " + << timeout_ << " seconds."; + + volatile int* null_pointer = NULL; + *null_pointer = timeout; + + crashed = true; +} diff --git a/chrome/gpu/gpu_watchdog_thread.h b/chrome/gpu/gpu_watchdog_thread.h new file mode 100644 index 0000000..d6e1117 --- /dev/null +++ b/chrome/gpu/gpu_watchdog_thread.h @@ -0,0 +1,41 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_GPU_GPU_WATCHDOG_THREAD_H_ +#define CHROME_GPU_GPU_WATCHDOG_THREAD_H_ + +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/thread.h" + +// A thread that intermitently sends tasks to a group of watched message loops +// and deliberately crashes if one of them does not respond after a timeout. +class GpuWatchdogThread : public base::Thread, + public base::RefCountedThreadSafe<GpuWatchdogThread> { + public: + GpuWatchdogThread(MessageLoop* watched_message_loop, int timeout); + virtual ~GpuWatchdogThread(); + + protected: + virtual void Init(); + virtual void CleanUp(); + + private: + void OnAcknowledge(); + void OnCheck(); + void PostAcknowledge(); + void OnExit(); + void Disable(); + + MessageLoop* watched_message_loop_; + int timeout_; + + typedef ScopedRunnableMethodFactory<GpuWatchdogThread> MethodFactory; + scoped_ptr<MethodFactory> method_factory_; + + DISALLOW_COPY_AND_ASSIGN(GpuWatchdogThread); +}; + +#endif // CHROME_GPU_GPU_WATCHDOG_THREAD_H_ |