Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / webui / memory_internals / memory_internals_proxy.cc
blob50b65a63844c892b7b492ae464f20c5558b148ec
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"
7 #include <map>
8 #include <set>
9 #include <string>
10 #include <utility>
11 #include <vector>
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"
44 #endif
46 using content::BrowserThread;
48 class Profile;
50 namespace {
52 class ProcessDetails : public MemoryDetails {
53 public:
54 typedef base::Callback<void(const ProcessData&)> DataCallback;
55 explicit ProcessDetails(const DataCallback& callback)
56 : callback_(callback) {}
58 // MemoryDetails:
59 void OnDetailsAvailable() override { callback_.Run(*ChromeBrowser()); }
61 private:
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))
75 return NULL;
76 int id;
77 if (process->GetInteger("pid", &id) && id == static_cast<int>(pid))
78 return process;
80 return NULL;
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);
91 if (tab_web_contents)
92 web_contents->insert(tab_web_contents);
95 #else
96 for (TabContentsIterator iter; !iter.done(); iter.Next())
97 web_contents->insert(*iter);
98 #endif
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)
106 continue;
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());
118 #endif
121 } // namespace
123 class RendererDetails {
124 public:
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() {}
133 void Request() {
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());
158 void Clear() {
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());
174 int IsClean() {
175 return web_contents_.empty();
178 private:
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);
203 handler_ = handler;
206 void MemoryInternalsProxy::Detach() {
207 DCHECK_CURRENTLY_ON(BrowserThread::UI);
208 handler_ = NULL;
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);
236 #else
237 for (TabContentsIterator iter; !iter.done(); iter.Next())
238 renderer_details_->AddWebContents(*iter);
239 #endif
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);
254 else
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())
282 return;
284 base::ListValue* processes;
285 if (!information_->GetList("processes", &processes))
286 return;
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);
292 int id;
293 if (!process->GetInteger("pid", &id) || id != static_cast<int>(pid))
294 continue;
295 // Convert units from Bytes to KiB.
296 process->SetInteger("v8_alloc", v8_allocated / 1024);
297 process->SetInteger("v8_used", v8_used / 1024);
298 break;
301 renderer_details_->RemoveWebContents(pid);
302 if (renderer_details_->IsClean())
303 FinishCollection();
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();
313 if (!process_host)
314 continue;
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);
319 if (!process)
320 continue;
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();
330 tabs->Append(tab);
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.
367 if (handler_)
368 handler_->OnUpdate(update);