blob: 3759d45b7485ac1ecc80994ac47c8c8b1a3ce571 [file] [log] [blame]
// 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 "chrome/browser/task_manager/providers/child_process_task_provider.h"
#include "base/bind.h"
#include "base/process/process.h"
#include "base/task/post_task.h"
#include "chrome/browser/task_manager/providers/child_process_task.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
using content::BrowserChildProcessHostIterator;
using content::BrowserThread;
using content::ChildProcessData;
namespace task_manager {
namespace {
// Collects and returns the child processes data on the IO thread to get all the
// pre-existing child process before we start observing
// |BrowserChildProcessObserver|.
std::unique_ptr<std::vector<ChildProcessData>> CollectChildProcessData() {
// The |BrowserChildProcessHostIterator| must only be used on the IO thread.
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::unique_ptr<std::vector<ChildProcessData>> child_processes(
new std::vector<ChildProcessData>());
for (BrowserChildProcessHostIterator itr; !itr.Done(); ++itr) {
const ChildProcessData& process_data = itr.GetData();
// Only add processes that have already started, i.e. with valid handles.
if (!process_data.GetProcess().IsValid())
continue;
child_processes->push_back(process_data.Duplicate());
}
return child_processes;
}
} // namespace
ChildProcessTaskProvider::ChildProcessTaskProvider() {}
ChildProcessTaskProvider::~ChildProcessTaskProvider() {
}
Task* ChildProcessTaskProvider::GetTaskOfUrlRequest(int child_id,
int route_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto itr = tasks_by_child_id_.find(child_id);
if (itr == tasks_by_child_id_.end())
return nullptr;
return itr->second;
}
void ChildProcessTaskProvider::BrowserChildProcessLaunchedAndConnected(
const content::ChildProcessData& data) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!data.GetProcess().IsValid())
return;
CreateTask(data);
}
void ChildProcessTaskProvider::BrowserChildProcessHostDisconnected(
const content::ChildProcessData& data) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DeleteTask(data.GetProcess().Handle());
}
void ChildProcessTaskProvider::StartUpdating() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(tasks_by_processid_.empty());
DCHECK(tasks_by_child_id_.empty());
// First, get the pre-existing child processes data.
base::PostTaskAndReplyWithResult(
FROM_HERE, {BrowserThread::IO}, base::Bind(&CollectChildProcessData),
base::Bind(&ChildProcessTaskProvider::ChildProcessDataCollected,
weak_ptr_factory_.GetWeakPtr()));
}
void ChildProcessTaskProvider::StopUpdating() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// ChildProcessDataCollected() should never be called after this, and hence
// we must invalidate the weak pointers.
weak_ptr_factory_.InvalidateWeakPtrs();
// First, stop observing.
BrowserChildProcessObserver::Remove(this);
// Remember: You can't notify the observer of tasks removal here,
// StopUpdating() is called after the observer has been cleared.
// Then delete all tasks (if any).
tasks_by_processid_.clear();
tasks_by_child_id_.clear();
}
void ChildProcessTaskProvider::ChildProcessDataCollected(
std::unique_ptr<const std::vector<content::ChildProcessData>>
child_processes) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (const auto& process_data : *child_processes)
CreateTask(process_data);
// Now start observing.
BrowserChildProcessObserver::Add(this);
}
void ChildProcessTaskProvider::CreateTask(
const content::ChildProcessData& data) {
std::unique_ptr<ChildProcessTask>& task =
tasks_by_processid_[data.GetProcess().Pid()];
if (task) {
// This task is already known to us. This case can happen when some of the
// child process data we collect upon StartUpdating() might be of
// BrowserChildProcessHosts whose process hadn't launched yet. So we just
// return.
return;
}
// Create the task and notify the observer.
task.reset(new ChildProcessTask(data));
tasks_by_child_id_[task->GetChildProcessUniqueID()] = task.get();
NotifyObserverTaskAdded(task.get());
}
void ChildProcessTaskProvider::DeleteTask(base::ProcessHandle handle) {
auto itr = tasks_by_processid_.find(base::GetProcId(handle));
// The following case should never happen since we start observing
// |BrowserChildProcessObserver| only after we collect all pre-existing child
// processes and are notified (on the UI thread) that the collection is
// completed at |ChildProcessDataCollected()|.
if (itr == tasks_by_processid_.end()) {
// BUG(crbug.com/611067): Temporarily removing due to test flakes. The
// reason why this happens is well understood (see bug), but there's no
// quick and easy fix.
// NOTREACHED();
return;
}
NotifyObserverTaskRemoved(itr->second.get());
// Clear from the child_id index.
tasks_by_child_id_.erase(itr->second->GetChildProcessUniqueID());
// Finally delete the task.
tasks_by_processid_.erase(itr);
}
} // namespace task_manager