Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: chrome/browser/extensions/api/processes/processes_api.cc

Issue 1584473004: Migrate ProcessesEventRouter to the new task manager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix duplicate entry in schemas.gypi Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/processes/processes_api.h" 5 #include "chrome/browser/extensions/api/processes/processes_api.h"
6 6
7 #include <stddef.h>
8 #include <stdint.h> 7 #include <stdint.h>
9 8
10 #include <algorithm> 9 #include <algorithm>
11 #include <utility>
12 10
13 #include "base/callback.h"
14 #include "base/json/json_writer.h"
15 #include "base/lazy_instance.h" 11 #include "base/lazy_instance.h"
16 #include "base/location.h" 12 #include "base/metrics/histogram_macros.h"
17 #include "base/metrics/histogram.h" 13 #include "base/process/process.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "base/values.h"
23 #include "chrome/browser/chrome_notification_types.h"
24 #include "chrome/browser/extensions/api/tabs/tabs_constants.h" 16 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/browser/extensions/extension_tab_util.h" 17 #include "chrome/browser/extensions/extension_tab_util.h"
27 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/task_manager/resource_provider.h" 19 #include "chrome/browser/task_management/task_manager_interface.h"
29 #include "chrome/browser/task_manager/task_manager.h"
30 #include "chrome/common/extensions/api/processes.h" 20 #include "chrome/common/extensions/api/processes.h"
31 #include "content/public/browser/browser_context.h" 21 #include "content/public/browser/browser_child_process_host.h"
32 #include "content/public/browser/notification_details.h" 22 #include "content/public/browser/child_process_data.h"
33 #include "content/public/browser/notification_service.h" 23
ncarter (slow) 2016/03/10 21:01:04 Weird newline here.
afakhry 2016/03/11 03:38:11 Done.
34 #include "content/public/browser/notification_source.h"
35 #include "content/public/browser/notification_types.h"
36 #include "content/public/browser/render_process_host.h" 24 #include "content/public/browser/render_process_host.h"
37 #include "content/public/browser/render_view_host.h"
38 #include "content/public/browser/render_widget_host.h"
39 #include "content/public/browser/render_widget_host_iterator.h"
40 #include "content/public/browser/web_contents.h" 25 #include "content/public/browser/web_contents.h"
26 #include "content/public/common/child_process_host.h"
41 #include "content/public/common/result_codes.h" 27 #include "content/public/common/result_codes.h"
42 #include "extensions/browser/event_router.h"
43 #include "extensions/browser/extension_function_registry.h"
44 #include "extensions/browser/extension_function_util.h"
45 #include "extensions/common/error_utils.h" 28 #include "extensions/common/error_utils.h"
29 #include "third_party/WebKit/public/web/WebCache.h"
46 30
47 namespace extensions { 31 namespace extensions {
48 32
49 namespace errors { 33 namespace errors {
50 const char kNotAllowedToTerminate[] = "Not allowed to terminate process: *."; 34 const char kNotAllowedToTerminate[] = "Not allowed to terminate process: *.";
51 const char kProcessNotFound[] = "Process not found: *."; 35 const char kProcessNotFound[] = "Process not found: *.";
52 const char kInavlidArgument[] = "Invalid argument: *."; 36 const char kInvalidArgument[] = "Invalid argument: *.";
53 } // namespace errors 37 } // namespace errors
54 38
55 namespace { 39 namespace {
56 40
57 #if defined(ENABLE_TASK_MANAGER) 41 base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI>>
42 g_processes_api_factory = LAZY_INSTANCE_INITIALIZER;
43
44 int64_t GetRefreshTypesFlagOnlyEssentialData() {
45 // This is the only non-optional data in the Process as defined by the API in
46 // processes.idl.
47 return task_management::REFRESH_TYPE_NACL;
48 }
49
50 // This does not include memory. The memory refresh flag will only be added once
51 // a listener to OnUpdatedWithMemory event is added.
52 int64_t GetRefreshTypesForProcessOptionalData() {
53 return task_management::REFRESH_TYPE_CPU |
54 task_management::REFRESH_TYPE_NETWORK_USAGE |
55 task_management::REFRESH_TYPE_SQLITE_MEMORY |
56 task_management::REFRESH_TYPE_V8_MEMORY |
57 task_management::REFRESH_TYPE_WEBCACHE_STATS;
58 }
58 59
59 scoped_ptr<api::processes::Cache> CreateCacheData( 60 scoped_ptr<api::processes::Cache> CreateCacheData(
60 const blink::WebCache::ResourceTypeStat& stat) { 61 const blink::WebCache::ResourceTypeStat& stat) {
61 scoped_ptr<api::processes::Cache> cache(new api::processes::Cache()); 62 scoped_ptr<api::processes::Cache> cache(new api::processes::Cache());
62 cache->size = static_cast<double>(stat.size); 63 cache->size = static_cast<double>(stat.size);
63 cache->live_size = static_cast<double>(stat.liveSize); 64 cache->live_size = static_cast<double>(stat.liveSize);
64 return cache; 65 return cache;
65 } 66 }
66 67
67 api::processes::ProcessType GetProcessType(TaskManagerModel* model, 68 api::processes::ProcessType GetProcessType(
68 int index) { 69 task_management::Task::Type task_type) {
69 // Determine process type. 70 switch (task_type) {
70 task_manager::Resource::Type resource_type = model->GetResourceType(index); 71 case task_management::Task::BROWSER:
71 switch (resource_type) {
72 case task_manager::Resource::BROWSER:
73 return api::processes::PROCESS_TYPE_BROWSER; 72 return api::processes::PROCESS_TYPE_BROWSER;
74 73
75 case task_manager::Resource::RENDERER: 74 case task_management::Task::RENDERER:
76 return api::processes::PROCESS_TYPE_RENDERER; 75 return api::processes::PROCESS_TYPE_RENDERER;
77 76
78 case task_manager::Resource::EXTENSION: 77 case task_management::Task::EXTENSION:
79 case task_manager::Resource::GUEST: 78 case task_management::Task::GUEST:
80 return api::processes::PROCESS_TYPE_EXTENSION; 79 return api::processes::PROCESS_TYPE_EXTENSION;
81 80
82 case task_manager::Resource::NOTIFICATION: 81 case task_management::Task::PLUGIN:
83 return api::processes::PROCESS_TYPE_NOTIFICATION;
84
85 case task_manager::Resource::PLUGIN:
86 return api::processes::PROCESS_TYPE_PLUGIN; 82 return api::processes::PROCESS_TYPE_PLUGIN;
87 83
88 case task_manager::Resource::WORKER: 84 case task_management::Task::WORKER:
89 return api::processes::PROCESS_TYPE_WORKER; 85 return api::processes::PROCESS_TYPE_WORKER;
90 86
91 case task_manager::Resource::NACL: 87 case task_management::Task::NACL:
92 return api::processes::PROCESS_TYPE_NACL; 88 return api::processes::PROCESS_TYPE_NACL;
93 89
94 case task_manager::Resource::UTILITY: 90 case task_management::Task::UTILITY:
95 return api::processes::PROCESS_TYPE_UTILITY; 91 return api::processes::PROCESS_TYPE_UTILITY;
96 92
97 case task_manager::Resource::GPU: 93 case task_management::Task::GPU:
98 return api::processes::PROCESS_TYPE_GPU; 94 return api::processes::PROCESS_TYPE_GPU;
99 95
100 case task_manager::Resource::ZYGOTE: 96 case task_management::Task::UNKNOWN:
101 case task_manager::Resource::SANDBOX_HELPER: 97 case task_management::Task::ARC:
102 case task_manager::Resource::UNKNOWN: 98 case task_management::Task::SANDBOX_HELPER:
99 case task_management::Task::ZYGOTE:
103 return api::processes::PROCESS_TYPE_OTHER; 100 return api::processes::PROCESS_TYPE_OTHER;
104 } 101 }
105 102
106 NOTREACHED() << "Unknown resource type."; 103 NOTREACHED() << "Unknown task type.";
107 return api::processes::PROCESS_TYPE_NONE; 104 return api::processes::PROCESS_TYPE_NONE;
108 } 105 }
109 106
110 void FillTabsForProcess(int process_id, api::processes::Process* out_process) { 107 // Fills |out_process| with the data of the process in which the task with |id|
108 // is running. If |include_optional| is true, this function will fill the
109 // optional fields in |api::processes::Process| except for |private_memory|,
110 // which should be filled later if needed.
111 void FillProcessData(
112 task_management::TaskId id,
113 task_management::TaskManagerInterface* task_manager,
114 bool include_optional,
115 api::processes::Process* out_process) {
111 DCHECK(out_process); 116 DCHECK(out_process);
112 117
113 // The tabs list only makes sense for render processes, so if we don't find 118 out_process->id = task_manager->GetChildProcessUniqueId(id);
114 // one, just return the empty list. 119 out_process->os_process_id = task_manager->GetProcessId(id);
115 content::RenderProcessHost* rph = 120 out_process->type = GetProcessType(task_manager->GetType(id));
116 content::RenderProcessHost::FromID(process_id); 121 out_process->profile = base::UTF16ToUTF8(task_manager->GetProfileName(id));
117 if (!rph) 122 out_process->nacl_debug_port = task_manager->GetNaClDebugStubPort(id);
118 return; 123
119 124 // Collect the tab IDs of all the tasks sharing this renderer if any.
120 int tab_id = -1; 125 const task_management::TaskIdList tasks_on_process =
121 // We need to loop through all the RVHs to ensure we collect the set of all 126 task_manager->GetIdsOfTasksSharingSameProcess(id);
122 // tabs using this renderer process. 127 for (const auto& task_id : tasks_on_process) {
123 scoped_ptr<content::RenderWidgetHostIterator> widgets( 128 linked_ptr<api::processes::TaskInfo> task_info(
124 content::RenderWidgetHost::GetRenderWidgetHosts()); 129 new api::processes::TaskInfo());
125 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { 130 task_info->title = base::UTF16ToUTF8(task_manager->GetTitle(task_id));
126 if (widget->GetProcess()->GetID() != process_id) 131 const int tab_id = task_manager->GetTabId(task_id);
127 continue; 132 if (tab_id != -1)
128 133 task_info->tab_id.reset(new int(tab_id));
129 content::RenderViewHost* host = content::RenderViewHost::From(widget); 134
130 content::WebContents* contents = 135 out_process->tasks.push_back(task_info);
131 content::WebContents::FromRenderViewHost(host); 136 }
132 if (contents) {
133 tab_id = ExtensionTabUtil::GetTabId(contents);
134 if (tab_id != -1)
135 out_process->tabs.push_back(tab_id);
136 }
137 }
138 }
139
140 // This function fills |out_process| with the data of the process with
141 // |process_id|. For memory details, which are not added by this function,
142 // the callers need to use AddMemoryDetails.
143 void FillProcessData(int process_id,
144 TaskManagerModel* model,
145 int index,
146 bool include_optional,
147 api::processes::Process* out_process) {
148 DCHECK(out_process);
149
150 out_process->id = process_id;
151 out_process->os_process_id = model->GetProcessId(index);
152 out_process->type = GetProcessType(model, index);
153 out_process->title = base::UTF16ToUTF8(model->GetResourceTitle(index));
154 out_process->profile =
155 base::UTF16ToUTF8(model->GetResourceProfileName(index));
156 out_process->nacl_debug_port = model->GetNaClDebugStubPort(index);
157
158 FillTabsForProcess(process_id, out_process);
159 137
160 // If we don't need to include the optional properties, just return now. 138 // If we don't need to include the optional properties, just return now.
161 if (!include_optional) 139 if (!include_optional)
162 return; 140 return;
163 141
164 out_process->cpu.reset(new double(model->GetCPUUsage(index))); 142 out_process->cpu.reset(new double(task_manager->GetCpuUsage(id)));
165 143
166 size_t mem = 0; 144 out_process->network.reset(new double(static_cast<double>(
167 if (model->GetV8Memory(index, &mem)) { 145 task_manager->GetProcessTotalNetworkUsage(id))));
146
147 int64_t v8_allocated = 0;
148 int64_t v8_used = 0;
149 if (task_manager->GetV8Memory(id, &v8_allocated, &v8_used)) {
168 out_process->js_memory_allocated.reset(new double(static_cast<double>( 150 out_process->js_memory_allocated.reset(new double(static_cast<double>(
169 mem))); 151 v8_allocated)));
170 } 152 out_process->js_memory_used.reset(new double(static_cast<double>(v8_used)));
171 153 }
172 if (model->GetV8MemoryUsed(index, &mem)) 154
173 out_process->js_memory_used.reset(new double(static_cast<double>(mem))); 155 const int64_t sqlite_bytes = task_manager->GetSqliteMemoryUsed(id);
174 156 if (sqlite_bytes != -1) {
175 if (model->GetSqliteMemoryUsedBytes(index, &mem)) 157 out_process->sqlite_memory.reset(new double(static_cast<double>(
176 out_process->sqlite_memory.reset(new double(static_cast<double>(mem))); 158 sqlite_bytes)));
159 }
177 160
178 blink::WebCache::ResourceTypeStats cache_stats; 161 blink::WebCache::ResourceTypeStats cache_stats;
179 if (model->GetWebCoreCacheStats(index, &cache_stats)) { 162 if (task_manager->GetWebCacheStats(id, &cache_stats)) {
180 out_process->image_cache = CreateCacheData(cache_stats.images); 163 out_process->image_cache = CreateCacheData(cache_stats.images);
181 out_process->script_cache = CreateCacheData(cache_stats.scripts); 164 out_process->script_cache = CreateCacheData(cache_stats.scripts);
182 out_process->css_cache = CreateCacheData(cache_stats.cssStyleSheets); 165 out_process->css_cache = CreateCacheData(cache_stats.cssStyleSheets);
183 } 166 }
184 167 }
185 // Network is reported by the TaskManager per resource (tab), not per
186 // process, therefore we need to iterate through the group of resources
187 // and aggregate the data.
188 int64_t net = 0;
189 int length = model->GetGroupRangeForResource(index).second;
190 for (int i = 0; i < length; ++i)
191 net += model->GetNetworkUsage(index + i);
192 out_process->network.reset(new double(static_cast<double>(net)));
193 }
194
195 // Since memory details are expensive to gather, we don't do it by default.
196 // This function is a helper to add memory details data to an existing
197 // Process object |out_process|.
198 void AddMemoryDetails(TaskManagerModel* model,
199 int index,
200 api::processes::Process* out_process) {
201 DCHECK(out_process);
202
203 size_t mem;
204 int64_t pr_mem =
205 model->GetPrivateMemory(index, &mem) ? static_cast<int64_t>(mem) : -1;
206 out_process->private_memory.reset(new double(static_cast<double>(pr_mem)));
207 }
208
209 #endif // defined(ENABLE_TASK_MANAGER)
210 168
211 } // namespace 169 } // namespace
212 170
171 ////////////////////////////////////////////////////////////////////////////////
172 // ProcessesEventRouter:
173 ////////////////////////////////////////////////////////////////////////////////
174
213 ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context) 175 ProcessesEventRouter::ProcessesEventRouter(content::BrowserContext* context)
214 : browser_context_(context), listeners_(0), task_manager_listening_(false) { 176 : task_management::TaskManagerObserver(base::TimeDelta::FromSeconds(1),
215 #if defined(ENABLE_TASK_MANAGER) 177 task_management::REFRESH_TYPE_NONE),
216 model_ = TaskManager::GetInstance()->model(); 178 browser_context_(context),
217 model_->AddObserver(this); 179 listeners_(0) {
218
219 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
220 content::NotificationService::AllSources());
221 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
222 content::NotificationService::AllSources());
223 #endif // defined(ENABLE_TASK_MANAGER)
224 } 180 }
225 181
226 ProcessesEventRouter::~ProcessesEventRouter() { 182 ProcessesEventRouter::~ProcessesEventRouter() {
227 #if defined(ENABLE_TASK_MANAGER)
228 registrar_.Remove(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
229 content::NotificationService::AllSources());
230 registrar_.Remove(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
231 content::NotificationService::AllSources());
232
233 if (task_manager_listening_)
234 model_->StopListening();
235
236 model_->RemoveObserver(this);
237 #endif // defined(ENABLE_TASK_MANAGER)
238 } 183 }
239 184
240 void ProcessesEventRouter::ListenerAdded() { 185 void ProcessesEventRouter::ListenerAdded() {
241 #if defined(ENABLE_TASK_MANAGER) 186 UpdateRefreshTypesFlagsBasedOnListeners();
242 // The task manager has its own ref count to balance other callers of 187
243 // StartUpdating/StopUpdating. 188 if (listeners_++ == 0) {
244 model_->StartUpdating(); 189 // The first listener to be added.
245 #endif // defined(ENABLE_TASK_MANAGER) 190 task_management::TaskManagerInterface::GetTaskManager()->AddObserver(this);
246 ++listeners_; 191 }
247 } 192 }
248 193
249 void ProcessesEventRouter::ListenerRemoved() { 194 void ProcessesEventRouter::ListenerRemoved() {
250 DCHECK_GT(listeners_, 0); 195 UpdateRefreshTypesFlagsBasedOnListeners();
251 --listeners_; 196
252 #if defined(ENABLE_TASK_MANAGER) 197 if (--listeners_ == 0) {
253 // The task manager has its own ref count to balance other callers of 198 // Last listener to be removed.
254 // StartUpdating/StopUpdating. 199 task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(
255 model_->StopUpdating(); 200 this);
256 #endif // defined(ENABLE_TASK_MANAGER) 201 }
257 } 202 }
258 203
259 void ProcessesEventRouter::StartTaskManagerListening() { 204 void ProcessesEventRouter::OnTaskAdded(task_management::TaskId id) {
260 #if defined(ENABLE_TASK_MANAGER)
261 if (!task_manager_listening_) {
262 model_->StartListening();
263 task_manager_listening_ = true;
264 }
265 #endif // defined(ENABLE_TASK_MANAGER)
266 }
267
268 void ProcessesEventRouter::Observe(
269 int type,
270 const content::NotificationSource& source,
271 const content::NotificationDetails& details) {
272
273 switch (type) {
274 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG:
275 ProcessHangEvent(
276 content::Source<content::RenderWidgetHost>(source).ptr());
277 break;
278 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
279 ProcessClosedEvent(
280 content::Source<content::RenderProcessHost>(source).ptr(),
281 content::Details<content::RenderProcessHost::RendererClosedDetails>(
282 details).ptr());
283 break;
284 default:
285 NOTREACHED() << "Unexpected observe of type " << type;
286 }
287 return;
288 }
289
290 void ProcessesEventRouter::OnItemsAdded(int start, int length) {
291 #if defined(ENABLE_TASK_MANAGER)
292 DCHECK_EQ(length, 1);
293 if (!HasEventListeners(api::processes::OnCreated::kEventName)) 205 if (!HasEventListeners(api::processes::OnCreated::kEventName))
294 return; 206 return;
295 207
296 // If the item being added is not the first one in the group, find the base 208 int child_process_host_id = 0;
297 // index and use it for retrieving the process data. 209 if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id))
298 if (!model_->IsResourceFirstInGroup(start)) 210 return;
299 start = model_->GetGroupIndexForResource(start);
300 211
301 api::processes::Process process; 212 api::processes::Process process;
302 FillProcessData(model_->GetUniqueChildProcessId(start), model_, start, 213 FillProcessData(id,
303 false /* include_optional */, &process); 214 observed_task_manager(),
215 false, // include_optional
216 &process);
304 DispatchEvent(events::PROCESSES_ON_CREATED, 217 DispatchEvent(events::PROCESSES_ON_CREATED,
305 api::processes::OnCreated::kEventName, 218 api::processes::OnCreated::kEventName,
306 api::processes::OnCreated::Create(process)); 219 api::processes::OnCreated::Create(process));
307 #endif // defined(ENABLE_TASK_MANAGER) 220 }
308 } 221
309 222 void ProcessesEventRouter::OnTaskToBeRemoved(task_management::TaskId id) {
310 void ProcessesEventRouter::OnItemsChanged(int start, int length) { 223 if (!HasEventListeners(api::processes::OnExited::kEventName))
311 #if defined(ENABLE_TASK_MANAGER) 224 return;
312 // If we don't have any listeners, return immediately. 225
313 if (listeners_ == 0) 226 int child_process_host_id = 0;
314 return; 227 if (!ShouldReportOnCreatedOrOnExited(id, &child_process_host_id))
315 228 return;
316 if (!model_) 229
317 return; 230 int exit_code = 0;
318 231 base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING;
319 // We need to know which type of onUpdated events to fire and whether to 232 observed_task_manager()->GetTerminationStatus(id, &status, &exit_code);
320 // collect memory or not. 233
321 bool updated = HasEventListeners(api::processes::OnUpdated::kEventName); 234 DispatchEvent(events::PROCESSES_ON_EXITED,
322 bool updated_memory = 235 api::processes::OnExited::kEventName,
236 api::processes::OnExited::Create(child_process_host_id,
237 status,
238 exit_code));
239 }
240
241 void ProcessesEventRouter::OnTasksRefreshedWithBackgroundCalculations(
242 const task_management::TaskIdList& task_ids) {
243 const bool has_on_updated_listeners =
244 HasEventListeners(api::processes::OnUpdated::kEventName);
245 const bool has_on_updated_with_memory_listeners =
323 HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName); 246 HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName);
324 if (!updated && !updated_memory) 247
325 return; 248 if (!has_on_updated_listeners && !has_on_updated_with_memory_listeners)
326 249 return;
250
251 // Get the data of tasks sharing the same process only once.
252 std::set<base::ProcessId> seen_processes;
327 base::DictionaryValue processes_dictionary; 253 base::DictionaryValue processes_dictionary;
328 for (int i = start; i < start + length; ++i) { 254 for (const auto& task_id : task_ids) {
329 if (model_->IsResourceFirstInGroup(i)) { 255 // We are not interested in tasks, but rather the processes on which they
330 int id = model_->GetUniqueChildProcessId(i); 256 // run.
331 api::processes::Process process; 257 const base::ProcessId proc_id =
332 FillProcessData(id, model_, i, true /* include_optional */, &process); 258 observed_task_manager()->GetProcessId(task_id);
333 if (updated_memory) 259 if (seen_processes.count(proc_id))
334 AddMemoryDetails(model_, i, &process); 260 continue;
335 processes_dictionary.Set(base::IntToString(id), process.ToValue()); 261
262 const int child_process_host_id =
263 observed_task_manager()->GetChildProcessUniqueId(task_id);
264 // Ignore tasks that don't have a valid child process host ID like ARC
265 // processes. We report the browser process info here though.
266 if (child_process_host_id == content::ChildProcessHost::kInvalidUniqueID)
267 continue;
268
269 seen_processes.insert(proc_id);
270 api::processes::Process process;
271 FillProcessData(task_id,
272 observed_task_manager(),
273 true, // include_optional
274 &process);
275
276 if (has_on_updated_with_memory_listeners) {
277 // Append the private memory usage to the process data.
278 const int64_t private_memory =
279 observed_task_manager()->GetPrivateMemoryUsage(task_id);
280 process.private_memory.reset(new double(static_cast<double>(
281 private_memory)));
336 } 282 }
337 } 283
338 284 // Store each process indexed by the string version of its ChildProcessHost
339 if (updated) { 285 // ID.
286 processes_dictionary.Set(base::IntToString(child_process_host_id),
287 process.ToValue());
288 }
289
290 // Done with data collection. Now dispatch the appropriate events according to
291 // the present listeners.
292 DCHECK(has_on_updated_listeners || has_on_updated_with_memory_listeners);
293 if (has_on_updated_listeners) {
340 api::processes::OnUpdated::Processes processes; 294 api::processes::OnUpdated::Processes processes;
341 processes.additional_properties.MergeDictionary(&processes_dictionary); 295 processes.additional_properties.MergeDictionary(&processes_dictionary);
342 // NOTE: If there are listeners to the updates with memory as well, 296 // NOTE: If there are listeners to the updates with memory as well,
343 // listeners to onUpdated (without memory) will also get the memory info 297 // listeners to onUpdated (without memory) will also get the memory info
344 // of processes as an added bonus. 298 // of processes as an added bonus.
345 DispatchEvent(events::PROCESSES_ON_UPDATED, 299 DispatchEvent(events::PROCESSES_ON_UPDATED,
346 api::processes::OnUpdated::kEventName, 300 api::processes::OnUpdated::kEventName,
347 api::processes::OnUpdated::Create(processes)); 301 api::processes::OnUpdated::Create(processes));
348 } 302 }
349 303
350 if (updated_memory) { 304 if (has_on_updated_with_memory_listeners) {
351 api::processes::OnUpdatedWithMemory::Processes processes; 305 api::processes::OnUpdatedWithMemory::Processes processes;
352 processes.additional_properties.MergeDictionary(&processes_dictionary); 306 processes.additional_properties.MergeDictionary(&processes_dictionary);
353 DispatchEvent(events::PROCESSES_ON_UPDATED_WITH_MEMORY, 307 DispatchEvent(events::PROCESSES_ON_UPDATED_WITH_MEMORY,
354 api::processes::OnUpdatedWithMemory::kEventName, 308 api::processes::OnUpdatedWithMemory::kEventName,
355 api::processes::OnUpdatedWithMemory::Create(processes));} 309 api::processes::OnUpdatedWithMemory::Create(processes));
356 #endif // defined(ENABLE_TASK_MANAGER) 310 }
357 } 311 }
358 312
359 void ProcessesEventRouter::OnItemsToBeRemoved(int start, int length) { 313 void ProcessesEventRouter::OnTaskUnresponsive(task_management::TaskId id) {
360 #if defined(ENABLE_TASK_MANAGER)
361 DCHECK_EQ(length, 1);
362
363 if (!HasEventListeners(api::processes::OnExited::kEventName))
364 return;
365
366 // Process exit for renderer processes has the data about exit code and
367 // termination status, therefore we will rely on notifications and not on
368 // the Task Manager data. We do use the rest of this method for non-renderer
369 // processes.
370 if (model_->GetResourceType(start) == task_manager::Resource::RENDERER)
371 return;
372
373 DispatchEvent(events::PROCESSES_ON_EXITED,
374 api::processes::OnExited::kEventName,
375 api::processes::OnExited::Create(
376 model_->GetUniqueChildProcessId(start),
377 0 /* exit_type */,
378 0 /* exit_code */));
379 #endif // defined(ENABLE_TASK_MANAGER)
380 }
381
382 void ProcessesEventRouter::ProcessHangEvent(content::RenderWidgetHost* widget) {
383 #if defined(ENABLE_TASK_MANAGER)
384 if (!HasEventListeners(api::processes::OnUnresponsive::kEventName)) 314 if (!HasEventListeners(api::processes::OnUnresponsive::kEventName))
385 return; 315 return;
386 316
387 int count = model_->ResourceCount(); 317 api::processes::Process process;
388 int id = widget->GetProcess()->GetID(); 318 FillProcessData(id,
389 319 observed_task_manager(),
390 for (int i = 0; i < count; ++i) { 320 false, // include_optional
391 if (model_->IsResourceFirstInGroup(i)) { 321 &process);
392 if (id == model_->GetUniqueChildProcessId(i)) { 322 DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE,
393 api::processes::Process process; 323 api::processes::OnUnresponsive::kEventName,
394 FillProcessData(id, model_, i, false /* include_optional */, &process); 324 api::processes::OnUnresponsive::Create(process));
395 DispatchEvent(events::PROCESSES_ON_UNRESPONSIVE,
396 api::processes::OnUnresponsive::kEventName,
397 api::processes::OnUnresponsive::Create(process));
398 return;
399 }
400 }
401 }
402 #endif // defined(ENABLE_TASK_MANAGER)
403 }
404
405 void ProcessesEventRouter::ProcessClosedEvent(
406 content::RenderProcessHost* rph,
407 content::RenderProcessHost::RendererClosedDetails* details) {
408 #if defined(ENABLE_TASK_MANAGER)
409 if (!HasEventListeners(api::processes::OnExited::kEventName))
410 return;
411
412 DispatchEvent(events::PROCESSES_ON_EXITED,
413 api::processes::OnExited::kEventName,
414 api::processes::OnExited::Create(rph->GetID(),
415 details->status,
416 details->exit_code));
417 #endif // defined(ENABLE_TASK_MANAGER)
418 } 325 }
419 326
420 void ProcessesEventRouter::DispatchEvent( 327 void ProcessesEventRouter::DispatchEvent(
421 events::HistogramValue histogram_value, 328 events::HistogramValue histogram_value,
422 const std::string& event_name, 329 const std::string& event_name,
423 scoped_ptr<base::ListValue> event_args) { 330 scoped_ptr<base::ListValue> event_args) const {
424 EventRouter* event_router = EventRouter::Get(browser_context_); 331 EventRouter* event_router = EventRouter::Get(browser_context_);
425 if (event_router) { 332 if (event_router) {
426 scoped_ptr<Event> event( 333 scoped_ptr<Event> event(
427 new Event(histogram_value, event_name, std::move(event_args))); 334 new Event(histogram_value, event_name, std::move(event_args)));
428 event_router->BroadcastEvent(std::move(event)); 335 event_router->BroadcastEvent(std::move(event));
429 } 336 }
430 } 337 }
431 338
432 bool ProcessesEventRouter::HasEventListeners(const std::string& event_name) { 339 bool ProcessesEventRouter::HasEventListeners(
340 const std::string& event_name) const {
433 EventRouter* event_router = EventRouter::Get(browser_context_); 341 EventRouter* event_router = EventRouter::Get(browser_context_);
434 return event_router && event_router->HasEventListener(event_name); 342 return event_router && event_router->HasEventListener(event_name);
435 } 343 }
436 344
345 bool ProcessesEventRouter::ShouldReportOnCreatedOrOnExited(
346 task_management::TaskId id,
347 int* out_child_process_host_id) const {
348 // Is it the first task to be created or the last one to be removed?
349 if (observed_task_manager()->GetNumberOfTasksOnSameProcess(id) != 1)
350 return false;
351
352 // Ignore tasks that don't have a valid child process host ID like ARC
353 // processes, as well as the browser process (neither onCreated() nor
354 // onExited() shouldn't report the browser process).
355 *out_child_process_host_id =
356 observed_task_manager()->GetChildProcessUniqueId(id);
357 if (*out_child_process_host_id ==
358 content::ChildProcessHost::kInvalidUniqueID ||
359 *out_child_process_host_id == 0) {
360 return false;
361 }
362
363 return true;
364 }
365
366 void ProcessesEventRouter::UpdateRefreshTypesFlagsBasedOnListeners() {
367 int64_t refresh_types = task_management::REFRESH_TYPE_NONE;
368 if (HasEventListeners(api::processes::OnCreated::kEventName) ||
369 HasEventListeners(api::processes::OnUnresponsive::kEventName)) {
370 refresh_types |= GetRefreshTypesFlagOnlyEssentialData();
371 }
372
373 if (HasEventListeners(api::processes::OnUpdated::kEventName))
374 refresh_types |= GetRefreshTypesForProcessOptionalData();
375
376 if (HasEventListeners(api::processes::OnUpdatedWithMemory::kEventName))
377 refresh_types |= task_management::REFRESH_TYPE_MEMORY;
378
379 SetRefreshTypesFlags(refresh_types);
380 }
381
382 ////////////////////////////////////////////////////////////////////////////////
383 // ProcessesAPI:
384 ////////////////////////////////////////////////////////////////////////////////
385
437 ProcessesAPI::ProcessesAPI(content::BrowserContext* context) 386 ProcessesAPI::ProcessesAPI(content::BrowserContext* context)
438 : browser_context_(context) { 387 : browser_context_(context) {
439 EventRouter* event_router = EventRouter::Get(browser_context_); 388 EventRouter* event_router = EventRouter::Get(browser_context_);
440 // Monitor when the following events are being listened to in order to know 389 // Monitor when the following events are being listened to in order to know
441 // when to start the task manager. 390 // when to start the task manager.
442 event_router->RegisterObserver(this, api::processes::OnUpdated::kEventName); 391 event_router->RegisterObserver(this, api::processes::OnUpdated::kEventName);
443 event_router->RegisterObserver( 392 event_router->RegisterObserver(
444 this, api::processes::OnUpdatedWithMemory::kEventName); 393 this, api::processes::OnUpdatedWithMemory::kEventName);
445 event_router->RegisterObserver(this, api::processes::OnCreated::kEventName); 394 event_router->RegisterObserver(this, api::processes::OnCreated::kEventName);
446 event_router->RegisterObserver(this, 395 event_router->RegisterObserver(this,
447 api::processes::OnUnresponsive::kEventName); 396 api::processes::OnUnresponsive::kEventName);
448 event_router->RegisterObserver(this, api::processes::OnExited::kEventName); 397 event_router->RegisterObserver(this, api::processes::OnExited::kEventName);
449 } 398 }
450 399
451 ProcessesAPI::~ProcessesAPI() { 400 ProcessesAPI::~ProcessesAPI() {
401 // This object has already been unregistered as an observer in Shutdown().
452 } 402 }
453 403
454 void ProcessesAPI::Shutdown() {
455 EventRouter::Get(browser_context_)->UnregisterObserver(this);
456 }
457
458 static base::LazyInstance<BrowserContextKeyedAPIFactory<ProcessesAPI> >
459 g_factory = LAZY_INSTANCE_INITIALIZER;
460
461 // static 404 // static
462 BrowserContextKeyedAPIFactory<ProcessesAPI>* 405 BrowserContextKeyedAPIFactory<ProcessesAPI>*
463 ProcessesAPI::GetFactoryInstance() { 406 ProcessesAPI::GetFactoryInstance() {
464 return g_factory.Pointer(); 407 return g_processes_api_factory.Pointer();
465 } 408 }
466 409
467 // static 410 // static
468 ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) { 411 ProcessesAPI* ProcessesAPI::Get(content::BrowserContext* context) {
469 return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context); 412 return BrowserContextKeyedAPIFactory<ProcessesAPI>::Get(context);
470 } 413 }
471 414
415 void ProcessesAPI::Shutdown() {
416 EventRouter::Get(browser_context_)->UnregisterObserver(this);
417 }
418
419 void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) {
420 // The ProcessesEventRouter will observe the TaskManager as long as there are
421 // listeners for the processes.onUpdated/.onUpdatedWithMemory/.onCreated ...
422 // etc. events.
423 processes_event_router()->ListenerAdded();
424 }
425
426 void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) {
427 // If a processes.onUpdated/.onUpdatedWithMemory/.onCreated ... etc. event
428 // listener is removed (or a process with one exits), then we let the
429 // extension API know that it has one fewer listener.
430 processes_event_router()->ListenerRemoved();
431 }
432
472 ProcessesEventRouter* ProcessesAPI::processes_event_router() { 433 ProcessesEventRouter* ProcessesAPI::processes_event_router() {
473 if (!processes_event_router_) 434 if (!processes_event_router_.get())
474 processes_event_router_.reset(new ProcessesEventRouter(browser_context_)); 435 processes_event_router_.reset(new ProcessesEventRouter(browser_context_));
475 return processes_event_router_.get(); 436 return processes_event_router_.get();
476 } 437 }
477 438
478 void ProcessesAPI::OnListenerAdded(const EventListenerInfo& details) {
479 // We lazily tell the TaskManager to start updating when listeners to the
480 // processes.onUpdated or processes.onUpdatedWithMemory events arrive.
481 processes_event_router()->ListenerAdded();
482 }
483
484 void ProcessesAPI::OnListenerRemoved(const EventListenerInfo& details) {
485 // If a processes.onUpdated or processes.onUpdatedWithMemory event listener
486 // is removed (or a process with one exits), then we let the extension API
487 // know that it has one fewer listener.
488 processes_event_router()->ListenerRemoved();
489 }
490
491 //////////////////////////////////////////////////////////////////////////////// 439 ////////////////////////////////////////////////////////////////////////////////
492 // ProcessesGetProcessIdForTabFunction: 440 // ProcessesGetProcessIdForTabFunction:
493 //////////////////////////////////////////////////////////////////////////////// 441 ////////////////////////////////////////////////////////////////////////////////
494 442
495 ProcessesGetProcessIdForTabFunction::ProcessesGetProcessIdForTabFunction()
496 : tab_id_(-1) {
497 }
498
499 ExtensionFunction::ResponseAction ProcessesGetProcessIdForTabFunction::Run() { 443 ExtensionFunction::ResponseAction ProcessesGetProcessIdForTabFunction::Run() {
500 #if defined(ENABLE_TASK_MANAGER) 444 // For this function, the task manager doesn't even need to be running.
501 scoped_ptr<api::processes::GetProcessIdForTab::Params> params( 445 scoped_ptr<api::processes::GetProcessIdForTab::Params> params(
502 api::processes::GetProcessIdForTab::Params::Create(*args_)); 446 api::processes::GetProcessIdForTab::Params::Create(*args_));
503 EXTENSION_FUNCTION_VALIDATE(params.get()); 447 EXTENSION_FUNCTION_VALIDATE(params.get());
504 tab_id_ = params->tab_id; 448
505 449 const int tab_id = params->tab_id;
506 if (tab_id_ < 0) { 450 content::WebContents* contents = nullptr;
507 return RespondNow(Error(errors::kInavlidArgument,
508 base::IntToString(tab_id_)));
509 }
510
511 // Add a reference, which is balanced in GetProcessIdForTab to keep the object
512 // around and allow for the callback to be invoked.
513 AddRef();
514
515 // If the task manager is already listening, just post a task to execute
516 // which will invoke the callback once we have returned from this function.
517 // Otherwise, wait for the notification that the task manager is done with
518 // the data gathering.
519 if (ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
520 ->processes_event_router()
521 ->is_task_manager_listening()) {
522 base::ThreadTaskRunnerHandle::Get()->PostTask(
523 FROM_HERE,
524 base::Bind(&ProcessesGetProcessIdForTabFunction::GetProcessIdForTab,
525 this));
526 } else {
527 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
528 base::Bind(&ProcessesGetProcessIdForTabFunction::GetProcessIdForTab,
529 this));
530
531 ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
532 ->processes_event_router()
533 ->StartTaskManagerListening();
534 }
535
536 return RespondLater();
537 #else
538 return RespondNow(Error(errors::kExtensionNotSupported));
539 #endif // defined(ENABLE_TASK_MANAGER)
540 }
541
542 void ProcessesGetProcessIdForTabFunction::GetProcessIdForTab() {
543 content::WebContents* contents = NULL;
544 int tab_index = -1; 451 int tab_index = -1;
545 if (!ExtensionTabUtil::GetTabById( 452 if (!ExtensionTabUtil::GetTabById(
546 tab_id_, 453 tab_id,
547 Profile::FromBrowserContext(browser_context()), 454 Profile::FromBrowserContext(browser_context()),
548 include_incognito(), 455 include_incognito(),
549 nullptr, 456 nullptr,
550 nullptr, 457 nullptr,
551 &contents, 458 &contents,
552 &tab_index)) { 459 &tab_index)) {
553 Respond(Error(tabs_constants::kTabNotFoundError, 460 return RespondNow(Error(tabs_constants::kTabNotFoundError,
554 base::IntToString(tab_id_))); 461 base::IntToString(tab_id)));
555 } else { 462 }
556 int process_id = contents->GetRenderProcessHost()->GetID(); 463
557 Respond(ArgumentList( 464 const int process_id = contents->GetRenderProcessHost()->GetID();
558 api::processes::GetProcessIdForTab::Results::Create(process_id))); 465 return RespondNow(ArgumentList(
559 } 466 api::processes::GetProcessIdForTab::Results::Create(process_id)));
560 467 }
561 // Balance the AddRef in the Run. 468
562 Release(); 469 ////////////////////////////////////////////////////////////////////////////////
563 } 470 // ProcessesTerminateFunction:
564 471 ////////////////////////////////////////////////////////////////////////////////
565 ////////////////////////////////////////////////////////////////////////////////
566 // ProcessesTerminateFunction
567 ////////////////////////////////////////////////////////////////////////////////
568
569 ProcessesTerminateFunction::ProcessesTerminateFunction() : process_id_(-1) {
570 }
571 472
572 ExtensionFunction::ResponseAction ProcessesTerminateFunction::Run() { 473 ExtensionFunction::ResponseAction ProcessesTerminateFunction::Run() {
573 #if defined(ENABLE_TASK_MANAGER) 474 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
475
476 // For this function, the task manager doesn't even need to be running.
574 scoped_ptr<api::processes::Terminate::Params> params( 477 scoped_ptr<api::processes::Terminate::Params> params(
575 api::processes::Terminate::Params::Create(*args_)); 478 api::processes::Terminate::Params::Create(*args_));
576 EXTENSION_FUNCTION_VALIDATE(params.get()); 479 EXTENSION_FUNCTION_VALIDATE(params.get());
577 process_id_ = params->process_id; 480
578 481 child_process_host_id_ = params->process_id;
579 // Add a reference, which is balanced in TerminateProcess to keep the object 482 if (child_process_host_id_ < 0) {
580 // around and allow for the callback to be invoked. 483 return RespondNow(Error(errors::kInvalidArgument,
581 AddRef(); 484 base::IntToString(child_process_host_id_)));
582 485 } else if (child_process_host_id_ == 0) {
583 // If the task manager is already listening, just post a task to execute 486 // Cannot kill the browser process.
584 // which will invoke the callback once we have returned from this function. 487 return RespondNow(Error(errors::kNotAllowedToTerminate,
585 // Otherwise, wait for the notification that the task manager is done with 488 base::IntToString(child_process_host_id_)));
586 // the data gathering. 489 }
587 if (ProcessesAPI::Get(Profile::FromBrowserContext(browser_context())) 490
588 ->processes_event_router() 491 // Check if it's a renderer.
589 ->is_task_manager_listening()) { 492 auto* render_process_host =
590 base::ThreadTaskRunnerHandle::Get()->PostTask( 493 content::RenderProcessHost::FromID(child_process_host_id_);
591 FROM_HERE, base::Bind(&ProcessesTerminateFunction::TerminateProcess, 494 if (render_process_host)
592 this)); 495 return RespondNow(TerminateIfAllowed(render_process_host->GetHandle()));
593 } else { 496
594 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback( 497 // This could be a non-renderer child process like a plugin or a nacl
595 base::Bind(&ProcessesTerminateFunction::TerminateProcess, this)); 498 // process. Try to get its handle from the BrowserChildProcessHost on the
596 499 // IO thread.
597 ProcessesAPI::Get(Profile::FromBrowserContext(browser_context())) 500 content::BrowserThread::PostTaskAndReplyWithResult(
598 ->processes_event_router() 501 content::BrowserThread::IO,
599 ->StartTaskManagerListening(); 502 FROM_HERE,
600 } 503 base::Bind(&ProcessesTerminateFunction::GetProcessHandleOnIO,
601 504 this,
505 child_process_host_id_),
506 base::Bind(&ProcessesTerminateFunction::OnProcessHandleOnUI, this));
507
508 // Promise to respond later.
602 return RespondLater(); 509 return RespondLater();
603 #else 510 }
604 return RespondNow(Error(errors::kExtensionNotSupported)); 511
605 #endif // defined(ENABLE_TASK_MANAGER) 512 base::ProcessHandle ProcessesTerminateFunction::GetProcessHandleOnIO(
606 } 513 int child_process_host_id) const {
607 514 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
608 void ProcessesTerminateFunction::TerminateProcess() { 515
609 #if defined(ENABLE_TASK_MANAGER) 516 auto* host = content::BrowserChildProcessHost::FromID(child_process_host_id);
610 TaskManagerModel* model = TaskManager::GetInstance()->model(); 517 if (host)
611 518 return host->GetData().handle;
612 bool found = false; 519
613 for (int i = 0, count = model->ResourceCount(); i < count; ++i) { 520 return base::kNullProcessHandle;
614 if (model->IsResourceFirstInGroup(i) && 521 }
615 process_id_ == model->GetUniqueChildProcessId(i)) { 522
616 base::ProcessHandle process_handle = model->GetProcess(i); 523 void ProcessesTerminateFunction::OnProcessHandleOnUI(
617 if (process_handle == base::GetCurrentProcessHandle()) { 524 base::ProcessHandle handle) {
618 // Cannot kill the browser process. 525 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
619 // TODO(kalman): Are there other sensitive processes? 526
620 Respond(Error(errors::kNotAllowedToTerminate, 527 Respond(TerminateIfAllowed(handle));
621 base::IntToString(process_id_))); 528 }
622 } else { 529
623 base::Process process = 530 ExtensionFunction::ResponseValue
624 base::Process::DeprecatedGetProcessFromHandle(process_handle); 531 ProcessesTerminateFunction::TerminateIfAllowed(base::ProcessHandle handle) {
625 bool did_terminate = 532 if (handle == base::kNullProcessHandle) {
626 process.Terminate(content::RESULT_CODE_KILLED, true); 533 return Error(errors::kProcessNotFound,
627 if (did_terminate) 534 base::IntToString(child_process_host_id_));
628 UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1); 535 }
629 536
630 Respond(ArgumentList( 537 if (handle == base::GetCurrentProcessHandle()) {
631 api::processes::Terminate::Results::Create(did_terminate))); 538 // Cannot kill the browser process.
632 } 539 return Error(errors::kNotAllowedToTerminate,
633 found = true; 540 base::IntToString(child_process_host_id_));
634 break; 541 }
635 } 542
636 } 543 base::Process process = base::Process::Open(base::GetProcId(handle));
637 544 if (!process.IsValid()) {
638 if (!found) 545 return Error(errors::kProcessNotFound,
639 Respond(Error(errors::kProcessNotFound, base::IntToString(process_id_))); 546 base::IntToString(child_process_host_id_));
640 547 }
641 // Balance the AddRef in the Run. 548
642 Release(); 549 const bool did_terminate =
643 #endif // defined(ENABLE_TASK_MANAGER) 550 process.Terminate(content::RESULT_CODE_KILLED, true /* wait */);
644 } 551 if (did_terminate)
645 552 UMA_HISTOGRAM_COUNTS("ChildProcess.KilledByExtensionAPI", 1);
646 //////////////////////////////////////////////////////////////////////////////// 553
647 // ProcessesGetProcessInfoFunction 554 return ArgumentList(
555 api::processes::Terminate::Results::Create(did_terminate));
556 }
557
558 ////////////////////////////////////////////////////////////////////////////////
559 // ProcessesGetProcessInfoFunction:
648 //////////////////////////////////////////////////////////////////////////////// 560 ////////////////////////////////////////////////////////////////////////////////
649 561
650 ProcessesGetProcessInfoFunction::ProcessesGetProcessInfoFunction() 562 ProcessesGetProcessInfoFunction::ProcessesGetProcessInfoFunction()
651 #if defined(ENABLE_TASK_MANAGER) 563 : task_management::TaskManagerObserver(
652 : memory_(false) 564 base::TimeDelta::FromSeconds(1),
653 #endif 565 GetRefreshTypesFlagOnlyEssentialData()) {
654 {
655 } 566 }
656 567
657 ExtensionFunction::ResponseAction ProcessesGetProcessInfoFunction::Run() { 568 ExtensionFunction::ResponseAction ProcessesGetProcessInfoFunction::Run() {
658 #if defined(ENABLE_TASK_MANAGER)
659 scoped_ptr<api::processes::GetProcessInfo::Params> params( 569 scoped_ptr<api::processes::GetProcessInfo::Params> params(
660 api::processes::GetProcessInfo::Params::Create(*args_)); 570 api::processes::GetProcessInfo::Params::Create(*args_));
661 EXTENSION_FUNCTION_VALIDATE(params.get()); 571 EXTENSION_FUNCTION_VALIDATE(params.get());
662 if (params->process_ids.as_integer) 572 if (params->process_ids.as_integer)
663 process_ids_.push_back(*params->process_ids.as_integer); 573 process_host_ids_.push_back(*params->process_ids.as_integer);
664 else 574 else
665 process_ids_.swap(*params->process_ids.as_integers); 575 process_host_ids_.swap(*params->process_ids.as_integers);
666 576
667 memory_ = params->include_memory; 577 include_memory_ = params->include_memory;
668 578 if (include_memory_)
669 // Add a reference, which is balanced in GatherProcessInfo to keep the object 579 AddRefreshType(task_management::REFRESH_TYPE_MEMORY);
670 // around and allow for the callback to be invoked. 580
581 // Keep this object alive until the first of either OnTasksRefreshed() or
582 // OnTasksRefreshedWithBackgroundCalculations() is received depending on
583 // |include_memory_|.
671 AddRef(); 584 AddRef();
672 585
673 // If the task manager is already listening, just post a task to execute 586 // The task manager needs to be enabled for this function.
674 // which will invoke the callback once we have returned from this function. 587 // Start observing the task manager and wait for the next refresh event.
675 // Otherwise, wait for the notification that the task manager is done with 588 task_management::TaskManagerInterface::GetTaskManager()->AddObserver(this);
676 // the data gathering.
677 if (ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
678 ->processes_event_router()
679 ->is_task_manager_listening()) {
680 base::ThreadTaskRunnerHandle::Get()->PostTask(
681 FROM_HERE,
682 base::Bind(&ProcessesGetProcessInfoFunction::GatherProcessInfo, this));
683 } else {
684 TaskManager::GetInstance()->model()->RegisterOnDataReadyCallback(
685 base::Bind(&ProcessesGetProcessInfoFunction::GatherProcessInfo, this));
686
687 ProcessesAPI::Get(Profile::FromBrowserContext(browser_context()))
688 ->processes_event_router()
689 ->StartTaskManagerListening();
690 }
691 589
692 return RespondLater(); 590 return RespondLater();
693 #else 591 }
694 return RespondNow(Error(errors::kExtensionNotSupported)); 592
695 #endif // defined(ENABLE_TASK_MANAGER) 593 void ProcessesGetProcessInfoFunction::OnTasksRefreshed(
696 } 594 const task_management::TaskIdList& task_ids) {
697 595 // Memory is background calculated and will be ready when
698 ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() { 596 // OnTasksRefreshedWithBackgroundCalculations() is invoked.
699 } 597 if (include_memory_)
700 598 return;
701 void ProcessesGetProcessInfoFunction::GatherProcessInfo() { 599
702 #if defined(ENABLE_TASK_MANAGER) 600 GatherDataAndRespond(task_ids);
703 TaskManagerModel* model = TaskManager::GetInstance()->model(); 601 }
704 api::processes::GetProcessInfo::Results::Processes processes; 602
705 603 void
604 ProcessesGetProcessInfoFunction::OnTasksRefreshedWithBackgroundCalculations(
605 const task_management::TaskIdList& task_ids) {
606 if (!include_memory_)
607 return;
608
609 GatherDataAndRespond(task_ids);
610 }
611
612 ProcessesGetProcessInfoFunction::~ProcessesGetProcessInfoFunction() {}
613
614 void ProcessesGetProcessInfoFunction::GatherDataAndRespond(
615 const task_management::TaskIdList& task_ids) {
706 // If there are no process IDs specified, it means we need to return all of 616 // If there are no process IDs specified, it means we need to return all of
707 // the ones we know of. 617 // the ones we know of.
708 if (process_ids_.size() == 0) { 618 const bool specific_processes_requested = !process_host_ids_.empty();
709 int resources = model->ResourceCount(); 619 std::set<base::ProcessId> seen_processes;
710 for (int i = 0; i < resources; ++i) { 620 // Create the results object as defined in the generated API from process.idl
711 if (model->IsResourceFirstInGroup(i)) { 621 // and fill it with the processes info.
712 int id = model->GetUniqueChildProcessId(i); 622 api::processes::GetProcessInfo::Results::Processes processes;
713 api::processes::Process process; 623 for (const auto& task_id : task_ids) {
714 FillProcessData(id, model, i, false, &process); 624 const base::ProcessId proc_id =
715 if (memory_) 625 observed_task_manager()->GetProcessId(task_id);
716 AddMemoryDetails(model, i, &process); 626 if (seen_processes.count(proc_id))
717 processes.additional_properties.Set(base::IntToString(id), 627 continue;
718 process.ToValue()); 628
719 } 629 const int child_process_host_id =
630 observed_task_manager()->GetChildProcessUniqueId(task_id);
631 // Ignore tasks that don't have a valid child process host ID like ARC
632 // processes. We report the browser process info here though.
633 if (child_process_host_id == content::ChildProcessHost::kInvalidUniqueID)
634 continue;
635
636 if (specific_processes_requested) {
637 // Note: we can't use |!process_host_ids_.empty()| directly in the above
638 // condition as we will erase from |process_host_ids_| below.
639 auto itr = std::find(process_host_ids_.begin(),
640 process_host_ids_.end(),
641 child_process_host_id);
642 if (itr == process_host_ids_.end())
643 continue;
644
645 // If found, we remove it from |process_host_ids|, so that at the end if
646 // anything remains in |process_host_ids|, those were invalid arguments
647 // that will be reported on the console.
648 process_host_ids_.erase(itr);
720 } 649 }
721 } else { 650
722 int resources = model->ResourceCount(); 651 seen_processes.insert(proc_id);
723 for (int i = 0; i < resources; ++i) { 652
724 if (model->IsResourceFirstInGroup(i)) { 653 // We do not include the optional data in this function results.
725 int id = model->GetUniqueChildProcessId(i); 654 api::processes::Process process;
726 std::vector<int>::iterator proc_id = std::find(process_ids_.begin(), 655 FillProcessData(task_id,
727 process_ids_.end(), id); 656 observed_task_manager(),
728 if (proc_id != process_ids_.end()) { 657 false, // include_optional
729 api::processes::Process process; 658 &process);
730 FillProcessData(id, model, i, false, &process); 659
731 if (memory_) 660 if (include_memory_) {
732 AddMemoryDetails(model, i, &process); 661 // Append the private memory usage to the process data.
733 processes.additional_properties.Set(base::IntToString(id), 662 const int64_t private_memory =
734 process.ToValue()); 663 observed_task_manager()->GetPrivateMemoryUsage(task_id);
735 664 process.private_memory.reset(new double(static_cast<double>(
736 process_ids_.erase(proc_id); 665 private_memory)));
737 if (process_ids_.size() == 0)
738 break;
739 }
740 }
741 } 666 }
742 // If not all processes were found, log them to the extension's console to 667
743 // help the developer, but don't fail the API call. 668 // Store each process indexed by the string version of its
744 for (int pid : process_ids_) { 669 // ChildProcessHost ID.
745 WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR, 670 processes.additional_properties.Set(
746 ErrorUtils::FormatErrorMessage(errors::kProcessNotFound, 671 base::IntToString(child_process_host_id),
747 base::IntToString(pid))); 672 process.ToValue());
748 } 673 }
674
675 // Report the invalid host ids sent in the arguments.
676 for (const auto& host_id : process_host_ids_) {
677 WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR,
678 ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
679 base::IntToString(host_id)));
749 } 680 }
750 681
751 // Send the response. 682 // Send the response.
752 Respond(ArgumentList( 683 Respond(ArgumentList(
753 api::processes::GetProcessInfo::Results::Create(processes))); 684 api::processes::GetProcessInfo::Results::Create(processes)));
754 685
755 // Balance the AddRef in the Run. 686 // Stop observing the task manager, and balance the AddRef() in Run().
687 task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(this);
756 Release(); 688 Release();
757 #endif // defined(ENABLE_TASK_MANAGER)
758 } 689 }
759 690
760 } // namespace extensions 691 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/api/processes/processes_api.h ('k') | chrome/browser/extensions/api/processes/processes_apitest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698