summaryrefslogtreecommitdiffstats
path: root/chrome/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/gpu')
-rw-r--r--chrome/gpu/gpu_main.cc85
-rw-r--r--chrome/gpu/gpu_thread.cc71
-rw-r--r--chrome/gpu/gpu_thread.h7
-rw-r--r--chrome/gpu/gpu_watchdog_thread.cc110
-rw-r--r--chrome/gpu/gpu_watchdog_thread.h41
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_