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"
11 #include "base/bind.h"
12 #include "base/strings/string16.h"
13 #include "base/sys_info.h"
14 #include "base/values.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/memory_details.h"
18 #include "chrome/browser/prerender/prerender_manager.h"
19 #include "chrome/browser/prerender/prerender_manager_factory.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/profiles/profile_manager.h"
22 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
23 #include "chrome/browser/ui/android/tab_model/tab_model.h"
24 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/browser_iterator.h"
27 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
28 #include "chrome/browser/ui/webui/memory_internals/memory_internals_handler.h"
29 #include "chrome/common/render_messages.h"
30 #include "content/public/browser/navigation_controller.h"
31 #include "content/public/browser/navigation_entry.h"
32 #include "content/public/browser/notification_observer.h"
33 #include "content/public/browser/notification_registrar.h"
34 #include "content/public/browser/notification_service.h"
35 #include "content/public/browser/render_process_host.h"
36 #include "content/public/browser/render_view_host.h"
37 #include "content/public/browser/web_contents.h"
38 #include "content/public/browser/web_ui.h"
40 #if defined(ENABLE_FULL_PRINTING)
41 #include "chrome/browser/printing/background_printing_manager.h"
44 using content::BrowserThread
;
50 class ProcessDetails
: public MemoryDetails
{
52 typedef base::Callback
<void(const ProcessData
&)> DataCallback
;
53 explicit ProcessDetails(const DataCallback
& callback
)
54 : callback_(callback
) {}
56 virtual void OnDetailsAvailable() OVERRIDE
{
57 const std::vector
<ProcessData
>& browser_processes
= processes();
59 callback_
.Run(browser_processes
[0]);
63 virtual ~ProcessDetails() {}
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 web_contents
->insert(model
->GetWebContentsAt(i
));
94 for (TabContentsIterator iter
; !iter
.done(); iter
.Next())
95 web_contents
->insert(*iter
);
97 // Add all the prerender pages.
98 std::vector
<Profile
*> profiles(
99 g_browser_process
->profile_manager()->GetLoadedProfiles());
100 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
101 prerender::PrerenderManager
* prerender_manager
=
102 prerender::PrerenderManagerFactory::GetForProfile(profiles
[i
]);
103 if (!prerender_manager
)
105 const std::vector
<content::WebContents
*> contentses
=
106 prerender_manager
->GetAllPrerenderingContents();
107 web_contents
->insert(contentses
.begin(), contentses
.end());
109 #if defined(ENABLE_FULL_PRINTING)
110 // Add all the pages being background printed.
111 printing::BackgroundPrintingManager
* printing_manager
=
112 g_browser_process
->background_printing_manager();
113 std::set
<content::WebContents
*> printing_contents
=
114 printing_manager
->CurrentContentSet();
115 web_contents
->insert(printing_contents
.begin(), printing_contents
.end());
121 class RendererDetails
: public content::NotificationObserver
{
123 typedef base::Callback
<void(const base::ProcessId pid
,
124 const size_t v8_allocated
,
125 const size_t v8_used
)> V8DataCallback
;
127 explicit RendererDetails(const V8DataCallback
& callback
)
128 : callback_(callback
) {
129 registrar_
.Add(this, chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED
,
130 content::NotificationService::AllSources());
132 virtual ~RendererDetails() {}
135 for (std::set
<content::WebContents
*>::iterator iter
= web_contents_
.begin();
136 iter
!= web_contents_
.end(); ++iter
)
137 (*iter
)->GetRenderViewHost()->Send(new ChromeViewMsg_GetV8HeapStats
);
140 void AddWebContents(content::WebContents
* content
) {
141 web_contents_
.insert(content
);
145 web_contents_
.clear();
148 void RemoveWebContents(base::ProcessId
) {
149 // We don't have to detect which content is the caller of this method.
150 if (!web_contents_
.empty())
151 web_contents_
.erase(web_contents_
.begin());
155 return web_contents_
.empty();
159 // NotificationObserver:
160 virtual void Observe(int type
,
161 const content::NotificationSource
& source
,
162 const content::NotificationDetails
& details
) OVERRIDE
{
163 const base::ProcessId
* pid
=
164 content::Source
<const base::ProcessId
>(source
).ptr();
165 const ChromeRenderMessageFilter::V8HeapStatsDetails
* v8_heap
=
166 content::Details
<const ChromeRenderMessageFilter::V8HeapStatsDetails
>(
169 v8_heap
->v8_memory_allocated(),
170 v8_heap
->v8_memory_used());
173 V8DataCallback callback_
;
174 content::NotificationRegistrar registrar_
;
175 std::set
<content::WebContents
*> web_contents_
; // This class does not own
177 DISALLOW_COPY_AND_ASSIGN(RendererDetails
);
180 MemoryInternalsProxy::MemoryInternalsProxy()
181 : information_(new base::DictionaryValue()),
182 renderer_details_(new RendererDetails(
183 base::Bind(&MemoryInternalsProxy::OnRendererAvailable
, this))) {}
185 void MemoryInternalsProxy::Attach(MemoryInternalsHandler
* handler
) {
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
190 void MemoryInternalsProxy::Detach() {
191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
195 void MemoryInternalsProxy::StartFetch(const base::ListValue
* list
) {
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
198 // Clear previous information before fetching new information.
199 information_
->Clear();
200 scoped_refptr
<ProcessDetails
> process(new ProcessDetails(
201 base::Bind(&MemoryInternalsProxy::OnProcessAvailable
, this)));
202 process
->StartFetch(MemoryDetails::SKIP_USER_METRICS
);
205 MemoryInternalsProxy::~MemoryInternalsProxy() {}
207 void MemoryInternalsProxy::RequestRendererDetails() {
208 renderer_details_
->Clear();
210 #if defined(OS_ANDROID)
211 for (TabModelList::const_iterator iter
= TabModelList::begin();
212 iter
!= TabModelList::end(); ++iter
) {
213 TabModel
* model
= *iter
;
214 for (int i
= 0; i
< model
->GetTabCount(); ++i
)
215 renderer_details_
->AddWebContents(model
->GetWebContentsAt(i
));
218 for (TabContentsIterator iter
; !iter
.done(); iter
.Next())
219 renderer_details_
->AddWebContents(*iter
);
222 renderer_details_
->Request();
225 void MemoryInternalsProxy::OnProcessAvailable(const ProcessData
& browser
) {
226 base::ListValue
* process_info
= new base::ListValue();
227 base::ListValue
* extension_info
= new base::ListValue();
228 information_
->Set("processes", process_info
);
229 information_
->Set("extensions", extension_info
);
230 for (PMIIterator iter
= browser
.processes
.begin();
231 iter
!= browser
.processes
.end(); ++iter
) {
232 base::DictionaryValue
* process
= new base::DictionaryValue();
233 if (iter
->renderer_type
== ProcessMemoryInformation::RENDERER_EXTENSION
)
234 extension_info
->Append(process
);
236 process_info
->Append(process
);
238 // From MemoryDetails.
239 process
->SetInteger("pid", iter
->pid
);
240 process
->SetString("type",
241 ProcessMemoryInformation::GetFullTypeNameInEnglish(
242 iter
->process_type
, iter
->renderer_type
));
243 process
->SetInteger("memory_private", iter
->working_set
.priv
);
245 base::ListValue
* titles
= new base::ListValue();
246 process
->Set("titles", titles
);
247 for (size_t i
= 0; i
< iter
->titles
.size(); ++i
)
248 titles
->AppendString(iter
->titles
[i
]);
251 std::set
<content::WebContents
*> web_contents
;
252 GetAllWebContents(&web_contents
);
253 ConvertTabsInformation(web_contents
, process_info
);
255 RequestRendererDetails();
258 void MemoryInternalsProxy::OnRendererAvailable(const base::ProcessId pid
,
259 const size_t v8_allocated
,
260 const size_t v8_used
) {
261 // Do not update while no renderers are registered.
262 if (renderer_details_
->IsClean())
265 base::ListValue
* processes
;
266 if (!information_
->GetList("processes", &processes
))
269 const size_t size
= processes
->GetSize();
270 for (size_t i
= 0; i
< size
; ++i
) {
271 base::DictionaryValue
* process
;
272 processes
->GetDictionary(i
, &process
);
274 if (!process
->GetInteger("pid", &id
) || id
!= static_cast<int>(pid
))
276 // Convert units from Bytes to KiB.
277 process
->SetInteger("v8_alloc", v8_allocated
/ 1024);
278 process
->SetInteger("v8_used", v8_used
/ 1024);
282 renderer_details_
->RemoveWebContents(pid
);
283 if (renderer_details_
->IsClean())
287 void MemoryInternalsProxy::ConvertTabsInformation(
288 const std::set
<content::WebContents
*>& web_contents
,
289 base::ListValue
* processes
) {
290 for (std::set
<content::WebContents
*>::const_iterator
291 iter
= web_contents
.begin(); iter
!= web_contents
.end(); ++iter
) {
292 content::WebContents
* web
= *iter
;
293 const base::ProcessId pid
= base::GetProcId(
294 web
->GetRenderProcessHost()->GetHandle());
296 // Find which process renders the web contents.
297 base::DictionaryValue
* process
= FindProcessFromPid(processes
, pid
);
301 // Prepare storage to register navigation histories.
302 base::ListValue
* tabs
;
303 if (!process
->GetList("history", &tabs
)) {
304 tabs
= new base::ListValue();
305 process
->Set("history", tabs
);
308 base::DictionaryValue
* tab
= new base::DictionaryValue();
311 base::ListValue
* histories
= new base::ListValue();
312 tab
->Set("history", histories
);
314 const content::NavigationController
& controller
= web
->GetController();
315 const int entry_size
= controller
.GetEntryCount();
316 for (int i
= 0; i
< entry_size
; ++i
) {
317 content::NavigationEntry
*entry
= controller
.GetEntryAtIndex(i
);
318 base::DictionaryValue
* history
= new base::DictionaryValue();
319 histories
->Append(history
);
320 history
->SetString("url", entry
->GetURL().spec());
321 history
->SetString("title", entry
->GetTitle());
322 history
->SetInteger("time", (base::Time::Now() -
323 entry
->GetTimestamp()).InSeconds());
325 tab
->SetInteger("index", controller
.GetCurrentEntryIndex());
329 void MemoryInternalsProxy::FinishCollection() {
330 information_
->SetInteger("uptime", base::SysInfo::Uptime());
331 information_
->SetString("os", base::SysInfo::OperatingSystemName());
332 information_
->SetString("os_version",
333 base::SysInfo::OperatingSystemVersion());
335 CallJavaScriptFunctionOnUIThread("g_main_view.onSetSnapshot", *information_
);
338 void MemoryInternalsProxy::CallJavaScriptFunctionOnUIThread(
339 const std::string
& function
, const base::Value
& args
) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
342 std::vector
<const base::Value
*> args_vector(1, &args
);
343 base::string16 update
=
344 content::WebUI::GetJavascriptCall(function
, args_vector
);
345 // Don't forward updates to a destructed UI.
347 handler_
->OnUpdate(update
);