memory_internals_proxy.cc revision a031efa9dbd9b321781674193e8ac7636d47eb1d
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.
4
5#include "chrome/browser/ui/webui/memory_internals/memory_internals_proxy.h"
6
7#include <set>
8#include <string>
9#include <vector>
10
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/search/instant_service.h"
24#include "chrome/browser/search/instant_service_factory.h"
25#include "chrome/browser/ui/android/tab_model/tab_model.h"
26#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
27#include "chrome/browser/ui/browser.h"
28#include "chrome/browser/ui/browser_instant_controller.h"
29#include "chrome/browser/ui/browser_iterator.h"
30#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
31#include "chrome/browser/ui/webui/memory_internals/memory_internals_handler.h"
32#include "chrome/common/render_messages.h"
33#include "content/public/browser/navigation_controller.h"
34#include "content/public/browser/navigation_entry.h"
35#include "content/public/browser/notification_observer.h"
36#include "content/public/browser/notification_registrar.h"
37#include "content/public/browser/notification_service.h"
38#include "content/public/browser/render_process_host.h"
39#include "content/public/browser/render_view_host.h"
40#include "content/public/browser/web_contents.h"
41#include "content/public/browser/web_ui.h"
42
43#if defined(ENABLE_FULL_PRINTING)
44#include "chrome/browser/printing/background_printing_manager.h"
45#endif
46
47using content::BrowserThread;
48
49class Profile;
50
51namespace {
52
53class ProcessDetails : public MemoryDetails {
54 public:
55  typedef base::Callback<void(const ProcessData&)> DataCallback;
56  explicit ProcessDetails(const DataCallback& callback)
57      : callback_(callback) {}
58  // MemoryDetails:
59  virtual void OnDetailsAvailable() OVERRIDE {
60    const std::vector<ProcessData>& browser_processes = processes();
61    // [0] means Chrome.
62    callback_.Run(browser_processes[0]);
63  }
64
65 private:
66  virtual ~ProcessDetails() {}
67
68  DataCallback callback_;
69
70  DISALLOW_COPY_AND_ASSIGN(ProcessDetails);
71};
72
73base::DictionaryValue* FindProcessFromPid(base::ListValue* processes,
74                                          base::ProcessId pid) {
75  const size_t n = processes->GetSize();
76  for (size_t i = 0; i < n; ++i) {
77    base::DictionaryValue* process;
78    if (!processes->GetDictionary(i, &process))
79      return NULL;
80    int id;
81    if (process->GetInteger("pid", &id) && id == static_cast<int>(pid))
82      return process;
83  }
84  return NULL;
85}
86
87void GetAllWebContents(std::set<content::WebContents*>* web_contents) {
88  // Add all the existing WebContentses.
89#if defined(OS_ANDROID)
90  for (TabModelList::const_iterator iter = TabModelList::begin();
91       iter != TabModelList::end(); ++iter) {
92    TabModel* model = *iter;
93    for (int i = 0; i < model->GetTabCount(); ++i)
94      web_contents->insert(model->GetWebContentsAt(i));
95  }
96#else
97  for (TabContentsIterator iter; !iter.done(); iter.Next())
98    web_contents->insert(*iter);
99#endif
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)
107      continue;
108    const std::vector<content::WebContents*> contentses =
109        prerender_manager->GetAllPrerenderingContents();
110    for (size_t j = 0; j < contentses.size(); ++j)
111      web_contents->insert(contentses[j]);
112  }
113  // Add all the Instant Extended prerendered NTPs.
114  for (size_t i = 0; i < profiles.size(); ++i) {
115    const InstantService* instant_service =
116        InstantServiceFactory::GetForProfile(profiles[i]);
117    if (instant_service && instant_service->GetNTPContents())
118      web_contents->insert(instant_service->GetNTPContents());
119  }
120#if defined(ENABLE_FULL_PRINTING)
121  // Add all the pages being background printed.
122  printing::BackgroundPrintingManager* printing_manager =
123      g_browser_process->background_printing_manager();
124  for (printing::BackgroundPrintingManager::WebContentsSet::const_iterator
125           iter = printing_manager->begin();
126       iter != printing_manager->end(); ++iter) {
127    web_contents->insert(*iter);
128  }
129#endif
130}
131
132}  // namespace
133
134class RendererDetails : public content::NotificationObserver {
135 public:
136  typedef base::Callback<void(const base::ProcessId pid,
137                              const size_t v8_allocated,
138                              const size_t v8_used)> V8DataCallback;
139
140  explicit RendererDetails(const V8DataCallback& callback)
141      : callback_(callback) {
142    registrar_.Add(this, chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED,
143                   content::NotificationService::AllSources());
144  }
145  virtual ~RendererDetails() {}
146
147  void Request() {
148    for (std::set<content::WebContents*>::iterator iter = web_contents_.begin();
149         iter != web_contents_.end(); ++iter)
150      (*iter)->GetRenderViewHost()->Send(new ChromeViewMsg_GetV8HeapStats);
151  }
152
153  void AddWebContents(content::WebContents* content) {
154    web_contents_.insert(content);
155  }
156
157  void Clear() {
158    web_contents_.clear();
159  }
160
161  void RemoveWebContents(base::ProcessId) {
162    // We don't have to detect which content is the caller of this method.
163    if (!web_contents_.empty())
164      web_contents_.erase(web_contents_.begin());
165  }
166
167  int IsClean() {
168    return web_contents_.empty();
169  }
170
171 private:
172  // NotificationObserver:
173  virtual void Observe(int type,
174                       const content::NotificationSource& source,
175                       const content::NotificationDetails& details) OVERRIDE {
176    const base::ProcessId* pid =
177        content::Source<const base::ProcessId>(source).ptr();
178    const ChromeRenderMessageFilter::V8HeapStatsDetails* v8_heap =
179        content::Details<const ChromeRenderMessageFilter::V8HeapStatsDetails>(
180            details).ptr();
181    callback_.Run(*pid,
182                  v8_heap->v8_memory_allocated(),
183                  v8_heap->v8_memory_used());
184  }
185
186  V8DataCallback callback_;
187  content::NotificationRegistrar registrar_;
188  std::set<content::WebContents*> web_contents_;  // This class does not own
189
190  DISALLOW_COPY_AND_ASSIGN(RendererDetails);
191};
192
193MemoryInternalsProxy::MemoryInternalsProxy()
194    : information_(new base::DictionaryValue()),
195      renderer_details_(new RendererDetails(
196          base::Bind(&MemoryInternalsProxy::OnRendererAvailable, this))) {}
197
198void MemoryInternalsProxy::Attach(MemoryInternalsHandler* handler) {
199  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200  handler_ = handler;
201}
202
203void MemoryInternalsProxy::Detach() {
204  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
205  handler_ = NULL;
206}
207
208void MemoryInternalsProxy::StartFetch(const base::ListValue* list) {
209  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210
211  // Clear previous information before fetching new information.
212  information_->Clear();
213  scoped_refptr<ProcessDetails> process(new ProcessDetails(
214      base::Bind(&MemoryInternalsProxy::OnProcessAvailable, this)));
215  process->StartFetch(MemoryDetails::SKIP_USER_METRICS);
216}
217
218MemoryInternalsProxy::~MemoryInternalsProxy() {}
219
220void MemoryInternalsProxy::RequestRendererDetails() {
221  renderer_details_->Clear();
222
223#if defined(OS_ANDROID)
224  for (TabModelList::const_iterator iter = TabModelList::begin();
225       iter != TabModelList::end(); ++iter) {
226    TabModel* model = *iter;
227    for (int i = 0; i < model->GetTabCount(); ++i)
228      renderer_details_->AddWebContents(model->GetWebContentsAt(i));
229  }
230#else
231  for (TabContentsIterator iter; !iter.done(); iter.Next())
232    renderer_details_->AddWebContents(*iter);
233#endif
234
235  renderer_details_->Request();
236}
237
238void MemoryInternalsProxy::OnProcessAvailable(const ProcessData& browser) {
239  base::ListValue* process_info = new ListValue();
240  base::ListValue* extension_info = new ListValue();
241  information_->Set("processes", process_info);
242  information_->Set("extensions", extension_info);
243  for (PMIIterator iter = browser.processes.begin();
244       iter != browser.processes.end(); ++iter) {
245    base::DictionaryValue* process = new DictionaryValue();
246    if (iter->renderer_type == ProcessMemoryInformation::RENDERER_EXTENSION)
247      extension_info->Append(process);
248    else
249      process_info->Append(process);
250
251    // From MemoryDetails.
252    process->SetInteger("pid", iter->pid);
253    process->SetString("type",
254                       ProcessMemoryInformation::GetFullTypeNameInEnglish(
255                           iter->process_type, iter->renderer_type));
256    process->SetInteger("memory_private", iter->working_set.priv);
257
258    base::ListValue* titles = new ListValue();
259    process->Set("titles", titles);
260    for (size_t i = 0; i < iter->titles.size(); ++i)
261      titles->AppendString(iter->titles[i]);
262  }
263
264  std::set<content::WebContents*> web_contents;
265  GetAllWebContents(&web_contents);
266  ConvertTabsInformation(web_contents, process_info);
267
268  RequestRendererDetails();
269}
270
271void MemoryInternalsProxy::OnRendererAvailable(const base::ProcessId pid,
272                                               const size_t v8_allocated,
273                                               const size_t v8_used) {
274  // Do not update while no renderers are registered.
275  if (renderer_details_->IsClean())
276    return;
277
278  base::ListValue* processes;
279  if (!information_->GetList("processes", &processes))
280    return;
281
282  const size_t size = processes->GetSize();
283  for (size_t i = 0; i < size; ++i) {
284    base::DictionaryValue* process;
285    processes->GetDictionary(i, &process);
286    int id;
287    if (!process->GetInteger("pid", &id) || id != static_cast<int>(pid))
288      continue;
289    // Convert units from Bytes to KiB.
290    process->SetInteger("v8_alloc", v8_allocated / 1024);
291    process->SetInteger("v8_used", v8_used / 1024);
292    break;
293  }
294
295  renderer_details_->RemoveWebContents(pid);
296  if (renderer_details_->IsClean())
297    FinishCollection();
298}
299
300void MemoryInternalsProxy::ConvertTabsInformation(
301    const std::set<content::WebContents*>& web_contents,
302    base::ListValue* processes) {
303  for (std::set<content::WebContents*>::const_iterator
304           iter = web_contents.begin(); iter != web_contents.end(); ++iter) {
305    content::WebContents* web = *iter;
306    const base::ProcessId pid = base::GetProcId(
307        web->GetRenderProcessHost()->GetHandle());
308
309    // Find which process renders the web contents.
310    base::DictionaryValue* process = FindProcessFromPid(processes, pid);
311    if (!process)
312      continue;
313
314    // Prepare storage to register navigation histories.
315    base::ListValue* tabs;
316    if (!process->GetList("history", &tabs)) {
317      tabs = new base::ListValue();
318      process->Set("history", tabs);
319    }
320
321    base::DictionaryValue* tab = new base::DictionaryValue();
322    tabs->Append(tab);
323
324    base::ListValue* histories = new base::ListValue();
325    tab->Set("history", histories);
326
327    const content::NavigationController& controller = web->GetController();
328    const int entry_size = controller.GetEntryCount();
329    for (int i = 0; i < entry_size; ++i) {
330      content::NavigationEntry *entry = controller.GetEntryAtIndex(i);
331      base::DictionaryValue* history = new base::DictionaryValue();
332      histories->Append(history);
333      history->SetString("url", entry->GetURL().spec());
334      history->SetString("title", entry->GetTitle());
335      history->SetInteger("time", (base::Time::Now() -
336                                   entry->GetTimestamp()).InSeconds());
337    }
338    tab->SetInteger("index", controller.GetCurrentEntryIndex());
339  }
340}
341
342void MemoryInternalsProxy::FinishCollection() {
343  information_->SetInteger("uptime", base::SysInfo::Uptime());
344  information_->SetString("os", base::SysInfo::OperatingSystemName());
345  information_->SetString("os_version",
346                          base::SysInfo::OperatingSystemVersion());
347
348  CallJavaScriptFunctionOnUIThread("g_main_view.onSetSnapshot", *information_);
349}
350
351void MemoryInternalsProxy::CallJavaScriptFunctionOnUIThread(
352    const std::string& function, const base::Value& args) {
353  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354
355  std::vector<const base::Value*> args_vector(1, &args);
356  string16 update = content::WebUI::GetJavascriptCall(function, args_vector);
357  // Don't forward updates to a destructed UI.
358  if (handler_)
359    handler_->OnUpdate(update);
360}
361