blob: 79c6a074d0ab72e58a5a8adb14ccc33adb30daa5 [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"
initial.commit09911bf2008-07-26 23:55:299#include "chrome/browser/browser_process.h"
[email protected]dcccb942009-02-01 18:23:0010#include "chrome/browser/chrome_thread.h"
[email protected]8c8657d62009-01-16 18:31:2611#include "chrome/browser/renderer_host/render_process_host.h"
[email protected]f3ec7742009-01-15 00:59:1612#include "chrome/browser/tab_contents/web_contents.h"
[email protected]6dffde322009-02-18 03:47:4813#include "chrome/common/child_process_host.h"
initial.commit09911bf2008-07-26 23:55:2914
15class RenderViewHostDelegate;
16
17// Template of static data we use for finding browser process information.
18// These entries must match the ordering for MemoryDetails::BrowserProcess.
19static ProcessData g_process_template[] = {
[email protected]1e1402c12008-08-28 00:58:4120 { L"Chromium", L"chrome.exe", },
initial.commit09911bf2008-07-26 23:55:2921 { L"IE", L"iexplore.exe", },
22 { L"Firefox", L"firefox.exe", },
23 { L"Opera", L"opera.exe", },
24 { L"Safari", L"safari.exe", },
25 };
26
27
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
41MemoryDetails::MemoryDetails()
42 : ui_loop_(NULL) {
43 for (int index = 0; index < arraysize(g_process_template); ++index) {
44 process_data_[index].name = g_process_template[index].name;
45 process_data_[index].process_name = g_process_template[index].process_name;
46 }
47}
48
49void MemoryDetails::StartFetch() {
50 ui_loop_ = MessageLoop::current();
51
52 DCHECK(ui_loop_ != g_browser_process->io_thread()->message_loop());
53 DCHECK(ui_loop_ != g_browser_process->file_thread()->message_loop());
54
55 // In order to process this request, we need to use the plugin information.
56 // However, plugin process information is only available from the IO thread.
57 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:1058 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnIOThread));
initial.commit09911bf2008-07-26 23:55:2959}
60
[email protected]a27a9382009-02-11 23:55:1061void MemoryDetails::CollectChildInfoOnIOThread() {
initial.commit09911bf2008-07-26 23:55:2962 DCHECK(MessageLoop::current() ==
63 ChromeThread::GetMessageLoop(ChromeThread::IO));
64
[email protected]a27a9382009-02-11 23:55:1065 std::vector<ProcessMemoryInformation> child_info;
66
[email protected]a436d922009-02-13 23:16:4267 // Collect the list of child processes.
[email protected]6dffde322009-02-18 03:47:4868 for (ChildProcessHost::Iterator iter; !iter.Done(); ++iter) {
[email protected]a27a9382009-02-11 23:55:1069 ProcessMemoryInformation info;
[email protected]6dffde322009-02-18 03:47:4870 info.pid = iter->pid();
[email protected]a27a9382009-02-11 23:55:1071 if (!info.pid)
72 continue;
73
[email protected]a436d922009-02-13 23:16:4274 info.type = iter->type();
75 info.titles.push_back(iter->name());
[email protected]a27a9382009-02-11 23:55:1076 child_info.push_back(info);
initial.commit09911bf2008-07-26 23:55:2977 }
78
79 // Now go do expensive memory lookups from the file thread.
80 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:1081 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info));
initial.commit09911bf2008-07-26 23:55:2982}
83
[email protected]a27a9382009-02-11 23:55:1084void MemoryDetails::CollectProcessData(
85 std::vector<ProcessMemoryInformation> child_info) {
initial.commit09911bf2008-07-26 23:55:2986 DCHECK(MessageLoop::current() ==
87 ChromeThread::GetMessageLoop(ChromeThread::FILE));
88
89 int array_size = 32;
[email protected]a27a9382009-02-11 23:55:1090 scoped_ptr_malloc<DWORD> process_list;
initial.commit09911bf2008-07-26 23:55:2991 DWORD bytes_used = 0;
92 do {
93 array_size *= 2;
[email protected]a27a9382009-02-11 23:55:1094 process_list.reset(static_cast<DWORD*>(
95 realloc(process_list.release(), sizeof(DWORD) * array_size)));
initial.commit09911bf2008-07-26 23:55:2996 // EnumProcesses doesn't return an error if the array is too small.
97 // We have to check if the return buffer is full, and if so, call it
98 // again. See msdn docs for more info.
[email protected]a27a9382009-02-11 23:55:1099 if (!EnumProcesses(process_list.get(), array_size * sizeof(DWORD),
initial.commit09911bf2008-07-26 23:55:29100 &bytes_used)) {
101 LOG(ERROR) << "EnumProcesses failed: " << GetLastError();
102 return;
103 }
[email protected]a27a9382009-02-11 23:55:10104 } while (bytes_used == (array_size * sizeof(DWORD)));
initial.commit09911bf2008-07-26 23:55:29105
[email protected]a27a9382009-02-11 23:55:10106 int num_processes = bytes_used / sizeof(DWORD);
initial.commit09911bf2008-07-26 23:55:29107
108 // Clear old data.
109 for (int index = 0; index < arraysize(g_process_template); index++)
110 process_data_[index].processes.clear();
111
112 for (int index = 0; index < num_processes; index++) {
[email protected]a27a9382009-02-11 23:55:10113 int pid = process_list.get()[index];
114 ScopedHandle handle(OpenProcess(
115 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
116 if (!handle.Get())
117 continue;
118 TCHAR name[MAX_PATH];
119 if (!GetModuleBaseName(handle, NULL, name, MAX_PATH - 1))
120 continue;
121 for (int index2 = 0; index2 < arraysize(g_process_template); index2++) {
122 if (_wcsicmp(process_data_[index2].process_name, name) != 0)
123 continue;
124 // Get Memory Information.
125 ProcessMemoryInformation info;
126 info.pid = pid;
127 if (info.pid == GetCurrentProcessId())
128 info.type = ChildProcessInfo::BROWSER_PROCESS;
129 else
130 info.type = ChildProcessInfo::UNKNOWN_PROCESS;
initial.commit09911bf2008-07-26 23:55:29131
[email protected]a27a9382009-02-11 23:55:10132 scoped_ptr<base::ProcessMetrics> metrics;
133 metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
134 metrics->GetCommittedKBytes(&info.committed);
135 metrics->GetWorkingSetKBytes(&info.working_set);
initial.commit09911bf2008-07-26 23:55:29136
[email protected]a27a9382009-02-11 23:55:10137 // 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 // Check if this is one of the child processes whose data we collected
144 // on the IO thread, and if so copy over that data.
145 for (size_t child = 0; child < child_info.size(); child++) {
146 if (child_info[child].pid != info.pid)
147 continue;
148 info.titles = child_info[child].titles;
149 info.type = child_info[child].type;
150 break;
151 }
152 } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) {
153 std::wstring str_name(name);
154 scoped_ptr<FileVersionInfo> version_info(
155 FileVersionInfo::CreateFileVersionInfo(str_name));
156 if (version_info != NULL) {
157 info.version = version_info->product_version();
158 info.product_name = version_info->product_name();
initial.commit09911bf2008-07-26 23:55:29159 }
160 }
[email protected]a27a9382009-02-11 23:55:10161
162 // Add the process info to our list.
163 process_data_[index2].processes.push_back(info);
164 break;
initial.commit09911bf2008-07-26 23:55:29165 }
166 }
initial.commit09911bf2008-07-26 23:55:29167
168 // Finally return to the browser thread.
169 ui_loop_->PostTask(FROM_HERE,
[email protected]a27a9382009-02-11 23:55:10170 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
initial.commit09911bf2008-07-26 23:55:29171}
172
[email protected]a27a9382009-02-11 23:55:10173void MemoryDetails::CollectChildInfoOnUIThread() {
initial.commit09911bf2008-07-26 23:55:29174 DCHECK(MessageLoop::current() == ui_loop_);
175
[email protected]a27a9382009-02-11 23:55:10176 // Get more information about the process.
initial.commit09911bf2008-07-26 23:55:29177 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
178 index++) {
[email protected]a27a9382009-02-11 23:55:10179 // Check if it's a renderer, if so get the list of page titles in it and
180 // check if it's a diagnostics-related process. We skip all diagnostics
181 // pages (e.g. "about:xxx" URLs). Iterate the RenderProcessHosts to find
182 // the tab contents. If it is of type TAB_CONTENTS_ABOUT_UI, mark the
183 // process as diagnostics related.
initial.commit09911bf2008-07-26 23:55:29184 RenderProcessHost::iterator renderer_iter;
185 for (renderer_iter = RenderProcessHost::begin(); renderer_iter !=
186 RenderProcessHost::end(); ++renderer_iter) {
187 DCHECK(renderer_iter->second);
[email protected]a27a9382009-02-11 23:55:10188 ProcessMemoryInformation& process =
189 process_data_[CHROME_BROWSER].processes[index];
190 if (process.pid != renderer_iter->second->process().pid())
191 continue;
192 process.type = ChildProcessInfo::RENDER_PROCESS;
193 // The RenderProcessHost may host multiple TabContents. Any
194 // of them which contain diagnostics information make the whole
195 // process be considered a diagnostics process.
196 //
197 // NOTE: This is a bit dangerous. We know that for now, listeners
198 // are always RenderWidgetHosts. But in theory, they don't
199 // have to be.
200 RenderProcessHost::listeners_iterator iter;
201 for (iter = renderer_iter->second->listeners_begin();
202 iter != renderer_iter->second->listeners_end(); ++iter) {
203 RenderWidgetHost* widget =
204 static_cast<RenderWidgetHost*>(iter->second);
205 DCHECK(widget);
206 if (!widget || !widget->IsRenderView())
207 continue;
initial.commit09911bf2008-07-26 23:55:29208
[email protected]a27a9382009-02-11 23:55:10209 RenderViewHost* host = static_cast<RenderViewHost*>(widget);
[email protected]6d53eb22009-02-13 20:48:39210 TabContents* contents = NULL;
211 if (host->delegate())
212 contents = host->delegate()->GetAsWebContents();
[email protected]a27a9382009-02-11 23:55:10213 if (!contents)
214 continue;
215 std::wstring title = contents->GetTitle();
216 if (!title.length())
217 title = L"Untitled";
218 process.titles.push_back(title);
219 if (contents->type() == TAB_CONTENTS_ABOUT_UI)
220 process.is_diagnostics = true;
initial.commit09911bf2008-07-26 23:55:29221 }
222 }
223 }
224
[email protected]a27a9382009-02-11 23:55:10225 // Get rid of other Chrome processes that are from a different profile.
226 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
227 index++) {
228 if (process_data_[CHROME_BROWSER].processes[index].type ==
229 ChildProcessInfo::UNKNOWN_PROCESS) {
230 process_data_[CHROME_BROWSER].processes.erase(
231 process_data_[CHROME_BROWSER].processes.begin() + index);
[email protected]a436d922009-02-13 23:16:42232 index--;
[email protected]a27a9382009-02-11 23:55:10233 }
234 }
235
initial.commit09911bf2008-07-26 23:55:29236 UpdateHistograms();
237
238 OnDetailsAvailable();
239}
240
241void MemoryDetails::UpdateHistograms() {
242 // Reports a set of memory metrics to UMA.
243 // Memory is measured in units of 10KB.
244
initial.commit09911bf2008-07-26 23:55:29245 ProcessData browser = process_data_[CHROME_BROWSER];
246 size_t aggregate_memory = 0;
[email protected]a27a9382009-02-11 23:55:10247 int plugin_count = 0;
248 int worker_count = 0;
initial.commit09911bf2008-07-26 23:55:29249 for (size_t index = 0; index < browser.processes.size(); index++) {
[email protected]921cd0cc2008-10-21 22:30:55250 int sample = static_cast<int>(browser.processes[index].working_set.priv);
251 aggregate_memory += sample;
[email protected]a27a9382009-02-11 23:55:10252 switch (browser.processes[index].type) {
253 case ChildProcessInfo::BROWSER_PROCESS:
[email protected]553dba62009-02-24 19:08:23254 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample);
[email protected]a27a9382009-02-11 23:55:10255 break;
256 case ChildProcessInfo::RENDER_PROCESS:
[email protected]553dba62009-02-24 19:08:23257 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample);
[email protected]a27a9382009-02-11 23:55:10258 break;
259 case ChildProcessInfo::PLUGIN_PROCESS:
[email protected]553dba62009-02-24 19:08:23260 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample);
[email protected]a27a9382009-02-11 23:55:10261 plugin_count++;
262 break;
263 case ChildProcessInfo::WORKER_PROCESS:
[email protected]553dba62009-02-24 19:08:23264 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample);
[email protected]a27a9382009-02-11 23:55:10265 worker_count++;
266 break;
initial.commit09911bf2008-07-26 23:55:29267 }
268 }
[email protected]a27a9382009-02-11 23:55:10269
[email protected]553dba62009-02-24 19:08:23270 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount",
initial.commit09911bf2008-07-26 23:55:29271 static_cast<int>(browser.processes.size()));
[email protected]553dba62009-02-24 19:08:23272 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count);
273 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count);
[email protected]921cd0cc2008-10-21 22:30:55274
275 int total_sample = static_cast<int>(aggregate_memory / 1000);
[email protected]553dba62009-02-24 19:08:23276 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample);
initial.commit09911bf2008-07-26 23:55:29277}