[go: nahoru, domu]

Linux: about:memory

(based on http://code.google.com/p/chromium/issues/detail?id=16251)

Add about:memory support to Linux. Rather than try and copy the
Windows output, we use a couple of metrics which make more sense on
Linux: USS and PSS.

http://codereview.chromium.org/177024


git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24979 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/base/process_util.h b/base/process_util.h
index f002f85..91cca615 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -286,12 +286,20 @@
 };
 
 // Working Set (resident) memory usage broken down by
+//
+// On Windows:
 // priv (private): These pages (kbytes) cannot be shared with any other process.
 // shareable:      These pages (kbytes) can be shared with other processes under
 //                 the right circumstances.
 // shared :        These pages (kbytes) are currently shared with at least one
 //                 other process.
+//
+// On Linux:
+// priv:           Pages mapped only by this process
+// shared:         PSS or 0 if the kernel doesn't support this
+// shareable:      0
 struct WorkingSetKBytes {
+  WorkingSetKBytes() : priv(0), shareable(0), shared(0) {}
   size_t priv;
   size_t shareable;
   size_t shared;
@@ -304,6 +312,7 @@
 // image:   These pages are mapped into the view of an image section (backed by
 //          file system)
 struct CommittedKBytes {
+  CommittedKBytes() : priv(0), mapped(0), image(0) {}
   size_t priv;
   size_t mapped;
   size_t image;
diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc
index a05e9bb..53e7d028 100644
--- a/base/process_util_linux.cc
+++ b/base/process_util_linux.cc
@@ -292,11 +292,10 @@
   ws_usage->priv = private_kb;
   // Sharable is not calculated, as it does not provide interesting data.
   ws_usage->shareable = 0;
-  if (have_pss) {
-   ws_usage->shared = pss_kb - private_kb;
-  } else {
-    ws_usage->shared = shared_kb;
-  }
+
+  ws_usage->shared = 0;
+  if (have_pss)
+    ws_usage->shared = pss_kb;
   return true;
 }
 
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc
index 6cc4491c..f4a5da6 100644
--- a/chrome/browser/browser_about_handler.cc
+++ b/chrome/browser/browser_about_handler.cc
@@ -620,17 +620,17 @@
   ListValue* browsers = new ListValue();
   root.Set(L"browsers", browsers);
 
-  ProcessData* browser_processes = processes();
+  const std::vector<ProcessData>& browser_processes = processes();
 
   // Aggregate per-process data into browser summary data.
   std::wstring log_string;
-  for (int index = 0; index < MemoryDetails::MAX_BROWSERS; index++) {
+  for (size_t index = 0; index < browser_processes.size(); index++) {
     if (browser_processes[index].processes.size() == 0)
       continue;
 
     // Sum the information for the processes within this browser.
     ProcessMemoryInformation aggregate;
-    ProcessMemoryInformationList::iterator iterator;
+    ProcessMemoryInformationList::const_iterator iterator;
     iterator = browser_processes[index].processes.begin();
     aggregate.pid = iterator->pid;
     aggregate.version = iterator->version;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index ca12d5d..b8406044 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -13,7 +13,15 @@
     <includes>
       <include name="IDR_ABOUT_PLUGINS_HTML" file="resources\about_plugins.html" type="BINDATA" />
       <include name="IDR_ABOUT_VERSION_HTML" file="resources\about_version.html" flattenhtml="true" type="BINDATA" />
-      <include name="IDR_ABOUT_MEMORY_HTML" file="resources\about_memory.html" flattenhtml="true" type="BINDATA" />
+
+      <if expr="os == 'linux2'">
+        <include name="IDR_ABOUT_MEMORY_HTML" file="resources\about_memory_linux.html" flattenhtml="true" type="BINDATA" />
+      </if>
+
+      <if expr="os != 'linux2'">
+        <include name="IDR_ABOUT_MEMORY_HTML" file="resources\about_memory.html" flattenhtml="true" type="BINDATA" />
+      </if>
+
       <include name="IDR_ABOUT_STATS_HTML" file="resources\about_stats.html" type="BINDATA" />
       <include name="IDR_SSL_ROAD_BLOCK_HTML" file="security\resources\ssl_roadblock.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_SSL_ERROR_HTML" file="security\resources\ssl_error.html" flattenhtml="true" type="BINDATA" />
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc
index cfdafc3..219333f 100644
--- a/chrome/browser/memory_details.cc
+++ b/chrome/browser/memory_details.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/memory_details.h"
-#include <psapi.h>
 
 #include "app/l10n_util.h"
 #include "base/file_version_info.h"
@@ -19,11 +18,10 @@
 #include "chrome/common/url_constants.h"
 #include "grit/chromium_strings.h"
 
-class RenderViewHostDelegate;
-
-// Template of static data we use for finding browser process information.
-// These entries must match the ordering for MemoryDetails::BrowserProcess.
-static ProcessData g_process_template[MemoryDetails::MAX_BROWSERS];
+#if defined(OS_LINUX)
+#include "chrome/browser/zygote_host_linux.h"
+#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"
+#endif
 
 // About threading:
 //
@@ -38,25 +36,6 @@
 // expensive parts of this operation over on the file thread.
 //
 
-MemoryDetails::MemoryDetails() : ui_loop_(NULL) {
-  static const std::wstring google_browser_name =
-      l10n_util::GetString(IDS_PRODUCT_NAME);
-  ProcessData g_process_template[MemoryDetails::MAX_BROWSERS] = {
-    { google_browser_name.c_str(), L"chrome.exe", },
-    { L"IE", L"iexplore.exe", },
-    { L"Firefox", L"firefox.exe", },
-    { L"Opera", L"opera.exe", },
-    { L"Safari", L"safari.exe", },
-    { L"IE (64bit)", L"iexplore.exe", },
-    { L"Konqueror", L"konqueror.exe", },
-  };
-
-  for (int index = 0; index < arraysize(g_process_template); ++index) {
-    process_data_[index].name = g_process_template[index].name;
-    process_data_[index].process_name = g_process_template[index].process_name;
-  }
-}
-
 void MemoryDetails::StartFetch() {
   ui_loop_ = MessageLoop::current();
 
@@ -92,106 +71,17 @@
       NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info));
 }
 
-void MemoryDetails::CollectProcessData(
-    std::vector<ProcessMemoryInformation> child_info) {
-  DCHECK(MessageLoop::current() ==
-      ChromeThread::GetMessageLoop(ChromeThread::FILE));
-
-  // Clear old data.
-  for (int index = 0; index < arraysize(g_process_template); index++)
-    process_data_[index].processes.clear();
-
-  SYSTEM_INFO system_info;
-  GetNativeSystemInfo(&system_info);
-  bool is_64bit_os =
-      system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
-
-  ScopedHandle snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
-  PROCESSENTRY32 process_entry = {sizeof(PROCESSENTRY32)};
-  if (!snapshot.Get()) {
-    LOG(ERROR) << "CreateToolhelp32Snaphot failed: " << GetLastError();
-    return;
-  }
-  if (!::Process32First(snapshot, &process_entry)) {
-    LOG(ERROR) << "Process32First failed: " << GetLastError();
-    return;
-  }
-  do {
-    int pid = process_entry.th32ProcessID;
-    ScopedHandle handle(::OpenProcess(
-        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
-    if (!handle.Get())
-      continue;
-    bool is_64bit_process = false;
-    // IsWow64Process() returns FALSE for a 32bit process on a 32bit OS.
-    // We need to check if the real OS is 64bit.
-    if (is_64bit_os) {
-      BOOL is_wow64 = FALSE;
-      // IsWow64Process() is supported by Windows XP SP2 or later.
-      IsWow64Process(handle, &is_wow64);
-      is_64bit_process = !is_wow64;
-    }
-    for (int index2 = 0; index2 < arraysize(g_process_template); index2++) {
-      if (_wcsicmp(process_data_[index2].process_name,
-          process_entry.szExeFile) != 0)
-        continue;
-      if (index2 == IE_BROWSER && is_64bit_process)
-        continue;  // Should use IE_64BIT_BROWSER
-      // Get Memory Information.
-      ProcessMemoryInformation info;
-      info.pid = pid;
-      if (info.pid == GetCurrentProcessId())
-        info.type = ChildProcessInfo::BROWSER_PROCESS;
-      else
-        info.type = ChildProcessInfo::UNKNOWN_PROCESS;
-
-      scoped_ptr<base::ProcessMetrics> metrics;
-      metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
-      metrics->GetCommittedKBytes(&info.committed);
-      metrics->GetWorkingSetKBytes(&info.working_set);
-
-      // Get Version Information.
-      TCHAR name[MAX_PATH];
-      if (index2 == CHROME_BROWSER) {
-        scoped_ptr<FileVersionInfo> version_info(
-            FileVersionInfo::CreateFileVersionInfoForCurrentModule());
-        if (version_info != NULL)
-          info.version = version_info->file_version();
-        // 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 != info.pid)
-            continue;
-          info.titles = child_info[child].titles;
-          info.type = child_info[child].type;
-          break;
-        }
-      } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) {
-        std::wstring str_name(name);
-        scoped_ptr<FileVersionInfo> version_info(
-            FileVersionInfo::CreateFileVersionInfo(str_name));
-        if (version_info != NULL) {
-          info.version = version_info->product_version();
-          info.product_name = version_info->product_name();
-        }
-      }
-
-      // Add the process info to our list.
-      process_data_[index2].processes.push_back(info);
-      break;
-    }
-  } while (::Process32Next(snapshot, &process_entry));
-
-  // Finally return to the browser thread.
-  ui_loop_->PostTask(FROM_HERE,
-      NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
-}
-
 void MemoryDetails::CollectChildInfoOnUIThread() {
   DCHECK(MessageLoop::current() == ui_loop_);
 
+#if defined(OS_LINUX)
+  const pid_t zygote_pid = Singleton<ZygoteHost>()->pid();
+  const pid_t sandbox_helper_pid = Singleton<RenderSandboxHostLinux>()->pid();
+#endif
+
+  ProcessData* const chrome_browser = ChromeBrowser();
   // Get more information about the process.
-  for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
+  for (size_t index = 0; index < chrome_browser->processes.size();
       index++) {
     // Check if it's a renderer, if so get the list of page titles in it and
     // check if it's a diagnostics-related process.  We skip all diagnostics
@@ -199,10 +89,11 @@
     // the tab contents.
     RenderProcessHost::iterator renderer_iter(
         RenderProcessHost::AllHostsIterator());
+    ProcessMemoryInformation& process =
+        chrome_browser->processes[index];
+
     for (; !renderer_iter.IsAtEnd(); renderer_iter.Advance()) {
       DCHECK(renderer_iter.GetCurrentValue());
-      ProcessMemoryInformation& process =
-          process_data_[CHROME_BROWSER].processes[index];
       if (process.pid != renderer_iter.GetCurrentValue()->process().pid())
         continue;
       process.type = ChildProcessInfo::RENDER_PROCESS;
@@ -257,15 +148,23 @@
           process.is_diagnostics = true;
       }
     }
+
+#if defined(OS_LINUX)
+    if (process.pid == zygote_pid) {
+      process.type = ChildProcessInfo::ZYGOTE_PROCESS;
+    } else if (process.pid == sandbox_helper_pid) {
+      process.type = ChildProcessInfo::SANDBOX_HELPER_PROCESS;
+    }
+#endif
   }
 
   // Get rid of other Chrome processes that are from a different profile.
-  for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
+  for (size_t index = 0; index < chrome_browser->processes.size();
       index++) {
-    if (process_data_[CHROME_BROWSER].processes[index].type ==
+    if (chrome_browser->processes[index].type ==
         ChildProcessInfo::UNKNOWN_PROCESS) {
-      process_data_[CHROME_BROWSER].processes.erase(
-          process_data_[CHROME_BROWSER].processes.begin() + index);
+      chrome_browser->processes.erase(
+          chrome_browser->processes.begin() + index);
       index--;
     }
   }
@@ -279,7 +178,7 @@
   // Reports a set of memory metrics to UMA.
   // Memory is measured in KB.
 
-  ProcessData browser = process_data_[CHROME_BROWSER];
+  const ProcessData& browser = *ChromeBrowser();
   size_t aggregate_memory = 0;
   int plugin_count = 0;
   int worker_count = 0;
@@ -301,6 +200,14 @@
        UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample);
        worker_count++;
        break;
+     case ChildProcessInfo::ZYGOTE_PROCESS:
+       UMA_HISTOGRAM_MEMORY_KB("Memory.Zygote", sample);
+       break;
+     case ChildProcessInfo::SANDBOX_HELPER_PROCESS:
+       UMA_HISTOGRAM_MEMORY_KB("Memory.SandboxHelper", sample);
+       break;
+     default:
+       NOTREACHED();
     }
   }
   UMA_HISTOGRAM_MEMORY_KB("Memory.BackingStore",
diff --git a/chrome/browser/memory_details.h b/chrome/browser/memory_details.h
index 8876c47..2e7e852 100644
--- a/chrome/browser/memory_details.h
+++ b/chrome/browser/memory_details.h
@@ -17,8 +17,11 @@
 // have multiple processes (of course!).  Even IE has multiple
 // processes these days.
 struct ProcessMemoryInformation {
-  ProcessMemoryInformation() {
-    memset(this, 0, sizeof(ProcessMemoryInformation));
+  ProcessMemoryInformation()
+      : pid(0),
+        num_processes(0),
+        is_diagnostics(false),
+        type(ChildProcessInfo::UNKNOWN_PROCESS) {
   }
 
   // The process id.
@@ -46,8 +49,8 @@
 
 // Browser Process Information.
 struct ProcessData {
-  const wchar_t* name;
-  const wchar_t* process_name;
+  std::wstring name;
+  std::wstring process_name;
   ProcessMemoryInformationList processes;
 };
 
@@ -71,26 +74,13 @@
 //    }
 class MemoryDetails : public base::RefCountedThreadSafe<MemoryDetails> {
  public:
-  // Known browsers which we collect details for.
-  enum {
-    CHROME_BROWSER = 0,
-    IE_BROWSER,
-    FIREFOX_BROWSER,
-    OPERA_BROWSER,
-    SAFARI_BROWSER,
-    IE_64BIT_BROWSER,
-    KONQUEROR_BROWSER,
-    MAX_BROWSERS
-  } BrowserProcess;
-
   // Constructor.
   MemoryDetails();
   virtual ~MemoryDetails() {}
 
-  // Access to the process detail information.  This is an array
-  // of MAX_BROWSER ProcessData structures.  This data is only available
+  // Access to the process detail information.  This data is only available
   // after OnDetailsAvailable() has been called.
-  ProcessData* processes() { return process_data_; }
+  const std::vector<ProcessData>& processes() { return process_data_; }
 
   // Initiate updating the current memory details.  These are fetched
   // asynchronously because data must be collected from multiple threads.
@@ -123,7 +113,10 @@
   // the global histograms for tracking memory usage.
   void UpdateHistograms();
 
-  ProcessData process_data_[MAX_BROWSERS];
+  // Returns a pointer to the ProcessData structure for Chrome.
+  ProcessData* ChromeBrowser();
+
+  std::vector<ProcessData> process_data_;
   MessageLoop* ui_loop_;
 
   DISALLOW_EVIL_CONSTRUCTORS(MemoryDetails);
diff --git a/chrome/browser/memory_details_linux.cc b/chrome/browser/memory_details_linux.cc
new file mode 100644
index 0000000..a1ed5e9
--- /dev/null
+++ b/chrome/browser/memory_details_linux.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2006-2008 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 <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "app/l10n_util.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_version_info.h"
+#include "base/string_util.h"
+#include "base/process_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/common/child_process_host.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/url_constants.h"
+#include "grit/chromium_strings.h"
+
+// Known browsers which we collect details for.
+enum BrowserType {
+  CHROME = 0,
+  FIREFOX,
+  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",
+  "Opera",
+  "Konqueror",
+  "Epiphany",
+  "Midori",
+};
+
+// A mapping from process name to the type of browser.
+static const struct {
+  const char process_name[17];
+  BrowserType browser;
+  } kBrowserBinaryNames[] = {
+  { "firefox", FIREFOX },
+  { "firefox-3.5", FIREFOX },
+  { "firefox-3.0", FIREFOX },
+  { "opera", OPERA },
+  { "konqueror", KONQUEROR },
+  { "epiphany-browser", EPIPHANY },
+  { "epiphany", EPIPHANY },
+  { "midori", MIDORI },
+  { "", MAX_BROWSERS },
+};
+
+MemoryDetails::MemoryDetails()
+  : ui_loop_(NULL) {
+}
+
+ProcessData* MemoryDetails::ChromeBrowser() {
+  return &process_data_[0];
+}
+
+struct Process {
+  pid_t pid;
+  pid_t parent;
+  std::string name;
+};
+
+// Walk /proc and get information on all the processes running on the system.
+static bool GetProcesses(std::vector<Process>* processes) {
+  processes->clear();
+
+  DIR* dir = opendir("/proc");
+  if (!dir)
+    return false;
+
+  struct dirent* dent;
+  while ((dent = readdir(dir))) {
+    bool candidate = true;
+
+    // Filter out names which aren't ^[0-9]*$
+    for (const char* p = dent->d_name; *p; ++p) {
+      if (*p < '0' || *p > '9') {
+        candidate = false;
+        break;
+      }
+    }
+
+    if (!candidate)
+      continue;
+
+    char buf[256];
+    snprintf(buf, sizeof(buf), "/proc/%s/stat", dent->d_name);
+    const int fd = open(buf, O_RDONLY);
+    if (fd < 0)
+      continue;
+
+    const ssize_t len = HANDLE_EINTR(read(fd, buf, sizeof(buf) - 1));
+    HANDLE_EINTR(close(fd));
+    if (len < 1)
+      continue;
+    buf[len] = 0;
+
+    // The start of the file looks like:
+    //   <pid> (<name>) R <parent pid>
+    unsigned pid, ppid;
+    char *process_name;
+    if (sscanf(buf, "%u (%a[^)]) %*c %u", &pid, &process_name, &ppid) != 3)
+      continue;
+
+    Process process;
+    process.pid = pid;
+    process.parent = ppid;
+    process.name = process_name;
+    free(process_name);
+    processes->push_back(process);
+  }
+
+  closedir(dir);
+  return true;
+}
+
+// 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
+// and append a record to |out|
+static void GetProcessDataMemoryInformation(
+    const std::vector<pid_t>& pids, ProcessData* out) {
+  for (std::vector<pid_t>::const_iterator
+       i = pids.begin(); i != pids.end(); ++i) {
+    ProcessMemoryInformation pmi;
+
+    pmi.pid = *i;
+    pmi.num_processes = 1;
+
+    if (pmi.pid == base::GetCurrentProcId())
+      pmi.type = ChildProcessInfo::BROWSER_PROCESS;
+    else
+      pmi.type = ChildProcessInfo::UNKNOWN_PROCESS;
+
+    base::ProcessMetrics* metrics =
+        base::ProcessMetrics::CreateProcessMetrics(*i);
+    metrics->GetWorkingSetKBytes(&pmi.working_set);
+    delete metrics;
+
+    out->processes.push_back(pmi);
+  }
+}
+
+// Find all children of the given process.
+static void GetAllChildren(const std::vector<Process>& processes,
+                           pid_t root, std::vector<pid_t>* out) {
+  out->clear();
+  out->push_back(root);
+
+  std::set<pid_t> wavefront, next_wavefront;
+  wavefront.insert(root);
+
+  while (wavefront.size()) {
+    for (std::vector<Process>::const_iterator
+         i = processes.begin(); i != processes.end(); ++i) {
+      if (wavefront.count(i->parent)) {
+        out->push_back(i->pid);
+        next_wavefront.insert(i->pid);
+      }
+    }
+
+    wavefront.clear();
+    wavefront.swap(next_wavefront);
+  }
+}
+
+void MemoryDetails::CollectProcessData(
+    std::vector<ProcessMemoryInformation> child_info) {
+  DCHECK(MessageLoop::current() ==
+      ChromeThread::GetMessageLoop(ChromeThread::FILE));
+
+  std::vector<Process> processes;
+  GetProcesses(&processes);
+  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 (std::vector<Process>::const_iterator
+       i = processes.begin(); i != processes.end(); ++i) {
+    const BrowserType type = GetBrowserType(i->name);
+    if (type != MAX_BROWSERS) {
+      bool found_parent = false;
+
+      // Find the parent of |i|
+      for (std::vector<Process>::const_iterator
+           j = processes.begin(); j != processes.end(); ++j) {
+        if (j->pid == i->parent) {
+          found_parent = true;
+
+          if (GetBrowserType(j->name) != type) {
+            // We went too far and ended up with something else, which means
+            // that |i| is a browser.
+            browsers_found.insert(i->pid);
+            break;
+          }
+        }
+      }
+
+      if (!found_parent)
+        browsers_found.insert(i->pid);
+    }
+  }
+
+  std::vector<pid_t> current_browser_processes;
+  GetAllChildren(processes, getpid(), &current_browser_processes);
+  ProcessData current_browser;
+  GetProcessDataMemoryInformation(current_browser_processes, &current_browser);
+  current_browser.name = chrome::kBrowserAppName;
+  current_browser.process_name = L"chrome";
+  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
+       i = browsers_found.begin(); i != browsers_found.end(); ++i) {
+    std::vector<pid_t> browser_processes;
+    GetAllChildren(processes, *i, &browser_processes);
+    ProcessData browser;
+    GetProcessDataMemoryInformation(browser_processes, &browser);
+
+    for (std::vector<Process>::const_iterator
+         j = processes.begin(); j != processes.end(); ++j) {
+      if (j->pid == *i) {
+        BrowserType type = GetBrowserType(j->name);
+        if (type != MAX_BROWSERS)
+          browser.name = ASCIIToWide(kBrowserPrettyNames[type]);
+        break;
+      }
+    }
+
+    process_data_.push_back(browser);
+  }
+
+  // Finally return to the browser thread.
+  ui_loop_->PostTask(FROM_HERE,
+      NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
+}
diff --git a/chrome/browser/memory_details_win.cc b/chrome/browser/memory_details_win.cc
new file mode 100644
index 0000000..4c3d8be
--- /dev/null
+++ b/chrome/browser/memory_details_win.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2006-2008 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 <psapi.h>
+
+#include "app/l10n_util.h"
+#include "base/file_version_info.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/renderer_host/backing_store_manager.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/child_process_host.h"
+#include "chrome/common/url_constants.h"
+#include "grit/chromium_strings.h"
+
+// Known browsers which we collect details for.
+enum {
+  CHROME_BROWSER = 0,
+  IE_BROWSER,
+  FIREFOX_BROWSER,
+  OPERA_BROWSER,
+  SAFARI_BROWSER,
+  IE_64BIT_BROWSER,
+  KONQUEROR_BROWSER,
+  MAX_BROWSERS
+} BrowserProcess;
+
+// Template of static data we use for finding browser process information.
+// These entries must match the ordering for MemoryDetails::BrowserProcess.
+static ProcessData g_process_template[MAX_BROWSERS];
+
+MemoryDetails::MemoryDetails()
+  : ui_loop_(NULL) {
+  static const std::wstring google_browser_name =
+      l10n_util::GetString(IDS_PRODUCT_NAME);
+  ProcessData g_process_template[MAX_BROWSERS] = {
+    { google_browser_name.c_str(), L"chrome.exe", },
+    { L"IE", L"iexplore.exe", },
+    { L"Firefox", L"firefox.exe", },
+    { L"Opera", L"opera.exe", },
+    { L"Safari", L"safari.exe", },
+    { L"IE (64bit)", L"iexplore.exe", },
+    { L"Konqueror", L"konqueror.exe", },
+  };
+
+  for (int index = 0; index < arraysize(g_process_template); ++index) {
+    ProcessData process;
+    process.name = g_process_template[index].name;
+    process.process_name = g_process_template[index].process_name;
+    process_data_.push_back(process);
+  }
+}
+
+ProcessData* MemoryDetails::ChromeBrowser() {
+  return &process_data_[CHROME_BROWSER];
+}
+
+void MemoryDetails::CollectProcessData(
+    std::vector<ProcessMemoryInformation> child_info) {
+  DCHECK(MessageLoop::current() ==
+      ChromeThread::GetMessageLoop(ChromeThread::FILE));
+
+  // Clear old data.
+  for (int index = 0; index < arraysize(g_process_template); index++)
+    process_data_[index].processes.clear();
+
+  SYSTEM_INFO system_info;
+  GetNativeSystemInfo(&system_info);
+  bool is_64bit_os =
+      system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
+
+  ScopedHandle snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+  PROCESSENTRY32 process_entry = {sizeof(PROCESSENTRY32)};
+  if (!snapshot.Get()) {
+    LOG(ERROR) << "CreateToolhelp32Snaphot failed: " << GetLastError();
+    return;
+  }
+  if (!::Process32First(snapshot, &process_entry)) {
+    LOG(ERROR) << "Process32First failed: " << GetLastError();
+    return;
+  }
+  do {
+    int pid = process_entry.th32ProcessID;
+    ScopedHandle handle(::OpenProcess(
+        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
+    if (!handle.Get())
+      continue;
+    bool is_64bit_process = false;
+    // IsWow64Process() returns FALSE for a 32bit process on a 32bit OS.
+    // We need to check if the real OS is 64bit.
+    if (is_64bit_os) {
+      BOOL is_wow64 = FALSE;
+      // IsWow64Process() is supported by Windows XP SP2 or later.
+      IsWow64Process(handle, &is_wow64);
+      is_64bit_process = !is_wow64;
+    }
+    for (int index2 = 0; index2 < arraysize(g_process_template); index2++) {
+      if (_wcsicmp(process_data_[index2].process_name.c_str(),
+          process_entry.szExeFile) != 0)
+        continue;
+      if (index2 == IE_BROWSER && is_64bit_process)
+        continue;  // Should use IE_64BIT_BROWSER
+      // Get Memory Information.
+      ProcessMemoryInformation info;
+      info.pid = pid;
+      if (info.pid == GetCurrentProcessId())
+        info.type = ChildProcessInfo::BROWSER_PROCESS;
+      else
+        info.type = ChildProcessInfo::UNKNOWN_PROCESS;
+
+      scoped_ptr<base::ProcessMetrics> metrics;
+      metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
+      metrics->GetCommittedKBytes(&info.committed);
+      metrics->GetWorkingSetKBytes(&info.working_set);
+
+      // Get Version Information.
+      TCHAR name[MAX_PATH];
+      if (index2 == CHROME_BROWSER) {
+        scoped_ptr<FileVersionInfo> version_info(
+            FileVersionInfo::CreateFileVersionInfoForCurrentModule());
+        if (version_info != NULL)
+          info.version = version_info->file_version();
+        // 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 != info.pid)
+            continue;
+          info.titles = child_info[child].titles;
+          info.type = child_info[child].type;
+          break;
+        }
+      } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) {
+        std::wstring str_name(name);
+        scoped_ptr<FileVersionInfo> version_info(
+            FileVersionInfo::CreateFileVersionInfo(str_name));
+        if (version_info != NULL) {
+          info.version = version_info->product_version();
+          info.product_name = version_info->product_name();
+        }
+      }
+
+      // Add the process info to our list.
+      process_data_[index2].processes.push_back(info);
+      break;
+    }
+  } while (::Process32Next(snapshot, &process_entry));
+
+  // Finally return to the browser thread.
+  ui_loop_->PostTask(FROM_HERE,
+      NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
+}
diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.cc b/chrome/browser/renderer_host/render_sandbox_host_linux.cc
index eeb7a98..8ea30ab 100644
--- a/chrome/browser/renderer_host/render_sandbox_host_linux.cc
+++ b/chrome/browser/renderer_host/render_sandbox_host_linux.cc
@@ -295,8 +295,8 @@
   const int child_lifeline_fd = pipefds[0];
   childs_lifeline_fd_ = pipefds[1];
 
-  const pid_t child = fork();
-  if (child == 0) {
+  pid_ = fork();
+  if (pid_ == 0) {
     SandboxIPCProcess handler(child_lifeline_fd, browser_socket);
     handler.Run();
     _exit(0);
diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.h b/chrome/browser/renderer_host/render_sandbox_host_linux.h
index 43fa447..5b38b4c 100644
--- a/chrome/browser/renderer_host/render_sandbox_host_linux.h
+++ b/chrome/browser/renderer_host/render_sandbox_host_linux.h
@@ -18,6 +18,7 @@
   // Get the file descriptor which renderers should be given in order to signal
   // crashes to the browser.
   int GetRendererSocket() const { return renderer_socket_; }
+  pid_t pid() const { return pid_; }
 
  private:
   friend struct DefaultSingletonTraits<RenderSandboxHostLinux>;
@@ -27,6 +28,7 @@
 
   int renderer_socket_;
   int childs_lifeline_fd_;
+  pid_t pid_;
 
   DISALLOW_EVIL_CONSTRUCTORS(RenderSandboxHostLinux);
 };
diff --git a/chrome/browser/resources/about_memory_linux.html b/chrome/browser/resources/about_memory_linux.html
index a06002fa..eeee845 100644
--- a/chrome/browser/resources/about_memory_linux.html
+++ b/chrome/browser/resources/about_memory_linux.html
@@ -268,15 +268,13 @@
 
 table.list#memoryDetails tr:not([class*='firstRow']) > *:nth-child(2),
 table.list#memoryDetails tr:not([class*='firstRow']) > *:nth-child(5),
-table.list#memoryDetails tr.firstRow th:nth-child(2),
-table.list#memoryDetails tr.firstRow th:nth-child(3) {
+table.list#memoryDetails tr.firstRow th:nth-child(2) {
   border-right: 1px solid #b5c6de;
 }
 
 table.list#browserComparison tr:not([class*='firstRow']) > *:nth-child(1),
 table.list#browserComparison tr:not([class*='firstRow']) > *:nth-child(4),
-table.list#browserComparison tr.firstRow th:nth-child(1),
-table.list#browserComparison tr.firstRow th:nth-child(2) {
+table.list#browserComparison tr.firstRow th:nth-child(1) {
   border-right: 1px solid #b5c6de;
 }
 table.list#browserComparison .name {
@@ -426,14 +424,11 @@
           <col class='name' />
           <col class='number' />
           <col class='number' />
-          <col class='number' />
-          <col class='number' />
-          <col class='number' />
         </colgroup>
         <tr class='firstRow doNotFilter'>
           <th>
           </th>
-          <th colspan='3'>
+          <th colspan='2'>
             Memory
             <div class='help'>
               <div>
@@ -446,38 +441,14 @@
                   This is the best indicator of browser memory resource usage.
                 </p>
                 <p>
-                  <strong>Shared:</strong>
-                  Resident memory size that is currently shared with 2 or more processes.
-                  Note: For browsers using multiple processes, if we simply added the shared memory
-                  of each individual process, this value would be inflated. Therefore, this value
-                  is computed as an approximate value for shared memory in each of the browser's
-                  processes. Note also that shared memory varies depending on what other processes
-                  are running on the system, and may be difficult to measure reproducibly.
+                  <strong>Proportional:</strong>
+                  Accounts for each page of memory as a fraction based on the number of
+                  processes that have it mapped. Thus, for each page of memory mapped by two
+                  processes, this sum will count half of the bytes towards each.
+                  Therefore, this number is greater than the private count.
                 </p>
-                <p>
-                  <strong>Total:</strong>
-                  The sum of the private + shared resident memory sizes.
-                </p>
-              </div>
-            </div>
-          </th>
-          <th colspan='2'>
-            Virtual memory
-            <div class='help'>
-              <div>
-                <p>
-                  <strong>Virtual memory</strong>
-                </p>
-                <p>
-                  <strong>Private:</strong>
-                  The resident and paged bytes committed for use by only this process.
-                </p>
-                <p>
-                  <strong>Mapped:</strong>
-                  Total bytes allocated by this process that are mapped into the
-                  view of a section, backed by either system pagefile or file system. This
-                  is primarily memory-mapped files.
-                </p>
+
+                <p><i>(Note that the memory for this tab is not included in the browser totals)</i></p>
               </div>
             </div>
           </th>
@@ -489,18 +460,8 @@
           <th class='name'>
             Private
           </th>
-          </th>
           <th class='number'>
-            Shared
-          </th>
-          <th class='number'>
-            Total
-          </th>
-          <th class='number'>
-            Private
-          </th>
-          <th class='number'>
-            Mapped
+            Proportional
           </th>
         </tr>
         <tr jsselect="browsers">
@@ -510,19 +471,10 @@
             </div>
           </td>
           <td class='number'>
-            <span class='th' jscontent="formatNumber(ws_priv + ws_shareable - ws_shared)"></span><span class='k'>k</span>
+            <span class='th' jscontent="formatNumber(ws_priv)"></span><span class='k'>k</span>
           </td>
           <td class='number'>
-            <span class='th' jscontent="formatNumber(ws_shared / processes)"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jscontent="formatNumber(ws_priv + ws_shareable - ws_shared + (ws_shared / processes))"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jscontent="formatNumber(comm_priv)"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jscontent="formatNumber(comm_map)"></span><span class='k'>k</span>
+            <span class='th' jscontent="formatNumber(ws_shared)"></span><span class='k'>k</span>
           </td>
         </tr>
       </table>
@@ -550,22 +502,15 @@
           <col class='name' />
           <col class='number' />
           <col class='number' />
-          <col class='number' />
-          <col class='number' />
-          <col class='number' />
         </colgroup>
         <tr class='firstRow doNotFilter'>
           <th>
           </th>
           <th>
           </th>
-          <th colspan='3'>
+          <th colspan='2'>
             Memory
           </th>
-          <th colspan='2'>
-            Virtual memory
-          </th>
-
         </tr>
         <tr class='secondRow doNotFilter'>
           <th class='pid'>
@@ -578,16 +523,7 @@
             Private
           </th>
           <th class='number'>
-            Shared
-          </th>
-          <th class='number'>
-            Total
-          </th>
-          <th class='number'>
-            Private
-          </th>
-          <th class='number'>
-            Mapped
+            Proportional
           </th>
         </tr>
 
@@ -601,19 +537,10 @@
             </div>
           </td>
           <td class='number'>
-            <span class='th' jseval="addToSum('tot_ws_priv', $this.ws_priv + $this.ws_shareable - $this.ws_shared)" jscontent="ws_priv + ws_shareable - ws_shared"></span><span class='k'>k</span>
+            <span class='th' jseval="addToSum('tot_ws_priv', $this.ws_priv)" jscontent="formatNumber(ws_priv)"></span><span class='k'>k</span>
           </td>
           <td class='number'>
-            <span class='th' jscontent="ws_shared"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jseval="addToSum('tot_ws_tot', $this.ws_priv + $this.ws_shareable)" jscontent="ws_priv + ws_shareable"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jseval="addToSum('tot_comm_priv', $this.comm_priv)" jscontent="comm_priv"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jseval="addToSum('tot_comm_map', $this.comm_map)" jscontent="comm_map"></span><span class='k'>k</span>
+            <span class='th' jscontent="formatNumber(ws_shared)"></span><span class='k'>k</span>
           </td>
         </tr>
         <tr jsselect="child_data">
@@ -627,19 +554,10 @@
             </div>
           </td>
           <td class='number'>
-            <span class='th' jseval="addToSum('tot_ws_priv', $this.ws_priv + $this.ws_shareable - $this.ws_shared)" jscontent="ws_priv + ws_shareable - ws_shared"></span><span class='k'>k</span>
+            <span class='th' jseval="addToSum('tot_ws_priv', $this.ws_priv)" jscontent="formatNumber(ws_priv)"></span><span class='k'>k</span>
           </td>
           <td class='number'>
-            <span class='th' jscontent="ws_shared"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jseval="addToSum('tot_ws_tot', $this.ws_priv + $this.ws_shareable)" jscontent="ws_priv + ws_shareable"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jseval="addToSum('tot_comm_priv', $this.comm_priv)" jscontent="comm_priv"></span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' jseval="addToSum('tot_comm_map', $this.comm_map)" jscontent="comm_map"></span><span class='k'>k</span>
+            <span class='th' jscontent="formatNumber(ws_shared)"></span><span class='k'>k</span>
           </td>
         </tr>
         <tr class='total doNotFilter'>
@@ -653,27 +571,6 @@
           </td>
           <td class='number'>
           </td>
-          <td class='number'>
-            <span class='th' id="tot_ws_tot">0</span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <span class='th' id="tot_comm_priv">0</span><span class='k'>k</span>
-          </td>
-          <td class='number'>
-            <div class='help'>
-              <div>
-                <p>
-                  This is an approximation. Conceptually, this is the total
-                  amount of in-memory pages for the entire logical Chromium
-                  application, without double counting shared pages (e.g.
-                  mapped
-                  DLLs, SharedMemory bitmaps, etc.) across the browser and
-                  renderers.
-                </p>
-              </div>
-            </div>
-            <span class='th' id="tot_comm_map">0</span><span class='k'>k</span>
-          </td>
         </tr>
 
         <tr class='noResults'>
diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc
index 627d62b..e0fe349 100644
--- a/chrome/browser/zygote_host_linux.cc
+++ b/chrome/browser/zygote_host_linux.cc
@@ -121,6 +121,7 @@
   base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
   CHECK(process != -1) << "Failed to launch zygote process";
 
+  pid_ = process;
   close(fds[1]);
   control_fd_ = fds[0];
 }
diff --git a/chrome/browser/zygote_host_linux.h b/chrome/browser/zygote_host_linux.h
index 94ac92ee..516a18b 100644
--- a/chrome/browser/zygote_host_linux.h
+++ b/chrome/browser/zygote_host_linux.h
@@ -8,6 +8,8 @@
 #include <string>
 #include <vector>
 
+#include <unistd.h>
+
 #include "base/global_descriptors_posix.h"
 #include "base/process.h"
 
@@ -39,12 +41,15 @@
     kCmdDidProcessCrash = 2,  // Check if child process crashed.
   };
 
+  pid_t pid() const { return pid_; }
+
  private:
   friend struct DefaultSingletonTraits<ZygoteHost>;
   ZygoteHost();
   void LaunchZygoteProcess();
 
   int control_fd_;  // the socket to the zygote
+  pid_t pid_;
 };
 
 #endif  // CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 177f6da3..2fcee59 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1464,6 +1464,8 @@
         'browser/login_prompt_mac.mm',
         'browser/login_prompt_win.cc',
         'browser/memory_details.cc',
+        'browser/memory_details_linux.cc',
+        'browser/memory_details_win.cc',
         'browser/memory_details.h',
         'browser/meta_table_helper.cc',
         'browser/meta_table_helper.h',
@@ -2600,7 +2602,6 @@
             'browser/ime_input.cc',
             'browser/importer/ie_importer.cc',
             'browser/jumplist.cc',
-            'browser/memory_details.cc',
             'browser/sandbox_policy.cc',
             'browser/tab_contents/web_drag_source.cc',
             'browser/tab_contents/web_drop_target.cc',
diff --git a/chrome/common/child_process_info.cc b/chrome/common/child_process_info.cc
index 770c9866..f6cb8ed 100644
--- a/chrome/common/child_process_info.cc
+++ b/chrome/common/child_process_info.cc
@@ -48,6 +48,10 @@
       return L"Web Worker";
     case UTILITY_PROCESS:
       return L"Utility";
+    case ZYGOTE_PROCESS:
+      return L"Zygote";
+    case SANDBOX_HELPER_PROCESS:
+      return L"Sandbox helper";
     case UNKNOWN_PROCESS:
       default:
       DCHECK(false) << "Unknown child process type!";
diff --git a/chrome/common/child_process_info.h b/chrome/common/child_process_info.h
index 3d80a1d..fa03c0f7 100644
--- a/chrome/common/child_process_info.h
+++ b/chrome/common/child_process_info.h
@@ -18,6 +18,8 @@
     PLUGIN_PROCESS,
     WORKER_PROCESS,
     UTILITY_PROCESS,
+    ZYGOTE_PROCESS,
+    SANDBOX_HELPER_PROCESS,
     UNKNOWN_PROCESS,
   };
 
diff --git a/chrome/common/temp_scaffolding_stubs.cc b/chrome/common/temp_scaffolding_stubs.cc
index 78e4085..94f3260 100644
--- a/chrome/common/temp_scaffolding_stubs.cc
+++ b/chrome/common/temp_scaffolding_stubs.cc
@@ -210,6 +210,7 @@
 
 //--------------------------------------------------------------------------
 
+#if defined(OS_MACOSX)
 MemoryDetails::MemoryDetails() {
   NOTIMPLEMENTED();
 }
@@ -218,6 +219,7 @@
   NOTIMPLEMENTED();
   OnDetailsAvailable();
 }
+#endif
 
 #if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
 // This should prompt the user if she wants to allow more than one concurrent