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