| 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 "app/l10n_util.h" |
| 8 #include "base/file_version_info.h" | 9 #include "base/file_version_info.h" |
| 9 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 10 #include "chrome/browser/browser_process.h" | 11 #include "chrome/browser/browser_process.h" |
| 11 #include "chrome/browser/chrome_thread.h" | 12 #include "chrome/browser/chrome_thread.h" |
| 12 #include "chrome/browser/renderer_host/render_process_host.h" | 13 #include "chrome/browser/renderer_host/render_process_host.h" |
| 13 #include "chrome/browser/tab_contents/navigation_entry.h" | 14 #include "chrome/browser/tab_contents/navigation_entry.h" |
| 14 #include "chrome/browser/tab_contents/tab_contents.h" | 15 #include "chrome/browser/tab_contents/tab_contents.h" |
| 15 #include "chrome/common/child_process_host.h" | 16 #include "chrome/common/child_process_host.h" |
| 16 #include "chrome/common/url_constants.h" | 17 #include "chrome/common/url_constants.h" |
| 18 #include "grit/chromium_strings.h" |
| 17 | 19 |
| 18 class RenderViewHostDelegate; | 20 // Known browsers which we collect details for. |
| 21 enum { |
| 22 CHROME_BROWSER = 0, |
| 23 IE_BROWSER, |
| 24 FIREFOX_BROWSER, |
| 25 OPERA_BROWSER, |
| 26 SAFARI_BROWSER, |
| 27 IE_64BIT_BROWSER, |
| 28 KONQUEROR_BROWSER, |
| 29 MAX_BROWSERS |
| 30 } BrowserProcess; |
| 19 | 31 |
| 20 // Template of static data we use for finding browser process information. | 32 // Template of static data we use for finding browser process information. |
| 21 // These entries must match the ordering for MemoryDetails::BrowserProcess. | 33 // These entries must match the ordering for MemoryDetails::BrowserProcess. |
| 22 static ProcessData | 34 static ProcessData g_process_template[MAX_BROWSERS]; |
| 23 g_process_template[MemoryDetails::MAX_BROWSERS] = { | 35 |
| 24 { L"Chromium", L"chrome.exe", }, | 36 MemoryDetails::MemoryDetails() |
| 37 : ui_loop_(NULL) { |
| 38 static const std::wstring google_browser_name = |
| 39 l10n_util::GetString(IDS_PRODUCT_NAME); |
| 40 ProcessData g_process_template[MAX_BROWSERS] = { |
| 41 { google_browser_name.c_str(), L"chrome.exe", }, |
| 25 { L"IE", L"iexplore.exe", }, | 42 { L"IE", L"iexplore.exe", }, |
| 26 { L"Firefox", L"firefox.exe", }, | 43 { L"Firefox", L"firefox.exe", }, |
| 27 { L"Opera", L"opera.exe", }, | 44 { L"Opera", L"opera.exe", }, |
| 28 { L"Safari", L"safari.exe", }, | 45 { L"Safari", L"safari.exe", }, |
| 29 { L"IE (64bit)", L"iexplore.exe", }, | 46 { L"IE (64bit)", L"iexplore.exe", }, |
| 30 { L"Konqueror", L"konqueror.exe", }, | 47 { L"Konqueror", L"konqueror.exe", }, |
| 31 }; | 48 }; |
| 32 | 49 |
| 33 | |
| 34 // About threading: | |
| 35 // | |
| 36 // This operation will hit no fewer than 3 threads. | |
| 37 // | |
| 38 // The ChildProcessInfo::Iterator can only be accessed from the IO thread. | |
| 39 // | |
| 40 // The RenderProcessHostIterator can only be accessed from the UI thread. | |
| 41 // | |
| 42 // This operation can take 30-100ms to complete. We never want to have | |
| 43 // one task run for that long on the UI or IO threads. So, we run the | |
| 44 // expensive parts of this operation over on the file thread. | |
| 45 // | |
| 46 | |
| 47 MemoryDetails::MemoryDetails() | |
| 48 : ui_loop_(NULL) { | |
| 49 for (int index = 0; index < arraysize(g_process_template); ++index) { | 50 for (int index = 0; index < arraysize(g_process_template); ++index) { |
| 50 process_data_[index].name = g_process_template[index].name; | 51 ProcessData process; |
| 51 process_data_[index].process_name = g_process_template[index].process_name; | 52 process.name = g_process_template[index].name; |
| 53 process.process_name = g_process_template[index].process_name; |
| 54 process_data_.push_back(process); |
| 52 } | 55 } |
| 53 } | 56 } |
| 54 | 57 |
| 55 void MemoryDetails::StartFetch() { | 58 ProcessData* MemoryDetails::ChromeBrowser() { |
| 56 ui_loop_ = MessageLoop::current(); | 59 return &process_data_[CHROME_BROWSER]; |
| 57 | |
| 58 DCHECK(ui_loop_ != g_browser_process->io_thread()->message_loop()); | |
| 59 DCHECK(ui_loop_ != g_browser_process->file_thread()->message_loop()); | |
| 60 | |
| 61 // In order to process this request, we need to use the plugin information. | |
| 62 // However, plugin process information is only available from the IO thread. | |
| 63 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, | |
| 64 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnIOThread)); | |
| 65 } | |
| 66 | |
| 67 void MemoryDetails::CollectChildInfoOnIOThread() { | |
| 68 DCHECK(MessageLoop::current() == | |
| 69 ChromeThread::GetMessageLoop(ChromeThread::IO)); | |
| 70 | |
| 71 std::vector<ProcessMemoryInformation> child_info; | |
| 72 | |
| 73 // Collect the list of child processes. | |
| 74 for (ChildProcessHost::Iterator iter; !iter.Done(); ++iter) { | |
| 75 ProcessMemoryInformation info; | |
| 76 info.pid = iter->GetProcessId(); | |
| 77 if (!info.pid) | |
| 78 continue; | |
| 79 | |
| 80 info.type = iter->type(); | |
| 81 info.titles.push_back(iter->name()); | |
| 82 child_info.push_back(info); | |
| 83 } | |
| 84 | |
| 85 // Now go do expensive memory lookups from the file thread. | |
| 86 ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE, | |
| 87 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info)); | |
| 88 } | 60 } |
| 89 | 61 |
| 90 void MemoryDetails::CollectProcessData( | 62 void MemoryDetails::CollectProcessData( |
| 91 std::vector<ProcessMemoryInformation> child_info) { | 63 std::vector<ProcessMemoryInformation> child_info) { |
| 92 DCHECK(MessageLoop::current() == | 64 DCHECK(MessageLoop::current() == |
| 93 ChromeThread::GetMessageLoop(ChromeThread::FILE)); | 65 ChromeThread::GetMessageLoop(ChromeThread::FILE)); |
| 94 | 66 |
| 95 // Clear old data. | 67 // Clear old data. |
| 96 for (int index = 0; index < arraysize(g_process_template); index++) | 68 for (int index = 0; index < arraysize(g_process_template); index++) |
| 97 process_data_[index].processes.clear(); | 69 process_data_[index].processes.clear(); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 // Add the process info to our list. | 146 // Add the process info to our list. |
| 175 process_data_[index2].processes.push_back(info); | 147 process_data_[index2].processes.push_back(info); |
| 176 break; | 148 break; |
| 177 } | 149 } |
| 178 } while (::Process32Next(snapshot, &process_entry)); | 150 } while (::Process32Next(snapshot, &process_entry)); |
| 179 | 151 |
| 180 // Finally return to the browser thread. | 152 // Finally return to the browser thread. |
| 181 ui_loop_->PostTask(FROM_HERE, | 153 ui_loop_->PostTask(FROM_HERE, |
| 182 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread)); | 154 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread)); |
| 183 } | 155 } |
| 184 | |
| 185 void MemoryDetails::CollectChildInfoOnUIThread() { | |
| 186 DCHECK(MessageLoop::current() == ui_loop_); | |
| 187 | |
| 188 // Get more information about the process. | |
| 189 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size(); | |
| 190 index++) { | |
| 191 // Check if it's a renderer, if so get the list of page titles in it and | |
| 192 // check if it's a diagnostics-related process. We skip all diagnostics | |
| 193 // pages (e.g. "about:xxx" URLs). Iterate the RenderProcessHosts to find | |
| 194 // the tab contents. | |
| 195 RenderProcessHost::iterator renderer_iter; | |
| 196 for (renderer_iter = RenderProcessHost::begin(); renderer_iter != | |
| 197 RenderProcessHost::end(); ++renderer_iter) { | |
| 198 DCHECK(renderer_iter->second); | |
| 199 ProcessMemoryInformation& process = | |
| 200 process_data_[CHROME_BROWSER].processes[index]; | |
| 201 if (process.pid != renderer_iter->second->process().pid()) | |
| 202 continue; | |
| 203 process.type = ChildProcessInfo::RENDER_PROCESS; | |
| 204 // The RenderProcessHost may host multiple TabContents. Any | |
| 205 // of them which contain diagnostics information make the whole | |
| 206 // process be considered a diagnostics process. | |
| 207 // | |
| 208 // NOTE: This is a bit dangerous. We know that for now, listeners | |
| 209 // are always RenderWidgetHosts. But in theory, they don't | |
| 210 // have to be. | |
| 211 RenderProcessHost::listeners_iterator iter; | |
| 212 for (iter = renderer_iter->second->listeners_begin(); | |
| 213 iter != renderer_iter->second->listeners_end(); ++iter) { | |
| 214 RenderWidgetHost* widget = | |
| 215 static_cast<RenderWidgetHost*>(iter->second); | |
| 216 DCHECK(widget); | |
| 217 if (!widget || !widget->IsRenderView()) | |
| 218 continue; | |
| 219 | |
| 220 RenderViewHost* host = static_cast<RenderViewHost*>(widget); | |
| 221 TabContents* contents = NULL; | |
| 222 if (host->delegate()) | |
| 223 contents = host->delegate()->GetAsTabContents(); | |
| 224 if (!contents) | |
| 225 continue; | |
| 226 std::wstring title = UTF16ToWideHack(contents->GetTitle()); | |
| 227 if (!title.length()) | |
| 228 title = L"Untitled"; | |
| 229 process.titles.push_back(title); | |
| 230 | |
| 231 // We need to check the pending entry as well as the display_url to | |
| 232 // see if it's an about:memory URL (we don't want to count these in the | |
| 233 // total memory usage of the browser). | |
| 234 // | |
| 235 // When we reach here, about:memory will be the pending entry since we | |
| 236 // haven't responded with any data such that it would be committed. If | |
| 237 // you have another about:memory tab open (which would be committed), | |
| 238 // we don't want to count it either, so we also check the last committed | |
| 239 // entry. | |
| 240 // | |
| 241 // Either the pending or last committed entries can be NULL. | |
| 242 const NavigationEntry* pending_entry = | |
| 243 contents->controller().pending_entry(); | |
| 244 const NavigationEntry* last_committed_entry = | |
| 245 contents->controller().GetLastCommittedEntry(); | |
| 246 if ((last_committed_entry && | |
| 247 LowerCaseEqualsASCII(last_committed_entry->display_url().spec(), | |
| 248 chrome::kAboutMemoryURL)) || | |
| 249 (pending_entry && | |
| 250 LowerCaseEqualsASCII(pending_entry->display_url().spec(), | |
| 251 chrome::kAboutMemoryURL))) | |
| 252 process.is_diagnostics = true; | |
| 253 } | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 // Get rid of other Chrome processes that are from a different profile. | |
| 258 for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size(); | |
| 259 index++) { | |
| 260 if (process_data_[CHROME_BROWSER].processes[index].type == | |
| 261 ChildProcessInfo::UNKNOWN_PROCESS) { | |
| 262 process_data_[CHROME_BROWSER].processes.erase( | |
| 263 process_data_[CHROME_BROWSER].processes.begin() + index); | |
| 264 index--; | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 UpdateHistograms(); | |
| 269 | |
| 270 OnDetailsAvailable(); | |
| 271 } | |
| 272 | |
| 273 void MemoryDetails::UpdateHistograms() { | |
| 274 // Reports a set of memory metrics to UMA. | |
| 275 // Memory is measured in units of 10KB. | |
| 276 | |
| 277 ProcessData browser = process_data_[CHROME_BROWSER]; | |
| 278 size_t aggregate_memory = 0; | |
| 279 int plugin_count = 0; | |
| 280 int worker_count = 0; | |
| 281 for (size_t index = 0; index < browser.processes.size(); index++) { | |
| 282 int sample = static_cast<int>(browser.processes[index].working_set.priv); | |
| 283 aggregate_memory += sample; | |
| 284 switch (browser.processes[index].type) { | |
| 285 case ChildProcessInfo::BROWSER_PROCESS: | |
| 286 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample); | |
| 287 break; | |
| 288 case ChildProcessInfo::RENDER_PROCESS: | |
| 289 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample); | |
| 290 break; | |
| 291 case ChildProcessInfo::PLUGIN_PROCESS: | |
| 292 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample); | |
| 293 plugin_count++; | |
| 294 break; | |
| 295 case ChildProcessInfo::WORKER_PROCESS: | |
| 296 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample); | |
| 297 worker_count++; | |
| 298 break; | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount", | |
| 303 static_cast<int>(browser.processes.size())); | |
| 304 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count); | |
| 305 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count); | |
| 306 | |
| 307 int total_sample = static_cast<int>(aggregate_memory / 1000); | |
| 308 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample); | |
| 309 } | |
| OLD | NEW |