| 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 |