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> | |
| 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 |
| OLD | NEW |