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