普通文本  |  246行  |  7.25 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/memory_details.h"

#include <sys/types.h>
#include <unistd.h>

#include <map>
#include <set>

#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/process/process_iterator.h"
#include "base/process/process_metrics.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/process_type.h"
#include "ui/base/l10n/l10n_util.h"

using base::ProcessEntry;
using content::BrowserThread;

// Known browsers which we collect details for.
enum BrowserType {
  CHROME = 0,
  FIREFOX,
  ICEWEASEL,
  OPERA,
  KONQUEROR,
  EPIPHANY,
  MIDORI,
  MAX_BROWSERS
} BrowserProcess;

// The pretty printed names of those browsers. Matches up with enum
// BrowserType.
static const char kBrowserPrettyNames[][10] = {
  "Chrome",
  "Firefox",
  "Iceweasel",
  "Opera",
  "Konqueror",
  "Epiphany",
  "Midori",
};

// A mapping from process name to the type of browser.
static const struct {
  const char process_name[16];
  BrowserType browser;
} kBrowserBinaryNames[] = {
  { "firefox", FIREFOX },
  { "firefox-3.5", FIREFOX },
  { "firefox-3.0", FIREFOX },
  { "firefox-bin", FIREFOX },
  { "iceweasel", ICEWEASEL },
  { "opera", OPERA },
  { "konqueror", KONQUEROR },
  { "epiphany-browse", EPIPHANY },
  { "epiphany", EPIPHANY },
  { "midori", MIDORI },
  { "", MAX_BROWSERS },
};

MemoryDetails::MemoryDetails()
    : user_metrics_mode_(UPDATE_USER_METRICS),
      memory_growth_tracker_(NULL) {
}

ProcessData* MemoryDetails::ChromeBrowser() {
  return &process_data_[0];
}

struct Process {
  pid_t pid;
  pid_t parent;
  std::string name;
};

typedef std::map<pid_t, Process> ProcessMap;

// Get information on all the processes running on the system.
static ProcessMap GetProcesses() {
  ProcessMap map;

  base::ProcessIterator process_iter(NULL);
  while (const ProcessEntry* process_entry = process_iter.NextProcessEntry()) {
    Process process;
    process.pid = process_entry->pid();
    process.parent = process_entry->parent_pid();
    process.name = process_entry->exe_file();
    map.insert(std::make_pair(process.pid, process));
  }
  return map;
}

// Given a process name, return the type of the browser which created that
// process, or |MAX_BROWSERS| if we don't know about it.
static BrowserType GetBrowserType(const std::string& process_name) {
  for (unsigned i = 0; kBrowserBinaryNames[i].process_name[0]; ++i) {
    if (strcmp(process_name.c_str(), kBrowserBinaryNames[i].process_name) == 0)
      return kBrowserBinaryNames[i].browser;
  }

  return MAX_BROWSERS;
}

// For each of a list of pids, collect memory information about that process.
static ProcessData GetProcessDataMemoryInformation(
    const std::vector<pid_t>& pids) {
  ProcessData process_data;
  for (std::vector<pid_t>::const_iterator iter = pids.begin();
       iter != pids.end();
       ++iter) {
    ProcessMemoryInformation pmi;

    pmi.pid = *iter;
    pmi.num_processes = 1;

    if (pmi.pid == base::GetCurrentProcId())
      pmi.process_type = content::PROCESS_TYPE_BROWSER;
    else
      pmi.process_type = content::PROCESS_TYPE_UNKNOWN;

    base::ProcessMetrics* metrics =
        base::ProcessMetrics::CreateProcessMetrics(*iter);
    metrics->GetWorkingSetKBytes(&pmi.working_set);
    delete metrics;

    process_data.processes.push_back(pmi);
  }
  return process_data;
}

// Find all children of the given process with pid |root|.
static std::vector<pid_t> GetAllChildren(const ProcessMap& processes,
                                         const pid_t root) {
  std::vector<pid_t> children;
  children.push_back(root);

  std::set<pid_t> wavefront, next_wavefront;
  wavefront.insert(root);

  while (wavefront.size()) {
    for (ProcessMap::const_iterator iter = processes.begin();
         iter != processes.end();
         ++iter) {
      const Process& process = iter->second;
      if (wavefront.count(process.parent)) {
        children.push_back(process.pid);
        next_wavefront.insert(process.pid);
      }
    }

    wavefront.clear();
    wavefront.swap(next_wavefront);
  }
  return children;
}

void MemoryDetails::CollectProcessData(
    const std::vector<ProcessMemoryInformation>& child_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  ProcessMap process_map = GetProcesses();
  std::set<pid_t> browsers_found;

  // For each process on the system, if it appears to be a browser process and
  // it's parent isn't a browser process, then record it in |browsers_found|.
  for (ProcessMap::const_iterator iter = process_map.begin();
       iter != process_map.end();
       ++iter) {
    const Process& current_process = iter->second;
    const BrowserType type = GetBrowserType(current_process.name);
    if (type == MAX_BROWSERS)
      continue;

    ProcessMap::const_iterator parent_iter =
        process_map.find(current_process.parent);
    if (parent_iter == process_map.end()) {
      browsers_found.insert(current_process.pid);
      continue;
    }

    if (GetBrowserType(parent_iter->second.name) != type) {
      // We found a process whose type is diffent from its parent's type.
      // That means it is the root process of the browser.
      browsers_found.insert(current_process.pid);
      break;
    }
  }

  ProcessData current_browser =
      GetProcessDataMemoryInformation(GetAllChildren(process_map, getpid()));
  current_browser.name = l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME);
  current_browser.process_name = base::ASCIIToUTF16("chrome");

  for (std::vector<ProcessMemoryInformation>::iterator
       i = current_browser.processes.begin();
       i != current_browser.processes.end(); ++i) {
    // Check if this is one of the child processes whose data we collected
    // on the IO thread, and if so copy over that data.
    for (size_t child = 0; child < child_info.size(); child++) {
      if (child_info[child].pid != i->pid)
        continue;
      i->titles = child_info[child].titles;
      i->process_type = child_info[child].process_type;
      break;
    }
  }

  process_data_.push_back(current_browser);

  // For each browser process, collect a list of its children and get the
  // memory usage of each.
  for (std::set<pid_t>::const_iterator iter = browsers_found.begin();
       iter != browsers_found.end();
       ++iter) {
    std::vector<pid_t> browser_processes = GetAllChildren(process_map, *iter);
    ProcessData browser = GetProcessDataMemoryInformation(browser_processes);

    ProcessMap::const_iterator process_iter = process_map.find(*iter);
    if (process_iter == process_map.end())
      continue;
    BrowserType type = GetBrowserType(process_iter->second.name);
    if (type != MAX_BROWSERS)
      browser.name = base::ASCIIToUTF16(kBrowserPrettyNames[type]);
    process_data_.push_back(browser);
  }

#if defined(OS_CHROMEOS)
  base::GetSwapInfo(&swap_info_);
#endif

  // Finally return to the browser thread.
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&MemoryDetails::CollectChildInfoOnUIThread, this));
}