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.
5 #include "chrome/browser/task_manager/child_process_resource_provider.h"
9 #include "base/i18n/rtl.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/strings/string16.h"
12 #include "chrome/browser/process_resource_usage.h"
13 #include "chrome/browser/task_manager/resource_provider.h"
14 #include "chrome/browser/task_manager/task_manager.h"
15 #include "chrome/grit/generated_resources.h"
16 #include "components/nacl/common/nacl_process_type.h"
17 #include "content/public/browser/browser_child_process_host.h"
18 #include "content/public/browser/browser_child_process_host_iterator.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/child_process_data.h"
21 #include "content/public/common/service_registry.h"
22 #include "grit/theme_resources.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/image/image_skia.h"
27 using content::BrowserChildProcessHostIterator
;
28 using content::BrowserThread
;
29 using content::WebContents
;
31 namespace task_manager
{
33 class ChildProcessResource
: public Resource
{
35 ChildProcessResource(int process_type
,
36 const base::string16
& name
,
37 base::ProcessHandle handle
,
38 int unique_process_id
);
39 ~ChildProcessResource() override
;
42 base::string16
GetTitle() const override
;
43 base::string16
GetProfileName() const override
;
44 gfx::ImageSkia
GetIcon() const override
;
45 base::ProcessHandle
GetProcess() const override
;
46 int GetUniqueChildProcessId() const override
;
47 Type
GetType() const override
;
48 bool SupportNetworkUsage() const override
;
49 void SetSupportNetworkUsage() override
;
50 void Refresh() override
;
51 bool ReportsV8MemoryStats() const override
;
52 size_t GetV8MemoryAllocated() const override
;
53 size_t GetV8MemoryUsed() const override
;
55 // Returns the pid of the child process.
56 int process_id() const { return pid_
; }
59 // Returns a localized title for the child process. For example, a plugin
60 // process would be "Plugin: Flash" when name is "Flash".
61 base::string16
GetLocalizedTitle() const;
63 static void ConnectResourceReporterOnIOThread(
64 int id
, mojo::InterfaceRequest
<ResourceUsageReporter
> req
);
68 base::ProcessHandle handle_
;
70 int unique_process_id_
;
71 mutable base::string16 title_
;
72 bool network_usage_support_
;
73 scoped_ptr
<ProcessResourceUsage
> resource_usage_
;
75 // The icon painted for the child processs.
76 // TODO(jcampan): we should have plugin specific icons for well-known
78 static gfx::ImageSkia
* default_icon_
;
80 DISALLOW_COPY_AND_ASSIGN(ChildProcessResource
);
83 gfx::ImageSkia
* ChildProcessResource::default_icon_
= NULL
;
86 void ChildProcessResource::ConnectResourceReporterOnIOThread(
87 int id
, mojo::InterfaceRequest
<ResourceUsageReporter
> req
) {
88 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
89 content::BrowserChildProcessHost
* host
=
90 content::BrowserChildProcessHost::FromID(id
);
94 content::ServiceRegistry
* registry
= host
->GetServiceRegistry();
98 registry
->ConnectToRemoteService(req
.Pass());
101 ChildProcessResource::ChildProcessResource(int process_type
,
102 const base::string16
& name
,
103 base::ProcessHandle handle
,
104 int unique_process_id
)
105 : process_type_(process_type
),
108 unique_process_id_(unique_process_id
),
109 network_usage_support_(false) {
110 // We cache the process id because it's not cheap to calculate, and it won't
111 // be available when we get the plugin disconnected notification.
112 pid_
= base::GetProcId(handle
);
113 if (!default_icon_
) {
114 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
115 default_icon_
= rb
.GetImageSkiaNamed(IDR_PLUGINS_FAVICON
);
116 // TODO(jabdelmalek): use different icon for web workers.
118 ResourceUsageReporterPtr service
;
119 mojo::InterfaceRequest
<ResourceUsageReporter
> request
=
120 mojo::GetProxy(&service
);
121 BrowserThread::PostTask(
122 BrowserThread::IO
, FROM_HERE
,
123 base::Bind(&ChildProcessResource::ConnectResourceReporterOnIOThread
,
124 unique_process_id
, base::Passed(&request
)));
125 resource_usage_
.reset(new ProcessResourceUsage(service
.Pass()));
128 ChildProcessResource::~ChildProcessResource() {
132 base::string16
ChildProcessResource::GetTitle() const {
134 title_
= GetLocalizedTitle();
139 base::string16
ChildProcessResource::GetProfileName() const {
140 return base::string16();
143 gfx::ImageSkia
ChildProcessResource::GetIcon() const {
144 return *default_icon_
;
147 base::ProcessHandle
ChildProcessResource::GetProcess() const {
151 int ChildProcessResource::GetUniqueChildProcessId() const {
152 return unique_process_id_
;
155 Resource::Type
ChildProcessResource::GetType() const {
156 // Translate types to Resource::Type, since ChildProcessData's type
157 // is not available for all TaskManager resources.
158 switch (process_type_
) {
159 case content::PROCESS_TYPE_PLUGIN
:
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
;
175 return Resource::UNKNOWN
;
179 bool ChildProcessResource::SupportNetworkUsage() const {
180 return network_usage_support_
;
183 void ChildProcessResource::SetSupportNetworkUsage() {
184 network_usage_support_
= true;
187 base::string16
ChildProcessResource::GetLocalizedTitle() const {
188 base::string16 title
= name_
;
190 switch (process_type_
) {
191 case content::PROCESS_TYPE_PLUGIN
:
192 case content::PROCESS_TYPE_PPAPI_PLUGIN
:
193 case content::PROCESS_TYPE_PPAPI_BROKER
:
194 title
= l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME
);
197 // Nothing to do for non-plugin processes.
202 // Explicitly mark name as LTR if there is no strong RTL character,
203 // to avoid the wrong concatenation result similar to "!Yahoo Mail: the
204 // best web-based Email: NIGULP", in which "NIGULP" stands for the Hebrew
205 // or Arabic word for "plugin".
206 base::i18n::AdjustStringForLocaleDirection(&title
);
208 switch (process_type_
) {
209 case content::PROCESS_TYPE_UTILITY
:
210 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX
, title
);
211 case content::PROCESS_TYPE_GPU
:
212 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_GPU_PREFIX
);
213 case content::PROCESS_TYPE_PLUGIN
:
214 case content::PROCESS_TYPE_PPAPI_PLUGIN
:
215 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_PREFIX
, title
);
216 case content::PROCESS_TYPE_PPAPI_BROKER
:
217 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_BROKER_PREFIX
,
219 case PROCESS_TYPE_NACL_BROKER
:
220 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NACL_BROKER_PREFIX
);
221 case PROCESS_TYPE_NACL_LOADER
:
222 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_NACL_PREFIX
, title
);
223 // These types don't need display names or get them from elsewhere.
224 case content::PROCESS_TYPE_BROWSER
:
225 case content::PROCESS_TYPE_RENDERER
:
226 case content::PROCESS_TYPE_ZYGOTE
:
227 case content::PROCESS_TYPE_SANDBOX_HELPER
:
228 case content::PROCESS_TYPE_MAX
:
231 case content::PROCESS_TYPE_UNKNOWN
:
232 NOTREACHED() << "Need localized name for child process type.";
238 void ChildProcessResource::Refresh() {
240 resource_usage_
->Refresh(base::Closure());
243 bool ChildProcessResource::ReportsV8MemoryStats() const {
245 return resource_usage_
->ReportsV8MemoryStats();
249 size_t ChildProcessResource::GetV8MemoryAllocated() const {
251 return resource_usage_
->GetV8MemoryAllocated();
255 size_t ChildProcessResource::GetV8MemoryUsed() const {
257 return resource_usage_
->GetV8MemoryUsed();
261 ////////////////////////////////////////////////////////////////////////////////
262 // ChildProcessResourceProvider class
263 ////////////////////////////////////////////////////////////////////////////////
265 ChildProcessResourceProvider::
266 ChildProcessResourceProvider(TaskManager
* task_manager
)
267 : task_manager_(task_manager
),
271 ChildProcessResourceProvider::~ChildProcessResourceProvider() {
274 Resource
* ChildProcessResourceProvider::GetResource(
278 PidResourceMap::iterator iter
= pid_to_resources_
.find(origin_pid
);
279 if (iter
!= pid_to_resources_
.end())
285 void ChildProcessResourceProvider::StartUpdating() {
289 // Get the existing child processes.
290 BrowserThread::PostTask(
291 BrowserThread::IO
, FROM_HERE
,
293 &ChildProcessResourceProvider::RetrieveChildProcessData
,
296 BrowserChildProcessObserver::Add(this);
299 void ChildProcessResourceProvider::StopUpdating() {
303 // Delete all the resources.
304 STLDeleteContainerPairSecondPointers(resources_
.begin(), resources_
.end());
307 pid_to_resources_
.clear();
309 BrowserChildProcessObserver::Remove(this);
312 void ChildProcessResourceProvider::BrowserChildProcessHostConnected(
313 const content::ChildProcessData
& data
) {
316 if (resources_
.count(data
.handle
)) {
317 // The case may happen that we have added a child_process_info as part of
318 // the iteration performed during StartUpdating() call but the notification
319 // that it has connected was not fired yet. So when the notification
320 // happens, we already know about this plugin and just ignore it.
323 AddToTaskManager(data
);
326 void ChildProcessResourceProvider::
327 BrowserChildProcessHostDisconnected(
328 const content::ChildProcessData
& data
) {
331 ChildProcessMap::iterator iter
= resources_
.find(data
.handle
);
332 if (iter
== resources_
.end()) {
333 // ChildProcessData disconnection notifications are asynchronous, so we
334 // might be notified for a plugin we don't know anything about (if it was
335 // closed before the task manager was shown and destroyed after that).
338 // Remove the resource from the Task Manager.
339 ChildProcessResource
* resource
= iter
->second
;
340 task_manager_
->RemoveResource(resource
);
341 // Remove it from the provider.
342 resources_
.erase(iter
);
343 // Remove it from our pid map.
344 PidResourceMap::iterator pid_iter
=
345 pid_to_resources_
.find(resource
->process_id());
346 DCHECK(pid_iter
!= pid_to_resources_
.end());
347 if (pid_iter
!= pid_to_resources_
.end())
348 pid_to_resources_
.erase(pid_iter
);
350 // Finally, delete the resource.
354 void ChildProcessResourceProvider::AddToTaskManager(
355 const content::ChildProcessData
& child_process_data
) {
356 ChildProcessResource
* resource
=
357 new ChildProcessResource(
358 child_process_data
.process_type
,
359 child_process_data
.name
,
360 child_process_data
.handle
,
361 child_process_data
.id
);
362 resources_
[child_process_data
.handle
] = resource
;
363 pid_to_resources_
[resource
->process_id()] = resource
;
364 task_manager_
->AddResource(resource
);
367 // The ChildProcessData::Iterator has to be used from the IO thread.
368 void ChildProcessResourceProvider::RetrieveChildProcessData() {
369 std::vector
<content::ChildProcessData
> child_processes
;
370 for (BrowserChildProcessHostIterator iter
; !iter
.Done(); ++iter
) {
371 // Only add processes which are already started, since we need their handle.
372 if (iter
.GetData().handle
== base::kNullProcessHandle
)
374 child_processes
.push_back(iter
.GetData());
376 // Now notify the UI thread that we have retrieved information about child
378 BrowserThread::PostTask(
379 BrowserThread::UI
, FROM_HERE
,
381 &ChildProcessResourceProvider::ChildProcessDataRetreived
,
382 this, child_processes
));
385 // This is called on the UI thread.
386 void ChildProcessResourceProvider::ChildProcessDataRetreived(
387 const std::vector
<content::ChildProcessData
>& child_processes
) {
388 for (size_t i
= 0; i
< child_processes
.size(); ++i
)
389 AddToTaskManager(child_processes
[i
]);
391 task_manager_
->model()->NotifyDataReady();
394 } // namespace task_manager