| Index: components/crash/content/browser/crash_micro_dump_manager_android.cc
|
| diff --git a/components/crash/content/browser/crash_micro_dump_manager_android.cc b/components/crash/content/browser/crash_micro_dump_manager_android.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3f7eb127a2c1bf7c189c17a62846675d55ef772d
|
| --- /dev/null
|
| +++ b/components/crash/content/browser/crash_micro_dump_manager_android.cc
|
| @@ -0,0 +1,158 @@
|
| +// Copyright 2015 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 "components/crash/content/browser/crash_micro_dump_manager_android.h"
|
| +
|
| +#include <fcntl.h>
|
| +#include <stdlib.h>
|
| +#include <sys/socket.h>
|
| +#include <sys/types.h>
|
| +#include <unistd.h>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/files/scoped_file.h"
|
| +#include "base/logging.h"
|
| +#include "base/posix/eintr_wrapper.h"
|
| +#include "base/stl_util.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/child_process_data.h"
|
| +#include "content/public/browser/notification_service.h"
|
| +#include "content/public/browser/notification_types.h"
|
| +#include "content/public/browser/render_process_host.h"
|
| +#include "ipc/ipc_channel.h"
|
| +
|
| +using content::BrowserThread;
|
| +
|
| +namespace breakpad {
|
| +
|
| +// static
|
| +CrashMicroDumpManager* CrashMicroDumpManager::instance_ = nullptr;
|
| +
|
| +// static
|
| +CrashMicroDumpManager* CrashMicroDumpManager::GetInstance() {
|
| + CHECK(instance_);
|
| + return instance_;
|
| +}
|
| +
|
| +CrashMicroDumpManager::CrashMicroDumpManager() {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + DCHECK(!instance_);
|
| +
|
| + instance_ = this;
|
| +
|
| + notification_registrar_.Add(this,
|
| + content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
|
| + content::NotificationService::AllSources());
|
| + notification_registrar_.Add(this,
|
| + content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
|
| + content::NotificationService::AllSources());
|
| +}
|
| +
|
| +CrashMicroDumpManager::~CrashMicroDumpManager() {
|
| + instance_ = nullptr;
|
| + {
|
| + base::AutoLock auto_lock(child_process_id_to_pipe_fd_lock_);
|
| + for (const auto& iter : child_process_id_to_pipe_fd_) {
|
| + IGNORE_EINTR(close(iter.second));
|
| + }
|
| + }
|
| +}
|
| +
|
| +base::File CrashMicroDumpManager::CreateCrashInfoChannel(int child_process_id) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
|
| + int local_pipe_fd = -1, child_pipe_fd = -1;
|
| + // IPC::SocketPair creates a pipe in non-blocking mode.
|
| + if (!IPC::SocketPair(&local_pipe_fd, &child_pipe_fd)) {
|
| + return base::File();
|
| + }
|
| + {
|
| + base::AutoLock auto_lock(child_process_id_to_pipe_fd_lock_);
|
| + DCHECK(!ContainsKey(child_process_id_to_pipe_fd_, child_process_id));
|
| + child_process_id_to_pipe_fd_[child_process_id] = local_pipe_fd;
|
| + }
|
| + return base::File(child_pipe_fd);
|
| +}
|
| +
|
| +// static
|
| +void CrashMicroDumpManager::HandleChildTermination(
|
| + int pipe_fd,
|
| + base::TerminationStatus termination_status) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::FILE);
|
| + CHECK(instance_);
|
| +
|
| + base::ScopedFD pipe(pipe_fd);
|
| + if (termination_status == base::TERMINATION_STATUS_NORMAL_TERMINATION) {
|
| + // Just close our end of the pipe via ScopedFD.
|
| + return;
|
| + }
|
| + int exit_code;
|
| + ssize_t num_read = HANDLE_EINTR(read(pipe_fd, &exit_code, sizeof(exit_code)));
|
| + if (num_read <= 0) {
|
| + // The child process hasn't written anything into the pipe. This implies
|
| + // that it was terminated via SIGKILL by the low memory killer, and thus we
|
| + // need to perform a clean exit.
|
| + exit(0);
|
| + } else {
|
| + LOG(FATAL) << "Renderer process crash detected (code " << exit_code
|
| + << "). Terminating browser.";
|
| + }
|
| +}
|
| +
|
| +void CrashMicroDumpManager::Observe(
|
| + int type,
|
| + const content::NotificationSource& source,
|
| + const content::NotificationDetails& details) {
|
| + content::RenderProcessHost* rph =
|
| + content::Source<content::RenderProcessHost>(source).ptr();
|
| + // In case of a normal termination we just need to close the pipe_fd we kept
|
| + // open.
|
| + base::TerminationStatus term_status =
|
| + base::TERMINATION_STATUS_NORMAL_TERMINATION;
|
| + switch (type) {
|
| + case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
|
| + // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer
|
| + // process is cleanly shutdown.
|
| + break;
|
| + }
|
| + case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
|
| + // Android fast shutdowns is a known case where the renderer is
|
| + // intentionally killed when we are done with it.
|
| + if (!rph->FastShutdownStarted()) {
|
| + content::RenderProcessHost::RendererClosedDetails* process_details =
|
| + content::Details<content::RenderProcessHost::RendererClosedDetails>(
|
| + details).ptr();
|
| + term_status = process_details->status;
|
| + }
|
| + break;
|
| + }
|
| + default:
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| + OnChildExit(rph->GetID(), term_status);
|
| +}
|
| +
|
| +void CrashMicroDumpManager::OnChildExit(
|
| + int child_process_id,
|
| + base::TerminationStatus termination_status) {
|
| + int pipe_fd = -1;
|
| + {
|
| + base::AutoLock auto_lock(child_process_id_to_pipe_fd_lock_);
|
| + const auto& iter = child_process_id_to_pipe_fd_.find(child_process_id);
|
| + if (iter == child_process_id_to_pipe_fd_.end()) {
|
| + // We might get a NOTIFICATION_RENDERER_PROCESS_TERMINATED and a
|
| + // NOTIFICATION_RENDERER_PROCESS_CLOSED.
|
| + return;
|
| + }
|
| + pipe_fd = iter->second;
|
| + child_process_id_to_pipe_fd_.erase(iter);
|
| + }
|
| + DCHECK(pipe_fd != -1);
|
| + BrowserThread::PostTask(
|
| + BrowserThread::FILE, FROM_HERE,
|
| + base::Bind(&CrashMicroDumpManager::HandleChildTermination, pipe_fd,
|
| + termination_status));
|
| +}
|
| +
|
| +} // namespace breakpad
|
|
|