| 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> | 6 #include <psapi.h> |
| 7 | 7 |
| 8 #include "base/file_version_info.h" | 8 #include "base/file_version_info.h" |
| 9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 10 #include "chrome/browser/browser_process.h" | 10 #include "chrome/browser/browser_process.h" |
| 11 #include "chrome/browser/chrome_thread.h" | 11 #include "chrome/browser/chrome_thread.h" |
| 12 #include "chrome/browser/renderer_host/render_process_host.h" | 12 #include "chrome/browser/renderer_host/render_process_host.h" |
| 13 #include "chrome/browser/tab_contents/navigation_entry.h" | 13 #include "chrome/browser/tab_contents/navigation_entry.h" |
| 14 #include "chrome/browser/tab_contents/web_contents.h" | 14 #include "chrome/browser/tab_contents/web_contents.h" |
| 15 #include "chrome/common/child_process_host.h" | 15 #include "chrome/common/child_process_host.h" |
| 16 #include "chrome/common/url_constants.h" | 16 #include "chrome/common/url_constants.h" |
| 17 | 17 |
| 18 class RenderViewHostDelegate; | 18 class RenderViewHostDelegate; |
| 19 | 19 |
| 20 // Template of static data we use for finding browser process information. | 20 // Template of static data we use for finding browser process information. |
| 21 // These entries must match the ordering for MemoryDetails::BrowserProcess. | 21 // These entries must match the ordering for MemoryDetails::BrowserProcess. |
| 22 static ProcessData g_process_template[] = { | 22 static ProcessData |
| 23 g_process_template[MemoryDetails::MAX_BROWSERS] = { |
| 23 { L"Chromium", L"chrome.exe", }, | 24 { L"Chromium", L"chrome.exe", }, |
| 24 { L"IE", L"iexplore.exe", }, | 25 { L"IE", L"iexplore.exe", }, |
| 25 { L"Firefox", L"firefox.exe", }, | 26 { L"Firefox", L"firefox.exe", }, |
| 26 { L"Opera", L"opera.exe", }, | 27 { L"Opera", L"opera.exe", }, |
| 27 { L"Safari", L"safari.exe", }, | 28 { L"Safari", L"safari.exe", }, |
| 29 { L"IE (64bit)", L"iexplore.exe", }, |
| 28 }; | 30 }; |
| 29 | 31 |
| 30 | 32 |
| 31 // About threading: | 33 // About threading: |
| 32 // | 34 // |
| 33 // This operation will hit no fewer than 3 threads. | 35 // This operation will hit no fewer than 3 threads. |
| 34 // | 36 // |
| 35 // The ChildProcessInfo::Iterator can only be accessed from the IO thread. | 37 // The ChildProcessInfo::Iterator can only be accessed from the IO thread. |
| 36 // | 38 // |
| 37 // The RenderProcessHostIterator can only be accessed from the UI thread. | 39 // The RenderProcessHostIterator can only be accessed from the UI thread. |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 82 // Now go do expensive memory lookups from the file thread. | 84 // Now go do expensive memory lookups from the file thread. |
| 83 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE, | 85 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE, |
| 84 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info)); | 86 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info)); |
| 85 } | 87 } |
| 86 | 88 |
| 87 void MemoryDetails::CollectProcessData( | 89 void MemoryDetails::CollectProcessData( |
| 88 std::vector<ProcessMemoryInformation> child_info) { | 90 std::vector<ProcessMemoryInformation> child_info) { |
| 89 DCHECK(MessageLoop::current() == | 91 DCHECK(MessageLoop::current() == |
| 90 ChromeThread::GetMessageLoop(ChromeThread::FILE)); | 92 ChromeThread::GetMessageLoop(ChromeThread::FILE)); |
| 91 | 93 |
| 92 int array_size = 32; | |
| 93 scoped_ptr_malloc<DWORD> process_list; | |
| 94 DWORD bytes_used = 0; | |
| 95 do { | |
| 96 array_size *= 2; | |
| 97 process_list.reset(static_cast<DWORD*>( | |
| 98 realloc(process_list.release(), sizeof(DWORD) * array_size))); | |
| 99 // EnumProcesses doesn't return an error if the array is too small. | |
| 100 // We have to check if the return buffer is full, and if so, call it | |
| 101 // again. See msdn docs for more info. | |
| 102 if (!EnumProcesses(process_list.get(), array_size * sizeof(DWORD), | |
| 103 &bytes_used)) { | |
| 104 LOG(ERROR) << "EnumProcesses failed: " << GetLastError(); | |
| 105 return; | |
| 106 } | |
| 107 } while (bytes_used == (array_size * sizeof(DWORD))); | |
| 108 | |
| 109 int num_processes = bytes_used / sizeof(DWORD); | |
| 110 | |
| 111 // Clear old data. | 94 // Clear old data. |
| 112 for (int index = 0; index < arraysize(g_process_template); index++) | 95 for (int index = 0; index < arraysize(g_process_template); index++) |
| 113 process_data_[index].processes.clear(); | 96 process_data_[index].processes.clear(); |
| 114 | 97 |
| 115 for (int index = 0; index < num_processes; index++) { | 98 SYSTEM_INFO system_info; |
| 116 int pid = process_list.get()[index]; | 99 GetNativeSystemInfo(&system_info); |
| 117 ScopedHandle handle(OpenProcess( | 100 bool is_64bit_os = |
| 101 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; |
| 102 |
| 103 ScopedHandle snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); |
| 104 PROCESSENTRY32 process_entry = {sizeof(PROCESSENTRY32)}; |
| 105 if (!snapshot.Get()) { |
| 106 LOG(ERROR) << "CreateToolhelp32Snaphot failed: " << GetLastError(); |
| 107 return; |
| 108 } |
| 109 if (!::Process32First(snapshot, &process_entry)) { |
| 110 LOG(ERROR) << "Process32First failed: " << GetLastError(); |
| 111 return; |
| 112 } |
| 113 do { |
| 114 int pid = process_entry.th32ProcessID; |
| 115 ScopedHandle handle(::OpenProcess( |
| 118 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); | 116 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); |
| 119 if (!handle.Get()) | 117 if (!handle.Get()) |
| 120 continue; | 118 continue; |
| 121 TCHAR name[MAX_PATH]; | 119 bool is_64bit_process = false; |
| 122 if (!GetModuleBaseName(handle, NULL, name, MAX_PATH - 1)) | 120 // IsWow64Process() returns FALSE for a 32bit process on a 32bit OS. |
| 123 continue; | 121 // We need to check if the real OS is 64bit. |
| 122 if (is_64bit_os) { |
| 123 BOOL is_wow64 = FALSE; |
| 124 // IsWow64Process() is supported by Windows XP SP2 or later. |
| 125 IsWow64Process(handle, &is_wow64); |
| 126 is_64bit_process = !is_wow64; |
| 127 } |
| 124 for (int index2 = 0; index2 < arraysize(g_process_template); index2++) { | 128 for (int index2 = 0; index2 < arraysize(g_process_template); index2++) { |
| 125 if (_wcsicmp(process_data_[index2].process_name, name) != 0) | 129 if (_wcsicmp(process_data_[index2].process_name, |
| 130 process_entry.szExeFile) != 0) |
| 126 continue; | 131 continue; |
| 132 if (index2 == IE_BROWSER && is_64bit_process) |
| 133 continue; // Should use IE_64BIT_BROWSER |
| 127 // Get Memory Information. | 134 // Get Memory Information. |
| 128 ProcessMemoryInformation info; | 135 ProcessMemoryInformation info; |
| 129 info.pid = pid; | 136 info.pid = pid; |
| 130 if (info.pid == GetCurrentProcessId()) | 137 if (info.pid == GetCurrentProcessId()) |
| 131 info.type = ChildProcessInfo::BROWSER_PROCESS; | 138 info.type = ChildProcessInfo::BROWSER_PROCESS; |
| 132 else | 139 else |
| 133 info.type = ChildProcessInfo::UNKNOWN_PROCESS; | 140 info.type = ChildProcessInfo::UNKNOWN_PROCESS; |
| 134 | 141 |
| 135 scoped_ptr<base::ProcessMetrics> metrics; | 142 scoped_ptr<base::ProcessMetrics> metrics; |
| 136 metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle)); | 143 metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle)); |
| 137 metrics->GetCommittedKBytes(&info.committed); | 144 metrics->GetCommittedKBytes(&info.committed); |
| 138 metrics->GetWorkingSetKBytes(&info.working_set); | 145 metrics->GetWorkingSetKBytes(&info.working_set); |
| 139 | 146 |
| 140 // Get Version Information. | 147 // Get Version Information. |
| 141 if (index2 == 0) { // Chrome | 148 TCHAR name[MAX_PATH]; |
| 149 if (index2 == CHROME_BROWSER) { |
| 142 scoped_ptr<FileVersionInfo> version_info( | 150 scoped_ptr<FileVersionInfo> version_info( |
| 143 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); | 151 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); |
| 144 if (version_info != NULL) | 152 if (version_info != NULL) |
| 145 info.version = version_info->file_version(); | 153 info.version = version_info->file_version(); |
| 146 // Check if this is one of the child processes whose data we collected | 154 // Check if this is one of the child processes whose data we collected |
| 147 // on the IO thread, and if so copy over that data. | 155 // on the IO thread, and if so copy over that data. |
| 148 for (size_t child = 0; child < child_info.size(); child++) { | 156 for (size_t child = 0; child < child_info.size(); child++) { |
| 149 if (child_info[child].pid != info.pid) | 157 if (child_info[child].pid != info.pid) |
| 150 continue; | 158 continue; |
| 151 info.titles = child_info[child].titles; | 159 info.titles = child_info[child].titles; |
| 152 info.type = child_info[child].type; | 160 info.type = child_info[child].type; |
| 153 break; | 161 break; |
| 154 } | 162 } |
| 155 } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) { | 163 } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) { |
| 156 std::wstring str_name(name); | 164 std::wstring str_name(name); |
| 157 scoped_ptr<FileVersionInfo> version_info( | 165 scoped_ptr<FileVersionInfo> version_info( |
| 158 FileVersionInfo::CreateFileVersionInfo(str_name)); | 166 FileVersionInfo::CreateFileVersionInfo(str_name)); |
| 159 if (version_info != NULL) { | 167 if (version_info != NULL) { |
| 160 info.version = version_info->product_version(); | 168 info.version = version_info->product_version(); |
| 161 info.product_name = version_info->product_name(); | 169 info.product_name = version_info->product_name(); |
| 162 } | 170 } |
| 163 } | 171 } |
| 164 | 172 |
| 165 // Add the process info to our list. | 173 // Add the process info to our list. |
| 166 process_data_[index2].processes.push_back(info); | 174 process_data_[index2].processes.push_back(info); |
| 167 break; | 175 break; |
| 168 } | 176 } |
| 169 } | 177 } while (::Process32Next(snapshot, &process_entry)); |
| 170 | 178 |
| 171 // Finally return to the browser thread. | 179 // Finally return to the browser thread. |
| 172 ui_loop_->PostTask(FROM_HERE, | 180 ui_loop_->PostTask(FROM_HERE, |
| 173 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread)); | 181 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread)); |
| 174 } | 182 } |
| 175 | 183 |
| 176 void MemoryDetails::CollectChildInfoOnUIThread() { | 184 void MemoryDetails::CollectChildInfoOnUIThread() { |
| 177 DCHECK(MessageLoop::current() == ui_loop_); | 185 DCHECK(MessageLoop::current() == ui_loop_); |
| 178 | 186 |
| 179 // Get more information about the process. | 187 // Get more information about the process. |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 } | 299 } |
| 292 | 300 |
| 293 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount", | 301 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount", |
| 294 static_cast<int>(browser.processes.size())); | 302 static_cast<int>(browser.processes.size())); |
| 295 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count); | 303 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count); |
| 296 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count); | 304 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count); |
| 297 | 305 |
| 298 int total_sample = static_cast<int>(aggregate_memory / 1000); | 306 int total_sample = static_cast<int>(aggregate_memory / 1000); |
| 299 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample); | 307 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample); |
| 300 } | 308 } |
| OLD | NEW |