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 |