Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2006-2008 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 <unistd.h>
      8 #include <fcntl.h>
      9 #include <dirent.h>
     10 
     11 #include <set>
     12 
     13 #include "base/eintr_wrapper.h"
     14 #include "base/file_version_info.h"
     15 #include "base/string_util.h"
     16 #include "base/process_util.h"
     17 #include "base/utf_string_conversions.h"
     18 #include "chrome/common/chrome_constants.h"
     19 #include "chrome/common/url_constants.h"
     20 #include "content/browser/browser_child_process_host.h"
     21 #include "content/browser/browser_thread.h"
     22 #include "content/browser/zygote_host_linux.h"
     23 #include "grit/chromium_strings.h"
     24 
     25 // Known browsers which we collect details for.
     26 enum BrowserType {
     27   CHROME = 0,
     28   FIREFOX,
     29   ICEWEASEL,
     30   OPERA,
     31   KONQUEROR,
     32   EPIPHANY,
     33   MIDORI,
     34   MAX_BROWSERS
     35 } BrowserProcess;
     36 
     37 // The pretty printed names of those browsers. Matches up with enum
     38 // BrowserType.
     39 static const char kBrowserPrettyNames[][10] = {
     40   "Chrome",
     41   "Firefox",
     42   "Iceweasel",
     43   "Opera",
     44   "Konqueror",
     45   "Epiphany",
     46   "Midori",
     47 };
     48 
     49 // A mapping from process name to the type of browser.
     50 static const struct {
     51   const char process_name[16];
     52   BrowserType browser;
     53   } kBrowserBinaryNames[] = {
     54   { "firefox", FIREFOX },
     55   { "firefox-3.5", FIREFOX },
     56   { "firefox-3.0", FIREFOX },
     57   { "firefox-bin", FIREFOX },
     58   { "iceweasel", ICEWEASEL },
     59   { "opera", OPERA },
     60   { "konqueror", KONQUEROR },
     61   { "epiphany-browse", EPIPHANY },
     62   { "epiphany", EPIPHANY },
     63   { "midori", MIDORI },
     64   { "", MAX_BROWSERS },
     65 };
     66 
     67 MemoryDetails::MemoryDetails() {
     68 }
     69 
     70 ProcessData* MemoryDetails::ChromeBrowser() {
     71   return &process_data_[0];
     72 }
     73 
     74 struct Process {
     75   pid_t pid;
     76   pid_t parent;
     77   std::string name;
     78 };
     79 
     80 // Walk /proc and get information on all the processes running on the system.
     81 static bool GetProcesses(std::vector<Process>* processes) {
     82   processes->clear();
     83 
     84   DIR* dir = opendir("/proc");
     85   if (!dir)
     86     return false;
     87 
     88   struct dirent* dent;
     89   while ((dent = readdir(dir))) {
     90     bool candidate = true;
     91 
     92     // Filter out names which aren't ^[0-9]*$
     93     for (const char* p = dent->d_name; *p; ++p) {
     94       if (*p < '0' || *p > '9') {
     95         candidate = false;
     96         break;
     97       }
     98     }
     99 
    100     if (!candidate)
    101       continue;
    102 
    103     char buf[256];
    104     snprintf(buf, sizeof(buf), "/proc/%s/stat", dent->d_name);
    105     const int fd = open(buf, O_RDONLY);
    106     if (fd < 0)
    107       continue;
    108 
    109     const ssize_t len = HANDLE_EINTR(read(fd, buf, sizeof(buf) - 1));
    110     if (HANDLE_EINTR(close(fd)) < 0)
    111       PLOG(ERROR) << "close";
    112     if (len < 1)
    113       continue;
    114     buf[len] = 0;
    115 
    116     // The start of the file looks like:
    117     //   <pid> (<name>) R <parent pid>
    118     unsigned pid, ppid;
    119     char *process_name;
    120     if (sscanf(buf, "%u (%a[^)]) %*c %u", &pid, &process_name, &ppid) != 3)
    121       continue;
    122 
    123     Process process;
    124     process.pid = pid;
    125     process.parent = ppid;
    126     process.name = process_name;
    127     free(process_name);
    128     processes->push_back(process);
    129   }
    130 
    131   closedir(dir);
    132   return true;
    133 }
    134 
    135 // Given a process name, return the type of the browser which created that
    136 // process, or |MAX_BROWSERS| if we don't know about it.
    137 static BrowserType GetBrowserType(const std::string& process_name) {
    138   for (unsigned i = 0; kBrowserBinaryNames[i].process_name[0]; ++i) {
    139     if (strcmp(process_name.c_str(), kBrowserBinaryNames[i].process_name) == 0)
    140       return kBrowserBinaryNames[i].browser;
    141   }
    142 
    143   return MAX_BROWSERS;
    144 }
    145 
    146 // For each of a list of pids, collect memory information about that process
    147 // and append a record to |out|
    148 static void GetProcessDataMemoryInformation(
    149     const std::vector<pid_t>& pids, ProcessData* out) {
    150   for (std::vector<pid_t>::const_iterator
    151        i = pids.begin(); i != pids.end(); ++i) {
    152     ProcessMemoryInformation pmi;
    153 
    154     pmi.pid = *i;
    155     pmi.num_processes = 1;
    156 
    157     if (pmi.pid == base::GetCurrentProcId())
    158       pmi.type = ChildProcessInfo::BROWSER_PROCESS;
    159     else
    160       pmi.type = ChildProcessInfo::UNKNOWN_PROCESS;
    161 
    162     base::ProcessMetrics* metrics =
    163         base::ProcessMetrics::CreateProcessMetrics(*i);
    164     metrics->GetWorkingSetKBytes(&pmi.working_set);
    165     delete metrics;
    166 
    167     out->processes.push_back(pmi);
    168   }
    169 }
    170 
    171 // Find all children of the given process.
    172 static void GetAllChildren(const std::vector<Process>& processes,
    173                            const pid_t root, const pid_t zygote,
    174                            std::vector<pid_t>* out) {
    175   out->clear();
    176   out->push_back(root);
    177 
    178   std::set<pid_t> wavefront, next_wavefront;
    179   wavefront.insert(root);
    180   bool zygote_found = zygote ? false : true;
    181 
    182   while (wavefront.size()) {
    183     for (std::vector<Process>::const_iterator
    184          i = processes.begin(); i != processes.end(); ++i) {
    185       // Handle the zygote separately. With the SUID sandbox and a separate
    186       // pid namespace, the zygote's parent process is not the browser.
    187       if (!zygote_found && zygote == i->pid) {
    188         zygote_found = true;
    189         out->push_back(i->pid);
    190         next_wavefront.insert(i->pid);
    191       } else if (wavefront.count(i->parent)) {
    192         out->push_back(i->pid);
    193         next_wavefront.insert(i->pid);
    194       }
    195     }
    196 
    197     wavefront.clear();
    198     wavefront.swap(next_wavefront);
    199   }
    200 }
    201 
    202 void MemoryDetails::CollectProcessData(
    203     const std::vector<ProcessMemoryInformation>& child_info) {
    204   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    205 
    206   std::vector<Process> processes;
    207   GetProcesses(&processes);
    208   std::set<pid_t> browsers_found;
    209 
    210   // For each process on the system, if it appears to be a browser process and
    211   // it's parent isn't a browser process, then record it in |browsers_found|.
    212   for (std::vector<Process>::const_iterator
    213        i = processes.begin(); i != processes.end(); ++i) {
    214     const BrowserType type = GetBrowserType(i->name);
    215     if (type != MAX_BROWSERS) {
    216       bool found_parent = false;
    217 
    218       // Find the parent of |i|
    219       for (std::vector<Process>::const_iterator
    220            j = processes.begin(); j != processes.end(); ++j) {
    221         if (j->pid == i->parent) {
    222           found_parent = true;
    223 
    224           if (GetBrowserType(j->name) != type) {
    225             // We went too far and ended up with something else, which means
    226             // that |i| is a browser.
    227             browsers_found.insert(i->pid);
    228             break;
    229           }
    230         }
    231       }
    232 
    233       if (!found_parent)
    234         browsers_found.insert(i->pid);
    235     }
    236   }
    237 
    238   std::vector<pid_t> current_browser_processes;
    239   const pid_t zygote = ZygoteHost::GetInstance()->pid();
    240   GetAllChildren(processes, getpid(), zygote, &current_browser_processes);
    241   ProcessData current_browser;
    242   GetProcessDataMemoryInformation(current_browser_processes, &current_browser);
    243   current_browser.name = WideToUTF16(chrome::kBrowserAppName);
    244   current_browser.process_name = ASCIIToUTF16("chrome");
    245   process_data_.push_back(current_browser);
    246 
    247   // For each browser process, collect a list of its children and get the
    248   // memory usage of each.
    249   for (std::set<pid_t>::const_iterator
    250        i = browsers_found.begin(); i != browsers_found.end(); ++i) {
    251     std::vector<pid_t> browser_processes;
    252     GetAllChildren(processes, *i, 0, &browser_processes);
    253     ProcessData browser;
    254     GetProcessDataMemoryInformation(browser_processes, &browser);
    255 
    256     for (std::vector<Process>::const_iterator
    257          j = processes.begin(); j != processes.end(); ++j) {
    258       if (j->pid == *i) {
    259         BrowserType type = GetBrowserType(j->name);
    260         if (type != MAX_BROWSERS)
    261           browser.name = ASCIIToUTF16(kBrowserPrettyNames[type]);
    262         break;
    263       }
    264     }
    265 
    266     process_data_.push_back(browser);
    267   }
    268 
    269   // Finally return to the browser thread.
    270   BrowserThread::PostTask(
    271       BrowserThread::UI, FROM_HERE,
    272       NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
    273 }
    274