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 base::WeakPtrFactory
<ChildProcessResource
> weak_factory_
;
82 DISALLOW_COPY_AND_ASSIGN(ChildProcessResource
);
85 gfx::ImageSkia
* ChildProcessResource::default_icon_
= NULL
;
88 void ChildProcessResource::ConnectResourceReporterOnIOThread(
89 int id
, mojo::InterfaceRequest
<ResourceUsageReporter
> req
) {
90 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
91 content::BrowserChildProcessHost
* host
=
92 content::BrowserChildProcessHost::FromID(id
);
96 content::ServiceRegistry
* registry
= host
->GetServiceRegistry();
100 registry
->ConnectToRemoteService(req
.Pass());
103 ChildProcessResource::ChildProcessResource(int process_type
,
104 const base::string16
& name
,
105 base::ProcessHandle handle
,
106 int unique_process_id
)
107 : process_type_(process_type
),
110 unique_process_id_(unique_process_id
),
111 network_usage_support_(false),
112 weak_factory_(this) {
113 // We cache the process id because it's not cheap to calculate, and it won't
114 // be available when we get the plugin disconnected notification.
115 pid_
= base::GetProcId(handle
);
116 if (!default_icon_
) {
117 ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
118 default_icon_
= rb
.GetImageSkiaNamed(IDR_PLUGINS_FAVICON
);
119 // TODO(jabdelmalek): use different icon for web workers.
121 ResourceUsageReporterPtr service
;
122 mojo::InterfaceRequest
<ResourceUsageReporter
> request
=
123 mojo::GetProxy(&service
);
124 BrowserThread::PostTask(
125 BrowserThread::IO
, FROM_HERE
,
126 base::Bind(&ChildProcessResource::ConnectResourceReporterOnIOThread
,
127 unique_process_id
, base::Passed(&request
)));
128 resource_usage_
.reset(new ProcessResourceUsage(service
.Pass()));
131 ChildProcessResource::~ChildProcessResource() {
135 base::string16
ChildProcessResource::GetTitle() const {
137 title_
= GetLocalizedTitle();
142 base::string16
ChildProcessResource::GetProfileName() const {
143 return base::string16();
146 gfx::ImageSkia
ChildProcessResource::GetIcon() const {
147 return *default_icon_
;
150 base::ProcessHandle
ChildProcessResource::GetProcess() const {
154 int ChildProcessResource::GetUniqueChildProcessId() const {
155 return unique_process_id_
;
158 Resource::Type
ChildProcessResource::GetType() const {
159 // Translate types to Resource::Type, since ChildProcessData's type
160 // is not available for all TaskManager resources.
161 switch (process_type_
) {
162 case content::PROCESS_TYPE_PLUGIN
:
163 case content::PROCESS_TYPE_PPAPI_PLUGIN
:
164 case content::PROCESS_TYPE_PPAPI_BROKER
:
165 return Resource::PLUGIN
;
166 case content::PROCESS_TYPE_UTILITY
:
167 return Resource::UTILITY
;
168 case content::PROCESS_TYPE_ZYGOTE
:
169 return Resource::ZYGOTE
;
170 case content::PROCESS_TYPE_SANDBOX_HELPER
:
171 return Resource::SANDBOX_HELPER
;
172 case content::PROCESS_TYPE_GPU
:
173 return Resource::GPU
;
174 case PROCESS_TYPE_NACL_LOADER
:
175 case PROCESS_TYPE_NACL_BROKER
:
176 return Resource::NACL
;
178 return Resource::UNKNOWN
;
182 bool ChildProcessResource::SupportNetworkUsage() const {
183 return network_usage_support_
;
186 void ChildProcessResource::SetSupportNetworkUsage() {
187 network_usage_support_
= true;
190 base::string16
ChildProcessResource::GetLocalizedTitle() const {
191 base::string16 title
= name_
;
193 switch (process_type_
) {
194 case content::PROCESS_TYPE_PLUGIN
:
195 case content::PROCESS_TYPE_PPAPI_PLUGIN
:
196 case content::PROCESS_TYPE_PPAPI_BROKER
:
197 title
= l10n_util::GetStringUTF16(IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME
);
200 // Nothing to do for non-plugin processes.
205 // Explicitly mark name as LTR if there is no strong RTL character,
206 // to avoid the wrong concatenation result similar to "!Yahoo Mail: the
207 // best web-based Email: NIGULP", in which "NIGULP" stands for the Hebrew
208 // or Arabic word for "plugin".
209 base::i18n::AdjustStringForLocaleDirection(&title
);
211 switch (process_type_
) {
212 case content::PROCESS_TYPE_UTILITY
:
213 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX
, title
);
214 case content::PROCESS_TYPE_GPU
:
215 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_GPU_PREFIX
);
216 case content::PROCESS_TYPE_PLUGIN
:
217 case content::PROCESS_TYPE_PPAPI_PLUGIN
:
218 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_PREFIX
, title
);
219 case content::PROCESS_TYPE_PPAPI_BROKER
:
220 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PLUGIN_BROKER_PREFIX
,
222 case PROCESS_TYPE_NACL_BROKER
:
223 return l10n_util::GetStringUTF16(IDS_TASK_MANAGER_NACL_BROKER_PREFIX
);
224 case PROCESS_TYPE_NACL_LOADER
:
225 return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_NACL_PREFIX
, title
);
226 // These types don't need display names or get them from elsewhere.
227 case content::PROCESS_TYPE_BROWSER
:
228 case content::PROCESS_TYPE_RENDERER
:
229 case content::PROCESS_TYPE_ZYGOTE
:
230 case content::PROCESS_TYPE_SANDBOX_HELPER
:
231 case content::PROCESS_TYPE_MAX
:
234 case content::PROCESS_TYPE_UNKNOWN
:
235 NOTREACHED() << "Need localized name for child process type.";
241 void ChildProcessResource::Refresh() {
243 resource_usage_
->Refresh(base::Closure());
246 bool ChildProcessResource::ReportsV8MemoryStats() const {
248 return resource_usage_
->ReportsV8MemoryStats();
252 size_t ChildProcessResource::GetV8MemoryAllocated() const {
254 return resource_usage_
->GetV8MemoryAllocated();
258 size_t ChildProcessResource::GetV8MemoryUsed() const {
260 return resource_usage_
->GetV8MemoryUsed();
264 ////////////////////////////////////////////////////////////////////////////////
265 // ChildProcessResourceProvider class
266 ////////////////////////////////////////////////////////////////////////////////
268 ChildProcessResourceProvider::
269 ChildProcessResourceProvider(TaskManager
* task_manager
)
270 : task_manager_(task_manager
),
274 ChildProcessResourceProvider::~ChildProcessResourceProvider() {
277 Resource
* ChildProcessResourceProvider::GetResource(
281 PidResourceMap::iterator iter
= pid_to_resources_
.find(origin_pid
);
282 if (iter
!= pid_to_resources_
.end())
288 void ChildProcessResourceProvider::StartUpdating() {
292 // Get the existing child processes.
293 BrowserThread::PostTask(
294 BrowserThread::IO
, FROM_HERE
,
296 &ChildProcessResourceProvider::RetrieveChildProcessData
,
299 BrowserChildProcessObserver::Add(this);
302 void ChildProcessResourceProvider::StopUpdating() {
306 // Delete all the resources.
307 STLDeleteContainerPairSecondPointers(resources_
.begin(), resources_
.end());
310 pid_to_resources_
.clear();
312 BrowserChildProcessObserver::Remove(this);
315 void ChildProcessResourceProvider::BrowserChildProcessHostConnected(
316 const content::ChildProcessData
& data
) {
319 if (resources_
.count(data
.handle
)) {
320 // The case may happen that we have added a child_process_info as part of
321 // the iteration performed during StartUpdating() call but the notification
322 // that it has connected was not fired yet. So when the notification
323 // happens, we already know about this plugin and just ignore it.
326 AddToTaskManager(data
);
329 void ChildProcessResourceProvider::
330 BrowserChildProcessHostDisconnected(
331 const content::ChildProcessData
& data
) {
334 ChildProcessMap::iterator iter
= resources_
.find(data
.handle
);
335 if (iter
== resources_
.end()) {
336 // ChildProcessData disconnection notifications are asynchronous, so we
337 // might be notified for a plugin we don't know anything about (if it was
338 // closed before the task manager was shown and destroyed after that).
341 // Remove the resource from the Task Manager.
342 ChildProcessResource
* resource
= iter
->second
;
343 task_manager_
->RemoveResource(resource
);
344 // Remove it from the provider.
345 resources_
.erase(iter
);
346 // Remove it from our pid map.
347 PidResourceMap::iterator pid_iter
=
348 pid_to_resources_
.find(resource
->process_id());
349 DCHECK(pid_iter
!= pid_to_resources_
.end());
350 if (pid_iter
!= pid_to_resources_
.end())
351 pid_to_resources_
.erase(pid_iter
);
353 // Finally, delete the resource.
357 void ChildProcessResourceProvider::AddToTaskManager(
358 const content::ChildProcessData
& child_process_data
) {
359 ChildProcessResource
* resource
=
360 new ChildProcessResource(
361 child_process_data
.process_type
,
362 child_process_data
.name
,
363 child_process_data
.handle
,
364 child_process_data
.id
);
365 resources_
[child_process_data
.handle
] = resource
;
366 pid_to_resources_
[resource
->process_id()] = resource
;
367 task_manager_
->AddResource(resource
);
370 // The ChildProcessData::Iterator has to be used from the IO thread.
371 void ChildProcessResourceProvider::RetrieveChildProcessData() {
372 std::vector
<content::ChildProcessData
> child_processes
;
373 for (BrowserChildProcessHostIterator iter
; !iter
.Done(); ++iter
) {
374 // Only add processes which are already started, since we need their handle.
375 if (iter
.GetData().handle
== base::kNullProcessHandle
)
377 child_processes
.push_back(iter
.GetData());
379 // Now notify the UI thread that we have retrieved information about child
381 BrowserThread::PostTask(
382 BrowserThread::UI
, FROM_HERE
,
384 &ChildProcessResourceProvider::ChildProcessDataRetreived
,
385 this, child_processes
));
388 // This is called on the UI thread.
389 void ChildProcessResourceProvider::ChildProcessDataRetreived(
390 const std::vector
<content::ChildProcessData
>& child_processes
) {
391 for (size_t i
= 0; i
< child_processes
.size(); ++i
)
392 AddToTaskManager(child_processes
[i
]);
394 task_manager_
->model()->NotifyDataReady();
397 } // namespace task_manager