1 // Copyright (c) 2012 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.
5 #include "chrome/browser/memory_details.h"
13 #include "base/bind.h"
14 #include "base/file_util.h"
15 #include "base/process/process_iterator.h"
16 #include "base/process/process_metrics.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "chrome/common/chrome_constants.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/common/process_type.h"
23 #include "grit/chromium_strings.h"
24 #include "ui/base/l10n/l10n_util.h"
26 using base::ProcessEntry
;
27 using content::BrowserThread
;
29 // Known browsers which we collect details for.
41 // The pretty printed names of those browsers. Matches up with enum
43 static const char kBrowserPrettyNames
[][10] = {
53 // A mapping from process name to the type of browser.
55 const char process_name
[16];
57 } kBrowserBinaryNames
[] = {
58 { "firefox", FIREFOX
},
59 { "firefox-3.5", FIREFOX
},
60 { "firefox-3.0", FIREFOX
},
61 { "firefox-bin", FIREFOX
},
62 { "iceweasel", ICEWEASEL
},
64 { "konqueror", KONQUEROR
},
65 { "epiphany-browse", EPIPHANY
},
66 { "epiphany", EPIPHANY
},
71 MemoryDetails::MemoryDetails()
72 : user_metrics_mode_(UPDATE_USER_METRICS
),
73 memory_growth_tracker_(NULL
) {
76 ProcessData
* MemoryDetails::ChromeBrowser() {
77 return &process_data_
[0];
86 typedef std::map
<pid_t
, Process
> ProcessMap
;
88 // Get information on all the processes running on the system.
89 static ProcessMap
GetProcesses() {
92 base::ProcessIterator
process_iter(NULL
);
93 while (const ProcessEntry
* process_entry
= process_iter
.NextProcessEntry()) {
95 process
.pid
= process_entry
->pid();
96 process
.parent
= process_entry
->parent_pid();
97 process
.name
= process_entry
->exe_file();
98 map
.insert(std::make_pair(process
.pid
, process
));
103 // Given a process name, return the type of the browser which created that
104 // process, or |MAX_BROWSERS| if we don't know about it.
105 static BrowserType
GetBrowserType(const std::string
& process_name
) {
106 for (unsigned i
= 0; kBrowserBinaryNames
[i
].process_name
[0]; ++i
) {
107 if (strcmp(process_name
.c_str(), kBrowserBinaryNames
[i
].process_name
) == 0)
108 return kBrowserBinaryNames
[i
].browser
;
114 // For each of a list of pids, collect memory information about that process.
115 static ProcessData
GetProcessDataMemoryInformation(
116 const std::vector
<pid_t
>& pids
) {
117 ProcessData process_data
;
118 for (std::vector
<pid_t
>::const_iterator iter
= pids
.begin();
121 ProcessMemoryInformation pmi
;
124 pmi
.num_processes
= 1;
126 if (pmi
.pid
== base::GetCurrentProcId())
127 pmi
.process_type
= content::PROCESS_TYPE_BROWSER
;
129 pmi
.process_type
= content::PROCESS_TYPE_UNKNOWN
;
131 base::ProcessMetrics
* metrics
=
132 base::ProcessMetrics::CreateProcessMetrics(*iter
);
133 metrics
->GetWorkingSetKBytes(&pmi
.working_set
);
136 process_data
.processes
.push_back(pmi
);
141 // Find all children of the given process with pid |root|.
142 static std::vector
<pid_t
> GetAllChildren(const ProcessMap
& processes
,
144 std::vector
<pid_t
> children
;
145 children
.push_back(root
);
147 std::set
<pid_t
> wavefront
, next_wavefront
;
148 wavefront
.insert(root
);
150 while (wavefront
.size()) {
151 for (ProcessMap::const_iterator iter
= processes
.begin();
152 iter
!= processes
.end();
154 const Process
& process
= iter
->second
;
155 if (wavefront
.count(process
.parent
)) {
156 children
.push_back(process
.pid
);
157 next_wavefront
.insert(process
.pid
);
162 wavefront
.swap(next_wavefront
);
167 void MemoryDetails::CollectProcessData(
168 const std::vector
<ProcessMemoryInformation
>& child_info
) {
169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
171 ProcessMap process_map
= GetProcesses();
172 std::set
<pid_t
> browsers_found
;
174 // For each process on the system, if it appears to be a browser process and
175 // it's parent isn't a browser process, then record it in |browsers_found|.
176 for (ProcessMap::const_iterator iter
= process_map
.begin();
177 iter
!= process_map
.end();
179 const Process
& current_process
= iter
->second
;
180 const BrowserType type
= GetBrowserType(current_process
.name
);
181 if (type
== MAX_BROWSERS
)
184 ProcessMap::const_iterator parent_iter
=
185 process_map
.find(current_process
.parent
);
186 if (parent_iter
== process_map
.end()) {
187 browsers_found
.insert(current_process
.pid
);
191 if (GetBrowserType(parent_iter
->second
.name
) != type
) {
192 // We found a process whose type is diffent from its parent's type.
193 // That means it is the root process of the browser.
194 browsers_found
.insert(current_process
.pid
);
199 ProcessData current_browser
=
200 GetProcessDataMemoryInformation(GetAllChildren(process_map
, getpid()));
201 current_browser
.name
= l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME
);
202 current_browser
.process_name
= base::ASCIIToUTF16("chrome");
204 for (std::vector
<ProcessMemoryInformation
>::iterator
205 i
= current_browser
.processes
.begin();
206 i
!= current_browser
.processes
.end(); ++i
) {
207 // Check if this is one of the child processes whose data we collected
208 // on the IO thread, and if so copy over that data.
209 for (size_t child
= 0; child
< child_info
.size(); child
++) {
210 if (child_info
[child
].pid
!= i
->pid
)
212 i
->titles
= child_info
[child
].titles
;
213 i
->process_type
= child_info
[child
].process_type
;
218 process_data_
.push_back(current_browser
);
220 // For each browser process, collect a list of its children and get the
221 // memory usage of each.
222 for (std::set
<pid_t
>::const_iterator iter
= browsers_found
.begin();
223 iter
!= browsers_found
.end();
225 std::vector
<pid_t
> browser_processes
= GetAllChildren(process_map
, *iter
);
226 ProcessData browser
= GetProcessDataMemoryInformation(browser_processes
);
228 ProcessMap::const_iterator process_iter
= process_map
.find(*iter
);
229 if (process_iter
== process_map
.end())
231 BrowserType type
= GetBrowserType(process_iter
->second
.name
);
232 if (type
!= MAX_BROWSERS
)
233 browser
.name
= base::ASCIIToUTF16(kBrowserPrettyNames
[type
]);
234 process_data_
.push_back(browser
);
237 #if defined(OS_CHROMEOS)
238 base::GetSwapInfo(&swap_info_
);
241 // Finally return to the browser thread.
242 BrowserThread::PostTask(
243 BrowserThread::UI
, FROM_HERE
,
244 base::Bind(&MemoryDetails::CollectChildInfoOnUIThread
, this));