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

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

Powered by Google App Engine
This is Rietveld 408576698