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 <set>
8#include <string>
9#include <vector>
10
11#include "base/bind.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/process/process_iterator.h"
14#include "chrome/common/chrome_constants.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/common/process_type.h"
17#include "grit/chromium_strings.h"
18#include "ui/base/l10n/l10n_util.h"
19
20using base::ProcessEntry;
21using base::ProcessId;
22using content::BrowserThread;
23
24namespace {
25
26// A helper for |CollectProcessData()| to include the chrome sandboxed
27// processes in android which are not running as a child of the browser
28// process.
29void AddNonChildChromeProcesses(
30    std::vector<ProcessMemoryInformation>* processes) {
31  base::ProcessIterator process_iter(NULL);
32  while (const ProcessEntry* process_entry = process_iter.NextProcessEntry()) {
33    const std::vector<std::string>& cmd_args = process_entry->cmd_line_args();
34    if (cmd_args.empty() ||
35        cmd_args[0].find(chrome::kHelperProcessExecutableName) ==
36            std::string::npos) {
37      continue;
38    }
39    ProcessMemoryInformation info;
40    info.pid = process_entry->pid();
41    processes->push_back(info);
42  }
43}
44
45// For each of the pids, collect memory information about that process
46// and append a record to |out|.
47void GetProcessDataMemoryInformation(
48    const std::set<ProcessId>& pids, ProcessData* out) {
49  for (std::set<ProcessId>::const_iterator i = pids.begin(); i != pids.end();
50       ++i) {
51    ProcessMemoryInformation pmi;
52
53    pmi.pid = *i;
54    pmi.num_processes = 1;
55
56    if (pmi.pid == base::GetCurrentProcId())
57      pmi.process_type = content::PROCESS_TYPE_BROWSER;
58    else
59      pmi.process_type = content::PROCESS_TYPE_UNKNOWN;
60
61    scoped_ptr<base::ProcessMetrics> metrics(
62        base::ProcessMetrics::CreateProcessMetrics(*i));
63    metrics->GetWorkingSetKBytes(&pmi.working_set);
64
65    out->processes.push_back(pmi);
66  }
67}
68
69// Find all children of the given process.
70void GetAllChildren(const std::vector<ProcessEntry>& processes,
71                    const std::set<ProcessId>& roots,
72                    std::set<ProcessId>* out) {
73  *out = roots;
74
75  std::set<ProcessId> wavefront;
76  for (std::set<ProcessId>::const_iterator i = roots.begin(); i != roots.end();
77       ++i) {
78    wavefront.insert(*i);
79  }
80
81  while (wavefront.size()) {
82    std::set<ProcessId> next_wavefront;
83    for (std::vector<ProcessEntry>::const_iterator i = processes.begin();
84         i != processes.end(); ++i) {
85      if (wavefront.count(i->parent_pid())) {
86        out->insert(i->pid());
87        next_wavefront.insert(i->pid());
88      }
89    }
90
91    wavefront.clear();
92    wavefront.swap(next_wavefront);
93  }
94}
95
96}  // namespace
97
98MemoryDetails::MemoryDetails()
99    : user_metrics_mode_(UPDATE_USER_METRICS) {
100}
101
102ProcessData* MemoryDetails::ChromeBrowser() {
103  return &process_data_[0];
104}
105
106void MemoryDetails::CollectProcessData(
107    const std::vector<ProcessMemoryInformation>& chrome_processes) {
108  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
109
110  std::vector<ProcessMemoryInformation> all_processes(chrome_processes);
111  AddNonChildChromeProcesses(&all_processes);
112
113  std::vector<ProcessEntry> processes;
114  base::ProcessIterator process_iter(NULL);
115  while (const ProcessEntry* process_entry = process_iter.NextProcessEntry()) {
116    processes.push_back(*process_entry);
117  }
118
119  std::set<ProcessId> roots;
120  roots.insert(base::GetCurrentProcId());
121  for (std::vector<ProcessMemoryInformation>::const_iterator
122       i = all_processes.begin(); i != all_processes.end(); ++i) {
123    roots.insert(i->pid);
124  }
125
126  std::set<ProcessId> current_browser_processes;
127  GetAllChildren(processes, roots, &current_browser_processes);
128
129  ProcessData current_browser;
130  GetProcessDataMemoryInformation(current_browser_processes, &current_browser);
131  current_browser.name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
132  current_browser.process_name =
133      reinterpret_cast<unsigned int>(chrome::kBrowserProcessExecutableName);
134  process_data_.push_back(current_browser);
135
136  // Finally return to the browser thread.
137  BrowserThread::PostTask(
138      BrowserThread::UI, FROM_HERE,
139      base::Bind(&MemoryDetails::CollectChildInfoOnUIThread, this));
140}
141