| OLD | NEW |
| (Empty) |
| 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 <map> | |
| 8 #include <set> | |
| 9 #include <string> | |
| 10 #include <utility> | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/bind.h" | |
| 14 #include "base/macros.h" | |
| 15 #include "base/memory/linked_ptr.h" | |
| 16 #include "base/memory/weak_ptr.h" | |
| 17 #include "base/strings/string16.h" | |
| 18 #include "base/sys_info.h" | |
| 19 #include "base/values.h" | |
| 20 #include "chrome/browser/browser_process.h" | |
| 21 #include "chrome/browser/chrome_notification_types.h" | |
| 22 #include "chrome/browser/memory_details.h" | |
| 23 #include "chrome/browser/prerender/prerender_manager.h" | |
| 24 #include "chrome/browser/prerender/prerender_manager_factory.h" | |
| 25 #include "chrome/browser/process_resource_usage.h" | |
| 26 #include "chrome/browser/profiles/profile.h" | |
| 27 #include "chrome/browser/profiles/profile_manager.h" | |
| 28 #include "chrome/browser/renderer_host/chrome_render_message_filter.h" | |
| 29 #include "chrome/browser/ui/android/tab_model/tab_model.h" | |
| 30 #include "chrome/browser/ui/android/tab_model/tab_model_list.h" | |
| 31 #include "chrome/browser/ui/browser.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 "chrome/common/features.h" | |
| 35 #include "content/public/browser/navigation_controller.h" | |
| 36 #include "content/public/browser/navigation_entry.h" | |
| 37 #include "content/public/browser/render_process_host.h" | |
| 38 #include "content/public/browser/render_view_host.h" | |
| 39 #include "content/public/browser/web_contents.h" | |
| 40 #include "content/public/browser/web_ui.h" | |
| 41 #include "content/public/common/service_registry.h" | |
| 42 | |
| 43 #if defined(ENABLE_PRINT_PREVIEW) | |
| 44 #include "chrome/browser/printing/background_printing_manager.h" | |
| 45 #endif | |
| 46 | |
| 47 using content::BrowserThread; | |
| 48 | |
| 49 class Profile; | |
| 50 | |
| 51 namespace { | |
| 52 | |
| 53 class ProcessDetails : public MemoryDetails { | |
| 54 public: | |
| 55 typedef base::Callback<void(const ProcessData&)> DataCallback; | |
| 56 explicit ProcessDetails(const DataCallback& callback) | |
| 57 : callback_(callback) {} | |
| 58 | |
| 59 // MemoryDetails: | |
| 60 void OnDetailsAvailable() override { callback_.Run(*ChromeBrowser()); } | |
| 61 | |
| 62 private: | |
| 63 ~ProcessDetails() override {} | |
| 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 BUILDFLAG(ANDROID_JAVA_UI) | |
| 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 content::WebContents* tab_web_contents = model->GetWebContentsAt(i); | |
| 92 if (tab_web_contents) | |
| 93 web_contents->insert(tab_web_contents); | |
| 94 } | |
| 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 web_contents->insert(contentses.begin(), contentses.end()); | |
| 111 } | |
| 112 #if defined(ENABLE_PRINT_PREVIEW) | |
| 113 // Add all the pages being background printed. | |
| 114 printing::BackgroundPrintingManager* printing_manager = | |
| 115 g_browser_process->background_printing_manager(); | |
| 116 std::set<content::WebContents*> printing_contents = | |
| 117 printing_manager->CurrentContentSet(); | |
| 118 web_contents->insert(printing_contents.begin(), printing_contents.end()); | |
| 119 #endif | |
| 120 } | |
| 121 | |
| 122 } // namespace | |
| 123 | |
| 124 class RendererDetails { | |
| 125 public: | |
| 126 typedef base::Callback<void(const base::ProcessId pid, | |
| 127 const size_t v8_allocated, | |
| 128 const size_t v8_used)> V8DataCallback; | |
| 129 | |
| 130 explicit RendererDetails(const V8DataCallback& callback) | |
| 131 : callback_(callback), weak_factory_(this) {} | |
| 132 ~RendererDetails() {} | |
| 133 | |
| 134 void Request() { | |
| 135 for (std::set<content::WebContents*>::iterator iter = web_contents_.begin(); | |
| 136 iter != web_contents_.end(); ++iter) { | |
| 137 auto rph = (*iter)->GetRenderViewHost()->GetProcess(); | |
| 138 auto resource_usage = resource_usage_reporters_[(*iter)]; | |
| 139 DCHECK(resource_usage.get()); | |
| 140 resource_usage->Refresh(base::Bind(&RendererDetails::OnRefreshDone, | |
| 141 weak_factory_.GetWeakPtr(), *iter, | |
| 142 base::GetProcId(rph->GetHandle()))); | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 void AddWebContents(content::WebContents* content) { | |
| 147 web_contents_.insert(content); | |
| 148 | |
| 149 auto rph = content->GetRenderViewHost()->GetProcess(); | |
| 150 content::ServiceRegistry* service_registry = rph->GetServiceRegistry(); | |
| 151 ResourceUsageReporterPtr service; | |
| 152 if (service_registry) | |
| 153 service_registry->ConnectToRemoteService(mojo::GetProxy(&service)); | |
| 154 resource_usage_reporters_.insert(std::make_pair( | |
| 155 content, | |
| 156 make_linked_ptr(new ProcessResourceUsage(std::move(service))))); | |
| 157 DCHECK_EQ(web_contents_.size(), resource_usage_reporters_.size()); | |
| 158 } | |
| 159 | |
| 160 void Clear() { | |
| 161 web_contents_.clear(); | |
| 162 resource_usage_reporters_.clear(); | |
| 163 weak_factory_.InvalidateWeakPtrs(); | |
| 164 } | |
| 165 | |
| 166 void RemoveWebContents(base::ProcessId) { | |
| 167 // This function should only be called once for each WebContents, so this | |
| 168 // should be non-empty every time it's called. | |
| 169 DCHECK(!web_contents_.empty()); | |
| 170 | |
| 171 // We don't have to detect which content is the caller of this method. | |
| 172 if (!web_contents_.empty()) | |
| 173 web_contents_.erase(web_contents_.begin()); | |
| 174 } | |
| 175 | |
| 176 int IsClean() { | |
| 177 return web_contents_.empty(); | |
| 178 } | |
| 179 | |
| 180 private: | |
| 181 void OnRefreshDone(content::WebContents* content, const base::ProcessId pid) { | |
| 182 auto iter = resource_usage_reporters_.find(content); | |
| 183 DCHECK(iter != resource_usage_reporters_.end()); | |
| 184 linked_ptr<ProcessResourceUsage> usage = iter->second; | |
| 185 callback_.Run(pid, usage->GetV8MemoryAllocated(), usage->GetV8MemoryUsed()); | |
| 186 } | |
| 187 | |
| 188 V8DataCallback callback_; | |
| 189 std::set<content::WebContents*> web_contents_; // This class does not own | |
| 190 std::map<content::WebContents*, linked_ptr<ProcessResourceUsage>> | |
| 191 resource_usage_reporters_; | |
| 192 | |
| 193 base::WeakPtrFactory<RendererDetails> weak_factory_; | |
| 194 | |
| 195 DISALLOW_COPY_AND_ASSIGN(RendererDetails); | |
| 196 }; | |
| 197 | |
| 198 MemoryInternalsProxy::MemoryInternalsProxy() | |
| 199 : information_(new base::DictionaryValue()), | |
| 200 renderer_details_(new RendererDetails( | |
| 201 base::Bind(&MemoryInternalsProxy::OnRendererAvailable, this))) {} | |
| 202 | |
| 203 void MemoryInternalsProxy::Attach(MemoryInternalsHandler* handler) { | |
| 204 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 205 handler_ = handler; | |
| 206 } | |
| 207 | |
| 208 void MemoryInternalsProxy::Detach() { | |
| 209 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 210 handler_ = NULL; | |
| 211 } | |
| 212 | |
| 213 void MemoryInternalsProxy::StartFetch(const base::ListValue* list) { | |
| 214 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 215 | |
| 216 // Clear previous information before fetching new information. | |
| 217 information_->Clear(); | |
| 218 scoped_refptr<ProcessDetails> process(new ProcessDetails( | |
| 219 base::Bind(&MemoryInternalsProxy::OnProcessAvailable, this))); | |
| 220 process->StartFetch(MemoryDetails::FROM_CHROME_ONLY); | |
| 221 } | |
| 222 | |
| 223 MemoryInternalsProxy::~MemoryInternalsProxy() {} | |
| 224 | |
| 225 void MemoryInternalsProxy::RequestRendererDetails() { | |
| 226 renderer_details_->Clear(); | |
| 227 | |
| 228 #if BUILDFLAG(ANDROID_JAVA_UI) | |
| 229 for (TabModelList::const_iterator iter = TabModelList::begin(); | |
| 230 iter != TabModelList::end(); ++iter) { | |
| 231 TabModel* model = *iter; | |
| 232 for (int i = 0; i < model->GetTabCount(); ++i) { | |
| 233 content::WebContents* tab_web_contents = model->GetWebContentsAt(i); | |
| 234 if (tab_web_contents) | |
| 235 renderer_details_->AddWebContents(tab_web_contents); | |
| 236 } | |
| 237 } | |
| 238 #else | |
| 239 for (TabContentsIterator iter; !iter.done(); iter.Next()) | |
| 240 renderer_details_->AddWebContents(*iter); | |
| 241 #endif | |
| 242 | |
| 243 renderer_details_->Request(); | |
| 244 } | |
| 245 | |
| 246 void MemoryInternalsProxy::OnProcessAvailable(const ProcessData& browser) { | |
| 247 base::ListValue* process_info = new base::ListValue(); | |
| 248 base::ListValue* extension_info = new base::ListValue(); | |
| 249 information_->Set("processes", process_info); | |
| 250 information_->Set("extensions", extension_info); | |
| 251 for (PMIIterator iter = browser.processes.begin(); | |
| 252 iter != browser.processes.end(); ++iter) { | |
| 253 base::DictionaryValue* process = new base::DictionaryValue(); | |
| 254 if (iter->renderer_type == ProcessMemoryInformation::RENDERER_EXTENSION) | |
| 255 extension_info->Append(process); | |
| 256 else | |
| 257 process_info->Append(process); | |
| 258 | |
| 259 // From MemoryDetails. | |
| 260 process->SetInteger("pid", iter->pid); | |
| 261 process->SetString("type", | |
| 262 ProcessMemoryInformation::GetFullTypeNameInEnglish( | |
| 263 iter->process_type, iter->renderer_type)); | |
| 264 process->SetInteger("memory_private", iter->working_set.priv); | |
| 265 | |
| 266 base::ListValue* titles = new base::ListValue(); | |
| 267 process->Set("titles", titles); | |
| 268 for (size_t i = 0; i < iter->titles.size(); ++i) | |
| 269 titles->AppendString(iter->titles[i]); | |
| 270 } | |
| 271 | |
| 272 std::set<content::WebContents*> web_contents; | |
| 273 GetAllWebContents(&web_contents); | |
| 274 ConvertTabsInformation(web_contents, process_info); | |
| 275 | |
| 276 RequestRendererDetails(); | |
| 277 } | |
| 278 | |
| 279 void MemoryInternalsProxy::OnRendererAvailable(const base::ProcessId pid, | |
| 280 const size_t v8_allocated, | |
| 281 const size_t v8_used) { | |
| 282 // Do not update while no renderers are registered. | |
| 283 if (renderer_details_->IsClean()) | |
| 284 return; | |
| 285 | |
| 286 base::ListValue* processes; | |
| 287 if (!information_->GetList("processes", &processes)) | |
| 288 return; | |
| 289 | |
| 290 const size_t size = processes->GetSize(); | |
| 291 for (size_t i = 0; i < size; ++i) { | |
| 292 base::DictionaryValue* process; | |
| 293 processes->GetDictionary(i, &process); | |
| 294 int id; | |
| 295 if (!process->GetInteger("pid", &id) || id != static_cast<int>(pid)) | |
| 296 continue; | |
| 297 // Convert units from Bytes to KiB. | |
| 298 process->SetInteger("v8_alloc", v8_allocated / 1024); | |
| 299 process->SetInteger("v8_used", v8_used / 1024); | |
| 300 break; | |
| 301 } | |
| 302 | |
| 303 renderer_details_->RemoveWebContents(pid); | |
| 304 if (renderer_details_->IsClean()) | |
| 305 FinishCollection(); | |
| 306 } | |
| 307 | |
| 308 void MemoryInternalsProxy::ConvertTabsInformation( | |
| 309 const std::set<content::WebContents*>& web_contents, | |
| 310 base::ListValue* processes) { | |
| 311 for (std::set<content::WebContents*>::const_iterator | |
| 312 iter = web_contents.begin(); iter != web_contents.end(); ++iter) { | |
| 313 content::WebContents* web = *iter; | |
| 314 content::RenderProcessHost* process_host = web->GetRenderProcessHost(); | |
| 315 if (!process_host) | |
| 316 continue; | |
| 317 | |
| 318 // Find which process renders the web contents. | |
| 319 const base::ProcessId pid = base::GetProcId(process_host->GetHandle()); | |
| 320 base::DictionaryValue* process = FindProcessFromPid(processes, pid); | |
| 321 if (!process) | |
| 322 continue; | |
| 323 | |
| 324 // Prepare storage to register navigation histories. | |
| 325 base::ListValue* tabs; | |
| 326 if (!process->GetList("history", &tabs)) { | |
| 327 tabs = new base::ListValue(); | |
| 328 process->Set("history", tabs); | |
| 329 } | |
| 330 | |
| 331 base::DictionaryValue* tab = new base::DictionaryValue(); | |
| 332 tabs->Append(tab); | |
| 333 | |
| 334 base::ListValue* histories = new base::ListValue(); | |
| 335 tab->Set("history", histories); | |
| 336 | |
| 337 const content::NavigationController& controller = web->GetController(); | |
| 338 const int entry_size = controller.GetEntryCount(); | |
| 339 for (int i = 0; i < entry_size; ++i) { | |
| 340 content::NavigationEntry *entry = controller.GetEntryAtIndex(i); | |
| 341 base::DictionaryValue* history = new base::DictionaryValue(); | |
| 342 histories->Append(history); | |
| 343 history->SetString("url", entry->GetURL().spec()); | |
| 344 history->SetString("title", entry->GetTitle()); | |
| 345 history->SetInteger("time", (base::Time::Now() - | |
| 346 entry->GetTimestamp()).InSeconds()); | |
| 347 } | |
| 348 tab->SetInteger("index", controller.GetCurrentEntryIndex()); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 void MemoryInternalsProxy::FinishCollection() { | |
| 353 information_->SetInteger("uptime", base::SysInfo::Uptime().InMilliseconds()); | |
| 354 information_->SetString("os", base::SysInfo::OperatingSystemName()); | |
| 355 information_->SetString("os_version", | |
| 356 base::SysInfo::OperatingSystemVersion()); | |
| 357 | |
| 358 CallJavaScriptFunctionOnUIThread("g_main_view.onSetSnapshot", *information_); | |
| 359 } | |
| 360 | |
| 361 void MemoryInternalsProxy::CallJavaScriptFunctionOnUIThread( | |
| 362 const std::string& function, const base::Value& args) { | |
| 363 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 364 | |
| 365 std::vector<const base::Value*> args_vector(1, &args); | |
| 366 base::string16 update = | |
| 367 content::WebUI::GetJavascriptCall(function, args_vector); | |
| 368 // Don't forward updates to a destructed UI. | |
| 369 if (handler_) | |
| 370 handler_->OnUpdate(update); | |
| 371 } | |
| OLD | NEW |