blob: cfdafc3055c40586e0e945f06a8f382afc7153bc [file] [log] [blame]
[email protected]76543b92009-08-31 17:27:451// Copyright (c) 2009 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
5#include "chrome/browser/memory_details.h"
6#include <psapi.h>
7
[email protected]ae5ca892009-07-30 18:00:478#include "app/l10n_util.h"
initial.commit09911bf2008-07-26 23:55:299#include "base/file_version_info.h"
[email protected]76543b92009-08-31 17:27:4510#include "base/process_util.h"
[email protected]4c4d8d22009-03-04 05:29:2711#include "base/string_util.h"
initial.commit09911bf2008-07-26 23:55:2912#include "chrome/browser/browser_process.h"
[email protected]dcccb942009-02-01 18:23:0013#include "chrome/browser/chrome_thread.h"
[email protected]57c4b852009-08-17 21:59:2914#include "chrome/browser/renderer_host/backing_store_manager.h"
[email protected]8c8657d62009-01-16 18:31:2615#include "chrome/browser/renderer_host/render_process_host.h"
[email protected]cd3d7892009-03-04 23:55:0616#include "chrome/browser/tab_contents/navigation_entry.h"
[email protected]57c6a652009-05-04 07:58:3417#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]6dffde322009-02-18 03:47:4818#include "chrome/common/child_process_host.h"
[email protected]cd3d7892009-03-04 23:55:0619#include "chrome/common/url_constants.h"
[email protected]ae5ca892009-07-30 18:00:4720#include "grit/chromium_strings.h"
initial.commit09911bf2008-07-26 23:55:2921
22class RenderViewHostDelegate;
23
24// Template of static data we use for finding browser process information.
25// These entries must match the ordering for MemoryDetails::BrowserProcess.
[email protected]ae5ca892009-07-30 18:00:4726static ProcessData g_process_template[MemoryDetails::MAX_BROWSERS];
initial.commit09911bf2008-07-26 23:55:2927
28// About threading:
29//
30// This operation will hit no fewer than 3 threads.
31//
[email protected]a436d922009-02-13 23:16:4232// The ChildProcessInfo::Iterator can only be accessed from the IO thread.
initial.commit09911bf2008-07-26 23:55:2933//
34// The RenderProcessHostIterator can only be accessed from the UI thread.
35//
36// This operation can take 30-100ms to complete. We never want to have
37// one task run for that long on the UI or IO threads. So, we run the
38// expensive parts of this operation over on the file thread.
39//
40
[email protected]76543b92009-08-31 17:27:4541MemoryDetails::MemoryDetails() : ui_loop_(NULL) {
[email protected]ae5ca892009-07-30 18:00:4742 static const std::wstring google_browser_name =
[email protected]76543b92009-08-31 17:27:4543 l10n_util::GetString(IDS_PRODUCT_NAME);
[email protected]ae5ca892009-07-30 18:00:4744 ProcessData g_process_template[MemoryDetails::MAX_BROWSERS] = {
45 { google_browser_name.c_str(), L"chrome.exe", },
46 { L"IE", L"iexplore.exe", },
47 { L"Firefox", L"firefox.exe", },
48 { L"Opera", L"opera.exe", },
49 { L"Safari", L"safari.exe", },
50 { L"IE (64bit)", L"iexplore.exe", },
51 { L"Konqueror", L"konqueror.exe", },
52 };
53
initial.commit09911bf2008-07-26 23:55:2954 for (int index = 0; index < arraysize(g_process_template); ++index) {
55 process_data_[index].name = g_process_template[index].name;
56 process_data_[index].process_name = g_process_template[index].process_name;
57 }
58}
59
60void MemoryDetails::StartFetch() {
61 ui_loop_ = MessageLoop::current();
62
63 DCHECK(ui_loop_ != g_browser_process->io_thread()->message_loop());
64 DCHECK(ui_loop_ != g_browser_process->file_thread()->message_loop());
65
66 // In order to process this request, we need to use the plugin information.
67 // However, plugin process information is only available from the IO thread.
68 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:1069 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnIOThread));
initial.commit09911bf2008-07-26 23:55:2970}
71
[email protected]a27a9382009-02-11 23:55:1072void MemoryDetails::CollectChildInfoOnIOThread() {
initial.commit09911bf2008-07-26 23:55:2973 DCHECK(MessageLoop::current() ==
74 ChromeThread::GetMessageLoop(ChromeThread::IO));
75
[email protected]a27a9382009-02-11 23:55:1076 std::vector<ProcessMemoryInformation> child_info;
77
[email protected]a436d922009-02-13 23:16:4278 // Collect the list of child processes.
[email protected]6dffde322009-02-18 03:47:4879 for (ChildProcessHost::Iterator iter; !iter.Done(); ++iter) {
[email protected]a27a9382009-02-11 23:55:1080 ProcessMemoryInformation info;
[email protected]76543b92009-08-31 17:27:4581 info.pid = base::GetProcId(iter->handle());
[email protected]a27a9382009-02-11 23:55:1082 if (!info.pid)
83 continue;
84
[email protected]a436d922009-02-13 23:16:4285 info.type = iter->type();
86 info.titles.push_back(iter->name());
[email protected]a27a9382009-02-11 23:55:1087 child_info.push_back(info);
initial.commit09911bf2008-07-26 23:55:2988 }
89
90 // Now go do expensive memory lookups from the file thread.
91 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:1092 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info));
initial.commit09911bf2008-07-26 23:55:2993}
94
[email protected]a27a9382009-02-11 23:55:1095void MemoryDetails::CollectProcessData(
96 std::vector<ProcessMemoryInformation> child_info) {
initial.commit09911bf2008-07-26 23:55:2997 DCHECK(MessageLoop::current() ==
98 ChromeThread::GetMessageLoop(ChromeThread::FILE));
99
initial.commit09911bf2008-07-26 23:55:29100 // Clear old data.
101 for (int index = 0; index < arraysize(g_process_template); index++)
102 process_data_[index].processes.clear();
103
[email protected]e288a8e72009-04-29 15:53:26104 SYSTEM_INFO system_info;
105 GetNativeSystemInfo(&system_info);
106 bool is_64bit_os =
107 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
108
109 ScopedHandle snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
110 PROCESSENTRY32 process_entry = {sizeof(PROCESSENTRY32)};
111 if (!snapshot.Get()) {
112 LOG(ERROR) << "CreateToolhelp32Snaphot failed: " << GetLastError();
113 return;
114 }
115 if (!::Process32First(snapshot, &process_entry)) {
116 LOG(ERROR) << "Process32First failed: " << GetLastError();
117 return;
118 }
119 do {
120 int pid = process_entry.th32ProcessID;
121 ScopedHandle handle(::OpenProcess(
[email protected]a27a9382009-02-11 23:55:10122 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
123 if (!handle.Get())
124 continue;
[email protected]e288a8e72009-04-29 15:53:26125 bool is_64bit_process = false;
126 // IsWow64Process() returns FALSE for a 32bit process on a 32bit OS.
127 // We need to check if the real OS is 64bit.
128 if (is_64bit_os) {
129 BOOL is_wow64 = FALSE;
130 // IsWow64Process() is supported by Windows XP SP2 or later.
131 IsWow64Process(handle, &is_wow64);
132 is_64bit_process = !is_wow64;
133 }
[email protected]a27a9382009-02-11 23:55:10134 for (int index2 = 0; index2 < arraysize(g_process_template); index2++) {
[email protected]e288a8e72009-04-29 15:53:26135 if (_wcsicmp(process_data_[index2].process_name,
136 process_entry.szExeFile) != 0)
[email protected]a27a9382009-02-11 23:55:10137 continue;
[email protected]e288a8e72009-04-29 15:53:26138 if (index2 == IE_BROWSER && is_64bit_process)
139 continue; // Should use IE_64BIT_BROWSER
[email protected]a27a9382009-02-11 23:55:10140 // Get Memory Information.
141 ProcessMemoryInformation info;
142 info.pid = pid;
143 if (info.pid == GetCurrentProcessId())
144 info.type = ChildProcessInfo::BROWSER_PROCESS;
145 else
146 info.type = ChildProcessInfo::UNKNOWN_PROCESS;
initial.commit09911bf2008-07-26 23:55:29147
[email protected]a27a9382009-02-11 23:55:10148 scoped_ptr<base::ProcessMetrics> metrics;
149 metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
150 metrics->GetCommittedKBytes(&info.committed);
151 metrics->GetWorkingSetKBytes(&info.working_set);
initial.commit09911bf2008-07-26 23:55:29152
[email protected]a27a9382009-02-11 23:55:10153 // Get Version Information.
[email protected]e288a8e72009-04-29 15:53:26154 TCHAR name[MAX_PATH];
155 if (index2 == CHROME_BROWSER) {
[email protected]a27a9382009-02-11 23:55:10156 scoped_ptr<FileVersionInfo> version_info(
[email protected]e288a8e72009-04-29 15:53:26157 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
[email protected]a27a9382009-02-11 23:55:10158 if (version_info != NULL)
159 info.version = version_info->file_version();
160 // Check if this is one of the child processes whose data we collected
161 // on the IO thread, and if so copy over that data.
162 for (size_t child = 0; child < child_info.size(); child++) {
163 if (child_info[child].pid != info.pid)
164 continue;
165 info.titles = child_info[child].titles;
166 info.type = child_info[child].type;
167 break;
168 }
169 } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) {
170 std::wstring str_name(name);
171 scoped_ptr<FileVersionInfo> version_info(
[email protected]e288a8e72009-04-29 15:53:26172 FileVersionInfo::CreateFileVersionInfo(str_name));
[email protected]a27a9382009-02-11 23:55:10173 if (version_info != NULL) {
174 info.version = version_info->product_version();
175 info.product_name = version_info->product_name();
initial.commit09911bf2008-07-26 23:55:29176 }
177 }
[email protected]a27a9382009-02-11 23:55:10178
179 // Add the process info to our list.
180 process_data_[index2].processes.push_back(info);
181 break;
initial.commit09911bf2008-07-26 23:55:29182 }
[email protected]e288a8e72009-04-29 15:53:26183 } while (::Process32Next(snapshot, &process_entry));
initial.commit09911bf2008-07-26 23:55:29184
185 // Finally return to the browser thread.
186 ui_loop_->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:10187 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
initial.commit09911bf2008-07-26 23:55:29188}
189
[email protected]a27a9382009-02-11 23:55:10190void MemoryDetails::CollectChildInfoOnUIThread() {
initial.commit09911bf2008-07-26 23:55:29191 DCHECK(MessageLoop::current() == ui_loop_);
192
[email protected]a27a9382009-02-11 23:55:10193 // Get more information about the process.
initial.commit09911bf2008-07-26 23:55:29194 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
195 index++) {
[email protected]a27a9382009-02-11 23:55:10196 // Check if it's a renderer, if so get the list of page titles in it and
197 // check if it's a diagnostics-related process. We skip all diagnostics
198 // pages (e.g. "about:xxx" URLs). Iterate the RenderProcessHosts to find
[email protected]cd3d7892009-03-04 23:55:06199 // the tab contents.
[email protected]9de09f82009-08-17 20:13:53200 RenderProcessHost::iterator renderer_iter(
201 RenderProcessHost::AllHostsIterator());
202 for (; !renderer_iter.IsAtEnd(); renderer_iter.Advance()) {
203 DCHECK(renderer_iter.GetCurrentValue());
[email protected]a27a9382009-02-11 23:55:10204 ProcessMemoryInformation& process =
205 process_data_[CHROME_BROWSER].processes[index];
[email protected]9de09f82009-08-17 20:13:53206 if (process.pid != renderer_iter.GetCurrentValue()->process().pid())
[email protected]a27a9382009-02-11 23:55:10207 continue;
208 process.type = ChildProcessInfo::RENDER_PROCESS;
209 // The RenderProcessHost may host multiple TabContents. Any
210 // of them which contain diagnostics information make the whole
211 // process be considered a diagnostics process.
212 //
213 // NOTE: This is a bit dangerous. We know that for now, listeners
214 // are always RenderWidgetHosts. But in theory, they don't
215 // have to be.
[email protected]9de09f82009-08-17 20:13:53216 RenderProcessHost::listeners_iterator iter(
217 renderer_iter.GetCurrentValue()->ListenersIterator());
218 for (; !iter.IsAtEnd(); iter.Advance()) {
219 const RenderWidgetHost* widget =
220 static_cast<const RenderWidgetHost*>(iter.GetCurrentValue());
[email protected]a27a9382009-02-11 23:55:10221 DCHECK(widget);
222 if (!widget || !widget->IsRenderView())
223 continue;
initial.commit09911bf2008-07-26 23:55:29224
[email protected]9de09f82009-08-17 20:13:53225 const RenderViewHost* host = static_cast<const RenderViewHost*>(widget);
[email protected]6d53eb22009-02-13 20:48:39226 TabContents* contents = NULL;
227 if (host->delegate())
[email protected]57c6a652009-05-04 07:58:34228 contents = host->delegate()->GetAsTabContents();
[email protected]a27a9382009-02-11 23:55:10229 if (!contents)
230 continue;
[email protected]4c4d8d22009-03-04 05:29:27231 std::wstring title = UTF16ToWideHack(contents->GetTitle());
[email protected]a27a9382009-02-11 23:55:10232 if (!title.length())
233 title = L"Untitled";
234 process.titles.push_back(title);
[email protected]cd3d7892009-03-04 23:55:06235
[email protected]ebe89e062009-08-13 23:16:54236 // We need to check the pending entry as well as the virtual_url to
[email protected]cd3d7892009-03-04 23:55:06237 // see if it's an about:memory URL (we don't want to count these in the
238 // total memory usage of the browser).
239 //
240 // When we reach here, about:memory will be the pending entry since we
241 // haven't responded with any data such that it would be committed. If
242 // you have another about:memory tab open (which would be committed),
243 // we don't want to count it either, so we also check the last committed
244 // entry.
245 //
246 // Either the pending or last committed entries can be NULL.
[email protected]6df40742009-03-19 22:24:50247 const NavigationEntry* pending_entry =
[email protected]ce3fa3c2009-04-20 19:55:57248 contents->controller().pending_entry();
[email protected]cd3d7892009-03-04 23:55:06249 const NavigationEntry* last_committed_entry =
[email protected]ce3fa3c2009-04-20 19:55:57250 contents->controller().GetLastCommittedEntry();
[email protected]cd3d7892009-03-04 23:55:06251 if ((last_committed_entry &&
[email protected]ebe89e062009-08-13 23:16:54252 LowerCaseEqualsASCII(last_committed_entry->virtual_url().spec(),
[email protected]cd3d7892009-03-04 23:55:06253 chrome::kAboutMemoryURL)) ||
254 (pending_entry &&
[email protected]ebe89e062009-08-13 23:16:54255 LowerCaseEqualsASCII(pending_entry->virtual_url().spec(),
[email protected]cd3d7892009-03-04 23:55:06256 chrome::kAboutMemoryURL)))
[email protected]a27a9382009-02-11 23:55:10257 process.is_diagnostics = true;
initial.commit09911bf2008-07-26 23:55:29258 }
259 }
260 }
261
[email protected]a27a9382009-02-11 23:55:10262 // Get rid of other Chrome processes that are from a different profile.
263 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
264 index++) {
265 if (process_data_[CHROME_BROWSER].processes[index].type ==
266 ChildProcessInfo::UNKNOWN_PROCESS) {
267 process_data_[CHROME_BROWSER].processes.erase(
268 process_data_[CHROME_BROWSER].processes.begin() + index);
[email protected]a436d922009-02-13 23:16:42269 index--;
[email protected]a27a9382009-02-11 23:55:10270 }
271 }
272
initial.commit09911bf2008-07-26 23:55:29273 UpdateHistograms();
274
275 OnDetailsAvailable();
276}
277
278void MemoryDetails::UpdateHistograms() {
279 // Reports a set of memory metrics to UMA.
[email protected]57c4b852009-08-17 21:59:29280 // Memory is measured in KB.
initial.commit09911bf2008-07-26 23:55:29281
initial.commit09911bf2008-07-26 23:55:29282 ProcessData browser = process_data_[CHROME_BROWSER];
283 size_t aggregate_memory = 0;
[email protected]a27a9382009-02-11 23:55:10284 int plugin_count = 0;
285 int worker_count = 0;
initial.commit09911bf2008-07-26 23:55:29286 for (size_t index = 0; index < browser.processes.size(); index++) {
[email protected]921cd0cc2008-10-21 22:30:55287 int sample = static_cast<int>(browser.processes[index].working_set.priv);
288 aggregate_memory += sample;
[email protected]a27a9382009-02-11 23:55:10289 switch (browser.processes[index].type) {
290 case ChildProcessInfo::BROWSER_PROCESS:
[email protected]553dba62009-02-24 19:08:23291 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample);
[email protected]a27a9382009-02-11 23:55:10292 break;
293 case ChildProcessInfo::RENDER_PROCESS:
[email protected]553dba62009-02-24 19:08:23294 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample);
[email protected]a27a9382009-02-11 23:55:10295 break;
296 case ChildProcessInfo::PLUGIN_PROCESS:
[email protected]553dba62009-02-24 19:08:23297 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample);
[email protected]a27a9382009-02-11 23:55:10298 plugin_count++;
299 break;
300 case ChildProcessInfo::WORKER_PROCESS:
[email protected]553dba62009-02-24 19:08:23301 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample);
[email protected]a27a9382009-02-11 23:55:10302 worker_count++;
303 break;
initial.commit09911bf2008-07-26 23:55:29304 }
305 }
[email protected]57c4b852009-08-17 21:59:29306 UMA_HISTOGRAM_MEMORY_KB("Memory.BackingStore",
307 BackingStoreManager::MemorySize() / 1024);
[email protected]a27a9382009-02-11 23:55:10308
[email protected]553dba62009-02-24 19:08:23309 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount",
initial.commit09911bf2008-07-26 23:55:29310 static_cast<int>(browser.processes.size()));
[email protected]553dba62009-02-24 19:08:23311 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count);
312 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count);
[email protected]921cd0cc2008-10-21 22:30:55313
314 int total_sample = static_cast<int>(aggregate_memory / 1000);
[email protected]553dba62009-02-24 19:08:23315 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample);
initial.commit09911bf2008-07-26 23:55:29316}