diff options
53 files changed, 469 insertions, 197 deletions
diff --git a/base/process_util.h b/base/process_util.h index 77d772b..a443de6 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -131,8 +131,18 @@ const uint32 kProcessAccessWaitForTermination = 0; // installers. enum { PROCESS_END_NORMAL_TERMINATION = 0, - PROCESS_END_KILLED_BY_USER = 1, - PROCESS_END_PROCESS_WAS_HUNG = 2 + PROCESS_END_PROCESS_WAS_KILLED = 1, + PROCESS_END_PROCESS_WAS_HUNG = 2, +}; + +// Return status values from GetTerminationStatus +enum TerminationStatus { + TERMINATION_STATUS_NORMAL_TERMINATION = 0, // zero exit status + TERMINATION_STATUS_PROCESS_WAS_KILLED = 1, // e.g. SIGKILL or task manager + TERMINATION_STATUS_PROCESS_WAS_HUNG = 2, + TERMINATION_STATUS_PROCESS_CRASHED = 3, // e.g. Segmentation fault + TERMINATION_STATUS_ABNORMAL_TERMINATION = 4, // non-zero exit status + TERMINATION_STATUS_STILL_RUNNING = 5 // child hasn't exited yet }; // Returns the id of the current process. @@ -188,7 +198,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); @@ -355,10 +365,12 @@ 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 (exit code) of the process and return an +// appropriate interpretation of the result. |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. +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 b1dbabd..c2242f1 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -193,6 +193,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; } @@ -586,40 +588,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 e92459c..1bd568e 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" @@ -27,6 +28,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 +43,24 @@ 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; +#else +const int kExpectedStillRunningExitCode = 0; +#endif + +// The longest we'll wait for a process, in milliseconds. +const int kMaxWaitTimeMs = 5000; + // Sleeps until file filename is created. void WaitToDie(const char* filename) { FILE *fp; @@ -62,6 +77,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 +115,145 @@ 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); +} + +MULTIPROCESS_TEST_MAIN(CrashingChildProcess) { + WaitToDie(kSignalFileCrash); +#if defined(OS_MACOSX) + // Have to reset the exception ports on mac so we get a crash. + base::RestoreDefaultExceptionHandler(); + // We disable crash dumps because we don't really need one if we are + // *trying* to cause a crash. + DebugUtil::DisableOSCrashDumps(); + // Mac gets a SIGBUS instead of SIGSEGV on x86 (but not x86_64), so + // we have to disable handling for both. + ::signal(SIGBUS, SIG_DFL); +#endif +#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); - SignalChildren("SlowChildProcess.die"); - EXPECT_TRUE(base::WaitForSingleProcess(handle, 5000)); + int exit_code = 42; + EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING, + base::GetTerminationStatus(handle, &exit_code)); + EXPECT_EQ(kExpectedStillRunningExitCode, exit_code); - EXPECT_FALSE(base::DidProcessCrash(&child_exited, handle)); + 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); +#if defined(OS_MACOSX) + // Mac gets a SIGBUS instead of SIGSEGV on x86 (but not x86_64), so + // we have to expect either one. + EXPECT_TRUE(signal == SIGBUS || signal == SIGSEGV); +#else + EXPECT_EQ(SIGSEGV, signal); +#endif +#endif + base::CloseProcessHandle(handle); + + // Reset signal handlers back to "normal". + base::EnableInProcessStackDumping(); + remove(kSignalFileCrash); +} + +MULTIPROCESS_TEST_MAIN(KilledChildProcess) { + WaitToDie(kSignalFileKill); +#if defined(OS_WIN) + // Kill ourselves. + HANDLE handle = ::OpenProcess(PROCESS_ALL_ACCESS, 0, ::GetCurrentProcessId()); + ::TerminateProcess(handle, base::PROCESS_END_PROCESS_WAS_KILLED); +#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(base::PROCESS_END_PROCESS_WAS_KILLED, 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 92077b1..3c0663f 100644 --- a/base/process_util_win.cc +++ b/base/process_util_win.cc @@ -394,22 +394,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 = TERMINATION_STATUS_NORMAL_TERMINATION; + } + // 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 +428,29 @@ 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 PROCESS_END_NORMAL_TERMINATION: + return TERMINATION_STATUS_NORMAL_TERMINATION; + case 0xC0000354: // STATUS_DEBUGGER_INACTIVE. + case 0xC000013A: // Control-C/end session. + case 0x40010004: // Debugger terminated process/end session. + case PROCESS_END_PROCESS_WAS_KILLED: // Task manager kill. + return TERMINATION_STATUS_PROCESS_WAS_KILLED; + case PROCESS_END_PROCESS_WAS_HUNG: + return TERMINATION_STATUS_PROCESS_WAS_HUNG; + 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 a317f26..35660ab 100644 --- a/chrome/browser/browser_child_process_host.cc +++ b/chrome/browser/browser_child_process_host.cc @@ -131,18 +131,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 cf30973..9d343d09 100644 --- a/chrome/browser/browser_child_process_host.h +++ b/chrome/browser/browser_child_process_host.h @@ -74,12 +74,25 @@ class BrowserChildProcessHost : public ResourceDispatcherHost::Receiver, CommandLine* cmd_line); // 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(); @@ -107,4 +120,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 f215084..426fc4c 100644 --- a/chrome/browser/child_process_launcher.cc +++ b/chrome/browser/child_process_launcher.cc @@ -310,27 +310,30 @@ 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 = Singleton<ZygoteHost>()->DidProcessCrash(handle, &child_exited); + status = Singleton<ZygoteHost>()->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 336ba24..5c51b75 100644 --- a/chrome/browser/child_process_security_policy_browsertest.cc +++ b/chrome/browser/child_process_security_policy_browsertest.cc @@ -43,7 +43,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); + base::TERMINATION_STATUS_PROCESS_WAS_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 32b67af..4518569 100644 --- a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc +++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc @@ -61,7 +61,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::TERMINATION_STATUS_PROCESS_WAS_KILLED, false); ASSERT_TRUE(WaitForExtensionCrash(extension_id)); ASSERT_FALSE( GetExtensionProcessManager()->GetBackgroundHostForExtension(extension)); diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.cc b/chrome/browser/extensions/sandboxed_extension_unpacker.cc index fc8aea8..300582d 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker.cc +++ b/chrome/browser/extensions/sandboxed_extension_unpacker.cc @@ -172,7 +172,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 8df1414..794f2e1 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 void OnUnpackExtensionSucceeded(const DictionaryValue& manifest); void OnUnpackExtensionFailed(const std::string& error_message); - void OnProcessCrashed(); + void OnProcessCrashed(int exit_code); void ReportFailure(const std::string& message); void ReportSuccess(); diff --git a/chrome/browser/importer/importer.cc b/chrome/browser/importer/importer.cc index 9a40471..766e59e 100644 --- a/chrome/browser/importer/importer.cc +++ b/chrome/browser/importer/importer.cc @@ -437,7 +437,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 4887138..d1668d3 100644 --- a/chrome/browser/importer/importer.h +++ b/chrome/browser/importer/importer.h @@ -327,7 +327,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 6667d50..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 @@ -1,6 +1,6 @@ -// Copyright (c) 2009 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. +// Copyright (c) 2009 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. #include "chrome/browser/in_process_webkit/indexed_db_key_utility_client.h" @@ -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 37b79b7..49f3fdb 100644 --- a/chrome/browser/metrics/metrics_service.cc +++ b/chrome/browser/metrics/metrics_service.cc @@ -610,7 +610,8 @@ void MetricsService::Observe(NotificationType type, { RenderProcessHost::RendererClosedDetails* process_details = Details<RenderProcessHost::RendererClosedDetails>(details).ptr(); - if (process_details->did_crash) { + if (process_details->status == + base::TERMINATION_STATUS_PROCESS_CRASHED) { 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 1ee53b9..2540d8c 100644 --- a/chrome/browser/nacl_host/nacl_process_host.cc +++ b/chrome/browser/nacl_host/nacl_process_host.cc @@ -162,10 +162,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 618492a..afd3e2d 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/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 a9d5805..2e77573d 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 { 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 5e254e5..1832464 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -924,16 +924,20 @@ 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) { UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildCrashes", extension_process_ ? 2 : 1); } - RendererClosedDetails details(did_crash, extension_process_); + RendererClosedDetails details(status, exit_code, extension_process_); NotificationService::current()->Notify( NotificationType::RENDERER_PROCESS_CLOSED, Source<RenderProcessHost>(this), @@ -946,7 +950,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 3781bed..21c3675 100644 --- a/chrome/browser/renderer_host/render_view_host.cc +++ b/chrome/browser/renderer_host/render_view_host.cc @@ -981,11 +981,13 @@ void RenderViewHost::OnMsgRenderViewReady() { delegate_->RenderViewReady(this); } -void RenderViewHost::OnMsgRenderViewGone() { +void RenderViewHost::OnMsgRenderViewGone(int status, int exit_code) { // Our base class RenderWidgetHost needs to reset some stuff. - RendererExited(); + RendererExited(static_cast<base::TerminationStatus>(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 76f1f01..1636d4e 100644 --- a/chrome/browser/renderer_host/render_view_host.h +++ b/chrome/browser/renderer_host/render_view_host.h @@ -525,7 +525,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); diff --git a/chrome/browser/renderer_host/render_view_host_delegate.h b/chrome/browser/renderer_host/render_view_host_delegate.h index d0c88d6..a75cb1d 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" @@ -682,7 +683,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 a6efd58..1487899 100644 --- a/chrome/browser/renderer_host/render_widget_host.cc +++ b/chrome/browser/renderer_host/render_widget_host.cc @@ -601,7 +601,8 @@ void RenderWidgetHost::ForwardEditCommandsForNextKeyEvent( // only handled by RenderView. } -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; @@ -626,7 +627,7 @@ void RenderWidgetHost::RendererExited() { is_hidden_ = false; if (view_) { - view_->RenderViewGone(); + view_->RenderViewGone(status, exit_code); view_ = NULL; // The View should be deleted by RenderViewGone. } @@ -736,7 +737,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 6c3fed7..297d74a 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" @@ -414,7 +415,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. @@ -474,7 +475,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 1c5bb17..9bd4fa3 100644 --- a/chrome/browser/renderer_host/render_widget_host_unittest.cc +++ b/chrome/browser/renderer_host/render_widget_host_unittest.cc @@ -392,7 +392,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 46d6bda..074fc88 100644 --- a/chrome/browser/renderer_host/render_widget_host_view.h +++ b/chrome/browser/renderer_host/render_widget_host_view.h @@ -14,10 +14,11 @@ #include <vector> #include "app/surface/transport_dib.h" +#include "base/process_util.h" #include "gfx/native_widget_types.h" -#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/WebKit/WebKit/chromium/public/WebPopupType.h" #include "third_party/WebKit/WebKit/chromium/public/WebTextInputType.h" +#include "third_party/skia/include/core/SkBitmap.h" namespace gfx { class Rect; @@ -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 5f4a843..77802a6 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.cc @@ -671,7 +671,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 099da46..10a7626 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_gtk.h +++ b/chrome/browser/renderer_host/render_widget_host_view_gtk.h @@ -75,7 +75,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 b9e41f9..909626e 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.h +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h @@ -207,7 +207,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 3281a0e..65473ed 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm @@ -713,7 +713,8 @@ void RenderWidgetHostViewMac::DidUpdateBackingStore( [cocoa_view_ displayIfNeeded]; } -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 25e8a88..f0a044f 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_views.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_views.cc @@ -215,7 +215,8 @@ void RenderWidgetHostViewViews::DidUpdateBackingStore( } } -void RenderWidgetHostViewViews::RenderViewGone() { +void RenderWidgetHostViewViews::RenderViewGone(base::TerminationStatus status, + int error_code) { Destroy(); } @@ -313,7 +314,7 @@ void RenderWidgetHostViewViews::Paint(gfx::Canvas* canvas) { DCHECK(!about_to_validate_and_paint_); // TODO(anicolao): get the damage somehow - //invalid_rect_ = damage_rect; + // invalid_rect_ = damage_rect; invalid_rect_ = bounds(); about_to_validate_and_paint_ = true; BackingStoreX* backing_store = static_cast<BackingStoreX*>( 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 331c1cd..3771393 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 0b66f0c..ff90c06 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_win.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_win.cc @@ -736,7 +736,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 9fbaf11..c401005 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_win.h +++ b/chrome/browser/renderer_host/render_widget_host_view_win.h @@ -144,7 +144,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 WillDestroyRenderWidget(RenderWidgetHost* rwh); virtual void Destroy(); virtual void SetTooltipText(const std::wstring& tooltip_text); 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 e3c9f39..e3c22c14 100644 --- a/chrome/browser/renderer_host/test/test_render_view_host.h +++ b/chrome/browser/renderer_host/test/test_render_view_host.h @@ -81,7 +81,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 0779b00..84dbde0 100644 --- a/chrome/browser/renderer_host/test/web_cache_manager_browsertest.cc +++ b/chrome/browser/renderer_host/test/web_cache_manager_browsertest.cc @@ -33,7 +33,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); + base::TERMINATION_STATUS_PROCESS_WAS_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 8663eef..8fd9f7f 100644 --- a/chrome/browser/tab_contents/interstitial_page.cc +++ b/chrome/browser/tab_contents/interstitial_page.cc @@ -327,7 +327,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/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 0793a4d..df102e1 100644 --- a/chrome/browser/task_manager/task_manager.cc +++ b/chrome/browser/task_manager/task_manager.cc @@ -895,7 +895,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, base::PROCESS_END_PROCESS_WAS_KILLED, false); } void TaskManager::ActivateProcess(int index) { diff --git a/chrome/browser/utility_process_host.cc b/chrome/browser/utility_process_host.cc index 22495f8..9805071 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)); } void UtilityProcessHost::Client::OnMessageReceived( diff --git a/chrome/browser/utility_process_host.h b/chrome/browser/utility_process_host.h index 48d5e38..da2a05a 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 { void OnMessageReceived(const IPC::Message& message); // BrowserChildProcessHost: - virtual void OnProcessCrashed(); + virtual void OnProcessCrashed(int exit_code); virtual bool CanShutdown() { return true; } 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 7c8a1ba..dbc7fac 100644 --- a/chrome/browser/web_resource/web_resource_service.cc +++ b/chrome/browser/web_resource/web_resource_service.cc @@ -141,7 +141,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 d88c9be..f88c597 100644 --- a/chrome/browser/zygote_host_linux.cc +++ b/chrome/browser/zygote_host_linux.cc @@ -306,13 +306,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 = 0; + static const unsigned kMaxMessageLength = 128; char buf[kMaxMessageLength]; ssize_t len; @@ -326,23 +331,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 dd00336..68a72ed 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; @@ -34,17 +35,18 @@ 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. + kCmdGetTerminationStatus = 2, // Check what happend to a child process. kCmdGetSandboxStatus = 3, // Read a bitmask of kSandbox* }; diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc index 65a44ae..2ab36e2 100644 --- a/chrome/browser/zygote_main_linux.cc +++ b/chrome/browser/zygote_main_linux.cc @@ -167,10 +167,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 +209,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 = 0; + } 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 1ba2c39..5de67e7 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -537,12 +537,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 75eacdf..40c1886 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -1163,7 +1163,9 @@ IPC_BEGIN_MESSAGES(ViewHost) // 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 6625cb0..94ba503 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 = base::TERMINATION_STATUS_NORMAL_TERMINATION, + TASKMAN_KILL = base::TERMINATION_STATUS_PROCESS_WAS_KILLED, + HUNG = base::TERMINATION_STATUS_PROCESS_WAS_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. |