1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/task_manager/providers/child_process_task_provider.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/process/process.h"
11 #include "chrome/browser/task_manager/providers/child_process_task.h"
12 #include "content/public/browser/browser_child_process_host_iterator.h"
13 #include "content/public/browser/browser_task_traits.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/child_process_data.h"
16
17 using content::BrowserChildProcessHostIterator;
18 using content::BrowserThread;
19 using content::ChildProcessData;
20
21 namespace task_manager {
22
23 namespace {
24
25 // Collects and returns the child processes data on the IO thread to get all the
26 // pre-existing child process before we start observing
27 // |BrowserChildProcessObserver|.
CollectChildProcessData()28 std::unique_ptr<std::vector<ChildProcessData>> CollectChildProcessData() {
29 // The |BrowserChildProcessHostIterator| must only be used on the IO thread.
30 DCHECK_CURRENTLY_ON(BrowserThread::IO);
31
32 std::unique_ptr<std::vector<ChildProcessData>> child_processes(
33 new std::vector<ChildProcessData>());
34 for (BrowserChildProcessHostIterator itr; !itr.Done(); ++itr) {
35 const ChildProcessData& process_data = itr.GetData();
36
37 // Only add processes that have already started, i.e. with valid handles.
38 if (!process_data.GetProcess().IsValid())
39 continue;
40
41 child_processes->push_back(process_data.Duplicate());
42 }
43
44 return child_processes;
45 }
46
47 } // namespace
48
ChildProcessTaskProvider()49 ChildProcessTaskProvider::ChildProcessTaskProvider() {}
50
~ChildProcessTaskProvider()51 ChildProcessTaskProvider::~ChildProcessTaskProvider() {
52 }
53
GetTaskOfUrlRequest(int child_id,int route_id)54 Task* ChildProcessTaskProvider::GetTaskOfUrlRequest(int child_id,
55 int route_id) {
56 DCHECK_CURRENTLY_ON(BrowserThread::UI);
57 auto itr = tasks_by_child_id_.find(child_id);
58 if (itr == tasks_by_child_id_.end())
59 return nullptr;
60
61 return itr->second;
62 }
63
BrowserChildProcessLaunchedAndConnected(const content::ChildProcessData & data)64 void ChildProcessTaskProvider::BrowserChildProcessLaunchedAndConnected(
65 const content::ChildProcessData& data) {
66 DCHECK_CURRENTLY_ON(BrowserThread::UI);
67 if (!data.GetProcess().IsValid())
68 return;
69
70 CreateTask(data);
71 }
72
BrowserChildProcessHostDisconnected(const content::ChildProcessData & data)73 void ChildProcessTaskProvider::BrowserChildProcessHostDisconnected(
74 const content::ChildProcessData& data) {
75 DCHECK_CURRENTLY_ON(BrowserThread::UI);
76 DeleteTask(data.GetProcess().Handle());
77 }
78
StartUpdating()79 void ChildProcessTaskProvider::StartUpdating() {
80 DCHECK_CURRENTLY_ON(BrowserThread::UI);
81 DCHECK(tasks_by_processid_.empty());
82 DCHECK(tasks_by_child_id_.empty());
83
84 // First, get the pre-existing child processes data.
85 content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
86 FROM_HERE, base::BindOnce(&CollectChildProcessData),
87 base::BindOnce(&ChildProcessTaskProvider::ChildProcessDataCollected,
88 weak_ptr_factory_.GetWeakPtr()));
89 }
90
StopUpdating()91 void ChildProcessTaskProvider::StopUpdating() {
92 DCHECK_CURRENTLY_ON(BrowserThread::UI);
93
94 // ChildProcessDataCollected() should never be called after this, and hence
95 // we must invalidate the weak pointers.
96 weak_ptr_factory_.InvalidateWeakPtrs();
97
98 // First, stop observing.
99 BrowserChildProcessObserver::Remove(this);
100
101 // Remember: You can't notify the observer of tasks removal here,
102 // StopUpdating() is called after the observer has been cleared.
103
104 // Then delete all tasks (if any).
105 tasks_by_processid_.clear();
106 tasks_by_child_id_.clear();
107 }
108
ChildProcessDataCollected(std::unique_ptr<const std::vector<content::ChildProcessData>> child_processes)109 void ChildProcessTaskProvider::ChildProcessDataCollected(
110 std::unique_ptr<const std::vector<content::ChildProcessData>>
111 child_processes) {
112 DCHECK_CURRENTLY_ON(BrowserThread::UI);
113
114 for (const auto& process_data : *child_processes)
115 CreateTask(process_data);
116
117 // Now start observing.
118 BrowserChildProcessObserver::Add(this);
119 }
120
CreateTask(const content::ChildProcessData & data)121 void ChildProcessTaskProvider::CreateTask(
122 const content::ChildProcessData& data) {
123 std::unique_ptr<ChildProcessTask>& task =
124 tasks_by_processid_[data.GetProcess().Pid()];
125 if (task) {
126 // This task is already known to us. This case can happen when some of the
127 // child process data we collect upon StartUpdating() might be of
128 // BrowserChildProcessHosts whose process hadn't launched yet. So we just
129 // return.
130 return;
131 }
132
133 // Create the task and notify the observer.
134 task = std::make_unique<ChildProcessTask>(
135 data, ChildProcessTask::ProcessSubtype::kNoSubtype);
136 tasks_by_child_id_[task->GetChildProcessUniqueID()] = task.get();
137 NotifyObserverTaskAdded(task.get());
138 }
139
DeleteTask(base::ProcessHandle handle)140 void ChildProcessTaskProvider::DeleteTask(base::ProcessHandle handle) {
141 auto itr = tasks_by_processid_.find(base::GetProcId(handle));
142
143 // The following case should never happen since we start observing
144 // |BrowserChildProcessObserver| only after we collect all pre-existing child
145 // processes and are notified (on the UI thread) that the collection is
146 // completed at |ChildProcessDataCollected()|.
147 if (itr == tasks_by_processid_.end()) {
148 // BUG(crbug.com/611067): Temporarily removing due to test flakes. The
149 // reason why this happens is well understood (see bug), but there's no
150 // quick and easy fix.
151 // NOTREACHED();
152 return;
153 }
154
155 NotifyObserverTaskRemoved(itr->second.get());
156
157 // Clear from the child_id index.
158 tasks_by_child_id_.erase(itr->second->GetChildProcessUniqueID());
159
160 // Finally delete the task.
161 tasks_by_processid_.erase(itr);
162 }
163
164 } // namespace task_manager
165