1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/memory_details.h" 6 7#include <set> 8#include <string> 9 10#include "base/basictypes.h" 11#include "base/file_path.h" 12#include "base/file_version_info.h" 13#include "base/mac/mac_util.h" 14#include "base/string_util.h" 15#include "base/process_util.h" 16#include "base/threading/thread.h" 17#include "base/utf_string_conversions.h" 18#include "chrome/browser/process_info_snapshot.h" 19#include "chrome/common/chrome_constants.h" 20#include "chrome/common/chrome_version_info.h" 21#include "chrome/common/url_constants.h" 22#include "content/browser/browser_child_process_host.h" 23#include "content/browser/browser_thread.h" 24#include "content/browser/renderer_host/backing_store_manager.h" 25#include "content/browser/renderer_host/render_process_host.h" 26#include "content/browser/tab_contents/navigation_entry.h" 27#include "grit/chromium_strings.h" 28#include "ui/base/l10n/l10n_util.h" 29 30// TODO(viettrungluu): Many of the TODOs below are subsumed by a general need to 31// refactor the about:memory code (not just on Mac, but probably on other 32// platforms as well). I've filed crbug.com/25456. 33 34class RenderViewHostDelegate; 35 36// Known browsers which we collect details for. |CHROME_BROWSER| *must* be the 37// first browser listed. The order here must match those in |process_template| 38// (in |MemoryDetails::MemoryDetails()| below). 39// TODO(viettrungluu): In the big refactoring (see above), get rid of this order 40// dependence. 41enum BrowserType { 42 // TODO(viettrungluu): possibly add more? 43 CHROME_BROWSER = 0, 44 SAFARI_BROWSER, 45 FIREFOX_BROWSER, 46 CAMINO_BROWSER, 47 OPERA_BROWSER, 48 OMNIWEB_BROWSER, 49 MAX_BROWSERS 50} BrowserProcess; 51 52 53MemoryDetails::MemoryDetails() { 54 static const std::string google_browser_name = 55 l10n_util::GetStringUTF8(IDS_PRODUCT_NAME); 56 // (Human and process) names of browsers; should match the ordering for 57 // |BrowserProcess| (i.e., |BrowserType|). 58 // TODO(viettrungluu): The current setup means that we can't detect both 59 // Chrome and Chromium at the same time! 60 // TODO(viettrungluu): Get localized browser names for other browsers 61 // (crbug.com/25779). 62 struct { 63 const char* name; 64 const char* process_name; 65 } process_template[MAX_BROWSERS] = { 66 { google_browser_name.c_str(), chrome::kBrowserProcessExecutableName, }, 67 { "Safari", "Safari", }, 68 { "Firefox", "firefox-bin", }, 69 { "Camino", "Camino", }, 70 { "Opera", "Opera", }, 71 { "OmniWeb", "OmniWeb", }, 72 }; 73 74 for (size_t index = 0; index < MAX_BROWSERS; ++index) { 75 ProcessData process; 76 process.name = UTF8ToUTF16(process_template[index].name); 77 process.process_name = UTF8ToUTF16(process_template[index].process_name); 78 process_data_.push_back(process); 79 } 80} 81 82ProcessData* MemoryDetails::ChromeBrowser() { 83 return &process_data_[CHROME_BROWSER]; 84} 85 86void MemoryDetails::CollectProcessData( 87 const std::vector<ProcessMemoryInformation>& child_info) { 88 // This must be run on the file thread to avoid jank (|ProcessInfoSnapshot| 89 // runs /bin/ps, which isn't instantaneous). 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 91 92 // Clear old data. 93 for (size_t index = 0; index < MAX_BROWSERS; index++) 94 process_data_[index].processes.clear(); 95 96 // First, we use |NamedProcessIterator| to get the PIDs of the processes we're 97 // interested in; we save our results to avoid extra calls to 98 // |NamedProcessIterator| (for performance reasons) and to avoid additional 99 // inconsistencies caused by racing. Then we run |/bin/ps| *once* to get 100 // information on those PIDs. Then we used our saved information to iterate 101 // over browsers, then over PIDs. 102 103 // Get PIDs of main browser processes. 104 std::vector<base::ProcessId> pids_by_browser[MAX_BROWSERS]; 105 std::vector<base::ProcessId> all_pids; 106 for (size_t index = CHROME_BROWSER; index < MAX_BROWSERS; index++) { 107 base::NamedProcessIterator process_it( 108 UTF16ToUTF8(process_data_[index].process_name), NULL); 109 110 while (const base::ProcessEntry* entry = process_it.NextProcessEntry()) { 111 pids_by_browser[index].push_back(entry->pid()); 112 all_pids.push_back(entry->pid()); 113 } 114 } 115 116 // Get PIDs of helpers. 117 std::vector<base::ProcessId> helper_pids; 118 { 119 base::NamedProcessIterator helper_it(chrome::kHelperProcessExecutableName, 120 NULL); 121 while (const base::ProcessEntry* entry = helper_it.NextProcessEntry()) { 122 helper_pids.push_back(entry->pid()); 123 all_pids.push_back(entry->pid()); 124 } 125 } 126 127 // Capture information about the processes we're interested in. 128 ProcessInfoSnapshot process_info; 129 process_info.Sample(all_pids); 130 131 // Handle the other processes first. 132 for (size_t index = CHROME_BROWSER + 1; index < MAX_BROWSERS; index++) { 133 for (std::vector<base::ProcessId>::const_iterator it = 134 pids_by_browser[index].begin(); 135 it != pids_by_browser[index].end(); ++it) { 136 ProcessMemoryInformation info; 137 info.pid = *it; 138 info.type = ChildProcessInfo::UNKNOWN_PROCESS; 139 140 // Try to get version information. To do this, we need first to get the 141 // executable's name (we can only believe |proc_info.command| if it looks 142 // like an absolute path). Then we need strip the executable's name back 143 // to the bundle's name. And only then can we try to get the version. 144 scoped_ptr<FileVersionInfo> version_info; 145 ProcessInfoSnapshot::ProcInfoEntry proc_info; 146 if (process_info.GetProcInfo(info.pid, &proc_info)) { 147 if (proc_info.command.length() > 1 && proc_info.command[0] == '/') { 148 FilePath bundle_name = 149 base::mac::GetAppBundlePath(FilePath(proc_info.command)); 150 if (!bundle_name.empty()) { 151 version_info.reset(FileVersionInfo::CreateFileVersionInfo( 152 bundle_name)); 153 } 154 } 155 } 156 if (version_info.get()) { 157 info.product_name = version_info->product_name(); 158 info.version = version_info->product_version(); 159 } else { 160 info.product_name = process_data_[index].name; 161 info.version = string16(); 162 } 163 164 // Memory info. 165 process_info.GetCommittedKBytesOfPID(info.pid, &info.committed); 166 process_info.GetWorkingSetKBytesOfPID(info.pid, &info.working_set); 167 168 // Add the process info to our list. 169 process_data_[index].processes.push_back(info); 170 } 171 } 172 173 // Collect data about Chrome/Chromium. 174 for (std::vector<base::ProcessId>::const_iterator it = 175 pids_by_browser[CHROME_BROWSER].begin(); 176 it != pids_by_browser[CHROME_BROWSER].end(); ++it) { 177 CollectProcessDataChrome(child_info, *it, process_info); 178 } 179 180 // And collect data about the helpers. 181 for (std::vector<base::ProcessId>::const_iterator it = helper_pids.begin(); 182 it != helper_pids.end(); ++it) { 183 CollectProcessDataChrome(child_info, *it, process_info); 184 } 185 186 // Finally return to the browser thread. 187 BrowserThread::PostTask( 188 BrowserThread::UI, FROM_HERE, 189 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread)); 190} 191 192void MemoryDetails::CollectProcessDataChrome( 193 const std::vector<ProcessMemoryInformation>& child_info, 194 base::ProcessId pid, 195 const ProcessInfoSnapshot& process_info) { 196 ProcessMemoryInformation info; 197 info.pid = pid; 198 if (info.pid == base::GetCurrentProcId()) 199 info.type = ChildProcessInfo::BROWSER_PROCESS; 200 else 201 info.type = ChildProcessInfo::UNKNOWN_PROCESS; 202 203 chrome::VersionInfo version_info; 204 if (version_info.is_valid()) { 205 info.product_name = ASCIIToUTF16(version_info.Name()); 206 info.version = ASCIIToUTF16(version_info.Version()); 207 } else { 208 info.product_name = process_data_[CHROME_BROWSER].name; 209 info.version = string16(); 210 } 211 212 // Check if this is one of the child processes whose data we collected 213 // on the IO thread, and if so copy over that data. 214 for (size_t child = 0; child < child_info.size(); child++) { 215 if (child_info[child].pid == info.pid) { 216 info.titles = child_info[child].titles; 217 info.type = child_info[child].type; 218 break; 219 } 220 } 221 222 // Memory info. 223 process_info.GetCommittedKBytesOfPID(info.pid, &info.committed); 224 process_info.GetWorkingSetKBytesOfPID(info.pid, &info.working_set); 225 226 // Add the process info to our list. 227 process_data_[CHROME_BROWSER].processes.push_back(info); 228} 229