blob: df81bc37b0f581a6b33797f9adc75f173d9f488c [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
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]4c4d8d22009-03-04 05:29:2710#include "base/string_util.h"
initial.commit09911bf2008-07-26 23:55:2911#include "chrome/browser/browser_process.h"
[email protected]dcccb942009-02-01 18:23:0012#include "chrome/browser/chrome_thread.h"
[email protected]8c8657d62009-01-16 18:31:2613#include "chrome/browser/renderer_host/render_process_host.h"
[email protected]cd3d7892009-03-04 23:55:0614#include "chrome/browser/tab_contents/navigation_entry.h"
[email protected]57c6a652009-05-04 07:58:3415#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]6dffde322009-02-18 03:47:4816#include "chrome/common/child_process_host.h"
[email protected]cd3d7892009-03-04 23:55:0617#include "chrome/common/url_constants.h"
[email protected]ae5ca892009-07-30 18:00:4718#include "grit/chromium_strings.h"
initial.commit09911bf2008-07-26 23:55:2919
20class RenderViewHostDelegate;
21
22// Template of static data we use for finding browser process information.
23// These entries must match the ordering for MemoryDetails::BrowserProcess.
[email protected]ae5ca892009-07-30 18:00:4724static ProcessData g_process_template[MemoryDetails::MAX_BROWSERS];
initial.commit09911bf2008-07-26 23:55:2925
26// About threading:
27//
28// This operation will hit no fewer than 3 threads.
29//
[email protected]a436d922009-02-13 23:16:4230// The ChildProcessInfo::Iterator can only be accessed from the IO thread.
initial.commit09911bf2008-07-26 23:55:2931//
32// The RenderProcessHostIterator can only be accessed from the UI thread.
33//
34// This operation can take 30-100ms to complete. We never want to have
35// one task run for that long on the UI or IO threads. So, we run the
36// expensive parts of this operation over on the file thread.
37//
38
39MemoryDetails::MemoryDetails()
40 : ui_loop_(NULL) {
[email protected]ae5ca892009-07-30 18:00:4741 static const std::wstring google_browser_name =
42 l10n_util::GetString(IDS_PRODUCT_NAME);
43 ProcessData g_process_template[MemoryDetails::MAX_BROWSERS] = {
44 { google_browser_name.c_str(), L"chrome.exe", },
45 { L"IE", L"iexplore.exe", },
46 { L"Firefox", L"firefox.exe", },
47 { L"Opera", L"opera.exe", },
48 { L"Safari", L"safari.exe", },
49 { L"IE (64bit)", L"iexplore.exe", },
50 { L"Konqueror", L"konqueror.exe", },
51 };
52
initial.commit09911bf2008-07-26 23:55:2953 for (int index = 0; index < arraysize(g_process_template); ++index) {
54 process_data_[index].name = g_process_template[index].name;
55 process_data_[index].process_name = g_process_template[index].process_name;
56 }
57}
58
59void MemoryDetails::StartFetch() {
60 ui_loop_ = MessageLoop::current();
61
62 DCHECK(ui_loop_ != g_browser_process->io_thread()->message_loop());
63 DCHECK(ui_loop_ != g_browser_process->file_thread()->message_loop());
64
65 // In order to process this request, we need to use the plugin information.
66 // However, plugin process information is only available from the IO thread.
67 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:1068 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnIOThread));
initial.commit09911bf2008-07-26 23:55:2969}
70
[email protected]a27a9382009-02-11 23:55:1071void MemoryDetails::CollectChildInfoOnIOThread() {
initial.commit09911bf2008-07-26 23:55:2972 DCHECK(MessageLoop::current() ==
73 ChromeThread::GetMessageLoop(ChromeThread::IO));
74
[email protected]a27a9382009-02-11 23:55:1075 std::vector<ProcessMemoryInformation> child_info;
76
[email protected]a436d922009-02-13 23:16:4277 // Collect the list of child processes.
[email protected]6dffde322009-02-18 03:47:4878 for (ChildProcessHost::Iterator iter; !iter.Done(); ++iter) {
[email protected]a27a9382009-02-11 23:55:1079 ProcessMemoryInformation info;
[email protected]dc993e32009-03-18 23:47:5480 info.pid = iter->GetProcessId();
[email protected]a27a9382009-02-11 23:55:1081 if (!info.pid)
82 continue;
83
[email protected]a436d922009-02-13 23:16:4284 info.type = iter->type();
85 info.titles.push_back(iter->name());
[email protected]a27a9382009-02-11 23:55:1086 child_info.push_back(info);
initial.commit09911bf2008-07-26 23:55:2987 }
88
89 // Now go do expensive memory lookups from the file thread.
90 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:1091 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info));
initial.commit09911bf2008-07-26 23:55:2992}
93
[email protected]a27a9382009-02-11 23:55:1094void MemoryDetails::CollectProcessData(
95 std::vector<ProcessMemoryInformation> child_info) {
initial.commit09911bf2008-07-26 23:55:2996 DCHECK(MessageLoop::current() ==
97 ChromeThread::GetMessageLoop(ChromeThread::FILE));
98
initial.commit09911bf2008-07-26 23:55:2999 // Clear old data.
100 for (int index = 0; index < arraysize(g_process_template); index++)
101 process_data_[index].processes.clear();
102
[email protected]e288a8e72009-04-29 15:53:26103 SYSTEM_INFO system_info;
104 GetNativeSystemInfo(&system_info);
105 bool is_64bit_os =
106 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
107
108 ScopedHandle snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
109 PROCESSENTRY32 process_entry = {sizeof(PROCESSENTRY32)};
110 if (!snapshot.Get()) {
111 LOG(ERROR) << "CreateToolhelp32Snaphot failed: " << GetLastError();
112 return;
113 }
114 if (!::Process32First(snapshot, &process_entry)) {
115 LOG(ERROR) << "Process32First failed: " << GetLastError();
116 return;
117 }
118 do {
119 int pid = process_entry.th32ProcessID;
120 ScopedHandle handle(::OpenProcess(
[email protected]a27a9382009-02-11 23:55:10121 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
122 if (!handle.Get())
123 continue;
[email protected]e288a8e72009-04-29 15:53:26124 bool is_64bit_process = false;
125 // IsWow64Process() returns FALSE for a 32bit process on a 32bit OS.
126 // We need to check if the real OS is 64bit.
127 if (is_64bit_os) {
128 BOOL is_wow64 = FALSE;
129 // IsWow64Process() is supported by Windows XP SP2 or later.
130 IsWow64Process(handle, &is_wow64);
131 is_64bit_process = !is_wow64;
132 }
[email protected]a27a9382009-02-11 23:55:10133 for (int index2 = 0; index2 < arraysize(g_process_template); index2++) {
[email protected]e288a8e72009-04-29 15:53:26134 if (_wcsicmp(process_data_[index2].process_name,
135 process_entry.szExeFile) != 0)
[email protected]a27a9382009-02-11 23:55:10136 continue;
[email protected]e288a8e72009-04-29 15:53:26137 if (index2 == IE_BROWSER && is_64bit_process)
138 continue; // Should use IE_64BIT_BROWSER
[email protected]a27a9382009-02-11 23:55:10139 // Get Memory Information.
140 ProcessMemoryInformation info;
141 info.pid = pid;
142 if (info.pid == GetCurrentProcessId())
143 info.type = ChildProcessInfo::BROWSER_PROCESS;
144 else
145 info.type = ChildProcessInfo::UNKNOWN_PROCESS;
initial.commit09911bf2008-07-26 23:55:29146
[email protected]a27a9382009-02-11 23:55:10147 scoped_ptr<base::ProcessMetrics> metrics;
148 metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
149 metrics->GetCommittedKBytes(&info.committed);
150 metrics->GetWorkingSetKBytes(&info.working_set);
initial.commit09911bf2008-07-26 23:55:29151
[email protected]a27a9382009-02-11 23:55:10152 // Get Version Information.
[email protected]e288a8e72009-04-29 15:53:26153 TCHAR name[MAX_PATH];
154 if (index2 == CHROME_BROWSER) {
[email protected]a27a9382009-02-11 23:55:10155 scoped_ptr<FileVersionInfo> version_info(
[email protected]e288a8e72009-04-29 15:53:26156 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
[email protected]a27a9382009-02-11 23:55:10157 if (version_info != NULL)
158 info.version = version_info->file_version();
159 // Check if this is one of the child processes whose data we collected
160 // on the IO thread, and if so copy over that data.
161 for (size_t child = 0; child < child_info.size(); child++) {
162 if (child_info[child].pid != info.pid)
163 continue;
164 info.titles = child_info[child].titles;
165 info.type = child_info[child].type;
166 break;
167 }
168 } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) {
169 std::wstring str_name(name);
170 scoped_ptr<FileVersionInfo> version_info(
[email protected]e288a8e72009-04-29 15:53:26171 FileVersionInfo::CreateFileVersionInfo(str_name));
[email protected]a27a9382009-02-11 23:55:10172 if (version_info != NULL) {
173 info.version = version_info->product_version();
174 info.product_name = version_info->product_name();
initial.commit09911bf2008-07-26 23:55:29175 }
176 }
[email protected]a27a9382009-02-11 23:55:10177
178 // Add the process info to our list.
179 process_data_[index2].processes.push_back(info);
180 break;
initial.commit09911bf2008-07-26 23:55:29181 }
[email protected]e288a8e72009-04-29 15:53:26182 } while (::Process32Next(snapshot, &process_entry));
initial.commit09911bf2008-07-26 23:55:29183
184 // Finally return to the browser thread.
185 ui_loop_->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:10186 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
initial.commit09911bf2008-07-26 23:55:29187}
188
[email protected]a27a9382009-02-11 23:55:10189void MemoryDetails::CollectChildInfoOnUIThread() {
initial.commit09911bf2008-07-26 23:55:29190 DCHECK(MessageLoop::current() == ui_loop_);
191
[email protected]a27a9382009-02-11 23:55:10192 // Get more information about the process.
initial.commit09911bf2008-07-26 23:55:29193 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
194 index++) {
[email protected]a27a9382009-02-11 23:55:10195 // Check if it's a renderer, if so get the list of page titles in it and
196 // check if it's a diagnostics-related process. We skip all diagnostics
197 // pages (e.g. "about:xxx" URLs). Iterate the RenderProcessHosts to find
[email protected]cd3d7892009-03-04 23:55:06198 // the tab contents.
[email protected]9de09f82009-08-17 20:13:53199 RenderProcessHost::iterator renderer_iter(
200 RenderProcessHost::AllHostsIterator());
201 for (; !renderer_iter.IsAtEnd(); renderer_iter.Advance()) {
202 DCHECK(renderer_iter.GetCurrentValue());
[email protected]a27a9382009-02-11 23:55:10203 ProcessMemoryInformation& process =
204 process_data_[CHROME_BROWSER].processes[index];
[email protected]9de09f82009-08-17 20:13:53205 if (process.pid != renderer_iter.GetCurrentValue()->process().pid())
[email protected]a27a9382009-02-11 23:55:10206 continue;
207 process.type = ChildProcessInfo::RENDER_PROCESS;
208 // The RenderProcessHost may host multiple TabContents. Any
209 // of them which contain diagnostics information make the whole
210 // process be considered a diagnostics process.
211 //
212 // NOTE: This is a bit dangerous. We know that for now, listeners
213 // are always RenderWidgetHosts. But in theory, they don't
214 // have to be.
[email protected]9de09f82009-08-17 20:13:53215 RenderProcessHost::listeners_iterator iter(
216 renderer_iter.GetCurrentValue()->ListenersIterator());
217 for (; !iter.IsAtEnd(); iter.Advance()) {
218 const RenderWidgetHost* widget =
219 static_cast<const RenderWidgetHost*>(iter.GetCurrentValue());
[email protected]a27a9382009-02-11 23:55:10220 DCHECK(widget);
221 if (!widget || !widget->IsRenderView())
222 continue;
initial.commit09911bf2008-07-26 23:55:29223
[email protected]9de09f82009-08-17 20:13:53224 const RenderViewHost* host = static_cast<const RenderViewHost*>(widget);
[email protected]6d53eb22009-02-13 20:48:39225 TabContents* contents = NULL;
226 if (host->delegate())
[email protected]57c6a652009-05-04 07:58:34227 contents = host->delegate()->GetAsTabContents();
[email protected]a27a9382009-02-11 23:55:10228 if (!contents)
229 continue;
[email protected]4c4d8d22009-03-04 05:29:27230 std::wstring title = UTF16ToWideHack(contents->GetTitle());
[email protected]a27a9382009-02-11 23:55:10231 if (!title.length())
232 title = L"Untitled";
233 process.titles.push_back(title);
[email protected]cd3d7892009-03-04 23:55:06234
[email protected]ebe89e062009-08-13 23:16:54235 // We need to check the pending entry as well as the virtual_url to
[email protected]cd3d7892009-03-04 23:55:06236 // see if it's an about:memory URL (we don't want to count these in the
237 // total memory usage of the browser).
238 //
239 // When we reach here, about:memory will be the pending entry since we
240 // haven't responded with any data such that it would be committed. If
241 // you have another about:memory tab open (which would be committed),
242 // we don't want to count it either, so we also check the last committed
243 // entry.
244 //
245 // Either the pending or last committed entries can be NULL.
[email protected]6df40742009-03-19 22:24:50246 const NavigationEntry* pending_entry =
[email protected]ce3fa3c2009-04-20 19:55:57247 contents->controller().pending_entry();
[email protected]cd3d7892009-03-04 23:55:06248 const NavigationEntry* last_committed_entry =
[email protected]ce3fa3c2009-04-20 19:55:57249 contents->controller().GetLastCommittedEntry();
[email protected]cd3d7892009-03-04 23:55:06250 if ((last_committed_entry &&
[email protected]ebe89e062009-08-13 23:16:54251 LowerCaseEqualsASCII(last_committed_entry->virtual_url().spec(),
[email protected]cd3d7892009-03-04 23:55:06252 chrome::kAboutMemoryURL)) ||
253 (pending_entry &&
[email protected]ebe89e062009-08-13 23:16:54254 LowerCaseEqualsASCII(pending_entry->virtual_url().spec(),
[email protected]cd3d7892009-03-04 23:55:06255 chrome::kAboutMemoryURL)))
[email protected]a27a9382009-02-11 23:55:10256 process.is_diagnostics = true;
initial.commit09911bf2008-07-26 23:55:29257 }
258 }
259 }
260
[email protected]a27a9382009-02-11 23:55:10261 // Get rid of other Chrome processes that are from a different profile.
262 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
263 index++) {
264 if (process_data_[CHROME_BROWSER].processes[index].type ==
265 ChildProcessInfo::UNKNOWN_PROCESS) {
266 process_data_[CHROME_BROWSER].processes.erase(
267 process_data_[CHROME_BROWSER].processes.begin() + index);
[email protected]a436d922009-02-13 23:16:42268 index--;
[email protected]a27a9382009-02-11 23:55:10269 }
270 }
271
initial.commit09911bf2008-07-26 23:55:29272 UpdateHistograms();
273
274 OnDetailsAvailable();
275}
276
277void MemoryDetails::UpdateHistograms() {
278 // Reports a set of memory metrics to UMA.
279 // Memory is measured in units of 10KB.
280
initial.commit09911bf2008-07-26 23:55:29281 ProcessData browser = process_data_[CHROME_BROWSER];
282 size_t aggregate_memory = 0;
[email protected]a27a9382009-02-11 23:55:10283 int plugin_count = 0;
284 int worker_count = 0;
initial.commit09911bf2008-07-26 23:55:29285 for (size_t index = 0; index < browser.processes.size(); index++) {
[email protected]921cd0cc2008-10-21 22:30:55286 int sample = static_cast<int>(browser.processes[index].working_set.priv);
287 aggregate_memory += sample;
[email protected]a27a9382009-02-11 23:55:10288 switch (browser.processes[index].type) {
289 case ChildProcessInfo::BROWSER_PROCESS:
[email protected]553dba62009-02-24 19:08:23290 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample);
[email protected]a27a9382009-02-11 23:55:10291 break;
292 case ChildProcessInfo::RENDER_PROCESS:
[email protected]553dba62009-02-24 19:08:23293 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample);
[email protected]a27a9382009-02-11 23:55:10294 break;
295 case ChildProcessInfo::PLUGIN_PROCESS:
[email protected]553dba62009-02-24 19:08:23296 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample);
[email protected]a27a9382009-02-11 23:55:10297 plugin_count++;
298 break;
299 case ChildProcessInfo::WORKER_PROCESS:
[email protected]553dba62009-02-24 19:08:23300 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample);
[email protected]a27a9382009-02-11 23:55:10301 worker_count++;
302 break;
initial.commit09911bf2008-07-26 23:55:29303 }
304 }
[email protected]a27a9382009-02-11 23:55:10305
[email protected]553dba62009-02-24 19:08:23306 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount",
initial.commit09911bf2008-07-26 23:55:29307 static_cast<int>(browser.processes.size()));
[email protected]553dba62009-02-24 19:08:23308 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count);
309 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count);
[email protected]921cd0cc2008-10-21 22:30:55310
311 int total_sample = static_cast<int>(aggregate_memory / 1000);
[email protected]553dba62009-02-24 19:08:23312 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample);
initial.commit09911bf2008-07-26 23:55:29313}