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