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