Chromium Code Reviews| 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 |