| // 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_dump_observer_android.h" |
| |
| #include <unistd.h> |
| |
| #include "base/bind.h" |
| #include "base/logging.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" |
| |
| using content::BrowserThread; |
| |
| namespace breakpad { |
| |
| namespace { |
| base::LazyInstance<CrashDumpObserver>::DestructorAtExit g_instance = |
| LAZY_INSTANCE_INITIALIZER; |
| } |
| |
| // static |
| void CrashDumpObserver::Create() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // If this DCHECK fails in a unit test then a previously executing |
| // test that makes use of CrashDumpObserver forgot to create a |
| // ShadowingAtExitManager. |
| DCHECK(!g_instance.IsCreated()); |
| g_instance.Get(); |
| } |
| |
| // static |
| CrashDumpObserver* CrashDumpObserver::GetInstance() { |
| DCHECK(g_instance.IsCreated()); |
| return g_instance.Pointer(); |
| } |
| |
| CrashDumpObserver::CrashDumpObserver() { |
| notification_registrar_.Add(this, |
| content::NOTIFICATION_RENDERER_PROCESS_CREATED, |
| content::NotificationService::AllSources()); |
| notification_registrar_.Add(this, |
| content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
| content::NotificationService::AllSources()); |
| notification_registrar_.Add(this, |
| content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| content::NotificationService::AllSources()); |
| BrowserChildProcessObserver::Add(this); |
| } |
| |
| CrashDumpObserver::~CrashDumpObserver() { |
| BrowserChildProcessObserver::Remove(this); |
| } |
| |
| void CrashDumpObserver::RegisterClient(std::unique_ptr<Client> client) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| base::AutoLock auto_lock(registered_clients_lock_); |
| registered_clients_.push_back(std::move(client)); |
| } |
| |
| void CrashDumpObserver::OnChildExit(int process_host_id, |
| base::ProcessHandle pid, |
| content::ProcessType process_type, |
| base::TerminationStatus termination_status, |
| base::android::ApplicationState app_state) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| std::vector<Client*> registered_clients_copy; |
| { |
| base::AutoLock auto_lock(registered_clients_lock_); |
| for (auto& client : registered_clients_) |
| registered_clients_copy.push_back(client.get()); |
| } |
| for (auto* client : registered_clients_copy) { |
| client->OnChildExit(process_host_id, pid, process_type, termination_status, |
| app_state); |
| } |
| } |
| |
| void CrashDumpObserver::BrowserChildProcessStarted( |
| int process_host_id, |
| content::PosixFileDescriptorInfo* mappings) { |
| std::vector<Client*> registered_clients_copy; |
| { |
| base::AutoLock auto_lock(registered_clients_lock_); |
| for (auto& client : registered_clients_) |
| registered_clients_copy.push_back(client.get()); |
| } |
| for (auto* client : registered_clients_copy) { |
| client->OnChildStart(process_host_id, mappings); |
| } |
| } |
| |
| void CrashDumpObserver::BrowserChildProcessHostDisconnected( |
| const content::ChildProcessData& data) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| OnChildExit(data.id, data.handle, |
| static_cast<content::ProcessType>(data.process_type), |
| base::TERMINATION_STATUS_MAX_ENUM, |
| base::android::ApplicationStatusListener::GetState()); |
| } |
| |
| void CrashDumpObserver::BrowserChildProcessCrashed( |
| const content::ChildProcessData& data, |
| int exit_code) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| OnChildExit(data.id, data.handle, |
| static_cast<content::ProcessType>(data.process_type), |
| base::TERMINATION_STATUS_ABNORMAL_TERMINATION, |
| base::android::APPLICATION_STATE_UNKNOWN); |
| } |
| |
| void CrashDumpObserver::Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| content::RenderProcessHost* rph = |
| content::Source<content::RenderProcessHost>(source).ptr(); |
| base::TerminationStatus term_status = base::TERMINATION_STATUS_MAX_ENUM; |
| base::android::ApplicationState app_state = |
| base::android::APPLICATION_STATE_UNKNOWN; |
| switch (type) { |
| case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { |
| // NOTIFICATION_RENDERER_PROCESS_TERMINATED is sent when the renderer |
| // process is cleanly shutdown. |
| term_status = base::TERMINATION_STATUS_NORMAL_TERMINATION; |
| break; |
| } |
| case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { |
| // We do not care about android fast shutdowns as it is a known case where |
| // the renderer is intentionally killed when we are done with it. |
| if (rph->FastShutdownStarted()) { |
| break; |
| } |
| content::RenderProcessHost::RendererClosedDetails* process_details = |
| content::Details<content::RenderProcessHost::RendererClosedDetails>( |
| details) |
| .ptr(); |
| term_status = process_details->status; |
| app_state = base::android::ApplicationStatusListener::GetState(); |
| break; |
| } |
| case content::NOTIFICATION_RENDERER_PROCESS_CREATED: { |
| // The child process pid isn't available when process is gone, keep a |
| // mapping between process_host_id and pid, so we can find it later. |
| process_host_id_to_pid_[rph->GetID()] = rph->GetHandle(); |
| return; |
| } |
| default: |
| NOTREACHED(); |
| return; |
| } |
| base::ProcessHandle pid = rph->GetHandle(); |
| const auto& iter = process_host_id_to_pid_.find(rph->GetID()); |
| if (iter != process_host_id_to_pid_.end()) { |
| if (pid == base::kNullProcessHandle) { |
| pid = iter->second; |
| } |
| process_host_id_to_pid_.erase(iter); |
| } |
| OnChildExit(rph->GetID(), pid, content::PROCESS_TYPE_RENDERER, term_status, |
| app_state); |
| } |
| |
| } // namespace breakpad |