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/ui/webui/memory_internals/memory_internals_proxy.h"
13 #include "base/bind.h"
14 #include "base/memory/linked_ptr.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/strings/string16.h"
17 #include "base/sys_info.h"
18 #include "base/values.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/memory_details.h"
22 #include "chrome/browser/prerender/prerender_manager.h"
23 #include "chrome/browser/prerender/prerender_manager_factory.h"
24 #include "chrome/browser/process_resource_usage.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/profiles/profile_manager.h"
27 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
28 #include "chrome/browser/ui/android/tab_model/tab_model.h"
29 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
30 #include "chrome/browser/ui/browser.h"
31 #include "chrome/browser/ui/browser_iterator.h"
32 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
33 #include "chrome/browser/ui/webui/memory_internals/memory_internals_handler.h"
34 #include "chrome/common/render_messages.h"
35 #include "content/public/browser/navigation_controller.h"
36 #include "content/public/browser/navigation_entry.h"
37 #include "content/public/browser/render_process_host.h"
38 #include "content/public/browser/render_view_host.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/browser/web_ui.h"
41 #include "content/public/common/service_registry.h"
43 #if defined(ENABLE_PRINT_PREVIEW)
44 #include "chrome/browser/printing/background_printing_manager.h"
47 using content::BrowserThread
;
53 class ProcessDetails
: public MemoryDetails
{
55 typedef base::Callback
<void(const ProcessData
&)> DataCallback
;
56 explicit ProcessDetails(const DataCallback
& callback
)
57 : callback_(callback
) {}
60 void OnDetailsAvailable() override
{ callback_
.Run(*ChromeBrowser()); }
63 ~ProcessDetails() override
{}
65 DataCallback callback_
;
67 DISALLOW_COPY_AND_ASSIGN(ProcessDetails
);
70 base::DictionaryValue
* FindProcessFromPid(base::ListValue
* processes
,
71 base::ProcessId pid
) {
72 const size_t n
= processes
->GetSize();
73 for (size_t i
= 0; i
< n
; ++i
) {
74 base::DictionaryValue
* process
;
75 if (!processes
->GetDictionary(i
, &process
))
78 if (process
->GetInteger("pid", &id
) && id
== static_cast<int>(pid
))
84 void GetAllWebContents(std::set
<content::WebContents
*>* web_contents
) {
85 // Add all the existing WebContentses.
86 #if defined(OS_ANDROID)
87 for (TabModelList::const_iterator iter
= TabModelList::begin();
88 iter
!= TabModelList::end(); ++iter
) {
89 TabModel
* model
= *iter
;
90 for (int i
= 0; i
< model
->GetTabCount(); ++i
) {
91 content::WebContents
* tab_web_contents
= model
->GetWebContentsAt(i
);
93 web_contents
->insert(tab_web_contents
);
97 for (TabContentsIterator iter
; !iter
.done(); iter
.Next())
98 web_contents
->insert(*iter
);
100 // Add all the prerender pages.
101 std::vector
<Profile
*> profiles(
102 g_browser_process
->profile_manager()->GetLoadedProfiles());
103 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
104 prerender::PrerenderManager
* prerender_manager
=
105 prerender::PrerenderManagerFactory::GetForProfile(profiles
[i
]);
106 if (!prerender_manager
)
108 const std::vector
<content::WebContents
*> contentses
=
109 prerender_manager
->GetAllPrerenderingContents();
110 web_contents
->insert(contentses
.begin(), contentses
.end());
112 #if defined(ENABLE_PRINT_PREVIEW)
113 // Add all the pages being background printed.
114 printing::BackgroundPrintingManager
* printing_manager
=
115 g_browser_process
->background_printing_manager();
116 std::set
<content::WebContents
*> printing_contents
=
117 printing_manager
->CurrentContentSet();
118 web_contents
->insert(printing_contents
.begin(), printing_contents
.end());
124 class RendererDetails
{
126 typedef base::Callback
<void(const base::ProcessId pid
,
127 const size_t v8_allocated
,
128 const size_t v8_used
)> V8DataCallback
;
130 explicit RendererDetails(const V8DataCallback
& callback
)
131 : callback_(callback
), weak_factory_(this) {}
132 ~RendererDetails() {}
135 for (std::set
<content::WebContents
*>::iterator iter
= web_contents_
.begin();
136 iter
!= web_contents_
.end(); ++iter
) {
137 auto rph
= (*iter
)->GetRenderViewHost()->GetProcess();
138 auto resource_usage
= resource_usage_reporters_
[(*iter
)];
139 DCHECK(resource_usage
.get());
140 resource_usage
->Refresh(base::Bind(&RendererDetails::OnRefreshDone
,
141 weak_factory_
.GetWeakPtr(), *iter
,
142 base::GetProcId(rph
->GetHandle())));
146 void AddWebContents(content::WebContents
* content
) {
147 web_contents_
.insert(content
);
149 auto rph
= content
->GetRenderViewHost()->GetProcess();
150 content::ServiceRegistry
* service_registry
= rph
->GetServiceRegistry();
151 ResourceUsageReporterPtr service
;
152 if (service_registry
)
153 service_registry
->ConnectToRemoteService(mojo::GetProxy(&service
));
154 resource_usage_reporters_
.insert(std::make_pair(
155 content
, make_linked_ptr(new ProcessResourceUsage(service
.Pass()))));
156 DCHECK_EQ(web_contents_
.size(), resource_usage_reporters_
.size());
160 web_contents_
.clear();
161 resource_usage_reporters_
.clear();
162 weak_factory_
.InvalidateWeakPtrs();
165 void RemoveWebContents(base::ProcessId
) {
166 // This function should only be called once for each WebContents, so this
167 // should be non-empty every time it's called.
168 DCHECK(!web_contents_
.empty());
170 // We don't have to detect which content is the caller of this method.
171 if (!web_contents_
.empty())
172 web_contents_
.erase(web_contents_
.begin());
176 return web_contents_
.empty();
180 void OnRefreshDone(content::WebContents
* content
, const base::ProcessId pid
) {
181 auto iter
= resource_usage_reporters_
.find(content
);
182 DCHECK(iter
!= resource_usage_reporters_
.end());
183 linked_ptr
<ProcessResourceUsage
> usage
= iter
->second
;
184 callback_
.Run(pid
, usage
->GetV8MemoryAllocated(), usage
->GetV8MemoryUsed());
187 V8DataCallback callback_
;
188 std::set
<content::WebContents
*> web_contents_
; // This class does not own
189 std::map
<content::WebContents
*, linked_ptr
<ProcessResourceUsage
>>
190 resource_usage_reporters_
;
192 base::WeakPtrFactory
<RendererDetails
> weak_factory_
;
194 DISALLOW_COPY_AND_ASSIGN(RendererDetails
);
197 MemoryInternalsProxy::MemoryInternalsProxy()
198 : information_(new base::DictionaryValue()),
199 renderer_details_(new RendererDetails(
200 base::Bind(&MemoryInternalsProxy::OnRendererAvailable
, this))) {}
202 void MemoryInternalsProxy::Attach(MemoryInternalsHandler
* handler
) {
203 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
207 void MemoryInternalsProxy::Detach() {
208 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
212 void MemoryInternalsProxy::StartFetch(const base::ListValue
* list
) {
213 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
215 // Clear previous information before fetching new information.
216 information_
->Clear();
217 scoped_refptr
<ProcessDetails
> process(new ProcessDetails(
218 base::Bind(&MemoryInternalsProxy::OnProcessAvailable
, this)));
219 process
->StartFetch(MemoryDetails::FROM_CHROME_ONLY
);
222 MemoryInternalsProxy::~MemoryInternalsProxy() {}
224 void MemoryInternalsProxy::RequestRendererDetails() {
225 renderer_details_
->Clear();
227 #if defined(OS_ANDROID)
228 for (TabModelList::const_iterator iter
= TabModelList::begin();
229 iter
!= TabModelList::end(); ++iter
) {
230 TabModel
* model
= *iter
;
231 for (int i
= 0; i
< model
->GetTabCount(); ++i
) {
232 content::WebContents
* tab_web_contents
= model
->GetWebContentsAt(i
);
233 if (tab_web_contents
)
234 renderer_details_
->AddWebContents(tab_web_contents
);
238 for (TabContentsIterator iter
; !iter
.done(); iter
.Next())
239 renderer_details_
->AddWebContents(*iter
);
242 renderer_details_
->Request();
245 void MemoryInternalsProxy::OnProcessAvailable(const ProcessData
& browser
) {
246 base::ListValue
* process_info
= new base::ListValue();
247 base::ListValue
* extension_info
= new base::ListValue();
248 information_
->Set("processes", process_info
);
249 information_
->Set("extensions", extension_info
);
250 for (PMIIterator iter
= browser
.processes
.begin();
251 iter
!= browser
.processes
.end(); ++iter
) {
252 base::DictionaryValue
* process
= new base::DictionaryValue();
253 if (iter
->renderer_type
== ProcessMemoryInformation::RENDERER_EXTENSION
)
254 extension_info
->Append(process
);
256 process_info
->Append(process
);
258 // From MemoryDetails.
259 process
->SetInteger("pid", iter
->pid
);
260 process
->SetString("type",
261 ProcessMemoryInformation::GetFullTypeNameInEnglish(
262 iter
->process_type
, iter
->renderer_type
));
263 process
->SetInteger("memory_private", iter
->working_set
.priv
);
265 base::ListValue
* titles
= new base::ListValue();
266 process
->Set("titles", titles
);
267 for (size_t i
= 0; i
< iter
->titles
.size(); ++i
)
268 titles
->AppendString(iter
->titles
[i
]);
271 std::set
<content::WebContents
*> web_contents
;
272 GetAllWebContents(&web_contents
);
273 ConvertTabsInformation(web_contents
, process_info
);
275 RequestRendererDetails();
278 void MemoryInternalsProxy::OnRendererAvailable(const base::ProcessId pid
,
279 const size_t v8_allocated
,
280 const size_t v8_used
) {
281 // Do not update while no renderers are registered.
282 if (renderer_details_
->IsClean())
285 base::ListValue
* processes
;
286 if (!information_
->GetList("processes", &processes
))
289 const size_t size
= processes
->GetSize();
290 for (size_t i
= 0; i
< size
; ++i
) {
291 base::DictionaryValue
* process
;
292 processes
->GetDictionary(i
, &process
);
294 if (!process
->GetInteger("pid", &id
) || id
!= static_cast<int>(pid
))
296 // Convert units from Bytes to KiB.
297 process
->SetInteger("v8_alloc", v8_allocated
/ 1024);
298 process
->SetInteger("v8_used", v8_used
/ 1024);
302 renderer_details_
->RemoveWebContents(pid
);
303 if (renderer_details_
->IsClean())
307 void MemoryInternalsProxy::ConvertTabsInformation(
308 const std::set
<content::WebContents
*>& web_contents
,
309 base::ListValue
* processes
) {
310 for (std::set
<content::WebContents
*>::const_iterator
311 iter
= web_contents
.begin(); iter
!= web_contents
.end(); ++iter
) {
312 content::WebContents
* web
= *iter
;
313 content::RenderProcessHost
* process_host
= web
->GetRenderProcessHost();
317 // Find which process renders the web contents.
318 const base::ProcessId pid
= base::GetProcId(process_host
->GetHandle());
319 base::DictionaryValue
* process
= FindProcessFromPid(processes
, pid
);
323 // Prepare storage to register navigation histories.
324 base::ListValue
* tabs
;
325 if (!process
->GetList("history", &tabs
)) {
326 tabs
= new base::ListValue();
327 process
->Set("history", tabs
);
330 base::DictionaryValue
* tab
= new base::DictionaryValue();
333 base::ListValue
* histories
= new base::ListValue();
334 tab
->Set("history", histories
);
336 const content::NavigationController
& controller
= web
->GetController();
337 const int entry_size
= controller
.GetEntryCount();
338 for (int i
= 0; i
< entry_size
; ++i
) {
339 content::NavigationEntry
*entry
= controller
.GetEntryAtIndex(i
);
340 base::DictionaryValue
* history
= new base::DictionaryValue();
341 histories
->Append(history
);
342 history
->SetString("url", entry
->GetURL().spec());
343 history
->SetString("title", entry
->GetTitle());
344 history
->SetInteger("time", (base::Time::Now() -
345 entry
->GetTimestamp()).InSeconds());
347 tab
->SetInteger("index", controller
.GetCurrentEntryIndex());
351 void MemoryInternalsProxy::FinishCollection() {
352 information_
->SetInteger("uptime", base::SysInfo::Uptime());
353 information_
->SetString("os", base::SysInfo::OperatingSystemName());
354 information_
->SetString("os_version",
355 base::SysInfo::OperatingSystemVersion());
357 CallJavaScriptFunctionOnUIThread("g_main_view.onSetSnapshot", *information_
);
360 void MemoryInternalsProxy::CallJavaScriptFunctionOnUIThread(
361 const std::string
& function
, const base::Value
& args
) {
362 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
364 std::vector
<const base::Value
*> args_vector(1, &args
);
365 base::string16 update
=
366 content::WebUI::GetJavascriptCall(function
, args_vector
);
367 // Don't forward updates to a destructed UI.
369 handler_
->OnUpdate(update
);