memory_details_linux.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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.
4
5#include "chrome/browser/memory_details.h"
6
7#include <sys/types.h>
8#include <unistd.h>
9
10#include <map>
11#include <set>
12
13#include "base/bind.h"
14#include "base/process_util.h"
15#include "base/string_util.h"
16#include "base/utf_string_conversions.h"
17#include "chrome/common/chrome_constants.h"
18#include "content/public/browser/browser_thread.h"
19#include "content/public/common/process_type.h"
20#include "grit/chromium_strings.h"
21#include "ui/base/l10n/l10n_util.h"
22
23using base::ProcessEntry;
24using content::BrowserThread;
25
26// Known browsers which we collect details for.
27enum BrowserType {
28  CHROME = 0,
29  FIREFOX,
30  ICEWEASEL,
31  OPERA,
32  KONQUEROR,
33  EPIPHANY,
34  MIDORI,
35  MAX_BROWSERS
36} BrowserProcess;
37
38// The pretty printed names of those browsers. Matches up with enum
39// BrowserType.
40static const char kBrowserPrettyNames[][10] = {
41  "Chrome",
42  "Firefox",
43  "Iceweasel",
44  "Opera",
45  "Konqueror",
46  "Epiphany",
47  "Midori",
48};
49
50// A mapping from process name to the type of browser.
51static const struct {
52  const char process_name[16];
53  BrowserType browser;
54} kBrowserBinaryNames[] = {
55  { "firefox", FIREFOX },
56  { "firefox-3.5", FIREFOX },
57  { "firefox-3.0", FIREFOX },
58  { "firefox-bin", FIREFOX },
59  { "iceweasel", ICEWEASEL },
60  { "opera", OPERA },
61  { "konqueror", KONQUEROR },
62  { "epiphany-browse", EPIPHANY },
63  { "epiphany", EPIPHANY },
64  { "midori", MIDORI },
65  { "", MAX_BROWSERS },
66};
67
68MemoryDetails::MemoryDetails()
69    : user_metrics_mode_(UPDATE_USER_METRICS) {
70}
71
72ProcessData* MemoryDetails::ChromeBrowser() {
73  return &process_data_[0];
74}
75
76struct Process {
77  pid_t pid;
78  pid_t parent;
79  std::string name;
80};
81
82typedef std::map<pid_t, Process> ProcessMap;
83
84// Get information on all the processes running on the system.
85static ProcessMap GetProcesses() {
86  ProcessMap map;
87
88  base::ProcessIterator process_iter(NULL);
89  while (const ProcessEntry* process_entry = process_iter.NextProcessEntry()) {
90    Process process;
91    process.pid = process_entry->pid();
92    process.parent = process_entry->parent_pid();
93    process.name = process_entry->exe_file();
94    map.insert(std::make_pair(process.pid, process));
95  }
96  return map;
97}
98
99// Given a process name, return the type of the browser which created that
100// process, or |MAX_BROWSERS| if we don't know about it.
101static BrowserType GetBrowserType(const std::string& process_name) {
102  for (unsigned i = 0; kBrowserBinaryNames[i].process_name[0]; ++i) {
103    if (strcmp(process_name.c_str(), kBrowserBinaryNames[i].process_name) == 0)
104      return kBrowserBinaryNames[i].browser;
105  }
106
107  return MAX_BROWSERS;
108}
109
110// For each of a list of pids, collect memory information about that process.
111static ProcessData GetProcessDataMemoryInformation(
112    const std::vector<pid_t>& pids) {
113  ProcessData process_data;
114  for (std::vector<pid_t>::const_iterator iter = pids.begin();
115       iter != pids.end();
116       ++iter) {
117    ProcessMemoryInformation pmi;
118
119    pmi.pid = *iter;
120    pmi.num_processes = 1;
121
122    if (pmi.pid == base::GetCurrentProcId())
123      pmi.process_type = content::PROCESS_TYPE_BROWSER;
124    else
125      pmi.process_type = content::PROCESS_TYPE_UNKNOWN;
126
127    base::ProcessMetrics* metrics =
128        base::ProcessMetrics::CreateProcessMetrics(*iter);
129    metrics->GetWorkingSetKBytes(&pmi.working_set);
130    delete metrics;
131
132    process_data.processes.push_back(pmi);
133  }
134  return process_data;
135}
136
137// Find all children of the given process with pid |root|.
138static std::vector<pid_t> GetAllChildren(const ProcessMap& processes,
139                                         const pid_t root) {
140  std::vector<pid_t> children;
141  children.push_back(root);
142
143  std::set<pid_t> wavefront, next_wavefront;
144  wavefront.insert(root);
145
146  while (wavefront.size()) {
147    for (ProcessMap::const_iterator iter = processes.begin();
148         iter != processes.end();
149         ++iter) {
150      const Process& process = iter->second;
151      if (wavefront.count(process.parent)) {
152        children.push_back(process.pid);
153        next_wavefront.insert(process.pid);
154      }
155    }
156
157    wavefront.clear();
158    wavefront.swap(next_wavefront);
159  }
160  return children;
161}
162
163void MemoryDetails::CollectProcessData(
164    const std::vector<ProcessMemoryInformation>& child_info) {
165  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
166
167  ProcessMap process_map = GetProcesses();
168  std::set<pid_t> browsers_found;
169
170  // For each process on the system, if it appears to be a browser process and
171  // it's parent isn't a browser process, then record it in |browsers_found|.
172  for (ProcessMap::const_iterator iter = process_map.begin();
173       iter != process_map.end();
174       ++iter) {
175    const Process& current_process = iter->second;
176    const BrowserType type = GetBrowserType(current_process.name);
177    if (type == MAX_BROWSERS)
178      continue;
179
180    ProcessMap::const_iterator parent_iter =
181        process_map.find(current_process.parent);
182    if (parent_iter == process_map.end()) {
183      browsers_found.insert(current_process.pid);
184      continue;
185    }
186
187    if (GetBrowserType(parent_iter->second.name) != type) {
188      // We found a process whose type is diffent from its parent's type.
189      // That means it is the root process of the browser.
190      browsers_found.insert(current_process.pid);
191      break;
192    }
193  }
194
195  ProcessData current_browser =
196      GetProcessDataMemoryInformation(GetAllChildren(process_map, getpid()));
197  current_browser.name = l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME);
198  current_browser.process_name = ASCIIToUTF16("chrome");
199
200  for (std::vector<ProcessMemoryInformation>::iterator
201       i = current_browser.processes.begin();
202       i != current_browser.processes.end(); ++i) {
203    // Check if this is one of the child processes whose data we collected
204    // on the IO thread, and if so copy over that data.
205    for (size_t child = 0; child < child_info.size(); child++) {
206      if (child_info[child].pid != i->pid)
207        continue;
208      i->titles = child_info[child].titles;
209      i->process_type = child_info[child].process_type;
210      break;
211    }
212  }
213
214  process_data_.push_back(current_browser);
215
216  // For each browser process, collect a list of its children and get the
217  // memory usage of each.
218  for (std::set<pid_t>::const_iterator iter = browsers_found.begin();
219       iter != browsers_found.end();
220       ++iter) {
221    std::vector<pid_t> browser_processes = GetAllChildren(process_map, *iter);
222    ProcessData browser = GetProcessDataMemoryInformation(browser_processes);
223
224    ProcessMap::const_iterator process_iter = process_map.find(*iter);
225    if (process_iter == process_map.end())
226      continue;
227    BrowserType type = GetBrowserType(process_iter->second.name);
228    if (type != MAX_BROWSERS)
229      browser.name = ASCIIToUTF16(kBrowserPrettyNames[type]);
230    process_data_.push_back(browser);
231  }
232
233  // Finally return to the browser thread.
234  BrowserThread::PostTask(
235      BrowserThread::UI, FROM_HERE,
236      base::Bind(&MemoryDetails::CollectChildInfoOnUIThread, this));
237}
238