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 "content/public/browser/navigation_controller.h"
35 #include "content/public/browser/navigation_entry.h"
36 #include "content/public/browser/render_process_host.h"
37 #include "content/public/browser/render_view_host.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/browser/web_ui.h"
40 #include "content/public/common/service_registry.h"
42 #if defined(ENABLE_PRINT_PREVIEW)
43 #include "chrome/browser/printing/background_printing_manager.h"
46 using content::BrowserThread
;
52 class ProcessDetails
: public MemoryDetails
{
54 typedef base::Callback
<void(const ProcessData
&)> DataCallback
;
55 explicit ProcessDetails(const DataCallback
& callback
)
56 : callback_(callback
) {}
59 void OnDetailsAvailable() override
{ callback_
.Run(*ChromeBrowser()); }
62 ~ProcessDetails() override
{}
64 DataCallback callback_
;
66 DISALLOW_COPY_AND_ASSIGN(ProcessDetails
);
69 base::DictionaryValue
* FindProcessFromPid(base::ListValue
* processes
,
70 base::ProcessId pid
) {
71 const size_t n
= processes
->GetSize();
72 for (size_t i
= 0; i
< n
; ++i
) {
73 base::DictionaryValue
* process
;
74 if (!processes
->GetDictionary(i
, &process
))
77 if (process
->GetInteger("pid", &id
) && id
== static_cast<int>(pid
))
83 void GetAllWebContents(std::set
<content::WebContents
*>* web_contents
) {
84 // Add all the existing WebContentses.
85 #if defined(OS_ANDROID)
86 for (TabModelList::const_iterator iter
= TabModelList::begin();
87 iter
!= TabModelList::end(); ++iter
) {
88 TabModel
* model
= *iter
;
89 for (int i
= 0; i
< model
->GetTabCount(); ++i
) {
90 content::WebContents
* tab_web_contents
= model
->GetWebContentsAt(i
);
92 web_contents
->insert(tab_web_contents
);
96 for (TabContentsIterator iter
; !iter
.done(); iter
.Next())
97 web_contents
->insert(*iter
);
99 // Add all the prerender pages.
100 std::vector
<Profile
*> profiles(
101 g_browser_process
->profile_manager()->GetLoadedProfiles());
102 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
103 prerender::PrerenderManager
* prerender_manager
=
104 prerender::PrerenderManagerFactory::GetForProfile(profiles
[i
]);
105 if (!prerender_manager
)
107 const std::vector
<content::WebContents
*> contentses
=
108 prerender_manager
->GetAllPrerenderingContents();
109 web_contents
->insert(contentses
.begin(), contentses
.end());
111 #if defined(ENABLE_PRINT_PREVIEW)
112 // Add all the pages being background printed.
113 printing::BackgroundPrintingManager
* printing_manager
=
114 g_browser_process
->background_printing_manager();
115 std::set
<content::WebContents
*> printing_contents
=
116 printing_manager
->CurrentContentSet();
117 web_contents
->insert(printing_contents
.begin(), printing_contents
.end());
123 class RendererDetails
{
125 typedef base::Callback
<void(const base::ProcessId pid
,
126 const size_t v8_allocated
,
127 const size_t v8_used
)> V8DataCallback
;
129 explicit RendererDetails(const V8DataCallback
& callback
)
130 : callback_(callback
), weak_factory_(this) {}
131 ~RendererDetails() {}
134 for (std::set
<content::WebContents
*>::iterator iter
= web_contents_
.begin();
135 iter
!= web_contents_
.end(); ++iter
) {
136 auto rph
= (*iter
)->GetRenderViewHost()->GetProcess();
137 auto resource_usage
= resource_usage_reporters_
[(*iter
)];
138 DCHECK(resource_usage
.get());
139 resource_usage
->Refresh(base::Bind(&RendererDetails::OnRefreshDone
,
140 weak_factory_
.GetWeakPtr(), *iter
,
141 base::GetProcId(rph
->GetHandle())));
145 void AddWebContents(content::WebContents
* content
) {
146 web_contents_
.insert(content
);
148 auto rph
= content
->GetRenderViewHost()->GetProcess();
149 content::ServiceRegistry
* service_registry
= rph
->GetServiceRegistry();
150 ResourceUsageReporterPtr service
;
151 if (service_registry
)
152 service_registry
->ConnectToRemoteService(mojo::GetProxy(&service
));
153 resource_usage_reporters_
.insert(std::make_pair(
154 content
, make_linked_ptr(new ProcessResourceUsage(service
.Pass()))));
155 DCHECK_EQ(web_contents_
.size(), resource_usage_reporters_
.size());
159 web_contents_
.clear();
160 resource_usage_reporters_
.clear();
161 weak_factory_
.InvalidateWeakPtrs();
164 void RemoveWebContents(base::ProcessId
) {
165 // This function should only be called once for each WebContents, so this
166 // should be non-empty every time it's called.
167 DCHECK(!web_contents_
.empty());
169 // We don't have to detect which content is the caller of this method.
170 if (!web_contents_
.empty())
171 web_contents_
.erase(web_contents_
.begin());
175 return web_contents_
.empty();
179 void OnRefreshDone(content::WebContents
* content
, const base::ProcessId pid
) {
180 auto iter
= resource_usage_reporters_
.find(content
);
181 DCHECK(iter
!= resource_usage_reporters_
.end());
182 linked_ptr
<ProcessResourceUsage
> usage
= iter
->second
;
183 callback_
.Run(pid
, usage
->GetV8MemoryAllocated(), usage
->GetV8MemoryUsed());
186 V8DataCallback callback_
;
187 std::set
<content::WebContents
*> web_contents_
; // This class does not own
188 std::map
<content::WebContents
*, linked_ptr
<ProcessResourceUsage
>>
189 resource_usage_reporters_
;
191 base::WeakPtrFactory
<RendererDetails
> weak_factory_
;
193 DISALLOW_COPY_AND_ASSIGN(RendererDetails
);
196 MemoryInternalsProxy::MemoryInternalsProxy()
197 : information_(new base::DictionaryValue()),
198 renderer_details_(new RendererDetails(
199 base::Bind(&MemoryInternalsProxy::OnRendererAvailable
, this))) {}
201 void MemoryInternalsProxy::Attach(MemoryInternalsHandler
* handler
) {
202 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
206 void MemoryInternalsProxy::Detach() {
207 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
211 void MemoryInternalsProxy::StartFetch(const base::ListValue
* list
) {
212 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
214 // Clear previous information before fetching new information.
215 information_
->Clear();
216 scoped_refptr
<ProcessDetails
> process(new ProcessDetails(
217 base::Bind(&MemoryInternalsProxy::OnProcessAvailable
, this)));
218 process
->StartFetch(MemoryDetails::FROM_CHROME_ONLY
);
221 MemoryInternalsProxy::~MemoryInternalsProxy() {}
223 void MemoryInternalsProxy::RequestRendererDetails() {
224 renderer_details_
->Clear();
226 #if defined(OS_ANDROID)
227 for (TabModelList::const_iterator iter
= TabModelList::begin();
228 iter
!= TabModelList::end(); ++iter
) {
229 TabModel
* model
= *iter
;
230 for (int i
= 0; i
< model
->GetTabCount(); ++i
) {
231 content::WebContents
* tab_web_contents
= model
->GetWebContentsAt(i
);
232 if (tab_web_contents
)
233 renderer_details_
->AddWebContents(tab_web_contents
);
237 for (TabContentsIterator iter
; !iter
.done(); iter
.Next())
238 renderer_details_
->AddWebContents(*iter
);
241 renderer_details_
->Request();
244 void MemoryInternalsProxy::OnProcessAvailable(const ProcessData
& browser
) {
245 base::ListValue
* process_info
= new base::ListValue();
246 base::ListValue
* extension_info
= new base::ListValue();
247 information_
->Set("processes", process_info
);
248 information_
->Set("extensions", extension_info
);
249 for (PMIIterator iter
= browser
.processes
.begin();
250 iter
!= browser
.processes
.end(); ++iter
) {
251 base::DictionaryValue
* process
= new base::DictionaryValue();
252 if (iter
->renderer_type
== ProcessMemoryInformation::RENDERER_EXTENSION
)
253 extension_info
->Append(process
);
255 process_info
->Append(process
);
257 // From MemoryDetails.
258 process
->SetInteger("pid", iter
->pid
);
259 process
->SetString("type",
260 ProcessMemoryInformation::GetFullTypeNameInEnglish(
261 iter
->process_type
, iter
->renderer_type
));
262 process
->SetInteger("memory_private", iter
->working_set
.priv
);
264 base::ListValue
* titles
= new base::ListValue();
265 process
->Set("titles", titles
);
266 for (size_t i
= 0; i
< iter
->titles
.size(); ++i
)
267 titles
->AppendString(iter
->titles
[i
]);
270 std::set
<content::WebContents
*> web_contents
;
271 GetAllWebContents(&web_contents
);
272 ConvertTabsInformation(web_contents
, process_info
);
274 RequestRendererDetails();
277 void MemoryInternalsProxy::OnRendererAvailable(const base::ProcessId pid
,
278 const size_t v8_allocated
,
279 const size_t v8_used
) {
280 // Do not update while no renderers are registered.
281 if (renderer_details_
->IsClean())
284 base::ListValue
* processes
;
285 if (!information_
->GetList("processes", &processes
))
288 const size_t size
= processes
->GetSize();
289 for (size_t i
= 0; i
< size
; ++i
) {
290 base::DictionaryValue
* process
;
291 processes
->GetDictionary(i
, &process
);
293 if (!process
->GetInteger("pid", &id
) || id
!= static_cast<int>(pid
))
295 // Convert units from Bytes to KiB.
296 process
->SetInteger("v8_alloc", v8_allocated
/ 1024);
297 process
->SetInteger("v8_used", v8_used
/ 1024);
301 renderer_details_
->RemoveWebContents(pid
);
302 if (renderer_details_
->IsClean())
306 void MemoryInternalsProxy::ConvertTabsInformation(
307 const std::set
<content::WebContents
*>& web_contents
,
308 base::ListValue
* processes
) {
309 for (std::set
<content::WebContents
*>::const_iterator
310 iter
= web_contents
.begin(); iter
!= web_contents
.end(); ++iter
) {
311 content::WebContents
* web
= *iter
;
312 content::RenderProcessHost
* process_host
= web
->GetRenderProcessHost();
316 // Find which process renders the web contents.
317 const base::ProcessId pid
= base::GetProcId(process_host
->GetHandle());
318 base::DictionaryValue
* process
= FindProcessFromPid(processes
, pid
);
322 // Prepare storage to register navigation histories.
323 base::ListValue
* tabs
;
324 if (!process
->GetList("history", &tabs
)) {
325 tabs
= new base::ListValue();
326 process
->Set("history", tabs
);
329 base::DictionaryValue
* tab
= new base::DictionaryValue();
332 base::ListValue
* histories
= new base::ListValue();
333 tab
->Set("history", histories
);
335 const content::NavigationController
& controller
= web
->GetController();
336 const int entry_size
= controller
.GetEntryCount();
337 for (int i
= 0; i
< entry_size
; ++i
) {
338 content::NavigationEntry
*entry
= controller
.GetEntryAtIndex(i
);
339 base::DictionaryValue
* history
= new base::DictionaryValue();
340 histories
->Append(history
);
341 history
->SetString("url", entry
->GetURL().spec());
342 history
->SetString("title", entry
->GetTitle());
343 history
->SetInteger("time", (base::Time::Now() -
344 entry
->GetTimestamp()).InSeconds());
346 tab
->SetInteger("index", controller
.GetCurrentEntryIndex());
350 void MemoryInternalsProxy::FinishCollection() {
351 information_
->SetInteger("uptime", base::SysInfo::Uptime());
352 information_
->SetString("os", base::SysInfo::OperatingSystemName());
353 information_
->SetString("os_version",
354 base::SysInfo::OperatingSystemVersion());
356 CallJavaScriptFunctionOnUIThread("g_main_view.onSetSnapshot", *information_
);
359 void MemoryInternalsProxy::CallJavaScriptFunctionOnUIThread(
360 const std::string
& function
, const base::Value
& args
) {
361 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
363 std::vector
<const base::Value
*> args_vector(1, &args
);
364 base::string16 update
=
365 content::WebUI::GetJavascriptCall(function
, args_vector
);
366 // Don't forward updates to a destructed UI.
368 handler_
->OnUpdate(update
);