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