blob: 0a79f80a6aef564e770f5d43fff242821d25b70b [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
8#include "base/file_version_info.h"
9#include "base/histogram.h"
10#include "base/image_util.h"
11#include "base/message_loop.h"
12#include "base/process_util.h"
13#include "base/thread.h"
14#include "chrome/browser/browser_process.h"
[email protected]921cd0cc2008-10-21 22:30:5515#include "chrome/browser/browser_trial.h"
initial.commit09911bf2008-07-26 23:55:2916#include "chrome/browser/plugin_process_host.h"
17#include "chrome/browser/plugin_service.h"
[email protected]8c8657d62009-01-16 18:31:2618#include "chrome/browser/renderer_host/render_process_host.h"
initial.commit09911bf2008-07-26 23:55:2919#include "chrome/browser/render_view_host.h"
[email protected]f3ec7742009-01-15 00:59:1620#include "chrome/browser/tab_contents/tab_contents.h"
21#include "chrome/browser/tab_contents/web_contents.h"
initial.commit09911bf2008-07-26 23:55:2922
23class RenderViewHostDelegate;
24
25// Template of static data we use for finding browser process information.
26// These entries must match the ordering for MemoryDetails::BrowserProcess.
27static ProcessData g_process_template[] = {
[email protected]1e1402c12008-08-28 00:58:4128 { L"Chromium", L"chrome.exe", },
initial.commit09911bf2008-07-26 23:55:2929 { L"IE", L"iexplore.exe", },
30 { L"Firefox", L"firefox.exe", },
31 { L"Opera", L"opera.exe", },
32 { L"Safari", L"safari.exe", },
33 };
34
35
36// About threading:
37//
38// This operation will hit no fewer than 3 threads.
39//
40// The PluginHostIterator can only be accessed from the IO thread.
41//
42// The RenderProcessHostIterator can only be accessed from the UI thread.
43//
44// This operation can take 30-100ms to complete. We never want to have
45// one task run for that long on the UI or IO threads. So, we run the
46// expensive parts of this operation over on the file thread.
47//
48
49MemoryDetails::MemoryDetails()
50 : ui_loop_(NULL) {
51 for (int index = 0; index < arraysize(g_process_template); ++index) {
52 process_data_[index].name = g_process_template[index].name;
53 process_data_[index].process_name = g_process_template[index].process_name;
54 }
55}
56
57void MemoryDetails::StartFetch() {
58 ui_loop_ = MessageLoop::current();
59
60 DCHECK(ui_loop_ != g_browser_process->io_thread()->message_loop());
61 DCHECK(ui_loop_ != g_browser_process->file_thread()->message_loop());
62
63 // In order to process this request, we need to use the plugin information.
64 // However, plugin process information is only available from the IO thread.
65 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
66 NewRunnableMethod(this, &MemoryDetails::CollectPluginInformation));
67}
68
69void MemoryDetails::CollectPluginInformation() {
70 DCHECK(MessageLoop::current() ==
71 ChromeThread::GetMessageLoop(ChromeThread::IO));
72
73 // Collect the list of plugins.
74 for (PluginProcessHostIterator plugin_iter;
75 !plugin_iter.Done(); ++plugin_iter) {
76 PluginProcessHost* plugin = const_cast<PluginProcessHost*>(*plugin_iter);
77 DCHECK(plugin);
78 if (!plugin || !plugin->process())
79 continue;
80
81 PluginProcessInformation info;
[email protected]176aa482008-11-14 03:25:1582 info.pid = base::GetProcId(plugin->process());
initial.commit09911bf2008-07-26 23:55:2983 if (info.pid != 0) {
[email protected]690a99c2009-01-06 16:48:4584 info.plugin_path = plugin->plugin_path();
initial.commit09911bf2008-07-26 23:55:2985 plugins_.push_back(info);
86 }
87 }
88
89 // Now go do expensive memory lookups from the file thread.
90 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE,
91 NewRunnableMethod(this, &MemoryDetails::CollectProcessData));
92}
93
94void MemoryDetails::CollectProcessData() {
95 DCHECK(MessageLoop::current() ==
96 ChromeThread::GetMessageLoop(ChromeThread::FILE));
97
98 int array_size = 32;
99 DWORD* process_list = NULL;
100 DWORD bytes_used = 0;
101 do {
102 array_size *= 2;
103 process_list = static_cast<DWORD*>(
104 realloc(process_list, sizeof(*process_list) * array_size));
105 // EnumProcesses doesn't return an error if the array is too small.
106 // We have to check if the return buffer is full, and if so, call it
107 // again. See msdn docs for more info.
108 if (!EnumProcesses(process_list, array_size * sizeof(*process_list),
109 &bytes_used)) {
110 LOG(ERROR) << "EnumProcesses failed: " << GetLastError();
111 return;
112 }
113 } while (bytes_used == (array_size * sizeof(*process_list)));
114
115 int num_processes = bytes_used / sizeof(*process_list);
116
117 // Clear old data.
118 for (int index = 0; index < arraysize(g_process_template); index++)
119 process_data_[index].processes.clear();
120
121 for (int index = 0; index < num_processes; index++) {
122 HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
123 FALSE, process_list[index]);
124 if (handle) {
125 TCHAR name[MAX_PATH];
126 if (GetModuleBaseName(handle, NULL, name, MAX_PATH-1)) {
127 for (int index2 = 0; index2 < arraysize(g_process_template); index2++) {
128 if (_wcsicmp(process_data_[index2].process_name, name) == 0) {
129 // Get Memory Information.
130 ProcessMemoryInformation info;
131 info.pid = process_list[index];
[email protected]176aa482008-11-14 03:25:15132 scoped_ptr<base::ProcessMetrics> metrics;
133 metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
initial.commit09911bf2008-07-26 23:55:29134 metrics->GetCommittedKBytes(&info.committed);
135 metrics->GetWorkingSetKBytes(&info.working_set);
136
137 // Get Version Information.
138 if (index2 == 0) { // Chrome
139 scoped_ptr<FileVersionInfo> version_info(
140 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
141 if (version_info != NULL)
142 info.version = version_info->file_version();
143 } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH-1)) {
144 std::wstring str_name(name);
145 scoped_ptr<FileVersionInfo> version_info(
146 FileVersionInfo::CreateFileVersionInfo(str_name));
147 if (version_info != NULL) {
148 info.version = version_info->product_version();
149 info.product_name = version_info->product_name();
150 }
151 }
152
153 // Add the process info to our list.
154 process_data_[index2].processes.push_back(info);
155 break;
156 }
157 }
158 }
159 CloseHandle(handle);
160 }
161 }
162 free(process_list);
163
164 // Finally return to the browser thread.
165 ui_loop_->PostTask(FROM_HERE,
166 NewRunnableMethod(this, &MemoryDetails::CollectRenderHostInformation));
167}
168
169void MemoryDetails::CollectRenderHostInformation() {
170 DCHECK(MessageLoop::current() == ui_loop_);
171
172 // Determine if this is a diagnostics-related process. We skip all
173 // diagnostics pages (e.g. "about:xxx" URLs). Iterate the RenderProcessHosts
174 // to find the tab contents. If it is of type TAB_CONTENTS_ABOUT_UI, mark
175 // the process as diagnostics related.
176 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
177 index++) {
178 RenderProcessHost::iterator renderer_iter;
179 for (renderer_iter = RenderProcessHost::begin(); renderer_iter !=
180 RenderProcessHost::end(); ++renderer_iter) {
181 DCHECK(renderer_iter->second);
182 if (process_data_[CHROME_BROWSER].processes[index].pid ==
[email protected]2f15de42008-11-11 22:35:19183 renderer_iter->second->process().pid()) {
initial.commit09911bf2008-07-26 23:55:29184 // The RenderProcessHost may host multiple TabContents. Any
185 // of them which contain diagnostics information make the whole
186 // process be considered a diagnostics process.
187 //
188 // NOTE: This is a bit dangerous. We know that for now, listeners
189 // are always RenderWidgetHosts. But in theory, they don't
190 // have to be.
191 RenderProcessHost::listeners_iterator iter;
192 for (iter = renderer_iter->second->listeners_begin();
193 iter != renderer_iter->second->listeners_end(); ++iter) {
194 RenderWidgetHost* widget =
195 static_cast<RenderWidgetHost*>(iter->second);
196 DCHECK(widget);
197 if (!widget || !widget->IsRenderView())
198 continue;
199
200 RenderViewHost* host = static_cast<RenderViewHost*>(widget);
201 TabContents* contents =
202 static_cast<WebContents*>(host->delegate());
203 DCHECK(contents);
204 if (!contents)
205 continue;
206
207 if (contents->type() == TAB_CONTENTS_ABOUT_UI)
208 process_data_[CHROME_BROWSER].processes[index].is_diagnostics =
209 true;
210 }
211 }
212 }
213 }
214
215 UpdateHistograms();
216
217 OnDetailsAvailable();
218}
219
220void MemoryDetails::UpdateHistograms() {
221 // Reports a set of memory metrics to UMA.
222 // Memory is measured in units of 10KB.
223
[email protected]921cd0cc2008-10-21 22:30:55224 // If field trial is active, report results in special histograms.
225 static scoped_refptr<FieldTrial> trial(
226 FieldTrialList::Find(BrowserTrial::kMemoryModelFieldTrial));
227
initial.commit09911bf2008-07-26 23:55:29228 DWORD browser_pid = GetCurrentProcessId();
229 ProcessData browser = process_data_[CHROME_BROWSER];
230 size_t aggregate_memory = 0;
231 for (size_t index = 0; index < browser.processes.size(); index++) {
[email protected]921cd0cc2008-10-21 22:30:55232 int sample = static_cast<int>(browser.processes[index].working_set.priv);
233 aggregate_memory += sample;
initial.commit09911bf2008-07-26 23:55:29234 if (browser.processes[index].pid == browser_pid) {
[email protected]921cd0cc2008-10-21 22:30:55235 if (trial.get()) {
236 if (trial->boolean_value())
237 UMA_HISTOGRAM_MEMORY_KB(L"Memory.Browser_trial_high_memory", sample);
238 else
239 UMA_HISTOGRAM_MEMORY_KB(L"Memory.Browser_trial_med_memory", sample);
240 } else {
241 UMA_HISTOGRAM_MEMORY_KB(L"Memory.Browser", sample);
242 }
initial.commit09911bf2008-07-26 23:55:29243 } else {
244 bool is_plugin_process = false;
245 for (size_t index2 = 0; index2 < plugins_.size(); index2++) {
246 if (browser.processes[index].pid == plugins_[index2].pid) {
[email protected]921cd0cc2008-10-21 22:30:55247 UMA_HISTOGRAM_MEMORY_KB(L"Memory.Plugin", sample);
initial.commit09911bf2008-07-26 23:55:29248 is_plugin_process = true;
249 break;
250 }
251 }
[email protected]921cd0cc2008-10-21 22:30:55252 if (!is_plugin_process) {
253 if (trial.get()) {
254 if (trial->boolean_value())
[email protected]0cbd64322008-10-22 01:07:22255 UMA_HISTOGRAM_MEMORY_KB(L"Memory.Renderer_trial_high_memory",
256 sample);
[email protected]921cd0cc2008-10-21 22:30:55257 else
[email protected]0cbd64322008-10-22 01:07:22258 UMA_HISTOGRAM_MEMORY_KB(L"Memory.Renderer_trial_med_memory",
259 sample);
[email protected]921cd0cc2008-10-21 22:30:55260 } else {
261 UMA_HISTOGRAM_MEMORY_KB(L"Memory.Renderer", sample);
262 }
263 }
initial.commit09911bf2008-07-26 23:55:29264 }
265 }
266 UMA_HISTOGRAM_COUNTS_100(L"Memory.ProcessCount",
267 static_cast<int>(browser.processes.size()));
268 UMA_HISTOGRAM_COUNTS_100(L"Memory.PluginProcessCount",
269 static_cast<int>(plugins_.size()));
[email protected]921cd0cc2008-10-21 22:30:55270
271 int total_sample = static_cast<int>(aggregate_memory / 1000);
272 if (trial.get()) {
273 if (trial->boolean_value())
274 UMA_HISTOGRAM_MEMORY_MB(L"Memory.Total_trial_high_memory", total_sample);
275 else
276 UMA_HISTOGRAM_MEMORY_MB(L"Memory.Total_trial_med_memory", total_sample);
277 } else {
278 UMA_HISTOGRAM_MEMORY_MB(L"Memory.Total", total_sample);
279 }
initial.commit09911bf2008-07-26 23:55:29280}
license.botbf09a502008-08-24 00:55:55281