OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/memory_details.h" | 5 #include "chrome/browser/memory_details.h" |
6 #include <psapi.h> | |
7 | 6 |
8 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
9 #include "base/file_version_info.h" | 8 #include "base/file_version_info.h" |
10 #include "base/string_util.h" | 9 #include "base/string_util.h" |
11 #include "chrome/browser/browser_process.h" | 10 #include "chrome/browser/browser_process.h" |
12 #include "chrome/browser/chrome_thread.h" | 11 #include "chrome/browser/chrome_thread.h" |
13 #include "chrome/browser/renderer_host/backing_store_manager.h" | 12 #include "chrome/browser/renderer_host/backing_store_manager.h" |
14 #include "chrome/browser/renderer_host/render_process_host.h" | 13 #include "chrome/browser/renderer_host/render_process_host.h" |
15 #include "chrome/browser/tab_contents/navigation_entry.h" | 14 #include "chrome/browser/tab_contents/navigation_entry.h" |
16 #include "chrome/browser/tab_contents/tab_contents.h" | 15 #include "chrome/browser/tab_contents/tab_contents.h" |
17 #include "chrome/common/child_process_host.h" | 16 #include "chrome/common/child_process_host.h" |
18 #include "chrome/common/url_constants.h" | 17 #include "chrome/common/url_constants.h" |
19 #include "grit/chromium_strings.h" | 18 #include "grit/chromium_strings.h" |
20 | 19 |
21 class RenderViewHostDelegate; | 20 #if defined(OS_LINUX) |
22 | 21 #include "chrome/browser/zygote_host_linux.h" |
23 // Template of static data we use for finding browser process information. | 22 #include "chrome/browser/renderer_host/render_sandbox_host_linux.h" |
24 // These entries must match the ordering for MemoryDetails::BrowserProcess. | 23 #endif |
25 static ProcessData g_process_template[MemoryDetails::MAX_BROWSERS]; | |
26 | 24 |
27 // About threading: | 25 // About threading: |
28 // | 26 // |
29 // This operation will hit no fewer than 3 threads. | 27 // This operation will hit no fewer than 3 threads. |
30 // | 28 // |
31 // The ChildProcessInfo::Iterator can only be accessed from the IO thread. | 29 // The ChildProcessInfo::Iterator can only be accessed from the IO thread. |
32 // | 30 // |
33 // The RenderProcessHostIterator can only be accessed from the UI thread. | 31 // The RenderProcessHostIterator can only be accessed from the UI thread. |
34 // | 32 // |
35 // This operation can take 30-100ms to complete. We never want to have | 33 // This operation can take 30-100ms to complete. We never want to have |
36 // one task run for that long on the UI or IO threads. So, we run the | 34 // one task run for that long on the UI or IO threads. So, we run the |
37 // expensive parts of this operation over on the file thread. | 35 // expensive parts of this operation over on the file thread. |
38 // | 36 // |
39 | 37 |
40 MemoryDetails::MemoryDetails() | |
41 : ui_loop_(NULL) { | |
42 static const std::wstring google_browser_name = | |
43 l10n_util::GetString(IDS_PRODUCT_NAME); | |
44 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 | |
54 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 | |
60 void MemoryDetails::StartFetch() { | 38 void MemoryDetails::StartFetch() { |
61 ui_loop_ = MessageLoop::current(); | 39 ui_loop_ = MessageLoop::current(); |
62 | 40 |
63 DCHECK(ui_loop_ != g_browser_process->io_thread()->message_loop()); | 41 DCHECK(ui_loop_ != g_browser_process->io_thread()->message_loop()); |
64 DCHECK(ui_loop_ != g_browser_process->file_thread()->message_loop()); | 42 DCHECK(ui_loop_ != g_browser_process->file_thread()->message_loop()); |
65 | 43 |
66 // In order to process this request, we need to use the plugin information. | 44 // 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. | 45 // However, plugin process information is only available from the IO thread. |
68 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, | 46 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, |
69 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnIOThread)); | 47 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnIOThread)); |
(...skipping 15 matching lines...) Expand all Loading... |
85 info.type = iter->type(); | 63 info.type = iter->type(); |
86 info.titles.push_back(iter->name()); | 64 info.titles.push_back(iter->name()); |
87 child_info.push_back(info); | 65 child_info.push_back(info); |
88 } | 66 } |
89 | 67 |
90 // Now go do expensive memory lookups from the file thread. | 68 // Now go do expensive memory lookups from the file thread. |
91 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE, | 69 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE, |
92 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info)); | 70 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info)); |
93 } | 71 } |
94 | 72 |
95 void MemoryDetails::CollectProcessData( | |
96 std::vector<ProcessMemoryInformation> child_info) { | |
97 DCHECK(MessageLoop::current() == | |
98 ChromeThread::GetMessageLoop(ChromeThread::FILE)); | |
99 | |
100 // Clear old data. | |
101 for (int index = 0; index < arraysize(g_process_template); index++) | |
102 process_data_[index].processes.clear(); | |
103 | |
104 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( | |
122 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); | |
123 if (!handle.Get()) | |
124 continue; | |
125 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 } | |
134 for (int index2 = 0; index2 < arraysize(g_process_template); index2++) { | |
135 if (_wcsicmp(process_data_[index2].process_name, | |
136 process_entry.szExeFile) != 0) | |
137 continue; | |
138 if (index2 == IE_BROWSER && is_64bit_process) | |
139 continue; // Should use IE_64BIT_BROWSER | |
140 // 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; | |
147 | |
148 scoped_ptr<base::ProcessMetrics> metrics; | |
149 metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle)); | |
150 metrics->GetCommittedKBytes(&info.committed); | |
151 metrics->GetWorkingSetKBytes(&info.working_set); | |
152 | |
153 // Get Version Information. | |
154 TCHAR name[MAX_PATH]; | |
155 if (index2 == CHROME_BROWSER) { | |
156 scoped_ptr<FileVersionInfo> version_info( | |
157 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); | |
158 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( | |
172 FileVersionInfo::CreateFileVersionInfo(str_name)); | |
173 if (version_info != NULL) { | |
174 info.version = version_info->product_version(); | |
175 info.product_name = version_info->product_name(); | |
176 } | |
177 } | |
178 | |
179 // Add the process info to our list. | |
180 process_data_[index2].processes.push_back(info); | |
181 break; | |
182 } | |
183 } while (::Process32Next(snapshot, &process_entry)); | |
184 | |
185 // Finally return to the browser thread. | |
186 ui_loop_->PostTask(FROM_HERE, | |
187 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread)); | |
188 } | |
189 | |
190 void MemoryDetails::CollectChildInfoOnUIThread() { | 73 void MemoryDetails::CollectChildInfoOnUIThread() { |
191 DCHECK(MessageLoop::current() == ui_loop_); | 74 DCHECK(MessageLoop::current() == ui_loop_); |
192 | 75 |
| 76 #if defined(OS_LINUX) |
| 77 const pid_t zygote_pid = Singleton<ZygoteHost>()->pid(); |
| 78 const pid_t sandbox_helper_pid = Singleton<RenderSandboxHostLinux>()->pid(); |
| 79 #endif |
| 80 |
| 81 ProcessData* const chrome_browser = ChromeBrowser(); |
193 // Get more information about the process. | 82 // Get more information about the process. |
194 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size(); | 83 for (size_t index = 0; index < chrome_browser->processes.size(); |
195 index++) { | 84 index++) { |
196 // Check if it's a renderer, if so get the list of page titles in it and | 85 // 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 | 86 // check if it's a diagnostics-related process. We skip all diagnostics |
198 // pages (e.g. "about:xxx" URLs). Iterate the RenderProcessHosts to find | 87 // pages (e.g. "about:xxx" URLs). Iterate the RenderProcessHosts to find |
199 // the tab contents. | 88 // the tab contents. |
200 RenderProcessHost::iterator renderer_iter( | 89 RenderProcessHost::iterator renderer_iter( |
201 RenderProcessHost::AllHostsIterator()); | 90 RenderProcessHost::AllHostsIterator()); |
| 91 ProcessMemoryInformation& process = |
| 92 chrome_browser->processes[index]; |
| 93 |
202 for (; !renderer_iter.IsAtEnd(); renderer_iter.Advance()) { | 94 for (; !renderer_iter.IsAtEnd(); renderer_iter.Advance()) { |
203 DCHECK(renderer_iter.GetCurrentValue()); | 95 DCHECK(renderer_iter.GetCurrentValue()); |
204 ProcessMemoryInformation& process = | |
205 process_data_[CHROME_BROWSER].processes[index]; | |
206 if (process.pid != renderer_iter.GetCurrentValue()->process().pid()) | 96 if (process.pid != renderer_iter.GetCurrentValue()->process().pid()) |
207 continue; | 97 continue; |
208 process.type = ChildProcessInfo::RENDER_PROCESS; | 98 process.type = ChildProcessInfo::RENDER_PROCESS; |
209 // The RenderProcessHost may host multiple TabContents. Any | 99 // The RenderProcessHost may host multiple TabContents. Any |
210 // of them which contain diagnostics information make the whole | 100 // of them which contain diagnostics information make the whole |
211 // process be considered a diagnostics process. | 101 // process be considered a diagnostics process. |
212 // | 102 // |
213 // NOTE: This is a bit dangerous. We know that for now, listeners | 103 // NOTE: This is a bit dangerous. We know that for now, listeners |
214 // are always RenderWidgetHosts. But in theory, they don't | 104 // are always RenderWidgetHosts. But in theory, they don't |
215 // have to be. | 105 // have to be. |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
250 contents->controller().GetLastCommittedEntry(); | 140 contents->controller().GetLastCommittedEntry(); |
251 if ((last_committed_entry && | 141 if ((last_committed_entry && |
252 LowerCaseEqualsASCII(last_committed_entry->virtual_url().spec(), | 142 LowerCaseEqualsASCII(last_committed_entry->virtual_url().spec(), |
253 chrome::kAboutMemoryURL)) || | 143 chrome::kAboutMemoryURL)) || |
254 (pending_entry && | 144 (pending_entry && |
255 LowerCaseEqualsASCII(pending_entry->virtual_url().spec(), | 145 LowerCaseEqualsASCII(pending_entry->virtual_url().spec(), |
256 chrome::kAboutMemoryURL))) | 146 chrome::kAboutMemoryURL))) |
257 process.is_diagnostics = true; | 147 process.is_diagnostics = true; |
258 } | 148 } |
259 } | 149 } |
| 150 |
| 151 #if defined(OS_LINUX) |
| 152 if (process.pid == zygote_pid) { |
| 153 process.type = ChildProcessInfo::ZYGOTE_PROCESS; |
| 154 } else if (process.pid == sandbox_helper_pid) { |
| 155 process.type = ChildProcessInfo::SANDBOX_HELPER_PROCESS; |
| 156 } |
| 157 #endif |
260 } | 158 } |
261 | 159 |
262 // Get rid of other Chrome processes that are from a different profile. | 160 // 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(); | 161 for (size_t index = 0; index < chrome_browser->processes.size(); |
264 index++) { | 162 index++) { |
265 if (process_data_[CHROME_BROWSER].processes[index].type == | 163 if (chrome_browser->processes[index].type == |
266 ChildProcessInfo::UNKNOWN_PROCESS) { | 164 ChildProcessInfo::UNKNOWN_PROCESS) { |
267 process_data_[CHROME_BROWSER].processes.erase( | 165 chrome_browser->processes.erase( |
268 process_data_[CHROME_BROWSER].processes.begin() + index); | 166 chrome_browser->processes.begin() + index); |
269 index--; | 167 index--; |
270 } | 168 } |
271 } | 169 } |
272 | 170 |
273 UpdateHistograms(); | 171 UpdateHistograms(); |
274 | 172 |
275 OnDetailsAvailable(); | 173 OnDetailsAvailable(); |
276 } | 174 } |
277 | 175 |
278 void MemoryDetails::UpdateHistograms() { | 176 void MemoryDetails::UpdateHistograms() { |
279 // Reports a set of memory metrics to UMA. | 177 // Reports a set of memory metrics to UMA. |
280 // Memory is measured in KB. | 178 // Memory is measured in KB. |
281 | 179 |
282 ProcessData browser = process_data_[CHROME_BROWSER]; | 180 const ProcessData& browser = *ChromeBrowser(); |
283 size_t aggregate_memory = 0; | 181 size_t aggregate_memory = 0; |
284 int plugin_count = 0; | 182 int plugin_count = 0; |
285 int worker_count = 0; | 183 int worker_count = 0; |
286 for (size_t index = 0; index < browser.processes.size(); index++) { | 184 for (size_t index = 0; index < browser.processes.size(); index++) { |
287 int sample = static_cast<int>(browser.processes[index].working_set.priv); | 185 int sample = static_cast<int>(browser.processes[index].working_set.priv); |
288 aggregate_memory += sample; | 186 aggregate_memory += sample; |
289 switch (browser.processes[index].type) { | 187 switch (browser.processes[index].type) { |
290 case ChildProcessInfo::BROWSER_PROCESS: | 188 case ChildProcessInfo::BROWSER_PROCESS: |
291 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample); | 189 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample); |
292 break; | 190 break; |
293 case ChildProcessInfo::RENDER_PROCESS: | 191 case ChildProcessInfo::RENDER_PROCESS: |
294 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample); | 192 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample); |
295 break; | 193 break; |
296 case ChildProcessInfo::PLUGIN_PROCESS: | 194 case ChildProcessInfo::PLUGIN_PROCESS: |
297 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample); | 195 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample); |
298 plugin_count++; | 196 plugin_count++; |
299 break; | 197 break; |
300 case ChildProcessInfo::WORKER_PROCESS: | 198 case ChildProcessInfo::WORKER_PROCESS: |
301 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample); | 199 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample); |
302 worker_count++; | 200 worker_count++; |
303 break; | 201 break; |
| 202 case ChildProcessInfo::ZYGOTE_PROCESS; |
| 203 UMA_HISTOGRAM_MEMORY_KB("Memory.Zygote", sample); |
| 204 break; |
| 205 case ChildProcessInfo::SANDBOX_HELPER_PROCESS; |
| 206 UMA_HISTOGRAM_MEMORY_KB("Memory.SandboxHelper", sample); |
| 207 break; |
| 208 default: |
| 209 NOTREACHED(); |
304 } | 210 } |
305 } | 211 } |
306 UMA_HISTOGRAM_MEMORY_KB("Memory.BackingStore", | 212 UMA_HISTOGRAM_MEMORY_KB("Memory.BackingStore", |
307 BackingStoreManager::MemorySize() / 1024); | 213 BackingStoreManager::MemorySize() / 1024); |
308 | 214 |
309 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount", | 215 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount", |
310 static_cast<int>(browser.processes.size())); | 216 static_cast<int>(browser.processes.size())); |
311 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count); | 217 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count); |
312 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count); | 218 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count); |
313 | 219 |
314 int total_sample = static_cast<int>(aggregate_memory / 1000); | 220 int total_sample = static_cast<int>(aggregate_memory / 1000); |
315 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample); | 221 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample); |
316 } | 222 } |
OLD | NEW |