diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/memory_details.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/memory_details.cc')
-rw-r--r-- | chrome/browser/memory_details.cc | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc new file mode 100644 index 0000000..144744b --- /dev/null +++ b/chrome/browser/memory_details.cc @@ -0,0 +1,278 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/memory_details.h" +#include <psapi.h> + +#include "base/file_version_info.h" +#include "base/histogram.h" +#include "base/image_util.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/plugin_process_host.h" +#include "chrome/browser/plugin_service.h" +#include "chrome/browser/render_process_host.h" +#include "chrome/browser/render_view_host.h" +#include "chrome/browser/tab_contents.h" +#include "chrome/browser/web_contents.h" + +class RenderViewHostDelegate; + +// Template of static data we use for finding browser process information. +// These entries must match the ordering for MemoryDetails::BrowserProcess. +static ProcessData g_process_template[] = { + { L"Chrome", L"chrome.exe", }, + { L"IE", L"iexplore.exe", }, + { L"Firefox", L"firefox.exe", }, + { L"Opera", L"opera.exe", }, + { L"Safari", L"safari.exe", }, + }; + + +// About threading: +// +// This operation will hit no fewer than 3 threads. +// +// The PluginHostIterator can only be accessed from the IO thread. +// +// The RenderProcessHostIterator can only be accessed from the UI thread. +// +// This operation can take 30-100ms to complete. We never want to have +// one task run for that long on the UI or IO threads. So, we run the +// expensive parts of this operation over on the file thread. +// + +MemoryDetails::MemoryDetails() + : ui_loop_(NULL) { + for (int index = 0; index < arraysize(g_process_template); ++index) { + process_data_[index].name = g_process_template[index].name; + process_data_[index].process_name = g_process_template[index].process_name; + } +} + +void MemoryDetails::StartFetch() { + ui_loop_ = MessageLoop::current(); + + DCHECK(ui_loop_ != g_browser_process->io_thread()->message_loop()); + DCHECK(ui_loop_ != g_browser_process->file_thread()->message_loop()); + + // In order to process this request, we need to use the plugin information. + // However, plugin process information is only available from the IO thread. + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &MemoryDetails::CollectPluginInformation)); +} + +void MemoryDetails::CollectPluginInformation() { + DCHECK(MessageLoop::current() == + ChromeThread::GetMessageLoop(ChromeThread::IO)); + + // Collect the list of plugins. + for (PluginProcessHostIterator plugin_iter; + !plugin_iter.Done(); ++plugin_iter) { + PluginProcessHost* plugin = const_cast<PluginProcessHost*>(*plugin_iter); + DCHECK(plugin); + if (!plugin || !plugin->process()) + continue; + + PluginProcessInformation info; + info.pid = process_util::GetProcId(plugin->process()); + if (info.pid != 0) { + info.dll_path = plugin->dll_path(); + plugins_.push_back(info); + } + } + + // Now go do expensive memory lookups from the file thread. + ChromeThread::GetMessageLoop(ChromeThread::FILE)->PostTask(FROM_HERE, + NewRunnableMethod(this, &MemoryDetails::CollectProcessData)); +} + +void MemoryDetails::CollectProcessData() { + DCHECK(MessageLoop::current() == + ChromeThread::GetMessageLoop(ChromeThread::FILE)); + + int array_size = 32; + DWORD* process_list = NULL; + DWORD bytes_used = 0; + do { + array_size *= 2; + process_list = static_cast<DWORD*>( + realloc(process_list, sizeof(*process_list) * array_size)); + // EnumProcesses doesn't return an error if the array is too small. + // We have to check if the return buffer is full, and if so, call it + // again. See msdn docs for more info. + if (!EnumProcesses(process_list, array_size * sizeof(*process_list), + &bytes_used)) { + LOG(ERROR) << "EnumProcesses failed: " << GetLastError(); + return; + } + } while (bytes_used == (array_size * sizeof(*process_list))); + + int num_processes = bytes_used / sizeof(*process_list); + + // Clear old data. + for (int index = 0; index < arraysize(g_process_template); index++) + process_data_[index].processes.clear(); + + for (int index = 0; index < num_processes; index++) { + HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, process_list[index]); + if (handle) { + TCHAR name[MAX_PATH]; + if (GetModuleBaseName(handle, NULL, name, MAX_PATH-1)) { + for (int index2 = 0; index2 < arraysize(g_process_template); index2++) { + if (_wcsicmp(process_data_[index2].process_name, name) == 0) { + // Get Memory Information. + ProcessMemoryInformation info; + info.pid = process_list[index]; + scoped_ptr<process_util::ProcessMetrics> metrics; + metrics.reset( + process_util::ProcessMetrics::CreateProcessMetrics(handle)); + metrics->GetCommittedKBytes(&info.committed); + metrics->GetWorkingSetKBytes(&info.working_set); + + // Get Version Information. + if (index2 == 0) { // Chrome + scoped_ptr<FileVersionInfo> version_info( + FileVersionInfo::CreateFileVersionInfoForCurrentModule()); + if (version_info != NULL) + info.version = version_info->file_version(); + } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH-1)) { + std::wstring str_name(name); + scoped_ptr<FileVersionInfo> version_info( + FileVersionInfo::CreateFileVersionInfo(str_name)); + if (version_info != NULL) { + info.version = version_info->product_version(); + info.product_name = version_info->product_name(); + } + } + + // Add the process info to our list. + process_data_[index2].processes.push_back(info); + break; + } + } + } + CloseHandle(handle); + } + } + free(process_list); + + // Finally return to the browser thread. + ui_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &MemoryDetails::CollectRenderHostInformation)); +} + +void MemoryDetails::CollectRenderHostInformation() { + DCHECK(MessageLoop::current() == ui_loop_); + + // Determine if this is a diagnostics-related process. We skip all + // diagnostics pages (e.g. "about:xxx" URLs). Iterate the RenderProcessHosts + // to find the tab contents. If it is of type TAB_CONTENTS_ABOUT_UI, mark + // the process as diagnostics related. + for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size(); + index++) { + RenderProcessHost::iterator renderer_iter; + for (renderer_iter = RenderProcessHost::begin(); renderer_iter != + RenderProcessHost::end(); ++renderer_iter) { + DCHECK(renderer_iter->second); + if (process_data_[CHROME_BROWSER].processes[index].pid == + renderer_iter->second->pid()) { + // The RenderProcessHost may host multiple TabContents. Any + // of them which contain diagnostics information make the whole + // process be considered a diagnostics process. + // + // NOTE: This is a bit dangerous. We know that for now, listeners + // are always RenderWidgetHosts. But in theory, they don't + // have to be. + RenderProcessHost::listeners_iterator iter; + for (iter = renderer_iter->second->listeners_begin(); + iter != renderer_iter->second->listeners_end(); ++iter) { + RenderWidgetHost* widget = + static_cast<RenderWidgetHost*>(iter->second); + DCHECK(widget); + if (!widget || !widget->IsRenderView()) + continue; + + RenderViewHost* host = static_cast<RenderViewHost*>(widget); + TabContents* contents = + static_cast<WebContents*>(host->delegate()); + DCHECK(contents); + if (!contents) + continue; + + if (contents->type() == TAB_CONTENTS_ABOUT_UI) + process_data_[CHROME_BROWSER].processes[index].is_diagnostics = + true; + } + } + } + } + + UpdateHistograms(); + + OnDetailsAvailable(); +} + +void MemoryDetails::UpdateHistograms() { + // Reports a set of memory metrics to UMA. + // Memory is measured in units of 10KB. + + DWORD browser_pid = GetCurrentProcessId(); + ProcessData browser = process_data_[CHROME_BROWSER]; + size_t aggregate_memory = 0; + for (size_t index = 0; index < browser.processes.size(); index++) { + aggregate_memory += browser.processes[index].working_set.priv; + if (browser.processes[index].pid == browser_pid) { + UMA_HISTOGRAM_MEMORY_KB(L"Memory.Browser", + static_cast<int>(browser.processes[index].working_set.priv)); + } else { + bool is_plugin_process = false; + for (size_t index2 = 0; index2 < plugins_.size(); index2++) { + if (browser.processes[index].pid == plugins_[index2].pid) { + UMA_HISTOGRAM_MEMORY_KB(L"Memory.Plugin", + static_cast<int>(browser.processes[index].working_set.priv)); + is_plugin_process = true; + break; + } + } + if (!is_plugin_process) + UMA_HISTOGRAM_MEMORY_KB(L"Memory.Renderer", + static_cast<int>(browser.processes[index].working_set.priv)); + } + } + UMA_HISTOGRAM_COUNTS_100(L"Memory.ProcessCount", + static_cast<int>(browser.processes.size())); + UMA_HISTOGRAM_COUNTS_100(L"Memory.PluginProcessCount", + static_cast<int>(plugins_.size())); + UMA_HISTOGRAM_MEMORY_MB(L"Memory.Total", + static_cast<int>(aggregate_memory / 1000)); +} |