| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/task_manager/child_process_resource_provider.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <utility> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/i18n/rtl.h" | |
| 12 #include "base/macros.h" | |
| 13 #include "base/memory/weak_ptr.h" | |
| 14 #include "base/strings/string16.h" | |
| 15 #include "chrome/browser/process_resource_usage.h" | |
| 16 #include "chrome/browser/task_manager/resource_provider.h" | |
| 17 #include "chrome/browser/task_manager/task_manager.h" | |
| 18 #include "chrome/grit/generated_resources.h" | |
| 19 #include "components/nacl/common/nacl_process_type.h" | |
| 20 #include "content/public/browser/browser_child_process_host.h" | |
| 21 #include "content/public/browser/browser_child_process_host_iterator.h" | |
| 22 #include "content/public/browser/browser_thread.h" | |
| 23 #include "content/public/browser/child_process_data.h" | |
| 24 #include "grit/theme_resources.h" | |
| 25 #include "services/shell/public/cpp/interface_provider.h" | |
| 26 #include "ui/base/l10n/l10n_util.h" | |
| 27 #include "ui/base/resource/resource_bundle.h" | |
| 28 #include "ui/gfx/image/image_skia.h" | |
| 29 | |
| 30 using content::BrowserChildProcessHostIterator; | |
| 31 using content::BrowserThread; | |
| 32 using content::WebContents; | |
| 33 | |
| 34 namespace task_manager { | |
| 35 | |
| 36 class ChildProcessResource : public Resource { | |
| 37 public: | |
| 38 ChildProcessResource(int process_type, | |
| 39 const base::string16& name, | |
| 40 base::ProcessHandle handle, | |
| 41 int unique_process_id); | |
| 42 ~ChildProcessResource() override; | |
| 43 | |
| 44 // Resource methods: | |
| 45 base::string16 GetTitle() const override; | |
| 46 base::string16 GetProfileName() const override; | |
| 47 gfx::ImageSkia GetIcon() const override; | |
| 48 base::ProcessHandle GetProcess() const override; | |
| 49 int GetUniqueChildProcessId() const override; | |
| 50 Type GetType() const override; | |
| 51 bool SupportNetworkUsage() const override; | |
| 52 void SetSupportNetworkUsage() override; | |
| 53 void Refresh() override; | |
| 54 bool ReportsV8MemoryStats() const override; | |
| 55 size_t GetV8MemoryAllocated() const override; | |
| 56 size_t GetV8MemoryUsed() const override; | |
| 57 | |
| 58 // Returns the pid of the child process. | |
| 59 int process_id() const { return pid_; } | |
| 60 | |
| 61 private: | |
| 62 // Returns a localized title for the child process. For example, a plugin | |
| 63 // process would be "Plugin: Flash" when name is "Flash". | |
| 64 base::string16 GetLocalizedTitle() const; | |
| 65 | |
| 66 static void ConnectResourceReporterOnIOThread( | |
| 67 int id, | |
| 68 mojo::InterfaceRequest<mojom::ResourceUsageReporter> req); | |
| 69 | |
| 70 int process_type_; | |
| 71 base::string16 name_; | |
| 72 base::ProcessHandle handle_; | |
| 73 int pid_; | |
| 74 int unique_process_id_; | |
| 75 mutable base::string16 title_; | |
| 76 bool network_usage_support_; | |
| 77 std::unique_ptr<ProcessResourceUsage> resource_usage_; | |
| 78 | |
| 79 // The icon painted for the child processs. | |
| 80 // TODO(jcampan): we should have plugin specific icons for well-known | |
| 81 // plugins. | |
| 82 static gfx::ImageSkia* default_icon_; | |
| 83 | |
| 84 DISALLOW_COPY_AND_ASSIGN(ChildProcessResource); | |
| 85 }; | |
| 86 | |
| 87 gfx::ImageSkia* ChildProcessResource::default_icon_ = NULL; | |
| 88 | |
| 89 // static | |
| 90 void ChildProcessResource::ConnectResourceReporterOnIOThread( | |
| 91 int id, | |
| 92 mojo::InterfaceRequest<mojom::ResourceUsageReporter> req) { | |
| 93 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 94 content::BrowserChildProcessHost* host = | |
| 95 content::BrowserChildProcessHost::FromID(id); | |
| 96 if (!host || !host->GetRemoteInterfaces()) | |
| 97 return; | |
| 98 | |
| 99 host->GetRemoteInterfaces()->GetInterface(std::move(req)); | |
| 100 } | |
| 101 | |
| 102 ChildProcessResource::ChildProcessResource(int process_type, | |
| 103 const base::string16& name, | |
| 104 base::ProcessHandle handle, | |
| 105 int unique_process_id) | |
| 106 : process_type_(process_type), | |
| 107 name_(name), | |
| 108 handle_(handle), | |
| 109 unique_process_id_(unique_process_id), | |
| 110 network_usage_support_(false) { | |
| 111 // We cache the process id because it's not cheap to calculate, and it won't | |
| 112 // be available when we get the plugin disconnected notification. | |
| 113 pid_ = base::GetProcId(handle); | |
| 114 if (!default_icon_) { | |
| 115 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 116 default_icon_ = rb.GetImageSkiaNamed(IDR_PLUGINS_FAVICON); | |
| 117 // TODO(jabdelmalek): use different icon for web workers. | |
| 118 } | |
| 119 mojom::ResourceUsageReporterPtr service; | |
| 120 mojo::InterfaceRequest<mojom::ResourceUsageReporter> request = | |
| 121 mojo::GetProxy(&service); | |
| 122 BrowserThread::PostTask( | |
| 123 BrowserThread::IO, FROM_HERE, | |
| 124 base::Bind(&ChildProcessResource::ConnectResourceReporterOnIOThread, | |
| 125 unique_process_id, base::Passed(&request))); | |
| 126 resource_usage_.reset(new ProcessResourceUsage(std::move(service))); | |
| 127 } | |
| 128 | |
| 129 ChildProcessResource::~ChildProcessResource() { | |
| 130 } | |
| 131 | |
| 132 // Resource methods: | |
| 133 base::string16 ChildProcessResource::GetTitle() const { | |
| 134 if (title_.empty()) | |
| 135 title_ = GetLocalizedTitle(); | |
| 136 | |
| 137 return title_; | |
| 138 } | |
| 139 | |
| 140 base::string16 ChildProcessResource::GetProfileName() const { | |
| 141 return base::string16(); | |
| 142 } | |
| 143 | |
| 144 gfx::ImageSkia ChildProcessResource::GetIcon() const { | |
| 145 return *default_icon_; | |
| 146 } | |
| 147 | |
| 148 base::ProcessHandle ChildProcessResource::GetProcess() const { | |
| 149 return handle_; | |
| 150 } | |
| 151 | |
| 152 int ChildProcessResource::GetUniqueChildProcessId() const { | |
| 153 return unique_process_id_; | |
| 154 } | |
| 155 | |
| 156 Resource::Type ChildProcessResource::GetType() const { | |
| 157 // Translate types to Resource::Type, since ChildProcessData's type | |
| 158 // is not available for all TaskManager resources. | |
| 159 switch (process_type_) { | |
| 160 case content::PROCESS_TYPE_PPAPI_PLUGIN: | |
| 161 case content::PROCESS_TYPE_PPAPI_BROKER: | |
| 162 return Resource::PLUGIN; | |
| 163 case content::PROCESS_TYPE_UTILITY: | |
| 164 return Resource::UTILITY; | |
| 165 case content::PROCESS_TYPE_ZYGOTE: | |
| 166 return Resource::ZYGOTE; | |
| 167 case content::PROCESS_TYPE_SANDBOX_HELPER: | |
| 168 return Resource::SANDBOX_HELPER; | |
| 169 case content::PROCESS_TYPE_GPU: | |
| 170 return Resource::GPU; | |
| 171 case PROCESS_TYPE_NACL_LOADER: | |
| 172 case PROCESS_TYPE_NACL_BROKER: | |
| 173 return Resource::NACL; | |
| 174 default: | |
| 175 return Resource::UNKNOWN; | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 bool ChildProcessResource::SupportNetworkUsage() const { | |
| 180 return network_usage_support_; | |
| 181 } | |
| 182 | |
| 183 void ChildProcessResource::SetSupportNetworkUsage() { | |
| 184 network_usage_support_ = true; | |
| 185 } | |
| 186 | |
| 187 base::string16 ChildProcessResource::GetLocalizedTitle() const { | |
| 188 base::string16 title = name_; | |
| 189 if (title.empty()) { | |
| 190 switch (process_type_) { | |
| 191 case content::PROCESS_TYPE_PPAPI_PLUGIN: | |
| 192 case content::PROCESS_TYPE_PPAPI_BROKER: | |
| 193 title = l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME); | |
| 194 break; | |
| 195 default: | |
| 196 // Nothing to do for non-plugin processes. | |
| 197 break; | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 // Explicitly mark name as LTR if there is no strong RTL character, | |
| 202 // to avoid the wrong concatenation result similar to "!Yahoo Mail: the | |
| 203 // best web-based Email: NIGULP", in which "NIGULP" stands for the Hebrew | |
| 204 // or Arabic word for "plugin". | |
| 205 base::i18n::AdjustStringForLocaleDirection(&title); | |
| 206 | |
| 207 switch (process_type_) { | |
| 208 case content::PROCESS_TYPE_UTILITY: | |
| 209 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX, title); | |
| 210 case content::PROCESS_TYPE_GPU: | |
| 211 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_GPU_PREFIX); | |
| 212 case content::PROCESS_TYPE_PPAPI_PLUGIN: | |
| 213 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_PREFIX, title); | |
| 214 case content::PROCESS_TYPE_PPAPI_BROKER: | |
| 215 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_BROKER_PREFIX, | |
| 216 title); | |
| 217 case PROCESS_TYPE_NACL_BROKER: | |
| 218 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NACL_BROKER_PREFIX); | |
| 219 case PROCESS_TYPE_NACL_LOADER: | |
| 220 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_NACL_PREFIX, title); | |
| 221 // These types don't need display names or get them from elsewhere. | |
| 222 case content::PROCESS_TYPE_BROWSER: | |
| 223 case content::PROCESS_TYPE_RENDERER: | |
| 224 case content::PROCESS_TYPE_ZYGOTE: | |
| 225 case content::PROCESS_TYPE_SANDBOX_HELPER: | |
| 226 case content::PROCESS_TYPE_MAX: | |
| 227 NOTREACHED(); | |
| 228 break; | |
| 229 case content::PROCESS_TYPE_UNKNOWN: | |
| 230 NOTREACHED() << "Need localized name for child process type."; | |
| 231 } | |
| 232 | |
| 233 return title; | |
| 234 } | |
| 235 | |
| 236 void ChildProcessResource::Refresh() { | |
| 237 if (resource_usage_) | |
| 238 resource_usage_->Refresh(base::Closure()); | |
| 239 } | |
| 240 | |
| 241 bool ChildProcessResource::ReportsV8MemoryStats() const { | |
| 242 if (resource_usage_) | |
| 243 return resource_usage_->ReportsV8MemoryStats(); | |
| 244 return false; | |
| 245 } | |
| 246 | |
| 247 size_t ChildProcessResource::GetV8MemoryAllocated() const { | |
| 248 if (resource_usage_) | |
| 249 return resource_usage_->GetV8MemoryAllocated(); | |
| 250 return 0; | |
| 251 } | |
| 252 | |
| 253 size_t ChildProcessResource::GetV8MemoryUsed() const { | |
| 254 if (resource_usage_) | |
| 255 return resource_usage_->GetV8MemoryUsed(); | |
| 256 return 0; | |
| 257 } | |
| 258 | |
| 259 //////////////////////////////////////////////////////////////////////////////// | |
| 260 // ChildProcessResourceProvider class | |
| 261 //////////////////////////////////////////////////////////////////////////////// | |
| 262 | |
| 263 ChildProcessResourceProvider:: | |
| 264 ChildProcessResourceProvider(TaskManager* task_manager) | |
| 265 : task_manager_(task_manager), | |
| 266 updating_(false) { | |
| 267 } | |
| 268 | |
| 269 ChildProcessResourceProvider::~ChildProcessResourceProvider() { | |
| 270 } | |
| 271 | |
| 272 Resource* ChildProcessResourceProvider::GetResource( | |
| 273 int origin_pid, | |
| 274 int child_id, | |
| 275 int route_id) { | |
| 276 PidResourceMap::iterator iter = pid_to_resources_.find(origin_pid); | |
| 277 if (iter != pid_to_resources_.end()) | |
| 278 return iter->second; | |
| 279 else | |
| 280 return NULL; | |
| 281 } | |
| 282 | |
| 283 void ChildProcessResourceProvider::StartUpdating() { | |
| 284 DCHECK(!updating_); | |
| 285 updating_ = true; | |
| 286 | |
| 287 // Get the existing child processes. | |
| 288 BrowserThread::PostTask( | |
| 289 BrowserThread::IO, FROM_HERE, | |
| 290 base::Bind( | |
| 291 &ChildProcessResourceProvider::RetrieveChildProcessData, | |
| 292 this)); | |
| 293 | |
| 294 BrowserChildProcessObserver::Add(this); | |
| 295 } | |
| 296 | |
| 297 void ChildProcessResourceProvider::StopUpdating() { | |
| 298 DCHECK(updating_); | |
| 299 updating_ = false; | |
| 300 | |
| 301 // Delete all the resources. | |
| 302 STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); | |
| 303 | |
| 304 resources_.clear(); | |
| 305 pid_to_resources_.clear(); | |
| 306 | |
| 307 BrowserChildProcessObserver::Remove(this); | |
| 308 } | |
| 309 | |
| 310 void ChildProcessResourceProvider::BrowserChildProcessHostConnected( | |
| 311 const content::ChildProcessData& data) { | |
| 312 DCHECK(updating_); | |
| 313 | |
| 314 if (resources_.count(data.handle)) { | |
| 315 // The case may happen that we have added a child_process_info as part of | |
| 316 // the iteration performed during StartUpdating() call but the notification | |
| 317 // that it has connected was not fired yet. So when the notification | |
| 318 // happens, we already know about this plugin and just ignore it. | |
| 319 return; | |
| 320 } | |
| 321 AddToTaskManager(data); | |
| 322 } | |
| 323 | |
| 324 void ChildProcessResourceProvider:: | |
| 325 BrowserChildProcessHostDisconnected( | |
| 326 const content::ChildProcessData& data) { | |
| 327 DCHECK(updating_); | |
| 328 | |
| 329 ChildProcessMap::iterator iter = resources_.find(data.handle); | |
| 330 if (iter == resources_.end()) { | |
| 331 // ChildProcessData disconnection notifications are asynchronous, so we | |
| 332 // might be notified for a plugin we don't know anything about (if it was | |
| 333 // closed before the task manager was shown and destroyed after that). | |
| 334 return; | |
| 335 } | |
| 336 // Remove the resource from the Task Manager. | |
| 337 ChildProcessResource* resource = iter->second; | |
| 338 task_manager_->RemoveResource(resource); | |
| 339 // Remove it from the provider. | |
| 340 resources_.erase(iter); | |
| 341 // Remove it from our pid map. | |
| 342 PidResourceMap::iterator pid_iter = | |
| 343 pid_to_resources_.find(resource->process_id()); | |
| 344 DCHECK(pid_iter != pid_to_resources_.end()); | |
| 345 if (pid_iter != pid_to_resources_.end()) | |
| 346 pid_to_resources_.erase(pid_iter); | |
| 347 | |
| 348 // Finally, delete the resource. | |
| 349 delete resource; | |
| 350 } | |
| 351 | |
| 352 void ChildProcessResourceProvider::AddToTaskManager( | |
| 353 const content::ChildProcessData& child_process_data) { | |
| 354 ChildProcessResource* resource = | |
| 355 new ChildProcessResource( | |
| 356 child_process_data.process_type, | |
| 357 child_process_data.name, | |
| 358 child_process_data.handle, | |
| 359 child_process_data.id); | |
| 360 resources_[child_process_data.handle] = resource; | |
| 361 pid_to_resources_[resource->process_id()] = resource; | |
| 362 task_manager_->AddResource(resource); | |
| 363 } | |
| 364 | |
| 365 // The ChildProcessData::Iterator has to be used from the IO thread. | |
| 366 void ChildProcessResourceProvider::RetrieveChildProcessData() { | |
| 367 std::vector<content::ChildProcessData> child_processes; | |
| 368 for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { | |
| 369 // Only add processes which are already started, since we need their handle. | |
| 370 if (iter.GetData().handle == base::kNullProcessHandle) | |
| 371 continue; | |
| 372 child_processes.push_back(iter.GetData()); | |
| 373 } | |
| 374 // Now notify the UI thread that we have retrieved information about child | |
| 375 // processes. | |
| 376 BrowserThread::PostTask( | |
| 377 BrowserThread::UI, FROM_HERE, | |
| 378 base::Bind( | |
| 379 &ChildProcessResourceProvider::ChildProcessDataRetreived, | |
| 380 this, child_processes)); | |
| 381 } | |
| 382 | |
| 383 // This is called on the UI thread. | |
| 384 void ChildProcessResourceProvider::ChildProcessDataRetreived( | |
| 385 const std::vector<content::ChildProcessData>& child_processes) { | |
| 386 for (size_t i = 0; i < child_processes.size(); ++i) | |
| 387 AddToTaskManager(child_processes[i]); | |
| 388 | |
| 389 task_manager_->model()->NotifyDataReady(); | |
| 390 } | |
| 391 | |
| 392 } // namespace task_manager | |
| OLD | NEW |