Home | History | Annotate | Download | only in memory_internals
      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/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"
     39 
     40 #if defined(ENABLE_FULL_PRINTING)
     41 #include "chrome/browser/printing/background_printing_manager.h"
     42 #endif
     43 
     44 using content::BrowserThread;
     45 
     46 class Profile;
     47 
     48 namespace {
     49 
     50 class ProcessDetails : public MemoryDetails {
     51  public:
     52   typedef base::Callback<void(const ProcessData&)> DataCallback;
     53   explicit ProcessDetails(const DataCallback& callback)
     54       : callback_(callback) {}
     55   // MemoryDetails:
     56   virtual void OnDetailsAvailable() OVERRIDE {
     57     const std::vector<ProcessData>& browser_processes = processes();
     58     // [0] means Chrome.
     59     callback_.Run(browser_processes[0]);
     60   }
     61 
     62  private:
     63   virtual ~ProcessDetails() {}
     64 
     65   DataCallback callback_;
     66 
     67   DISALLOW_COPY_AND_ASSIGN(ProcessDetails);
     68 };
     69 
     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))
     76       return NULL;
     77     int id;
     78     if (process->GetInteger("pid", &id) && id == static_cast<int>(pid))
     79       return process;
     80   }
     81   return NULL;
     82 }
     83 
     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));
     92   }
     93 #else
     94   for (TabContentsIterator iter; !iter.done(); iter.Next())
     95     web_contents->insert(*iter);
     96 #endif
     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)
    104       continue;
    105     const std::vector<content::WebContents*> contentses =
    106         prerender_manager->GetAllPrerenderingContents();
    107     web_contents->insert(contentses.begin(), contentses.end());
    108   }
    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());
    116 #endif
    117 }
    118 
    119 }  // namespace
    120 
    121 class RendererDetails : public content::NotificationObserver {
    122  public:
    123   typedef base::Callback<void(const base::ProcessId pid,
    124                               const size_t v8_allocated,
    125                               const size_t v8_used)> V8DataCallback;
    126 
    127   explicit RendererDetails(const V8DataCallback& callback)
    128       : callback_(callback) {
    129     registrar_.Add(this, chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED,
    130                    content::NotificationService::AllSources());
    131   }
    132   virtual ~RendererDetails() {}
    133 
    134   void Request() {
    135     for (std::set<content::WebContents*>::iterator iter = web_contents_.begin();
    136          iter != web_contents_.end(); ++iter)
    137       (*iter)->GetRenderViewHost()->Send(new ChromeViewMsg_GetV8HeapStats);
    138   }
    139 
    140   void AddWebContents(content::WebContents* content) {
    141     web_contents_.insert(content);
    142   }
    143 
    144   void Clear() {
    145     web_contents_.clear();
    146   }
    147 
    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());
    152   }
    153 
    154   int IsClean() {
    155     return web_contents_.empty();
    156   }
    157 
    158  private:
    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>(
    167             details).ptr();
    168     callback_.Run(*pid,
    169                   v8_heap->v8_memory_allocated(),
    170                   v8_heap->v8_memory_used());
    171   }
    172 
    173   V8DataCallback callback_;
    174   content::NotificationRegistrar registrar_;
    175   std::set<content::WebContents*> web_contents_;  // This class does not own
    176 
    177   DISALLOW_COPY_AND_ASSIGN(RendererDetails);
    178 };
    179 
    180 MemoryInternalsProxy::MemoryInternalsProxy()
    181     : information_(new base::DictionaryValue()),
    182       renderer_details_(new RendererDetails(
    183           base::Bind(&MemoryInternalsProxy::OnRendererAvailable, this))) {}
    184 
    185 void MemoryInternalsProxy::Attach(MemoryInternalsHandler* handler) {
    186   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    187   handler_ = handler;
    188 }
    189 
    190 void MemoryInternalsProxy::Detach() {
    191   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    192   handler_ = NULL;
    193 }
    194 
    195 void MemoryInternalsProxy::StartFetch(const base::ListValue* list) {
    196   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    197 
    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);
    203 }
    204 
    205 MemoryInternalsProxy::~MemoryInternalsProxy() {}
    206 
    207 void MemoryInternalsProxy::RequestRendererDetails() {
    208   renderer_details_->Clear();
    209 
    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));
    216   }
    217 #else
    218   for (TabContentsIterator iter; !iter.done(); iter.Next())
    219     renderer_details_->AddWebContents(*iter);
    220 #endif
    221 
    222   renderer_details_->Request();
    223 }
    224 
    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);
    235     else
    236       process_info->Append(process);
    237 
    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);
    244 
    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]);
    249   }
    250 
    251   std::set<content::WebContents*> web_contents;
    252   GetAllWebContents(&web_contents);
    253   ConvertTabsInformation(web_contents, process_info);
    254 
    255   RequestRendererDetails();
    256 }
    257 
    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())
    263     return;
    264 
    265   base::ListValue* processes;
    266   if (!information_->GetList("processes", &processes))
    267     return;
    268 
    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);
    273     int id;
    274     if (!process->GetInteger("pid", &id) || id != static_cast<int>(pid))
    275       continue;
    276     // Convert units from Bytes to KiB.
    277     process->SetInteger("v8_alloc", v8_allocated / 1024);
    278     process->SetInteger("v8_used", v8_used / 1024);
    279     break;
    280   }
    281 
    282   renderer_details_->RemoveWebContents(pid);
    283   if (renderer_details_->IsClean())
    284     FinishCollection();
    285 }
    286 
    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());
    295 
    296     // Find which process renders the web contents.
    297     base::DictionaryValue* process = FindProcessFromPid(processes, pid);
    298     if (!process)
    299       continue;
    300 
    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);
    306     }
    307 
    308     base::DictionaryValue* tab = new base::DictionaryValue();
    309     tabs->Append(tab);
    310 
    311     base::ListValue* histories = new base::ListValue();
    312     tab->Set("history", histories);
    313 
    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());
    324     }
    325     tab->SetInteger("index", controller.GetCurrentEntryIndex());
    326   }
    327 }
    328 
    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());
    334 
    335   CallJavaScriptFunctionOnUIThread("g_main_view.onSetSnapshot", *information_);
    336 }
    337 
    338 void MemoryInternalsProxy::CallJavaScriptFunctionOnUIThread(
    339     const std::string& function, const base::Value& args) {
    340   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    341 
    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.
    346   if (handler_)
    347     handler_->OnUpdate(update);
    348 }
    349