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