summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/process_util.h31
-rw-r--r--base/process_util_posix.cc41
-rw-r--r--base/process_util_unittest.cc172
-rw-r--r--base/process_util_win.cc86
-rw-r--r--chrome/browser/browser_child_process_host.cc32
-rw-r--r--chrome/browser/browser_child_process_host.h22
-rw-r--r--chrome/browser/child_process_launcher.cc23
-rw-r--r--chrome/browser/child_process_launcher.h7
-rw-r--r--chrome/browser/child_process_security_policy_browsertest.cc3
-rw-r--r--chrome/browser/extensions/extension_crash_recovery_browsertest.cc4
-rw-r--r--chrome/browser/extensions/extension_host.cc4
-rw-r--r--chrome/browser/extensions/extension_host.h4
-rw-r--r--chrome/browser/extensions/sandboxed_extension_unpacker.cc2
-rw-r--r--chrome/browser/extensions/sandboxed_extension_unpacker.h2
-rw-r--r--chrome/browser/gpu_process_host.cc4
-rw-r--r--chrome/browser/gpu_process_host.h2
-rw-r--r--chrome/browser/importer/importer.cc2
-rw-r--r--chrome/browser/importer/importer.h2
-rw-r--r--chrome/browser/in_process_webkit/indexed_db_key_utility_client.cc2
-rw-r--r--chrome/browser/in_process_webkit/indexed_db_key_utility_client.h2
-rw-r--r--chrome/browser/metrics/metrics_service.cc8
-rw-r--r--chrome/browser/nacl_host/nacl_process_host.cc7
-rw-r--r--chrome/browser/nacl_host/nacl_process_host.h2
-rw-r--r--chrome/browser/notifications/balloon_host.cc7
-rw-r--r--chrome/browser/notifications/balloon_host.h4
-rw-r--r--chrome/browser/profile_import_process_host.cc5
-rw-r--r--chrome/browser/profile_import_process_host.h4
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc26
-rw-r--r--chrome/browser/renderer_host/render_process_host.h11
-rw-r--r--chrome/browser/renderer_host/render_view_host.cc17
-rw-r--r--chrome/browser/renderer_host/render_view_host.h10
-rw-r--r--chrome/browser/renderer_host/render_view_host_delegate.h5
-rw-r--r--chrome/browser/renderer_host/render_widget_host.cc7
-rw-r--r--chrome/browser/renderer_host/render_widget_host.h5
-rw-r--r--chrome/browser/renderer_host/render_widget_host_unittest.cc2
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view.h4
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_gtk.cc3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_gtk.h3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.h3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_mac.mm3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_views.cc6
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_views.h3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_win.cc3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_win.h3
-rw-r--r--chrome/browser/renderer_host/test/test_render_view_host.h3
-rw-r--r--chrome/browser/renderer_host/test/web_cache_manager_browsertest.cc3
-rw-r--r--chrome/browser/tab_contents/interstitial_page.cc4
-rw-r--r--chrome/browser/tab_contents/interstitial_page.h5
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc21
-rw-r--r--chrome/browser/tab_contents/tab_contents.h17
-rw-r--r--chrome/browser/tab_contents/web_contents_unittest.cc18
-rw-r--r--chrome/browser/task_manager/task_manager.cc3
-rw-r--r--chrome/browser/utility_process_host.cc4
-rw-r--r--chrome/browser/utility_process_host.h4
-rw-r--r--chrome/browser/web_resource/web_resource_service.cc2
-rw-r--r--chrome/browser/zygote_host_linux.cc32
-rw-r--r--chrome/browser/zygote_host_linux.h24
-rw-r--r--chrome/browser/zygote_main_linux.cc30
-rw-r--r--chrome/common/notification_type.h16
-rw-r--r--chrome/common/render_messages_internal.h5
-rw-r--r--chrome/common/result_codes.h6
-rw-r--r--chrome/test/chrome_process_util.cc3
62 files changed, 560 insertions, 238 deletions
diff --git a/base/process_util.h b/base/process_util.h
index ca43289..ccbb687 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -118,13 +118,15 @@ const uint32 kProcessAccessQueryLimitedInfomation = 0;
const uint32 kProcessAccessWaitForTermination = 0;
#endif // defined(OS_POSIX)
-// A minimalistic but hopefully cross-platform set of exit codes.
-// Do not change the enumeration values or you will break third-party
-// installers.
-enum {
- PROCESS_END_NORMAL_TERMINATION = 0,
- PROCESS_END_KILLED_BY_USER = 1,
- PROCESS_END_PROCESS_WAS_HUNG = 2
+// Return status values from GetTerminationStatus. Don't use these as
+// exit code arguments to KillProcess*(), use platform/application
+// specific values instead.
+enum TerminationStatus {
+ TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
+ TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
+ TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
+ TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault
+ TERMINATION_STATUS_STILL_RUNNING // child hasn't exited yet
};
// Returns the id of the current process.
@@ -180,7 +182,7 @@ bool AdjustOOMScore(ProcessId process, int score);
#endif
#if defined(OS_POSIX)
-// Close all file descriptors, expect those which are a destination in the
+// Close all file descriptors, except those which are a destination in the
// given multimap. Only call this function in a child process where you know
// that there aren't any other threads.
void CloseSuperfluousFds(const InjectiveMultimap& saved_map);
@@ -347,10 +349,15 @@ bool KillProcessGroup(ProcessHandle process_group_id);
bool KillProcessById(ProcessId process_id, int exit_code, bool wait);
#endif
-// Get the termination status (exit code) of the process and return true if the
-// status indicates the process crashed. |child_exited| is set to true iff the
-// child process has terminated. (|child_exited| may be NULL.)
-bool DidProcessCrash(bool* child_exited, ProcessHandle handle);
+// Get the termination status of the process by interpreting the
+// circumstances of the child process' death. |exit_code| is set to
+// the status returned by waitpid() on POSIX, and from
+// GetExitCodeProcess() on Windows. |exit_code| may be NULL if the
+// caller is not interested in it. Note that on Linux, this function
+// will only return a useful result the first time it is called after
+// the child exits (because it will reap the child and the information
+// will no longer be available).
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code);
// Waits for process to exit. In POSIX systems, if the process hasn't been
// signaled then puts the exit code in |exit_code|; otherwise it's considered
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index 92ef964..3d29674 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -237,6 +237,8 @@ bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) {
sleep_ms *= 2;
}
+ // If we're waiting and the child hasn't died by now, force it
+ // with a SIGKILL.
if (!exited)
result = kill(process_id, SIGKILL) == 0;
}
@@ -634,40 +636,45 @@ void RaiseProcessToHighPriority() {
// setpriority() or sched_getscheduler, but these all require extra rights.
}
-bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
- int status;
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
+ int status = 0;
const pid_t result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
if (result == -1) {
PLOG(ERROR) << "waitpid(" << handle << ")";
- if (child_exited)
- *child_exited = false;
- return false;
+ if (exit_code)
+ *exit_code = 0;
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
} else if (result == 0) {
// the child hasn't exited yet.
- if (child_exited)
- *child_exited = false;
- return false;
+ if (exit_code)
+ *exit_code = 0;
+ return TERMINATION_STATUS_STILL_RUNNING;
}
- if (child_exited)
- *child_exited = true;
+ if (exit_code)
+ *exit_code = status;
if (WIFSIGNALED(status)) {
switch (WTERMSIG(status)) {
- case SIGSEGV:
- case SIGILL:
case SIGABRT:
+ case SIGBUS:
case SIGFPE:
- return true;
+ case SIGILL:
+ case SIGSEGV:
+ return TERMINATION_STATUS_PROCESS_CRASHED;
+ case SIGINT:
+ case SIGKILL:
+ case SIGTERM:
+ return TERMINATION_STATUS_PROCESS_WAS_KILLED;
default:
- return false;
+ break;
}
}
- if (WIFEXITED(status))
- return WEXITSTATUS(status) != 0;
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ return TERMINATION_STATUS_ABNORMAL_TERMINATION;
- return false;
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
}
bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc
index 34c444c..0eaf5d4 100644
--- a/base/process_util_unittest.cc
+++ b/base/process_util_unittest.cc
@@ -7,6 +7,7 @@
#include <limits>
#include "base/command_line.h"
+#include "base/debug_util.h"
#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/logging.h"
@@ -15,6 +16,7 @@
#include "base/process_util.h"
#include "base/scoped_ptr.h"
#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
@@ -27,6 +29,7 @@
#if defined(OS_POSIX)
#include <dlfcn.h>
#include <fcntl.h>
+#include <signal.h>
#include <sys/resource.h>
#include <sys/socket.h>
#endif
@@ -41,11 +44,25 @@
namespace {
#if defined(OS_WIN)
-const wchar_t* const kProcessName = L"base_unittests.exe";
+const wchar_t kProcessName[] = L"base_unittests.exe";
#else
-const wchar_t* const kProcessName = L"base_unittests";
+const wchar_t kProcessName[] = L"base_unittests";
#endif // defined(OS_WIN)
+const char kSignalFileSlow[] = "SlowChildProcess.die";
+const char kSignalFileCrash[] = "CrashingChildProcess.die";
+const char kSignalFileKill[] = "KilledChildProcess.die";
+
+#if defined(OS_WIN)
+const int kExpectedStillRunningExitCode = 0x102;
+const int kExpectedKilledExitCode = 1;
+#else
+const int kExpectedStillRunningExitCode = 0;
+#endif
+
+// The longest we'll wait for a process, in milliseconds.
+const int kMaxWaitTimeMs = TestTimeouts::action_max_timeout_ms();
+
// Sleeps until file filename is created.
void WaitToDie(const char* filename) {
FILE *fp;
@@ -62,6 +79,27 @@ void SignalChildren(const char* filename) {
fclose(fp);
}
+// Using a pipe to the child to wait for an event was considered, but
+// there were cases in the past where pipes caused problems (other
+// libraries closing the fds, child deadlocking). This is a simple
+// case, so it's not worth the risk. Using wait loops is discouraged
+// in most instances.
+base::TerminationStatus WaitForChildTermination(base::ProcessHandle handle,
+ int* exit_code) {
+ // Now we wait until the result is something other than STILL_RUNNING.
+ base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING;
+ const int kIntervalMs = 20;
+ int waited = 0;
+ do {
+ status = base::GetTerminationStatus(handle, exit_code);
+ PlatformThread::Sleep(kIntervalMs);
+ waited += kIntervalMs;
+ } while (status == base::TERMINATION_STATUS_STILL_RUNNING &&
+ waited < kMaxWaitTimeMs);
+
+ return status;
+}
+
} // namespace
class ProcessUtilTest : public base::MultiProcessTest {
@@ -79,40 +117,140 @@ MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
TEST_F(ProcessUtilTest, SpawnChild) {
base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
ASSERT_NE(base::kNullProcessHandle, handle);
- EXPECT_TRUE(base::WaitForSingleProcess(handle, 5000));
+ EXPECT_TRUE(base::WaitForSingleProcess(handle, kMaxWaitTimeMs));
base::CloseProcessHandle(handle);
}
MULTIPROCESS_TEST_MAIN(SlowChildProcess) {
- WaitToDie("SlowChildProcess.die");
+ WaitToDie(kSignalFileSlow);
return 0;
}
TEST_F(ProcessUtilTest, KillSlowChild) {
- remove("SlowChildProcess.die");
+ remove(kSignalFileSlow);
base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false);
ASSERT_NE(base::kNullProcessHandle, handle);
- SignalChildren("SlowChildProcess.die");
- EXPECT_TRUE(base::WaitForSingleProcess(handle, 5000));
+ SignalChildren(kSignalFileSlow);
+ EXPECT_TRUE(base::WaitForSingleProcess(handle, kMaxWaitTimeMs));
base::CloseProcessHandle(handle);
- remove("SlowChildProcess.die");
+ remove(kSignalFileSlow);
}
-TEST_F(ProcessUtilTest, DidProcessCrash) {
- remove("SlowChildProcess.die");
+TEST_F(ProcessUtilTest, GetTerminationStatusExit) {
+ remove(kSignalFileSlow);
base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false);
ASSERT_NE(base::kNullProcessHandle, handle);
- bool child_exited = true;
- EXPECT_FALSE(base::DidProcessCrash(&child_exited, handle));
- EXPECT_FALSE(child_exited);
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(kSignalFileSlow);
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_NORMAL_TERMINATION, status);
+ EXPECT_EQ(0, exit_code);
+ base::CloseProcessHandle(handle);
+ remove(kSignalFileSlow);
+}
- SignalChildren("SlowChildProcess.die");
- EXPECT_TRUE(base::WaitForSingleProcess(handle, 5000));
+#if !defined(OS_MACOSX)
+// This test is disabled on Mac, since it's flaky due to ReportCrash
+// taking a variable amount of time to parse and load the debug and
+// symbol data for this unit test's executable before firing the
+// signal handler.
+//
+// TODO(gspencer): turn this test process into a very small program
+// with no symbols (instead of using the multiprocess testing
+// framework) to reduce the ReportCrash overhead.
+
+MULTIPROCESS_TEST_MAIN(CrashingChildProcess) {
+ WaitToDie(kSignalFileCrash);
+#if defined(OS_POSIX)
+ // Have to disable to signal handler for segv so we can get a crash
+ // instead of an abnormal termination through the crash dump handler.
+ ::signal(SIGSEGV, SIG_DFL);
+#endif
+ // Make this process have a segmentation fault.
+ int* oops = NULL;
+ *oops = 0xDEAD;
+ return 1;
+}
+
+TEST_F(ProcessUtilTest, GetTerminationStatusCrash) {
+ remove(kSignalFileCrash);
+ base::ProcessHandle handle = this->SpawnChild("CrashingChildProcess",
+ false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
- EXPECT_FALSE(base::DidProcessCrash(&child_exited, handle));
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(kSignalFileCrash);
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_CRASHED, status);
+
+#if defined(OS_WIN)
+ EXPECT_EQ(0xc0000005, exit_code);
+#elif defined(OS_POSIX)
+ int signaled = WIFSIGNALED(exit_code);
+ EXPECT_NE(0, signaled);
+ int signal = WTERMSIG(exit_code);
+ EXPECT_EQ(SIGSEGV, signal);
+#endif
+ base::CloseProcessHandle(handle);
+
+ // Reset signal handlers back to "normal".
+ base::EnableInProcessStackDumping();
+ remove(kSignalFileCrash);
+}
+#endif // !defined(OS_MACOSX)
+
+MULTIPROCESS_TEST_MAIN(KilledChildProcess) {
+ WaitToDie(kSignalFileKill);
+#if defined(OS_WIN)
+ // Kill ourselves.
+ HANDLE handle = ::OpenProcess(PROCESS_ALL_ACCESS, 0, ::GetCurrentProcessId());
+ ::TerminateProcess(handle, kExpectedKilledExitCode);
+#elif defined(OS_POSIX)
+ // Send a SIGKILL to this process, just like the OOM killer would.
+ ::kill(getpid(), SIGKILL);
+#endif
+ return 1;
+}
+
+TEST_F(ProcessUtilTest, GetTerminationStatusKill) {
+ remove(kSignalFileKill);
+ base::ProcessHandle handle = this->SpawnChild("KilledChildProcess",
+ false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(kSignalFileKill);
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_WAS_KILLED, status);
+#if defined(OS_WIN)
+ EXPECT_EQ(kExpectedKilledExitCode, exit_code);
+#elif defined(OS_POSIX)
+ int signaled = WIFSIGNALED(exit_code);
+ EXPECT_NE(0, signaled);
+ int signal = WTERMSIG(exit_code);
+ EXPECT_EQ(SIGKILL, signal);
+#endif
base::CloseProcessHandle(handle);
- remove("SlowChildProcess.die");
+ remove(kSignalFileKill);
}
// Ensure that the priority of a process is restored correctly after
diff --git a/base/process_util_win.cc b/base/process_util_win.cc
index 097888e..71f0a1a9 100644
--- a/base/process_util_win.cc
+++ b/base/process_util_win.cc
@@ -30,6 +30,20 @@ namespace {
// System pagesize. This value remains constant on x86/64 architectures.
const int PAGESIZE_KB = 4;
+// Exit codes with special meanings on Windows.
+const DWORD kNormalTerminationExitCode = 0;
+const DWORD kDebuggerInactiveExitCode = 0xC0000354;
+const DWORD kKeyboardInterruptExitCode = 0xC000013A;
+const DWORD kDebuggerTerminatedExitCode = 0x40010004;
+
+// This exit code is used by the Windows task manager when it kills a
+// process. It's value is obviously not that unique, and it's
+// surprising to me that the task manager uses this value, but it
+// seems to be common practice on Windows to test for it as an
+// indication that the task manager has killed something if the
+// process goes away.
+const DWORD kProcessKilledExitCode = 1;
+
// HeapSetInformation function pointer.
typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
@@ -105,10 +119,10 @@ bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE |
- PROCESS_TERMINATE |
- PROCESS_QUERY_INFORMATION |
- PROCESS_VM_READ |
- SYNCHRONIZE,
+ PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION |
+ PROCESS_VM_READ |
+ SYNCHRONIZE,
FALSE, pid);
if (result == INVALID_HANDLE_VALUE)
@@ -394,22 +408,33 @@ bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
return result;
}
-bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
- DWORD exitcode = 0;
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
+ DWORD tmp_exit_code = 0;
- if (!::GetExitCodeProcess(handle, &exitcode)) {
+ if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
NOTREACHED();
- // Assume the child has exited.
- if (child_exited)
- *child_exited = true;
- return false;
+ if (exit_code) {
+ // This really is a random number. We haven't received any
+ // information about the exit code, presumably because this
+ // process doesn't have permission to get the exit code, or
+ // because of some other cause for GetExitCodeProcess to fail
+ // (MSDN docs don't give the possible failure error codes for
+ // this function, so it could be anything). But we don't want
+ // to leave exit_code uninitialized, since that could cause
+ // random interpretations of the exit code. So we assume it
+ // terminated "normally" in this case.
+ *exit_code = kNormalTerminationExitCode;
+ }
+ // Assume the child has exited normally if we can't get the exit
+ // code.
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
}
- if (exitcode == STILL_ACTIVE) {
+ if (tmp_exit_code == STILL_ACTIVE) {
DWORD wait_result = WaitForSingleObject(handle, 0);
if (wait_result == WAIT_TIMEOUT) {
- if (child_exited)
- *child_exited = false;
- return false;
+ if (exit_code)
+ *exit_code = wait_result;
+ return TERMINATION_STATUS_STILL_RUNNING;
}
DCHECK_EQ(WAIT_OBJECT_0, wait_result);
@@ -417,27 +442,24 @@ bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
// Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
NOTREACHED();
- return false;
+ return TERMINATION_STATUS_ABNORMAL_TERMINATION;
}
- // We're sure the child has exited.
- if (child_exited)
- *child_exited = true;
+ if (exit_code)
+ *exit_code = tmp_exit_code;
- // Warning, this is not generic code; it heavily depends on the way
- // the rest of the code kills a process.
-
- if (exitcode == PROCESS_END_NORMAL_TERMINATION ||
- exitcode == PROCESS_END_KILLED_BY_USER ||
- exitcode == PROCESS_END_PROCESS_WAS_HUNG ||
- exitcode == 0xC0000354 || // STATUS_DEBUGGER_INACTIVE.
- exitcode == 0xC000013A || // Control-C/end session.
- exitcode == 0x40010004) { // Debugger terminated process/end session.
- return false;
+ switch (tmp_exit_code) {
+ case kNormalTerminationExitCode:
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE.
+ case kKeyboardInterruptExitCode: // Control-C/end session.
+ case kDebuggerTerminatedExitCode: // Debugger terminated process.
+ case kProcessKilledExitCode: // Task manager kill.
+ return TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ default:
+ // All other exit codes indicate crashes.
+ return TERMINATION_STATUS_PROCESS_CRASHED;
}
-
- // All other exit codes indicate crashes.
- return true;
}
bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
diff --git a/chrome/browser/browser_child_process_host.cc b/chrome/browser/browser_child_process_host.cc
index 6ad0c44..6b7d47b 100644
--- a/chrome/browser/browser_child_process_host.cc
+++ b/chrome/browser/browser_child_process_host.cc
@@ -141,18 +141,34 @@ void BrowserChildProcessHost::Notify(NotificationType type) {
BrowserThread::UI, FROM_HERE, new ChildNotificationTask(type, this));
}
-bool BrowserChildProcessHost::DidChildCrash() {
- return child_process_->DidProcessCrash();
+base::TerminationStatus BrowserChildProcessHost::GetChildTerminationStatus(
+ int* exit_code) {
+ return child_process_->GetChildTerminationStatus(exit_code);
}
void BrowserChildProcessHost::OnChildDied() {
if (handle() != base::kNullProcessHandle) {
- bool did_crash = DidChildCrash();
- if (did_crash) {
- OnProcessCrashed();
- // Report that this child process crashed.
- Notify(NotificationType::CHILD_PROCESS_CRASHED);
- UMA_HISTOGRAM_COUNTS("ChildProcess.Crashes", this->type());
+ int exit_code;
+ base::TerminationStatus status = GetChildTerminationStatus(&exit_code);
+ switch (status) {
+ case base::TERMINATION_STATUS_PROCESS_CRASHED: {
+ OnProcessCrashed(exit_code);
+
+ // Report that this child process crashed.
+ Notify(NotificationType::CHILD_PROCESS_CRASHED);
+ UMA_HISTOGRAM_COUNTS("ChildProcess.Crashes", this->type());
+ break;
+ }
+ case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: {
+ OnProcessWasKilled(exit_code);
+
+ // Report that this child process was killed.
+ Notify(NotificationType::CHILD_PROCESS_WAS_KILLED);
+ UMA_HISTOGRAM_COUNTS("ChildProcess.Kills", this->type());
+ break;
+ }
+ default:
+ break;
}
// Notify in the main loop of the disconnection.
Notify(NotificationType::CHILD_PROCESS_HOST_DISCONNECTED);
diff --git a/chrome/browser/browser_child_process_host.h b/chrome/browser/browser_child_process_host.h
index f70c035..b7bd39a 100644
--- a/chrome/browser/browser_child_process_host.h
+++ b/chrome/browser/browser_child_process_host.h
@@ -78,12 +78,25 @@ class BrowserChildProcessHost : public ResourceDispatcherHost::Receiver,
base::ProcessHandle GetChildProcessHandle() const;
// ChildProcessLauncher::Client implementation.
- virtual void OnProcessLaunched() { }
+ virtual void OnProcessLaunched() {}
// Derived classes can override this to know if the process crashed.
- virtual void OnProcessCrashed() {}
-
- virtual bool DidChildCrash();
+ // |exit_code| is the status returned when the process crashed (for
+ // posix, as returned from waitpid(), for Windows, as returned from
+ // GetExitCodeProcess()).
+ virtual void OnProcessCrashed(int exit_code) {}
+
+ // Derived classes can override this to know if the process was
+ // killed. |exit_code| is the status returned when the process
+ // was killed (for posix, as returned from waitpid(), for Windows,
+ // as returned from GetExitCodeProcess()).
+ virtual void OnProcessWasKilled(int exit_code) {}
+
+ // Returns the termination status of a child. |exit_code| is the
+ // status returned when the process exited (for posix, as returned
+ // from waitpid(), for Windows, as returned from
+ // GetExitCodeProcess()). |exit_code| may be NULL.
+ virtual base::TerminationStatus GetChildTerminationStatus(int* exit_code);
// Overrides from ChildProcessHost
virtual void OnChildDied();
@@ -111,4 +124,3 @@ class BrowserChildProcessHost : public ResourceDispatcherHost::Receiver,
};
#endif // CHROME_BROWSER_BROWSER_CHILD_PROCESS_HOST_H_
-
diff --git a/chrome/browser/child_process_launcher.cc b/chrome/browser/child_process_launcher.cc
index a4a610a..8ba6941 100644
--- a/chrome/browser/child_process_launcher.cc
+++ b/chrome/browser/child_process_launcher.cc
@@ -311,28 +311,29 @@ base::ProcessHandle ChildProcessLauncher::GetHandle() {
return context_->process_.handle();
}
-bool ChildProcessLauncher::DidProcessCrash() {
- bool did_crash, child_exited;
+base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
+ int* exit_code) {
+ base::TerminationStatus status;
base::ProcessHandle handle = context_->process_.handle();
#if defined(OS_LINUX)
if (context_->zygote_) {
- did_crash = ZygoteHost::GetInstance()->DidProcessCrash(handle,
- &child_exited);
+ status = ZygoteHost::GetInstance()->GetTerminationStatus(handle, exit_code);
} else
#endif
{
- did_crash = base::DidProcessCrash(&child_exited, handle);
+ status = base::GetTerminationStatus(handle, exit_code);
}
- // POSIX: If the process crashed, then the kernel closed the socket for it
- // and so the child has already died by the time we get here. Since
- // DidProcessCrash called waitpid with WNOHANG, it'll reap the process.
- // However, if DidProcessCrash didn't reap the child, we'll need to in
+ // POSIX: If the process crashed, then the kernel closed the socket
+ // for it and so the child has already died by the time we get
+ // here. Since GetTerminationStatus called waitpid with WNOHANG,
+ // it'll reap the process. However, if GetTerminationStatus didn't
+ // reap the child (because it was still running), we'll need to
// Terminate via ProcessWatcher. So we can't close the handle here.
- if (child_exited)
+ if (status != base::TERMINATION_STATUS_STILL_RUNNING)
context_->process_.Close();
- return did_crash;
+ return status;
}
void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
diff --git a/chrome/browser/child_process_launcher.h b/chrome/browser/child_process_launcher.h
index 95fb6ea..a3add53 100644
--- a/chrome/browser/child_process_launcher.h
+++ b/chrome/browser/child_process_launcher.h
@@ -50,8 +50,11 @@ class ChildProcessLauncher {
// Getter for the process handle. Only call after the process has started.
base::ProcessHandle GetHandle();
- // Call this when the process exits to know if a process crashed or not.
- bool DidProcessCrash();
+ // Call this when the child process exits to know what happened to
+ // it. |exit_code| is the exit code of the process if it exited
+ // (e.g. status from waitpid if on posix, from GetExitCodeProcess on
+ // Windows). |exit_code| may be NULL.
+ base::TerminationStatus GetChildTerminationStatus(int* exit_code);
// Changes whether the process runs in the background or not. Only call
// this after the process has started.
diff --git a/chrome/browser/child_process_security_policy_browsertest.cc b/chrome/browser/child_process_security_policy_browsertest.cc
index bedac81..4bcd3dc 100644
--- a/chrome/browser/child_process_security_policy_browsertest.cc
+++ b/chrome/browser/child_process_security_policy_browsertest.cc
@@ -11,6 +11,7 @@
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/common/result_codes.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -43,7 +44,7 @@ IN_PROC_BROWSER_TEST_F(ChildProcessSecurityPolicyInProcessBrowserTest, NoLeak) {
TabContents* tab = browser()->GetTabContentsAt(0);
ASSERT_TRUE(tab != NULL);
base::KillProcess(tab->GetRenderProcessHost()->GetHandle(),
- base::PROCESS_END_KILLED_BY_USER, true);
+ ResultCodes::KILLED, true);
tab->controller().Reload(true);
EXPECT_EQ(
diff --git a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
index 50144e1..1914ec7 100644
--- a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
+++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
@@ -14,6 +14,7 @@
#include "chrome/browser/tab_contents/infobar_delegate.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/common/result_codes.h"
#include "chrome/test/ui_test_utils.h"
class ExtensionCrashRecoveryTest : public ExtensionBrowserTest {
@@ -61,8 +62,7 @@ class ExtensionCrashRecoveryTest : public ExtensionBrowserTest {
RenderProcessHost* extension_rph =
extension_host->render_view_host()->process();
- base::KillProcess(extension_rph->GetHandle(),
- base::PROCESS_END_KILLED_BY_USER, false);
+ base::KillProcess(extension_rph->GetHandle(), ResultCodes::KILLED, false);
ASSERT_TRUE(WaitForExtensionCrash(extension_id));
ASSERT_FALSE(
GetExtensionProcessManager()->GetBackgroundHostForExtension(extension));
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index cb6bd6b..1051399 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -288,7 +288,9 @@ void ExtensionHost::ClearInspectorSettings() {
RenderViewHostDelegateHelper::ClearInspectorSettings(profile());
}
-void ExtensionHost::RenderViewGone(RenderViewHost* render_view_host) {
+void ExtensionHost::RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code) {
// During browser shutdown, we may use sudden termination on an extension
// process, so it is expected to lose our connection to the render view.
// Do nothing.
diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h
index 935b76c..801bdab 100644
--- a/chrome/browser/extensions/extension_host.h
+++ b/chrome/browser/extensions/extension_host.h
@@ -112,7 +112,9 @@ class ExtensionHost : public RenderViewHostDelegate,
virtual ViewType::Type GetRenderViewType() const;
virtual FileSelect* GetFileSelectDelegate();
virtual int GetBrowserWindowID() const;
- virtual void RenderViewGone(RenderViewHost* render_view_host);
+ virtual void RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code);
virtual void DidNavigate(RenderViewHost* render_view_host,
const ViewHostMsg_FrameNavigate_Params& params);
virtual void DidStopLoading();
diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.cc b/chrome/browser/extensions/sandboxed_extension_unpacker.cc
index faf236b..fc01884 100644
--- a/chrome/browser/extensions/sandboxed_extension_unpacker.cc
+++ b/chrome/browser/extensions/sandboxed_extension_unpacker.cc
@@ -181,7 +181,7 @@ void SandboxedExtensionUnpacker::OnUnpackExtensionFailed(
ReportFailure(error);
}
-void SandboxedExtensionUnpacker::OnProcessCrashed() {
+void SandboxedExtensionUnpacker::OnProcessCrashed(int exit_code) {
// Don't report crashes if they happen after we got a response.
if (got_response_)
return;
diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.h b/chrome/browser/extensions/sandboxed_extension_unpacker.h
index acb4081..e47b26c 100644
--- a/chrome/browser/extensions/sandboxed_extension_unpacker.h
+++ b/chrome/browser/extensions/sandboxed_extension_unpacker.h
@@ -124,7 +124,7 @@ class SandboxedExtensionUnpacker : public UtilityProcessHost::Client {
// SandboxedExtensionUnpacker
virtual void OnUnpackExtensionSucceeded(const DictionaryValue& manifest);
virtual void OnUnpackExtensionFailed(const std::string& error_message);
- virtual void OnProcessCrashed();
+ virtual void OnProcessCrashed(int exit_code);
void ReportFailure(const std::string& message);
void ReportSuccess();
diff --git a/chrome/browser/gpu_process_host.cc b/chrome/browser/gpu_process_host.cc
index af4229a..7ddbdf1 100644
--- a/chrome/browser/gpu_process_host.cc
+++ b/chrome/browser/gpu_process_host.cc
@@ -495,12 +495,12 @@ void GpuProcessHost::OnChildDied() {
BrowserChildProcessHost::OnChildDied();
}
-void GpuProcessHost::OnProcessCrashed() {
+void GpuProcessHost::OnProcessCrashed(int exit_code) {
if (++g_gpu_crash_count >= kGpuMaxCrashCount) {
// The gpu process is too unstable to use. Disable it for current session.
RenderViewHostDelegateHelper::set_gpu_enabled(false);
}
- BrowserChildProcessHost::OnProcessCrashed();
+ BrowserChildProcessHost::OnProcessCrashed(exit_code);
}
bool GpuProcessHost::CanLaunchGpuProcess() const {
diff --git a/chrome/browser/gpu_process_host.h b/chrome/browser/gpu_process_host.h
index a2b3c9e..c60c307 100644
--- a/chrome/browser/gpu_process_host.h
+++ b/chrome/browser/gpu_process_host.h
@@ -113,7 +113,7 @@ class GpuProcessHost : public BrowserChildProcessHost, public NonThreadSafe {
virtual bool CanShutdown();
virtual void OnChildDied();
- virtual void OnProcessCrashed();
+ virtual void OnProcessCrashed(int exit_code);
bool CanLaunchGpuProcess() const;
bool LaunchGpuProcess();
diff --git a/chrome/browser/importer/importer.cc b/chrome/browser/importer/importer.cc
index 861db31..6cf596b 100644
--- a/chrome/browser/importer/importer.cc
+++ b/chrome/browser/importer/importer.cc
@@ -460,7 +460,7 @@ void ExternalProcessImporterClient::NotifyItemFinishedOnIOThread(
profile_import_process_host_->ReportImportItemFinished(import_item);
}
-void ExternalProcessImporterClient::OnProcessCrashed() {
+void ExternalProcessImporterClient::OnProcessCrashed(int exit_code) {
if (cancelled_)
return;
diff --git a/chrome/browser/importer/importer.h b/chrome/browser/importer/importer.h
index 515c58c..bc25850 100644
--- a/chrome/browser/importer/importer.h
+++ b/chrome/browser/importer/importer.h
@@ -344,7 +344,7 @@ class ExternalProcessImporterClient
void NotifyItemFinishedOnIOThread(importer::ImportItem import_item);
// Cancel import on process crash.
- virtual void OnProcessCrashed();
+ virtual void OnProcessCrashed(int exit_code);
// Notifies the importerhost that import has finished, and calls Release().
void Cleanup();
diff --git a/chrome/browser/in_process_webkit/indexed_db_key_utility_client.cc b/chrome/browser/in_process_webkit/indexed_db_key_utility_client.cc
index 920da34..e287c2c 100644
--- a/chrome/browser/in_process_webkit/indexed_db_key_utility_client.cc
+++ b/chrome/browser/in_process_webkit/indexed_db_key_utility_client.cc
@@ -148,7 +148,7 @@ IndexedDBKeyUtilityClient::Client::Client(IndexedDBKeyUtilityClient* parent)
: parent_(parent) {
}
-void IndexedDBKeyUtilityClient::Client::OnProcessCrashed() {
+void IndexedDBKeyUtilityClient::Client::OnProcessCrashed(int exit_code) {
if (parent_->state_ == STATE_CREATING_KEYS)
parent_->FinishCreatingKeys();
}
diff --git a/chrome/browser/in_process_webkit/indexed_db_key_utility_client.h b/chrome/browser/in_process_webkit/indexed_db_key_utility_client.h
index ee03ed9..6b8aec1 100644
--- a/chrome/browser/in_process_webkit/indexed_db_key_utility_client.h
+++ b/chrome/browser/in_process_webkit/indexed_db_key_utility_client.h
@@ -45,7 +45,7 @@ class IndexedDBKeyUtilityClient
explicit Client(IndexedDBKeyUtilityClient* parent);
// UtilityProcessHost::Client
- virtual void OnProcessCrashed();
+ virtual void OnProcessCrashed(int exit_code);
virtual void OnIDBKeysFromValuesAndKeyPathSucceeded(
int id, const std::vector<IndexedDBKey>& keys);
virtual void OnIDBKeysFromValuesAndKeyPathFailed(int id);
diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc
index 092548b..3041549 100644
--- a/chrome/browser/metrics/metrics_service.cc
+++ b/chrome/browser/metrics/metrics_service.cc
@@ -606,11 +606,13 @@ void MetricsService::Observe(NotificationType type,
LogLoadStarted();
break;
- case NotificationType::RENDERER_PROCESS_CLOSED:
- {
+ case NotificationType::RENDERER_PROCESS_CLOSED: {
RenderProcessHost::RendererClosedDetails* process_details =
Details<RenderProcessHost::RendererClosedDetails>(details).ptr();
- if (process_details->did_crash) {
+ if (process_details->status ==
+ base::TERMINATION_STATUS_PROCESS_CRASHED ||
+ process_details->status ==
+ base::TERMINATION_STATUS_ABNORMAL_TERMINATION) {
if (process_details->was_extension_renderer) {
LogExtensionRendererCrash();
} else {
diff --git a/chrome/browser/nacl_host/nacl_process_host.cc b/chrome/browser/nacl_host/nacl_process_host.cc
index ded7390..df661e4 100644
--- a/chrome/browser/nacl_host/nacl_process_host.cc
+++ b/chrome/browser/nacl_host/nacl_process_host.cc
@@ -163,10 +163,11 @@ void NaClProcessHost::OnProcessLaunchedByBroker(base::ProcessHandle handle) {
OnProcessLaunched();
}
-bool NaClProcessHost::DidChildCrash() {
+base::TerminationStatus NaClProcessHost::GetChildTerminationStatus(
+ int* exit_code) {
if (running_on_wow64_)
- return base::DidProcessCrash(NULL, handle());
- return BrowserChildProcessHost::DidChildCrash();
+ return base::GetTerminationStatus(handle(), exit_code);
+ return BrowserChildProcessHost::GetChildTerminationStatus(exit_code);
}
void NaClProcessHost::OnChildDied() {
diff --git a/chrome/browser/nacl_host/nacl_process_host.h b/chrome/browser/nacl_host/nacl_process_host.h
index 3b35bf7..5d6cc35 100644
--- a/chrome/browser/nacl_host/nacl_process_host.h
+++ b/chrome/browser/nacl_host/nacl_process_host.h
@@ -37,7 +37,7 @@ class NaClProcessHost : public BrowserChildProcessHost {
void OnProcessLaunchedByBroker(base::ProcessHandle handle);
protected:
- virtual bool DidChildCrash();
+ virtual base::TerminationStatus GetChildTerminationStatus(int* exit_code);
virtual void OnChildDied();
private:
diff --git a/chrome/browser/notifications/balloon_host.cc b/chrome/browser/notifications/balloon_host.cc
index 8c778d0..13b092f 100644
--- a/chrome/browser/notifications/balloon_host.cc
+++ b/chrome/browser/notifications/balloon_host.cc
@@ -91,7 +91,8 @@ const string16& BalloonHost::GetSource() const {
WebPreferences BalloonHost::GetWebkitPrefs() {
WebPreferences web_prefs =
- RenderViewHostDelegateHelper::GetWebkitPrefs(GetProfile(), enable_dom_ui_);
+ RenderViewHostDelegateHelper::GetWebkitPrefs(GetProfile(),
+ enable_dom_ui_);
web_prefs.allow_scripts_to_close_windows = true;
return web_prefs;
}
@@ -132,7 +133,9 @@ void BalloonHost::RenderViewReady(RenderViewHost* render_view_host) {
Source<BalloonHost>(this), NotificationService::NoDetails());
}
-void BalloonHost::RenderViewGone(RenderViewHost* render_view_host) {
+void BalloonHost::RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code) {
Close(render_view_host);
}
diff --git a/chrome/browser/notifications/balloon_host.h b/chrome/browser/notifications/balloon_host.h
index c5a88e7..c3c058b 100644
--- a/chrome/browser/notifications/balloon_host.h
+++ b/chrome/browser/notifications/balloon_host.h
@@ -49,7 +49,9 @@ class BalloonHost : public RenderViewHostDelegate,
virtual void Close(RenderViewHost* render_view_host);
virtual void RenderViewCreated(RenderViewHost* render_view_host);
virtual void RenderViewReady(RenderViewHost* render_view_host);
- virtual void RenderViewGone(RenderViewHost* render_view_host);
+ virtual void RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code);
virtual void UpdateTitle(RenderViewHost* render_view_host,
int32 page_id, const std::wstring& title) {}
virtual int GetBrowserWindowID() const;
diff --git a/chrome/browser/profile_import_process_host.cc b/chrome/browser/profile_import_process_host.cc
index 26e4ec1..45ec4f6 100644
--- a/chrome/browser/profile_import_process_host.cc
+++ b/chrome/browser/profile_import_process_host.cc
@@ -127,12 +127,13 @@ void ProfileImportProcessHost::OnMessageReceived(const IPC::Message& message) {
message));
}
-void ProfileImportProcessHost::OnProcessCrashed() {
+void ProfileImportProcessHost::OnProcessCrashed(int exit_code) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
BrowserThread::PostTask(
thread_id_, FROM_HERE,
NewRunnableMethod(import_process_client_.get(),
- &ImportProcessClient::OnProcessCrashed));
+ &ImportProcessClient::OnProcessCrashed,
+ exit_code));
}
bool ProfileImportProcessHost::CanShutdown() {
diff --git a/chrome/browser/profile_import_process_host.h b/chrome/browser/profile_import_process_host.h
index a6129a1..fabb947 100644
--- a/chrome/browser/profile_import_process_host.h
+++ b/chrome/browser/profile_import_process_host.h
@@ -40,7 +40,7 @@ class ProfileImportProcessHost : public BrowserChildProcessHost {
// These methods are used by the ProfileImportProcessHost to pass messages
// received from the external process back to the ImportProcessClient in
// ImporterHost.
- virtual void OnProcessCrashed() {}
+ virtual void OnProcessCrashed(int exit_status) {}
virtual void OnImportStart() {}
virtual void OnImportFinished(bool succeeded, std::string error_msg) {}
virtual void OnImportItemStart(int item) {}
@@ -126,7 +126,7 @@ class ProfileImportProcessHost : public BrowserChildProcessHost {
virtual void OnMessageReceived(const IPC::Message& message);
// Overridden from BrowserChildProcessHost:
- virtual void OnProcessCrashed();
+ virtual void OnProcessCrashed(int exit_code);
virtual bool CanShutdown();
virtual URLRequestContext* GetRequestContext(
uint32 request_id,
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc
index 98b642b..e2c8780 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -989,16 +989,26 @@ void BrowserRenderProcessHost::OnChannelError() {
if (!channel_.get())
return;
- // NULL in single process mode or if fast termination happened.
- bool did_crash =
- child_process_.get() ? child_process_->DidProcessCrash() : false;
-
- if (did_crash) {
+ // child_process_ can be NULL in single process mode or if fast
+ // termination happened.
+ int exit_code = 0;
+ base::TerminationStatus status =
+ child_process_.get() ?
+ child_process_->GetChildTerminationStatus(&exit_code) :
+ base::TERMINATION_STATUS_NORMAL_TERMINATION;
+
+ if (status == base::TERMINATION_STATUS_PROCESS_CRASHED ||
+ status == base::TERMINATION_STATUS_ABNORMAL_TERMINATION) {
UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildCrashes",
extension_process_ ? 2 : 1);
}
- RendererClosedDetails details(did_crash, extension_process_);
+ if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED) {
+ UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildKills",
+ extension_process_ ? 2 : 1);
+ }
+
+ RendererClosedDetails details(status, exit_code, extension_process_);
NotificationService::current()->Notify(
NotificationType::RENDERER_PROCESS_CLOSED,
Source<RenderProcessHost>(this),
@@ -1011,7 +1021,9 @@ void BrowserRenderProcessHost::OnChannelError() {
IDMap<IPC::Channel::Listener>::iterator iter(&listeners_);
while (!iter.IsAtEnd()) {
iter.GetCurrentValue()->OnMessageReceived(
- ViewHostMsg_RenderViewGone(iter.GetCurrentKey()));
+ ViewHostMsg_RenderViewGone(iter.GetCurrentKey(),
+ static_cast<int>(status),
+ exit_code));
iter.Advance();
}
diff --git a/chrome/browser/renderer_host/render_process_host.h b/chrome/browser/renderer_host/render_process_host.h
index a5fd378..c9a8abc 100644
--- a/chrome/browser/renderer_host/render_process_host.h
+++ b/chrome/browser/renderer_host/render_process_host.h
@@ -11,6 +11,7 @@
#include "app/surface/transport_dib.h"
#include "base/id_map.h"
#include "base/process.h"
+#include "base/process_util.h"
#include "base/scoped_ptr.h"
#include "base/time.h"
#include "chrome/common/visitedlink_common.h"
@@ -48,11 +49,15 @@ class RenderProcessHost : public IPC::Channel::Sender,
// Details for RENDERER_PROCESS_CLOSED notifications.
struct RendererClosedDetails {
- RendererClosedDetails(bool did_crash, bool was_extension_renderer) {
- this->did_crash = did_crash;
+ RendererClosedDetails(base::TerminationStatus status,
+ int exit_code,
+ bool was_extension_renderer) {
+ this->status = status;
+ this->exit_code = exit_code;
this->was_extension_renderer = was_extension_renderer;
}
- bool did_crash;
+ base::TerminationStatus status;
+ int exit_code;
bool was_extension_renderer;
};
diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc
index 1b1cf0b..dc358b6 100644
--- a/chrome/browser/renderer_host/render_view_host.cc
+++ b/chrome/browser/renderer_host/render_view_host.cc
@@ -140,7 +140,8 @@ RenderViewHost::RenderViewHost(SiteInstance* instance,
session_storage_namespace_(session_storage),
is_extension_process_(false),
autofill_query_id_(0),
- save_accessibility_tree_for_testing_(false) {
+ save_accessibility_tree_for_testing_(false),
+ render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) {
if (!session_storage_namespace_) {
session_storage_namespace_ =
new SessionStorageNamespace(process()->profile());
@@ -998,15 +999,23 @@ void RenderViewHost::OnMsgRunModal(IPC::Message* reply_msg) {
}
void RenderViewHost::OnMsgRenderViewReady() {
+ render_view_termination_status_ = base::TERMINATION_STATUS_STILL_RUNNING;
WasResized();
delegate_->RenderViewReady(this);
}
-void RenderViewHost::OnMsgRenderViewGone() {
+void RenderViewHost::OnMsgRenderViewGone(int status, int exit_code) {
+ // Keep the termination status so we can get at it later when we
+ // need to know why it died.
+ render_view_termination_status_ =
+ static_cast<base::TerminationStatus>(status);
+
// Our base class RenderWidgetHost needs to reset some stuff.
- RendererExited();
+ RendererExited(render_view_termination_status_, exit_code);
- delegate_->RenderViewGone(this);
+ delegate_->RenderViewGone(this,
+ static_cast<base::TerminationStatus>(status),
+ exit_code);
}
// Called when the renderer navigates. For every frame loaded, we'll get this
diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h
index b5399bf..81223ea 100644
--- a/chrome/browser/renderer_host/render_view_host.h
+++ b/chrome/browser/renderer_host/render_view_host.h
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+#include "base/process_util.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/renderer_host/render_widget_host.h"
#include "chrome/browser/ui/find_bar/find_bar_controller.h"
@@ -127,6 +128,10 @@ class RenderViewHost : public RenderWidgetHost {
// because it is overridden by TestRenderViewHost.
virtual bool IsRenderViewLive() const;
+ base::TerminationStatus render_view_termination_status() const {
+ return render_view_termination_status_;
+ }
+
// Send the renderer process the current preferences supplied by the
// RenderViewHostDelegate.
void SyncRendererPrefs();
@@ -550,7 +555,7 @@ class RenderViewHost : public RenderWidgetHost {
void OnMsgShowFullscreenWidget(int route_id);
void OnMsgRunModal(IPC::Message* reply_msg);
void OnMsgRenderViewReady();
- void OnMsgRenderViewGone();
+ void OnMsgRenderViewGone(int status, int error_code);
void OnMsgNavigate(const IPC::Message& msg);
void OnMsgUpdateState(int32 page_id,
const std::string& state);
@@ -819,6 +824,9 @@ class RenderViewHost : public RenderWidgetHost {
// The most recently received accessibility tree - for unit testing only.
webkit_glue::WebAccessibility accessibility_tree_;
+ // The termination status of the last render view that terminated.
+ base::TerminationStatus render_view_termination_status_;
+
DISALLOW_COPY_AND_ASSIGN(RenderViewHost);
};
diff --git a/chrome/browser/renderer_host/render_view_host_delegate.h b/chrome/browser/renderer_host/render_view_host_delegate.h
index a566062..89f7d94 100644
--- a/chrome/browser/renderer_host/render_view_host_delegate.h
+++ b/chrome/browser/renderer_host/render_view_host_delegate.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/process_util.h"
#include "base/ref_counted.h"
#include "base/string16.h"
#include "chrome/common/content_settings_types.h"
@@ -711,7 +712,9 @@ class RenderViewHostDelegate {
virtual void RenderViewReady(RenderViewHost* render_view_host) {}
// The RenderView died somehow (crashed or was killed by the user).
- virtual void RenderViewGone(RenderViewHost* render_view_host) {}
+ virtual void RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code) {}
// The RenderView is going to be deleted. This is called when each
// RenderView is going to be destroyed
diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc
index 3fa5610..3c71440b 100644
--- a/chrome/browser/renderer_host/render_widget_host.cc
+++ b/chrome/browser/renderer_host/render_widget_host.cc
@@ -643,7 +643,8 @@ void RenderWidgetHost::ForwardTouchEvent(
}
#endif
-void RenderWidgetHost::RendererExited() {
+void RenderWidgetHost::RendererExited(base::TerminationStatus status,
+ int exit_code) {
// Clearing this flag causes us to re-create the renderer when recovering
// from a crashed renderer.
renderer_initialized_ = false;
@@ -671,7 +672,7 @@ void RenderWidgetHost::RendererExited() {
is_accelerated_compositing_active_ = false;
if (view_) {
- view_->RenderViewGone();
+ view_->RenderViewGone(status, exit_code);
view_ = NULL; // The View should be deleted by RenderViewGone.
}
@@ -777,7 +778,7 @@ void RenderWidgetHost::OnMsgRenderViewReady() {
WasResized();
}
-void RenderWidgetHost::OnMsgRenderViewGone() {
+void RenderWidgetHost::OnMsgRenderViewGone(int status, int exit_code) {
// TODO(evanm): This synchronously ends up calling "delete this".
// Is that really what we want in response to this message? I'm matching
// previous behavior of the code here.
diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h
index b0192a4..90204aa 100644
--- a/chrome/browser/renderer_host/render_widget_host.h
+++ b/chrome/browser/renderer_host/render_widget_host.h
@@ -12,6 +12,7 @@
#include "app/surface/transport_dib.h"
#include "base/gtest_prod_util.h"
+#include "base/process_util.h"
#include "base/scoped_ptr.h"
#include "base/string16.h"
#include "base/timer.h"
@@ -415,7 +416,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// Called when we receive a notification indicating that the renderer
// process has gone. This will reset our state so that our state will be
// consistent if a new renderer is created.
- void RendererExited();
+ void RendererExited(base::TerminationStatus status, int exit_code);
// Retrieves an id the renderer can use to refer to its view.
// This is used for various IPC messages, including plugins.
@@ -473,7 +474,7 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// IPC message handlers
void OnMsgRenderViewReady();
- void OnMsgRenderViewGone();
+ void OnMsgRenderViewGone(int status, int error_code);
void OnMsgClose();
void OnMsgRequestMove(const gfx::Rect& pos);
void OnMsgPaintAtSizeAck(int tag, const gfx::Size& size);
diff --git a/chrome/browser/renderer_host/render_widget_host_unittest.cc b/chrome/browser/renderer_host/render_widget_host_unittest.cc
index 72495d3..758a5bd 100644
--- a/chrome/browser/renderer_host/render_widget_host_unittest.cc
+++ b/chrome/browser/renderer_host/render_widget_host_unittest.cc
@@ -397,7 +397,7 @@ TEST_F(RenderWidgetHostTest, ResizeThenCrash) {
// resize ack logic is cleared. Must clear the view first so it doesn't get
// deleted.
host_->set_view(NULL);
- host_->RendererExited();
+ host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
EXPECT_FALSE(host_->resize_ack_pending_);
EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
diff --git a/chrome/browser/renderer_host/render_widget_host_view.h b/chrome/browser/renderer_host/render_widget_host_view.h
index af6300e..14cf129 100644
--- a/chrome/browser/renderer_host/render_widget_host_view.h
+++ b/chrome/browser/renderer_host/render_widget_host_view.h
@@ -14,6 +14,7 @@
#include <vector>
#include "app/surface/transport_dib.h"
+#include "base/process_util.h"
#include "gfx/native_widget_types.h"
#include "gfx/rect.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -152,7 +153,8 @@ class RenderWidgetHostView {
const std::vector<gfx::Rect>& copy_rects) = 0;
// Notifies the View that the renderer has ceased to exist.
- virtual void RenderViewGone() = 0;
+ virtual void RenderViewGone(base::TerminationStatus status,
+ int error_code) = 0;
// Notifies the View that the renderer will be delete soon.
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) = 0;
diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
index 78b26e2..cf3418a 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc
@@ -681,7 +681,8 @@ void RenderWidgetHostViewGtk::DidUpdateBackingStore(
}
}
-void RenderWidgetHostViewGtk::RenderViewGone() {
+void RenderWidgetHostViewGtk::RenderViewGone(base::TerminationStatus status,
+ int error_code) {
Destroy();
plugin_container_manager_.set_host_widget(NULL);
}
diff --git a/chrome/browser/renderer_host/render_widget_host_view_gtk.h b/chrome/browser/renderer_host/render_widget_host_view_gtk.h
index cd89ddb..7af66f8 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_gtk.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.h
@@ -77,7 +77,8 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView,
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects);
- virtual void RenderViewGone();
+ virtual void RenderViewGone(base::TerminationStatus status,
+ int error_code);
virtual void Destroy();
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) {}
virtual void SetTooltipText(const std::wstring& tooltip_text);
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h
index f4dfd25..46efeee 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h
@@ -200,7 +200,8 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView {
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects);
- virtual void RenderViewGone();
+ virtual void RenderViewGone(base::TerminationStatus status,
+ int error_code);
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) {};
virtual void Destroy();
virtual void SetTooltipText(const std::wstring& tooltip_text);
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
index f88025e..842bf9a 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
@@ -794,7 +794,8 @@ void RenderWidgetHostViewMac::DidUpdateBackingStore(
HandleDelayedGpuViewHiding();
}
-void RenderWidgetHostViewMac::RenderViewGone() {
+void RenderWidgetHostViewMac::RenderViewGone(base::TerminationStatus status,
+ int error_code) {
// TODO(darin): keep this around, and draw sad-tab into it.
UpdateCursorIfOverSelf();
Destroy();
diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.cc b/chrome/browser/renderer_host/render_widget_host_view_views.cc
index cdc2e38..ea808f0 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_views.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_views.cc
@@ -131,7 +131,8 @@ RenderWidgetHostViewViews::RenderWidgetHostViewViews(RenderWidgetHost* host)
}
RenderWidgetHostViewViews::~RenderWidgetHostViewViews() {
- RenderViewGone();
+ RenderViewGone(base::TERMINATION_STATUS_NORMAL_TERMINATION,
+ ResultCodes::NORMAL_EXIT);
}
void RenderWidgetHostViewViews::InitAsChild() {
@@ -286,7 +287,8 @@ void RenderWidgetHostViewViews::DidUpdateBackingStore(
}
}
-void RenderWidgetHostViewViews::RenderViewGone() {
+void RenderWidgetHostViewViews::RenderViewGone(base::TerminationStatus status,
+ int error_code) {
GetRenderWidgetHost()->ViewDestroyed();
Destroy();
}
diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.h b/chrome/browser/renderer_host/render_widget_host_view_views.h
index a9a918f..9c5c8cb 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_views.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_views.h
@@ -60,7 +60,8 @@ class RenderWidgetHostViewViews : public RenderWidgetHostView,
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects);
- virtual void RenderViewGone();
+ virtual void RenderViewGone(base::TerminationStatus status,
+ int error_code);
virtual void Destroy();
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) {}
virtual void SetTooltipText(const std::wstring& tooltip_text);
diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.cc b/chrome/browser/renderer_host/render_widget_host_view_win.cc
index a665794..f5afe70 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_win.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_win.cc
@@ -722,7 +722,8 @@ void RenderWidgetHostViewWin::DidUpdateBackingStore(
Redraw();
}
-void RenderWidgetHostViewWin::RenderViewGone() {
+void RenderWidgetHostViewWin::RenderViewGone(base::TerminationStatus status,
+ int error_code) {
// TODO(darin): keep this around, and draw sad-tab into it.
UpdateCursorIfOverSelf();
being_destroyed_ = true;
diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.h b/chrome/browser/renderer_host/render_widget_host_view_win.h
index 282dfe9..1f67814 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_win.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_win.h
@@ -148,7 +148,8 @@ class RenderWidgetHostViewWin
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& copy_rects);
- virtual void RenderViewGone();
+ virtual void RenderViewGone(base::TerminationStatus status,
+ int error_code);
virtual void WillWmDestroy(); // called by TabContents before DestroyWindow
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh);
virtual void Destroy();
diff --git a/chrome/browser/renderer_host/test/test_render_view_host.h b/chrome/browser/renderer_host/test/test_render_view_host.h
index a2ff8f7..7aa9638 100644
--- a/chrome/browser/renderer_host/test/test_render_view_host.h
+++ b/chrome/browser/renderer_host/test/test_render_view_host.h
@@ -83,7 +83,8 @@ class TestRenderWidgetHostView : public RenderWidgetHostView {
virtual void DidUpdateBackingStore(
const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
const std::vector<gfx::Rect>& rects) {}
- virtual void RenderViewGone() { delete this; }
+ virtual void RenderViewGone(base::TerminationStatus status,
+ int error_code) { delete this; }
virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) { }
virtual void Destroy() {}
virtual void PrepareToDestroy() {}
diff --git a/chrome/browser/renderer_host/test/web_cache_manager_browsertest.cc b/chrome/browser/renderer_host/test/web_cache_manager_browsertest.cc
index 06ce085..3681c70 100644
--- a/chrome/browser/renderer_host/test/web_cache_manager_browsertest.cc
+++ b/chrome/browser/renderer_host/test/web_cache_manager_browsertest.cc
@@ -10,6 +10,7 @@
#include "chrome/browser/renderer_host/web_cache_manager.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/common/result_codes.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -33,7 +34,7 @@ IN_PROC_BROWSER_TEST_F(WebCacheManagerBrowserTest, FLAKY_CrashOnceOnly) {
TabContents* tab = browser()->GetTabContentsAt(0);
ASSERT_TRUE(tab != NULL);
base::KillProcess(tab->GetRenderProcessHost()->GetHandle(),
- base::PROCESS_END_KILLED_BY_USER, true);
+ ResultCodes::KILLED, true);
browser()->SelectTabContentsAt(0, true);
browser()->NewTab();
diff --git a/chrome/browser/tab_contents/interstitial_page.cc b/chrome/browser/tab_contents/interstitial_page.cc
index d3c0429..bc9920b 100644
--- a/chrome/browser/tab_contents/interstitial_page.cc
+++ b/chrome/browser/tab_contents/interstitial_page.cc
@@ -333,7 +333,9 @@ const GURL& InterstitialPage::GetURL() const {
return url_;
}
-void InterstitialPage::RenderViewGone(RenderViewHost* render_view_host) {
+void InterstitialPage::RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code) {
// Our renderer died. This should not happen in normal cases.
// Just dismiss the interstitial.
DontProceed();
diff --git a/chrome/browser/tab_contents/interstitial_page.h b/chrome/browser/tab_contents/interstitial_page.h
index 50ede04..2faad77 100644
--- a/chrome/browser/tab_contents/interstitial_page.h
+++ b/chrome/browser/tab_contents/interstitial_page.h
@@ -9,6 +9,7 @@
#include <map>
#include <string>
+#include "base/process_util.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/renderer_host/render_view_host_delegate.h"
#include "chrome/common/notification_observer.h"
@@ -124,7 +125,9 @@ class InterstitialPage : public NotificationObserver,
// RenderViewHostDelegate implementation:
virtual View* GetViewDelegate();
virtual const GURL& GetURL() const;
- virtual void RenderViewGone(RenderViewHost* render_view_host);
+ virtual void RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code);
virtual void DidNavigate(RenderViewHost* render_view_host,
const ViewHostMsg_FrameNavigate_Params& params);
virtual void UpdateTitle(RenderViewHost* render_view_host,
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index 34ec892..f95634f 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -336,7 +336,8 @@ TabContents::TabContents(Profile* profile,
bookmark_drag_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(fav_icon_helper_(this)),
is_loading_(false),
- is_crashed_(false),
+ crashed_status_(base::TERMINATION_STATUS_STILL_RUNNING),
+ crashed_error_code_(0),
waiting_for_response_(false),
max_page_id_(-1),
current_load_start_(),
@@ -767,11 +768,12 @@ void TabContents::RemoveNavigationObserver(WebNavigationObserver* observer) {
web_navigation_observers_.RemoveObserver(observer);
}
-void TabContents::SetIsCrashed(bool state) {
- if (state == is_crashed_)
+void TabContents::SetIsCrashed(base::TerminationStatus status, int error_code) {
+ if (status == crashed_status_)
return;
- is_crashed_ = state;
+ crashed_status_ = status;
+ crashed_error_code_ = error_code;
NotifyNavigationStateChanged(INVALIDATE_TAB);
}
@@ -2423,7 +2425,7 @@ void TabContents::RenderViewReady(RenderViewHost* rvh) {
NotifyConnected();
bool was_crashed = is_crashed();
- SetIsCrashed(false);
+ SetIsCrashed(base::TERMINATION_STATUS_STILL_RUNNING, 0);
// Restore the focus to the tab (otherwise the focus will be on the top
// window).
@@ -2433,7 +2435,9 @@ void TabContents::RenderViewReady(RenderViewHost* rvh) {
}
}
-void TabContents::RenderViewGone(RenderViewHost* rvh) {
+void TabContents::RenderViewGone(RenderViewHost* rvh,
+ base::TerminationStatus status,
+ int error_code) {
// Ask the print preview if this renderer was valuable.
if (!printing_->OnRenderViewGone(rvh))
return;
@@ -2444,7 +2448,7 @@ void TabContents::RenderViewGone(RenderViewHost* rvh) {
SetIsLoading(false, NULL);
NotifyDisconnected();
- SetIsCrashed(true);
+ SetIsCrashed(status, error_code);
// Remove all infobars.
for (int i = infobar_delegate_count() - 1; i >=0 ; --i)
@@ -3087,7 +3091,8 @@ void TabContents::DidStartLoadingFromRenderManager(
void TabContents::RenderViewGoneFromRenderManager(
RenderViewHost* render_view_host) {
- RenderViewGone(render_view_host);
+ DCHECK(crashed_status_ != base::TERMINATION_STATUS_STILL_RUNNING);
+ RenderViewGone(render_view_host, crashed_status_, crashed_error_code_);
}
void TabContents::UpdateRenderViewSizeForRenderManager() {
diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h
index 122380a..7800ee9 100644
--- a/chrome/browser/tab_contents/tab_contents.h
+++ b/chrome/browser/tab_contents/tab_contents.h
@@ -308,8 +308,14 @@ class TabContents : public PageNavigator,
// Indicates whether this tab should be considered crashed. The setter will
// also notify the delegate when the flag is changed.
- bool is_crashed() const { return is_crashed_; }
- void SetIsCrashed(bool state);
+ bool is_crashed() const {
+ return (crashed_status_ == base::TERMINATION_STATUS_PROCESS_CRASHED ||
+ crashed_status_ == base::TERMINATION_STATUS_ABNORMAL_TERMINATION ||
+ crashed_status_ == base::TERMINATION_STATUS_PROCESS_WAS_KILLED);
+ }
+ base::TerminationStatus crashed_status() const { return crashed_status_; }
+ int crashed_error_code() const { return crashed_error_code_; }
+ void SetIsCrashed(base::TerminationStatus status, int error_code);
// Call this after updating a page action to notify clients about the changes.
void PageActionStateChanged();
@@ -958,7 +964,9 @@ class TabContents : public PageNavigator,
virtual int GetBrowserWindowID() const;
virtual void RenderViewCreated(RenderViewHost* render_view_host);
virtual void RenderViewReady(RenderViewHost* render_view_host);
- virtual void RenderViewGone(RenderViewHost* render_view_host);
+ virtual void RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code);
virtual void RenderViewDeleted(RenderViewHost* render_view_host);
virtual void DidNavigate(RenderViewHost* render_view_host,
const ViewHostMsg_FrameNavigate_Params& params);
@@ -1146,7 +1154,8 @@ class TabContents : public PageNavigator,
bool is_loading_;
// Indicates if the tab is considered crashed.
- bool is_crashed_;
+ base::TerminationStatus crashed_status_;
+ int crashed_error_code_;
// See waiting_for_response() above.
bool waiting_for_response_;
diff --git a/chrome/browser/tab_contents/web_contents_unittest.cc b/chrome/browser/tab_contents/web_contents_unittest.cc
index abb1578..67d93a3 100644
--- a/chrome/browser/tab_contents/web_contents_unittest.cc
+++ b/chrome/browser/tab_contents/web_contents_unittest.cc
@@ -120,8 +120,8 @@ class TestInterstitialPage : public InterstitialPage {
DidNavigate(render_view_host(), params);
}
- void TestRenderViewGone() {
- RenderViewGone(render_view_host());
+ void TestRenderViewGone(base::TerminationStatus status, int error_code) {
+ RenderViewGone(render_view_host(), status, error_code);
}
bool is_showing() const {
@@ -1151,7 +1151,9 @@ TEST_F(TabContentsTest, ShowInterstitialCrashRendererThenGoBack) {
interstitial->TestDidNavigate(2, interstitial_url);
// Crash the renderer
- rvh()->TestOnMessageReceived(ViewHostMsg_RenderViewGone(0));
+ rvh()->TestOnMessageReceived(
+ ViewHostMsg_RenderViewGone(
+ 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
// While the interstitial is showing, go back.
controller().GoBack();
@@ -1186,7 +1188,9 @@ TEST_F(TabContentsTest, ShowInterstitialCrashRendererThenNavigate) {
interstitial->Show();
// Crash the renderer
- rvh()->TestOnMessageReceived(ViewHostMsg_RenderViewGone(0));
+ rvh()->TestOnMessageReceived(
+ ViewHostMsg_RenderViewGone(
+ 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1));
interstitial->TestDidNavigate(2, interstitial_url);
}
@@ -1429,7 +1433,8 @@ TEST_F(TabContentsTest, InterstitialCrasher) {
TestInterstitialPageStateGuard state_guard(interstitial);
interstitial->Show();
// Simulate a renderer crash before the interstitial is shown.
- interstitial->TestRenderViewGone();
+ interstitial->TestRenderViewGone(
+ base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
// The interstitial should have been dismissed.
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
@@ -1440,7 +1445,8 @@ TEST_F(TabContentsTest, InterstitialCrasher) {
interstitial->Show();
interstitial->TestDidNavigate(1, url);
// Simulate a renderer crash.
- interstitial->TestRenderViewGone();
+ interstitial->TestRenderViewGone(
+ base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
// The interstitial should have been dismissed.
EXPECT_TRUE(deleted);
EXPECT_EQ(TestInterstitialPage::CANCELED, state);
diff --git a/chrome/browser/task_manager/task_manager.cc b/chrome/browser/task_manager/task_manager.cc
index 2dc5029..da19fda 100644
--- a/chrome/browser/task_manager/task_manager.cc
+++ b/chrome/browser/task_manager/task_manager.cc
@@ -26,6 +26,7 @@
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/task_manager/task_manager_resource_providers.h"
#include "chrome/common/pref_names.h"
+#include "chrome/common/result_codes.h"
#include "chrome/common/url_constants.h"
#include "grit/app_resources.h"
#include "grit/chromium_strings.h"
@@ -944,7 +945,7 @@ void TaskManager::KillProcess(int index) {
base::ProcessHandle process = model_->GetResourceProcessHandle(index);
DCHECK(process);
if (process != base::GetCurrentProcessHandle())
- base::KillProcess(process, base::PROCESS_END_KILLED_BY_USER, false);
+ base::KillProcess(process, ResultCodes::KILLED, false);
}
void TaskManager::ActivateProcess(int index) {
diff --git a/chrome/browser/utility_process_host.cc b/chrome/browser/utility_process_host.cc
index 1756d3e..b8d6a77 100644
--- a/chrome/browser/utility_process_host.cc
+++ b/chrome/browser/utility_process_host.cc
@@ -159,10 +159,10 @@ void UtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
NewRunnableMethod(client_.get(), &Client::OnMessageReceived, message));
}
-void UtilityProcessHost::OnProcessCrashed() {
+void UtilityProcessHost::OnProcessCrashed(int exit_code) {
BrowserThread::PostTask(
client_thread_id_, FROM_HERE,
- NewRunnableMethod(client_.get(), &Client::OnProcessCrashed));
+ NewRunnableMethod(client_.get(), &Client::OnProcessCrashed, exit_code));
}
bool UtilityProcessHost::CanShutdown() {
diff --git a/chrome/browser/utility_process_host.h b/chrome/browser/utility_process_host.h
index 559f39e..96879a8 100644
--- a/chrome/browser/utility_process_host.h
+++ b/chrome/browser/utility_process_host.h
@@ -39,7 +39,7 @@ class UtilityProcessHost : public BrowserChildProcessHost {
Client() {}
// Called when the process has crashed.
- virtual void OnProcessCrashed() {}
+ virtual void OnProcessCrashed(int exit_code) {}
// Called when the extension has unpacked successfully. |manifest| is the
// parsed manifest.json file. |catalogs| contains list of all parsed
@@ -153,7 +153,7 @@ class UtilityProcessHost : public BrowserChildProcessHost {
virtual void OnMessageReceived(const IPC::Message& message);
// BrowserChildProcessHost:
- virtual void OnProcessCrashed();
+ virtual void OnProcessCrashed(int exit_code);
virtual bool CanShutdown();
virtual URLRequestContext* GetRequestContext(
uint32 request_id,
diff --git a/chrome/browser/web_resource/web_resource_service.cc b/chrome/browser/web_resource/web_resource_service.cc
index ab557f5..e1de48a 100644
--- a/chrome/browser/web_resource/web_resource_service.cc
+++ b/chrome/browser/web_resource/web_resource_service.cc
@@ -157,7 +157,7 @@ class WebResourceService::UnpackerClient
~UnpackerClient() {}
// UtilityProcessHost::Client
- virtual void OnProcessCrashed() {
+ virtual void OnProcessCrashed(int exit_code) {
if (got_response_)
return;
diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc
index 74b6852..e4e7ce3 100644
--- a/chrome/browser/zygote_host_linux.cc
+++ b/chrome/browser/zygote_host_linux.cc
@@ -27,6 +27,7 @@
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/process_watcher.h"
+#include "chrome/common/result_codes.h"
#include "sandbox/linux/suid/suid_unsafe_environment_variables.h"
@@ -316,13 +317,18 @@ void ZygoteHost::EnsureProcessTerminated(pid_t process) {
PLOG(ERROR) << "write";
}
-bool ZygoteHost::DidProcessCrash(base::ProcessHandle handle,
- bool* child_exited) {
+base::TerminationStatus ZygoteHost::GetTerminationStatus(
+ base::ProcessHandle handle,
+ int* exit_code) {
DCHECK(init_);
Pickle pickle;
- pickle.WriteInt(kCmdDidProcessCrash);
+ pickle.WriteInt(kCmdGetTerminationStatus);
pickle.WriteInt(handle);
+ // Set this now to handle the early termination cases.
+ if (exit_code)
+ *exit_code = ResultCodes::NORMAL_EXIT;
+
static const unsigned kMaxMessageLength = 128;
char buf[kMaxMessageLength];
ssize_t len;
@@ -336,23 +342,23 @@ bool ZygoteHost::DidProcessCrash(base::ProcessHandle handle,
if (len == -1) {
LOG(WARNING) << "Error reading message from zygote: " << errno;
- return false;
+ return base::TERMINATION_STATUS_NORMAL_TERMINATION;
} else if (len == 0) {
LOG(WARNING) << "Socket closed prematurely.";
- return false;
+ return base::TERMINATION_STATUS_NORMAL_TERMINATION;
}
Pickle read_pickle(buf, len);
- bool did_crash, tmp_child_exited;
+ int status, tmp_exit_code;
void* iter = NULL;
- if (!read_pickle.ReadBool(&iter, &did_crash) ||
- !read_pickle.ReadBool(&iter, &tmp_child_exited)) {
- LOG(WARNING) << "Error parsing DidProcessCrash response from zygote.";
- return false;
+ if (!read_pickle.ReadInt(&iter, &status) ||
+ !read_pickle.ReadInt(&iter, &tmp_exit_code)) {
+ LOG(WARNING) << "Error parsing GetTerminationStatus response from zygote.";
+ return base::TERMINATION_STATUS_NORMAL_TERMINATION;
}
- if (child_exited)
- *child_exited = tmp_child_exited;
+ if (exit_code)
+ *exit_code = tmp_exit_code;
- return did_crash;
+ return static_cast<base::TerminationStatus>(status);
}
diff --git a/chrome/browser/zygote_host_linux.h b/chrome/browser/zygote_host_linux.h
index 01a6443..e13f1b4 100644
--- a/chrome/browser/zygote_host_linux.h
+++ b/chrome/browser/zygote_host_linux.h
@@ -14,6 +14,7 @@
#include "base/global_descriptors_posix.h"
#include "base/lock.h"
#include "base/process.h"
+#include "base/process_util.h"
template<typename Type>
struct DefaultSingletonTraits;
@@ -37,26 +38,27 @@ class ZygoteHost {
const base::GlobalDescriptors::Mapping& mapping);
void EnsureProcessTerminated(pid_t process);
- // Get the termination status (exit code) of the process and return true if
- // the status indicates the process crashed. |child_exited| is set to true
- // iff the child process has terminated. (|child_exited| may be NULL.)
- bool DidProcessCrash(base::ProcessHandle handle, bool* child_exited);
+ // Get the termination status (and, optionally, the exit code) of
+ // the process. |exit_code| is set to the exit code of the child
+ // process. (|exit_code| may be NULL.)
+ base::TerminationStatus GetTerminationStatus(base::ProcessHandle handle,
+ int* exit_code);
// These are the command codes used on the wire between the browser and the
// zygote.
enum {
- kCmdFork = 0, // Fork off a new renderer.
- kCmdReap = 1, // Reap a renderer child.
- kCmdDidProcessCrash = 2, // Check if child process crashed.
- kCmdGetSandboxStatus = 3, // Read a bitmask of kSandbox*
+ kCmdFork = 0, // Fork off a new renderer.
+ kCmdReap = 1, // Reap a renderer child.
+ kCmdGetTerminationStatus = 2, // Check what happend to a child process.
+ kCmdGetSandboxStatus = 3, // Read a bitmask of kSandbox*
};
// These form a bitmask which describes the conditions of the sandbox that
// the zygote finds itself in.
enum {
- kSandboxSUID = 1 << 0, // SUID sandbox active
- kSandboxPIDNS = 1 << 1, // SUID sandbox is using the PID namespace
- kSandboxNetNS = 1 << 2, // SUID sandbox is using the network namespace
+ kSandboxSUID = 1 << 0, // SUID sandbox active
+ kSandboxPIDNS = 1 << 1, // SUID sandbox is using the PID namespace
+ kSandboxNetNS = 1 << 2, // SUID sandbox is using the network namespace
kSandboxSeccomp = 1 << 3, // seccomp sandbox active.
};
diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc
index 5943d11..1b26a6b 100644
--- a/chrome/browser/zygote_main_linux.cc
+++ b/chrome/browser/zygote_main_linux.cc
@@ -41,6 +41,7 @@
#include "chrome/common/main_function_params.h"
#include "chrome/common/pepper_plugin_registry.h"
#include "chrome/common/process_watcher.h"
+#include "chrome/common/result_codes.h"
#include "chrome/common/sandbox_methods_linux.h"
#include "media/base/media.h"
@@ -167,10 +168,10 @@ class Zygote {
break;
HandleReapRequest(fd, pickle, iter);
return false;
- case ZygoteHost::kCmdDidProcessCrash:
+ case ZygoteHost::kCmdGetTerminationStatus:
if (!fds.empty())
break;
- HandleDidProcessCrash(fd, pickle, iter);
+ HandleGetTerminationStatus(fd, pickle, iter);
return false;
case ZygoteHost::kCmdGetSandboxStatus:
HandleGetSandboxStatus(fd, pickle, iter);
@@ -209,26 +210,31 @@ class Zygote {
ProcessWatcher::EnsureProcessTerminated(actual_child);
}
- void HandleDidProcessCrash(int fd, const Pickle& pickle, void* iter) {
+ void HandleGetTerminationStatus(int fd, const Pickle& pickle, void* iter) {
base::ProcessHandle child;
if (!pickle.ReadInt(&iter, &child)) {
- LOG(WARNING) << "Error parsing DidProcessCrash request from browser";
+ LOG(WARNING) << "Error parsing GetTerminationStatus request "
+ << "from browser";
return;
}
- bool child_exited;
- bool did_crash;
+ base::TerminationStatus status;
+ int exit_code;
if (g_suid_sandbox_active)
child = real_pids_to_sandbox_pids[child];
- if (child)
- did_crash = base::DidProcessCrash(&child_exited, child);
- else
- did_crash = child_exited = false;
+ if (child) {
+ status = base::GetTerminationStatus(child, &exit_code);
+ } else {
+ // Assume that if we can't find the child in the sandbox, then
+ // it terminated normally.
+ status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
+ exit_code = ResultCodes::NORMAL_EXIT;
+ }
Pickle write_pickle;
- write_pickle.WriteBool(did_crash);
- write_pickle.WriteBool(child_exited);
+ write_pickle.WriteInt(static_cast<int>(status));
+ write_pickle.WriteInt(exit_code);
if (HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())) !=
write_pickle.size()) {
PLOG(ERROR) << "write";
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 5bd1e96..2ea5c9a 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -549,12 +549,20 @@ class NotificationType {
// The details are in a Details<ChildProcessInfo>.
CHILD_PROCESS_HOST_DISCONNECTED,
- // This message is sent when a child process disappears unexpectedly.
- // There is no usable source, since it is sent from an ephemeral task;
- // register for AllSources() to receive this notification. The details are
- // in a Details<ChildProcessInfo>.
+ // This message is sent when a child process disappears
+ // unexpectedly as a result of a crash. There is no usable
+ // source, since it is sent from an ephemeral task; register for
+ // AllSources() to receive this notification. The details are in
+ // a Details<ChildProcessInfo>.
CHILD_PROCESS_CRASHED,
+ // This message is sent when a child process disappears
+ // unexpectedly as a result of a termination signal. There is no
+ // usable source, since it is sent from an ephemeral task;
+ // register for AllSources() to receive this notification. The
+ // details are in a Details<ChildProcessInfo>.
+ CHILD_PROCESS_WAS_KILLED,
+
// This message indicates that an instance of a particular child was
// created in a page. (If one page contains several regions rendered by
// the same child, this notification will occur once for each region
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index 304dcae..2479c5a 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -1138,9 +1138,12 @@ IPC_MESSAGE_CONTROL1(ViewHostMsg_UpdatedCacheStats,
// a ViewMsg_CreatingNew_ACK.
IPC_MESSAGE_ROUTED0(ViewHostMsg_RenderViewReady)
+
// Indicates the renderer process is gone. This actually is sent by the
// browser process to itself, but keeps the interface cleaner.
-IPC_MESSAGE_ROUTED0(ViewHostMsg_RenderViewGone)
+IPC_MESSAGE_ROUTED2(ViewHostMsg_RenderViewGone,
+ int, /* this really is base::TerminationStatus */
+ int /* exit_code */)
// Sent by the renderer process to request that the browser close the view.
// This corresponds to the window.close() API, and the browser may ignore
diff --git a/chrome/common/result_codes.h b/chrome/common/result_codes.h
index 38fe54d..ae842bb 100644
--- a/chrome/common/result_codes.h
+++ b/chrome/common/result_codes.h
@@ -21,9 +21,9 @@
class ResultCodes {
public:
enum ExitCode {
- NORMAL_EXIT = base::PROCESS_END_NORMAL_TERMINATION,
- TASKMAN_KILL = base::PROCESS_END_KILLED_BY_USER,
- HUNG = base::PROCESS_END_PROCESS_WAS_HUNG,
+ NORMAL_EXIT = 0, // Process terminated normally.
+ KILLED = 1, // Process was killed by user or system.
+ HUNG = 2, // Process hung.
INVALID_CMDLINE_URL, // An invalid command line url was given.
SBOX_INIT_FAILED, // The sandbox could not be initialized.
GOOGLE_UPDATE_INIT_FAILED, // The Google Update client stub init failed.
diff --git a/chrome/test/chrome_process_util.cc b/chrome/test/chrome_process_util.cc
index 11228e2..7de9908 100644
--- a/chrome/test/chrome_process_util.cc
+++ b/chrome/test/chrome_process_util.cc
@@ -27,7 +27,7 @@ void TerminateAllChromeProcesses(base::ProcessId browser_pid) {
continue;
}
- base::KillProcess(handle, ResultCodes::TASKMAN_KILL, true);
+ base::KillProcess(handle, ResultCodes::KILLED, true);
base::CloseProcessHandle(handle);
}
}
@@ -117,4 +117,3 @@ ChromeTestProcessMetrics::ChromeTestProcessMetrics(
#endif
process_handle_ = process;
}
-