Bug 912165 - Remove the Linux-only smaps memory reporters. r=mccr8.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 03 Sep 2013 21:09:15 -0700
changeset 159732 6d93007abf58519fe158a6cf176b5bf6dad61004
parent 159731 71b3fa2eb87d128d642e40175a223ab4ea0e514d
child 159733 aca02ab3f2d39c16cf36fd5b7b77edd884270998
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs912165
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 912165 - Remove the Linux-only smaps memory reporters. r=mccr8.
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/test_aboutmemory.xul
xpcom/base/MapsMemoryReporter.cpp
xpcom/base/MapsMemoryReporter.h
xpcom/base/moz.build
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryReporterManager.cpp
xpcom/build/nsXPComInit.cpp
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -268,97 +268,34 @@ function appendElementWithText(aP, aTagN
   // importantly, it avoids creating a JS-land object for the node, saving
   // memory.
   e.textContent = aText;
   return e;
 }
 
 //---------------------------------------------------------------------------
 
-const kTreeDescriptions = {
-  'explicit' :
+const explicitTreeDescription =
 "This tree covers explicit memory allocations by the application.  It includes \
 \n\n\
 * allocations made at the operating system level (via calls to functions such as \
 VirtualAlloc, vm_allocate, and mmap), \
 \n\n\
 * allocations made at the heap allocation level (via functions such as malloc, \
 calloc, realloc, memalign, operator new, and operator new[]) that have not been \
 explicitly decommitted (i.e. evicted from memory and swap), and \
 \n\n\
 * where possible, the overhead of the heap allocator itself.\
 \n\n\
 It excludes memory that is mapped implicitly such as code and data segments, \
 and thread stacks. \
 \n\n\
 'explicit' is not guaranteed to cover every explicit allocation, but it does cover \
 most (including the entire heap), and therefore it is the single best number to \
-focus on when trying to reduce memory usage.",
-
-  'rss':
-"This tree shows how much space in physical memory each of the process's \
-mappings is currently using (the mapping's 'resident set size', or 'RSS'). \
-This is a good measure of the 'cost' of the mapping, although it does not \
-take into account the fact that shared libraries may be mapped by multiple \
-processes but appear only once in physical memory. \
-\n\n\
-Note that the 'rss' value here might not equal the value for 'resident' \
-under 'Other Measurements' because the two measurements are not taken at \
-exactly the same time.",
-
-  'pss':
-"This tree shows how much space in physical memory can be 'blamed' on this \
-process.  For each mapping, its 'proportional set size' (PSS) is the \
-mapping's resident size divided by the number of processes which use the \
-mapping.  So if a mapping is private to this process, its PSS should equal \
-its RSS.  But if a mapping is shared between three processes, its PSS in each \
-of the processes would be 1/3 its RSS.",
-
-  'size':
-"This tree shows how much virtual addres space each of the process's mappings \
-takes up (a.k.a. the mapping's 'vsize').  A mapping may have a large size but use \
-only a small amount of physical memory; the resident set size of a mapping is \
-a better measure of the mapping's 'cost'. \
-\n\n\
-Note that the 'size' value here might not equal the value for 'vsize' under \
-'Other Measurements' because the two measurements are not taken at exactly \
-the same time.",
-
-  'swap':
-"This tree shows how much space in the swap file each of the process's \
-mappings is currently using. Mappings which are not in the swap file (i.e., \
-nodes which would have a value of 0 in this tree) are omitted."
-};
-
-const kSectionNames = {
-  'explicit': 'Explicit Allocations',
-  'rss':      'Resident Set Size (RSS) Breakdown',
-  'pss':      'Proportional Set Size (PSS) Breakdown',
-  'size':     'Virtual Size Breakdown',
-  'swap':     'Swap Breakdown',
-  'other':    'Other Measurements'
-};
-
-const kSmapsTreeNames    = ['rss',  'pss',  'size',  'swap' ];
-const kSmapsTreePrefixes = ['rss/', 'pss/', 'size/', 'swap/'];
-
-function isExplicitPath(aUnsafePath)
-{
-  return aUnsafePath.startsWith("explicit/");
-}
-
-function isSmapsPath(aUnsafePath)
-{
-  for (let i = 0; i < kSmapsTreePrefixes.length; i++) {
-    if (aUnsafePath.startsWith(kSmapsTreePrefixes[i])) {
-      return true;
-    }
-  }
-  return false;
-}
+focus on when trying to reduce memory usage.";
 
 //---------------------------------------------------------------------------
 
 function appendButton(aP, aTitle, aOnClick, aText, aId)
 {
   let b = appendElementWithText(aP, "button", "", aText);
   b.title = aTitle;
   b.onclick = aOnClick;
@@ -541,18 +478,17 @@ function updateAboutMemoryFromReporters(
 {
   // First, clear the contents of main.  Necessary because
   // updateAboutMemoryFromReporters() might be called more than once due to the
   // "child-memory-reporter-update" observer.
   updateMainAndFooter("", SHOW_FOOTER);
 
   try {
     // Process the reports from the memory reporters.
-    appendAboutMemoryMain(processMemoryReporters, gMgr.hasMozMallocUsableSize,
-                          /* forceShowSmaps = */ false);
+    appendAboutMemoryMain(processMemoryReporters, gMgr.hasMozMallocUsableSize);
 
   } catch (ex) {
     handleException(ex);
   }
 }
 
 // Increment this if the JSON format changes.
 var gCurrentFileFormatVersion = 1;
@@ -571,18 +507,17 @@ function updateAboutMemoryFromJSONObject
                 "data version number missing or doesn't match");
     assertInput(aObj.hasMozMallocUsableSize !== undefined,
                 "missing 'hasMozMallocUsableSize' property");
     assertInput(aObj.reports && aObj.reports instanceof Array,
                 "missing or non-array 'reports' property");
     let process = function(aIgnoreReporter, aIgnoreReport, aHandleReport) {
       processMemoryReportsFromFile(aObj.reports, aIgnoreReport, aHandleReport);
     }
-    appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize,
-                          /* forceShowSmaps = */ true);
+    appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize);
   } catch (ex) {
     handleException(ex);
   }
 }
 
 /**
  * Populate about:memory using the data in the given JSON string.
  *
@@ -760,16 +695,18 @@ DReport.prototype = {
     //   "path": "size/other-files/icon-theme.cache/[r--p]",
     //   "description": "/usr/share/icons/gnome/icon-theme.cache (read-only, not executable, private)"
     //
     //   "path": "size/other-files/icon-theme.cache/[r--p]"
     //   "description": "/usr/share/icons/hicolor/icon-theme.cache (read-only, not executable, private)"
     //
     // In those cases, we just use the description from the first-encountered
     // one, which is what about:memory also does.
+    // (Note: reports with those paths are no longer generated, but allowing
+    // the descriptions to differ seems reasonable.)
   },
 
   merge: function(aJr) {
     this.assertCompatible(aJr.kind, aJr.units);
     this._amount += aJr.amount;
     this._nMerged++;
   },
 
@@ -925,24 +862,20 @@ function PColl()
  * Processes reports (whether from reporters or from a file) and append the
  * main part of the page.
  *
  * @param aProcessReports
  *        Function that extracts the memory reports from the reporters or from
  *        file.
  * @param aHasMozMallocUsableSize
  *        Boolean indicating if moz_malloc_usable_size works.
- * @param aForceShowSmaps
- *        True if we should show the smaps memory reporters even if we're not
- *        in verbose mode.
  */
-function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize,
-                               aForceShowSmaps)
+function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize)
 {
-  let pcollsByProcess = getPCollsByProcess(aProcessReports, aForceShowSmaps);
+  let pcollsByProcess = getPCollsByProcess(aProcessReports);
 
   // Sort the processes.
   let processes = Object.keys(pcollsByProcess);
   processes.sort(function(aProcessA, aProcessB) {
     assert(aProcessA != aProcessB,
            "Elements of Object.keys() should be unique, but " +
            "saw duplicate '" + aProcessA + "' elem.");
 
@@ -993,65 +926,50 @@ function appendAboutMemoryMain(aProcessR
 
 /**
  * This function reads all the memory reports, and puts that data in structures
  * that will be used to generate the page.
  *
  * @param aProcessReports
  *        Function that extracts the memory reports from the reporters or from
  *        file.
- * @param aForceShowSmaps
- *        True if we should show the smaps memory reporters even if we're not
- *        in verbose mode.
  * @return The table of PColls by process.
  */
-function getPCollsByProcess(aProcessReports, aForceShowSmaps)
+function getPCollsByProcess(aProcessReports)
 {
   let pcollsByProcess = {};
 
   // This regexp matches sentences and sentence fragments, i.e. strings that
   // start with a capital letter and ends with a '.'.  (The final sentence may
   // be in parentheses, so a ')' might appear after the '.'.)
   const gSentenceRegExp = /^[A-Z].*\.\)?$/m;
 
-  // Ignore the "smaps" reporter in non-verbose mode unless we're reading from
-  // a file or the clipboard.  (Note that reports from these reporters can
-  // reach here via a "content-child" reporter if they were in a child
-  // process.)
-  //
   // Ignore any "redundant/"-prefixed reporters and reports, which are only
   // used by telemetry.
 
   function ignoreReporter(aName)
   {
-    return (aName === "smaps" && !gVerbose.checked && !aForceShowSmaps) ||
-           aName.startsWith("redundant/");
+    return aName.startsWith("redundant/");
   }
 
   function ignoreReport(aUnsafePath)
   {
-    return (isSmapsPath(aUnsafePath) && !gVerbose.checked && !aForceShowSmaps) ||
-           aUnsafePath.startsWith("redundant/");
+    return aUnsafePath.startsWith("redundant/");
   }
 
   function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
                         aDescription, aPresence)
   {
-    if (isExplicitPath(aUnsafePath)) {
+    if (aUnsafePath.startsWith("explicit/")) {
       assertInput(aKind === KIND_HEAP || aKind === KIND_NONHEAP,
                   "bad explicit kind");
       assertInput(aUnits === UNITS_BYTES, "bad explicit units");
       assertInput(gSentenceRegExp.test(aDescription),
                   "non-sentence explicit description");
 
-    } else if (isSmapsPath(aUnsafePath)) {
-      assertInput(aKind === KIND_NONHEAP, "bad smaps kind");
-      assertInput(aUnits === UNITS_BYTES, "bad smaps units");
-      assertInput(aDescription !== "", "empty smaps description");
-
     } else {
       assertInput(gSentenceRegExp.test(aDescription),
                   "non-sentence other description");
     }
 
     assert(aPresence === undefined ||
            aPresence == DReport.PRESENT_IN_FIRST_ONLY ||
            aPresence == DReport.PRESENT_IN_SECOND_ONLY);
@@ -1429,49 +1347,31 @@ function appendProcessAboutMemoryElement
 
   // We'll fill this in later.
   let warningsDiv = appendElement(aP, "div", "accuracyWarning");
 
   // The explicit tree.
   let hasKnownHeapAllocated;
   {
     let treeName = "explicit";
-    let pre = appendSectionHeader(aP, kSectionNames[treeName]);
+    let pre = appendSectionHeader(aP, "Explicit Allocations");
     let t = aTrees[treeName];
     if (t) {
       fillInTree(t);
       hasKnownHeapAllocated =
         aDegenerates &&
         addHeapUnclassifiedNode(t, aDegenerates["heap-allocated"], aHeapTotal);
       sortTreeAndInsertAggregateNodes(t._amount, t);
-      t._description = kTreeDescriptions[treeName];
+      t._description = explicitTreeDescription;
       appendTreeElements(pre, t, aProcess, "");
       delete aTrees[treeName];
     }
     appendTextNode(aP, "\n");  // gives nice spacing when we cut and paste
   }
 
-  // The smaps trees, which are only present in aTrees in verbose mode or when
-  // we're reading from a file or the clipboard.
-  kSmapsTreeNames.forEach(function(aTreeName) {
-    // |t| will be undefined if we don't have any reports for the given
-    // unsafePath.
-    let t = aTrees[aTreeName];
-    if (t) {
-      let pre = appendSectionHeader(aP, kSectionNames[aTreeName]);
-      fillInTree(t);
-      sortTreeAndInsertAggregateNodes(t._amount, t);
-      t._description = kTreeDescriptions[aTreeName];
-      t._hideKids = true;   // smaps trees are always initially collapsed
-      appendTreeElements(pre, t, aProcess, "");
-      delete aTrees[aTreeName];
-      appendTextNode(aP, "\n");  // gives nice spacing when we cut and paste
-    }
-  });
-
   // Fill in and sort all the non-degenerate other trees.
   let otherTrees = [];
   for (let unsafeName in aTrees) {
     let t = aTrees[unsafeName];
     assert(!t._isDegenerate, "tree is degenerate");
     fillInTree(t);
     sortTreeAndInsertAggregateNodes(t._amount, t);
     otherTrees.push(t);
@@ -1489,17 +1389,17 @@ function appendProcessAboutMemoryElement
     if (length > maxStringLength) {
       maxStringLength = length;
     }
     otherDegenerates.push(t);
   }
   otherDegenerates.sort(TreeNode.compareUnsafeNames);
 
   // Now generate the elements, putting non-degenerate trees first.
-  let pre = appendSectionHeader(aP, kSectionNames['other']);
+  let pre = appendSectionHeader(aP, "Other Measurements");
   for (let i = 0; i < otherTrees.length; i++) {
     let t = otherTrees[i];
     appendTreeElements(pre, t, aProcess, "");
     appendTextNode(pre, "\n");  // blank lines after non-degenerate trees
   }
   for (let i = 0; i < otherDegenerates.length; i++) {
     let t = otherDegenerates[i];
     let padText = pad("", maxStringLength - t.toString().length, ' ');
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -111,31 +111,16 @@
         }
         f("other3",           OTHER,   COUNT, 777);
         f("other2",           OTHER,   BYTES, 222 * MB);
         f("perc2",            OTHER,   PERCENTAGE, 10000);
         f("perc1",            OTHER,   PERCENTAGE, 4567);
         f("compartments/user/https:\\\\very-long-url.com\\very-long\\oh-so-long\\really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789", OTHER, COUNT, 1);
       }
     },
-    { name: "smaps",
-      collectReports: function(aCbObj, aClosure) {
-        // The amounts are given in pages, so multiply here by 4kb.
-        function f(aP, aA) {
-          aCbObj.callback("", aP, NONHEAP, BYTES, aA * 4 * KB, "Desc.", aClosure);
-        }
-        f("size/a",      24);
-        f("swap/a",       1);
-        f("swap/a",       2);
-        f("size/a",       19);
-        f("swap/b/c",     10);
-        f("rss/a",        42);
-        f("pss/a",        43);
-      }
-    },
     { name: "compartments",
       collectReports: function(aCbObj, aClosure) {
         function f(aP) {
           aCbObj.callback("", aP, OTHER, COUNT, 1, "Desc.", aClosure);
         }
         f("compartments/user/bar");
         f("compartments/system/bar");
       }
@@ -184,22 +169,16 @@
         f("2nd", "explicit/a/b/c",  HEAP,    BYTES,  1 * MB); // dup: merge
         f("2nd", "explicit/a/b/c",  HEAP,    BYTES,  1 * MB); // dup: merge
         f("2nd", "explicit/flip\\the\\backslashes",
                                     HEAP,    BYTES,200 * MB);
         f("2nd", "explicit/compartment(compartment-url)",
                                     HEAP,    BYTES,200 * MB);
         f("2nd", "other0",          OTHER,   BYTES,666 * MB);
         f("2nd", "other1",          OTHER,   BYTES,111 * MB);
-        // If the "smaps" reporter is in a child process it'll be passed to
-        // the main process under a different name.  The fact that we skip
-        // the "smaps" reporter in the main process won't cause these
-        // to be skipped;  the report-level skipping will be hit instead.
-        f("2nd", "size/e",          NONHEAP, BYTES,24*4*KB);
-        f("2nd", "size/f",          NONHEAP, BYTES,24*4*KB);
         f("2nd", "redundant/blah",  NONHEAP, BYTES,24*4*KB); // ignored!
 
         // Check that we can handle "heap-allocated" not being present.
         f("3rd", "explicit/a/b",    HEAP,    BYTES,333 * MB);
         f("3rd", "explicit/a/c",    HEAP,    BYTES,444 * MB);
         f("3rd", "other1",          OTHER,   BYTES,  1 * MB);
         f("3rd", "resident",        OTHER,   BYTES,100 * MB);
 
@@ -432,32 +411,16 @@ 653,876,224 B (100.0%) -- explicit\n\
 ├───15,728,640 B (02.41%) -- g\n\
 │   ├───6,291,456 B (00.96%) ── a\n\
 │   ├───5,242,880 B (00.80%) ── b\n\
 │   └───4,194,304 B (00.64%) ── other\n\
 ├───11,534,336 B (01.76%) ── heap-unclassified\n\
 ├──────510,976 B (00.08%) ── d\n\
 └──────102,400 B (00.02%) ── e\n\
 \n\
-Resident Set Size (RSS) Breakdown\n\
-\n\
-172,032 B (100.0%) ++ rss\n\
-\n\
-Proportional Set Size (PSS) Breakdown\n\
-\n\
-176,128 B (100.0%) ++ pss\n\
-\n\
-Virtual Size Breakdown\n\
-\n\
-176,128 B (100.0%) ++ size\n\
-\n\
-Swap Breakdown\n\
-\n\
-53,248 B (100.0%) ++ swap\n\
-\n\
 Other Measurements\n\
 \n\
 5 (100.0%) -- compartments\n\
 ├──3 (60.00%) -- user\n\
 │  ├──1 (20.00%) ── bar\n\
 │  ├──1 (20.00%) ── foo\n\
 │  └──1 (20.00%) ── https://very-long-url.com/very-long/oh-so-long/really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789\n\
 └──2 (40.00%) -- system\n\
@@ -544,20 +507,16 @@ 2nd\n\
 Explicit Allocations\n\
 \n\
 1,048,576,000 B (100.0%) -- explicit\n\
 ├────523,239,424 B (49.90%) ── a/b/c [3]\n\
 ├────209,715,200 B (20.00%) ── compartment(compartment-url)\n\
 ├────209,715,200 B (20.00%) ── flip/the/backslashes\n\
 └────105,906,176 B (10.10%) ── heap-unclassified\n\
 \n\
-Virtual Size Breakdown\n\
-\n\
-196,608 B (100.0%) ++ size\n\
-\n\
 Other Measurements\n\
 \n\
 1,048,576,000 B ── heap-allocated\n\
   104,857,600 B ── heap-unallocated\n\
   698,351,616 B ── other0\n\
   116,391,936 B ── other1\n\
 \n\
 5th\n\
deleted file mode 100644
--- a/xpcom/base/MapsMemoryReporter.cpp
+++ /dev/null
@@ -1,578 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/Util.h"
-
-#include "mozilla/MapsMemoryReporter.h"
-#include "nsIMemoryReporter.h"
-#include "nsString.h"
-#include "nsCOMPtr.h"
-#include "nsTHashtable.h"
-#include "nsHashKeys.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/unused.h"
-#include <stdio.h>
-
-namespace mozilla {
-namespace MapsMemoryReporter {
-
-#if !defined(XP_LINUX)
-#error "This doesn't have a prayer of working if we're not on Linux."
-#endif
-
-// mozillaLibraries is a list of all the shared libraries we build.  This list
-// is used for determining whether a library is a "Mozilla library" or a
-// "third-party library".  But even if this list is missing items, about:memory
-// will identify a library in the same directory as libxul.so as a "Mozilla
-// library".
-const char* mozillaLibraries[] =
-{
-  "libfreebl3.so",
-  "libmozalloc.so",
-  "libmozsqlite3.so",
-  "libnspr4.so",
-  "libnss3.so",
-  "libnssckbi.so",
-  "libnssdbm3.so",
-  "libnssutil3.so",
-  "libplc4.so",
-  "libplds4.so",
-  "libsmime3.so",
-  "libsoftokn3.so",
-  "libssl3.so",
-  "libxpcom.so",
-  "libxul.so"
-};
-
-namespace {
-
-bool EndsWithLiteral(const nsCString &aHaystack, const char *aNeedle)
-{
-  int32_t idx = aHaystack.RFind(aNeedle);
-  if (idx == -1) {
-    return false;
-  }
-
-  return idx + strlen(aNeedle) == aHaystack.Length();
-}
-
-void GetDirname(const nsCString &aPath, nsACString &aOut)
-{
-  int32_t idx = aPath.RFind("/");
-  if (idx == -1) {
-    aOut.Truncate();
-  }
-  else {
-    aOut.Assign(Substring(aPath, 0, idx));
-  }
-}
-
-void GetBasename(const nsCString &aPath, nsACString &aOut)
-{
-  nsCString out;
-  int32_t idx = aPath.RFind("/");
-  if (idx == -1) {
-    out.Assign(aPath);
-  }
-  else {
-    out.Assign(Substring(aPath, idx + 1));
-  }
-
-  // On Android, some entries in /dev/ashmem end with "(deleted)" (e.g.
-  // "/dev/ashmem/libxul.so(deleted)").  We don't care about this modifier, so
-  // cut it off when getting the entry's basename.
-  if (EndsWithLiteral(out, "(deleted)")) {
-    out.Assign(Substring(out, 0, out.RFind("(deleted)")));
-  }
-  out.StripChars(" ");
-
-  aOut.Assign(out);
-}
-
-// MapsReporter::CollectReports uses this stuct to keep track of whether it's
-// seen a mapping under 'rss', 'pss', 'size', and 'swap'.
-struct CategoriesSeen {
-  CategoriesSeen() :
-    mSeenRss(false),
-    mSeenPss(false),
-    mSeenSize(false),
-    mSeenSwap(false)
-  {
-  }
-
-  bool mSeenRss;
-  bool mSeenPss;
-  bool mSeenSize;
-  bool mSeenSwap;
-};
-
-} // anonymous namespace
-
-class MapsReporter MOZ_FINAL : public nsIMemoryReporter
-{
-public:
-  MapsReporter();
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  NS_IMETHOD GetName(nsACString &aName)
-  {
-      aName.AssignLiteral("smaps");
-      return NS_OK;
-  }
-
-  NS_IMETHOD
-  CollectReports(nsIMemoryReporterCallback *aCb,
-                 nsISupports *aClosure);
-
-private:
-  // Search through /proc/self/maps for libxul.so, and set mLibxulDir to the
-  // the directory containing libxul.
-  nsresult FindLibxul();
-
-  nsresult
-  ParseMapping(FILE *aFile,
-               nsIMemoryReporterCallback *aCb,
-               nsISupports *aClosure,
-               CategoriesSeen *aCategoriesSeen);
-
-  void
-  GetReporterNameAndDescription(const char *aPath,
-                                const char *aPermissions,
-                                nsACString &aName,
-                                nsACString &aDesc);
-
-  nsresult
-  ParseMapBody(FILE *aFile,
-               const nsACString &aName,
-               const nsACString &aDescription,
-               nsIMemoryReporterCallback *aCb,
-               nsISupports *aClosure,
-               CategoriesSeen *aCategoriesSeen);
-
-  bool mSearchedForLibxul;
-  nsCString mLibxulDir;
-  nsTHashtable<nsCStringHashKey> mMozillaLibraries;
-};
-
-NS_IMPL_ISUPPORTS1(MapsReporter, nsIMemoryReporter)
-
-MapsReporter::MapsReporter()
-  : mSearchedForLibxul(false)
-  , mMozillaLibraries(ArrayLength(mozillaLibraries))
-{
-  const uint32_t len = ArrayLength(mozillaLibraries);
-  for (uint32_t i = 0; i < len; i++) {
-    nsAutoCString str;
-    str.Assign(mozillaLibraries[i]);
-    mMozillaLibraries.PutEntry(str);
-  }
-}
-
-NS_IMETHODIMP
-MapsReporter::CollectReports(nsIMemoryReporterCallback *aCb,
-                             nsISupports *aClosure)
-{
-  CategoriesSeen categoriesSeen;
-
-  FILE *f = fopen("/proc/self/smaps", "r");
-  if (!f)
-    return NS_ERROR_FAILURE;
-
-  while (true) {
-    nsresult rv = ParseMapping(f, aCb, aClosure, &categoriesSeen);
-    if (NS_FAILED(rv))
-      break;
-  }
-
-  fclose(f);
-
-  // For sure we should have created some node under 'rss' and
-  // 'size'; otherwise we're probably not reading smaps correctly.  If we
-  // didn't create a node under 'swap', create one here so about:memory
-  // knows to create an empty 'swap' tree;  it needs a 'total' child because
-  // about:memory expects at least one report whose path begins with 'swap/'.
-
-  NS_ASSERTION(categoriesSeen.mSeenSize, "Didn't create a size node?");
-  NS_ASSERTION(categoriesSeen.mSeenRss, "Didn't create a rss node?");
-  NS_ASSERTION(categoriesSeen.mSeenPss, "Didn't create a pss node?");
-  if (!categoriesSeen.mSeenSwap) {
-    nsresult rv;
-    rv = aCb->Callback(NS_LITERAL_CSTRING(""),
-                       NS_LITERAL_CSTRING("swap/total"),
-                       nsIMemoryReporter::KIND_NONHEAP,
-                       nsIMemoryReporter::UNITS_BYTES,
-                       0,
-                       NS_LITERAL_CSTRING("This process uses no swap space."),
-                       aClosure);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return NS_OK;
-}
-
-nsresult
-MapsReporter::FindLibxul()
-{
-  if (mSearchedForLibxul)
-    return NS_OK;
-
-  mSearchedForLibxul = true;
-
-  mLibxulDir.Truncate();
-
-  // Note that we're scanning /proc/self/*maps*, not smaps, here.
-  FILE *f = fopen("/proc/self/maps", "r");
-  if (!f) {
-    return NS_ERROR_FAILURE;
-  }
-
-  while (true) {
-    // Skip any number of non-slash characters, then capture starting with the
-    // slash to the newline.  This is the path part of /proc/self/maps.
-    char path[1025];
-    int numRead = fscanf(f, "%*[^/]%1024[^\n]", path);
-    if (numRead != 1) {
-      break;
-    }
-
-    nsAutoCString pathStr;
-    pathStr.Append(path);
-
-    nsAutoCString basename;
-    GetBasename(pathStr, basename);
-
-    if (basename.EqualsLiteral("libxul.so")) {
-      GetDirname(pathStr, mLibxulDir);
-      break;
-    }
-  }
-
-  fclose(f);
-  return mLibxulDir.IsEmpty() ? NS_ERROR_FAILURE : NS_OK;
-}
-
-nsresult
-MapsReporter::ParseMapping(
-  FILE *aFile,
-  nsIMemoryReporterCallback *aCb,
-  nsISupports *aClosure,
-  CategoriesSeen *aCategoriesSeen)
-{
-  // We need to use native types in order to get good warnings from fscanf, so
-  // let's make sure that the native types have the sizes we expect.
-  static_assert(sizeof(long long) == sizeof(int64_t),
-                "size of (long long) is expected to match (int64_t)");
-  static_assert(sizeof(int) == sizeof(int32_t),
-                "size of (int) is expected to match (int32_t)");
-
-  // Don't bail if FindLibxul fails.  We can still gather meaningful stats
-  // here.
-  FindLibxul();
-
-  // The first line of an entry in /proc/self/smaps looks just like an entry
-  // in /proc/maps:
-  //
-  //   address           perms offset  dev   inode  pathname
-  //   02366000-025d8000 rw-p 00000000 00:00 0      [heap]
-
-  const int argCount = 8;
-
-  unsigned long long addrStart, addrEnd;
-  char perms[5];
-  unsigned long long offset;
-  // The 2.6 and 3.0 kernels allocate 12 bits for the major device number and
-  // 20 bits for the minor device number.  Future kernels might allocate more.
-  // 64 bits ought to be enough for anybody.
-  char devMajor[17];
-  char devMinor[17];
-  unsigned int inode;
-  char path[1025];
-
-  // A path might not be present on this line; set it to the empty string.
-  path[0] = '\0';
-
-  // This is a bit tricky.  Whitespace in a scanf pattern matches *any*
-  // whitespace, including newlines.  We want this pattern to match a line
-  // with or without a path, but we don't want to look to a new line for the
-  // path.  Thus we have %u%1024[^\n] at the end of the pattern.  This will
-  // capture into the path some leading whitespace, which we'll later trim off.
-  int numRead = fscanf(aFile,
-                       "%llx-%llx %4s %llx "
-                       "%16[0-9a-fA-F]:%16[0-9a-fA-F] %u%1024[^\n]",
-                       &addrStart, &addrEnd, perms, &offset, devMajor,
-                       devMinor, &inode, path);
-
-  // Eat up any whitespace at the end of this line, including the newline.
-  unused << fscanf(aFile, " ");
-
-  // We might or might not have a path, but the rest of the arguments should be
-  // there.
-  if (numRead != argCount && numRead != argCount - 1) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsAutoCString name, description;
-  GetReporterNameAndDescription(path, perms, name, description);
-
-  while (true) {
-    nsresult rv = ParseMapBody(aFile, name, description, aCb,
-                               aClosure, aCategoriesSeen);
-    if (NS_FAILED(rv))
-      break;
-  }
-
-  return NS_OK;
-}
-
-static bool
-IsAnonymous(const nsACString &aName)
-{
-  // Recent kernels (e.g. 3.5) have multiple [stack:nnnn] entries, where |nnnn|
-  // is a thread ID.  However, [stack:nnnn] entries count both stack memory
-  // *and* anonymous memory because the kernel only knows about the start of
-  // each thread stack, not its end.  So we treat such entries as anonymous
-  // memory instead of stack.  This is consistent with older kernels that don't
-  // even show [stack:nnnn] entries.
-  return aName.IsEmpty() ||
-         StringBeginsWith(aName, NS_LITERAL_CSTRING("[stack:"));
-}
-
-void
-MapsReporter::GetReporterNameAndDescription(
-  const char *aPath,
-  const char *aPerms,
-  nsACString &aName,
-  nsACString &aDesc)
-{
-  aName.Truncate();
-  aDesc.Truncate();
-
-  // If aPath points to a file, we have its absolute path, plus some
-  // whitespace.  Truncate this to its basename, and put the absolute path in
-  // the description.
-  nsAutoCString absPath;
-  absPath.Append(aPath);
-  absPath.StripChars(" ");
-
-  nsAutoCString basename;
-  GetBasename(absPath, basename);
-
-  if (basename.EqualsLiteral("[heap]")) {
-    aName.Append("anonymous/anonymous, within brk()");
-    aDesc.Append("Memory in anonymous mappings within the boundaries "
-                 "defined by brk() / sbrk().  This is likely to be just "
-                 "a portion of the application's heap; the remainder "
-                 "lives in other anonymous mappings. This node corresponds to "
-                 "'[heap]' in /proc/self/smaps.");
-  }
-  else if (basename.EqualsLiteral("[stack]")) {
-    aName.Append("main thread's stack");
-    aDesc.Append("The stack size of the process's main thread.  This node "
-                 "corresponds to '[stack]' in /proc/self/smaps.");
-  }
-  else if (basename.EqualsLiteral("[vdso]")) {
-    aName.Append("vdso");
-    aDesc.Append("The virtual dynamically-linked shared object, also known as "
-                 "the 'vsyscall page'. This is a memory region mapped by the "
-                 "operating system for the purpose of allowing processes to "
-                 "perform some privileged actions without the overhead of a "
-                 "syscall.");
-  }
-  else if (!IsAnonymous(basename)) {
-    nsAutoCString dirname;
-    GetDirname(absPath, dirname);
-
-    // Hack: A file is a shared library if the basename contains ".so" and its
-    // dirname contains "/lib", or if the basename ends with ".so".
-    if (EndsWithLiteral(basename, ".so") ||
-        (basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) {
-      aName.Append("shared-libraries/");
-      if ((!mLibxulDir.IsEmpty() && dirname.Equals(mLibxulDir)) ||
-          mMozillaLibraries.Contains(basename)) {
-        aName.Append("shared-libraries-mozilla/");
-      }
-      else {
-        aName.Append("shared-libraries-other/");
-      }
-    }
-    else {
-      aName.Append("other-files/");
-      if (EndsWithLiteral(basename, ".xpi")) {
-        aName.Append("extensions/");
-      }
-      else if (dirname.Find("/fontconfig") != -1) {
-        aName.Append("fontconfig/");
-      }
-    }
-
-    aName.Append(basename);
-    aDesc.Append(absPath);
-  }
-  else {
-    aName.Append("anonymous/anonymous, outside brk()");
-    aDesc.Append("Memory in anonymous mappings outside the boundaries defined "
-                 "by brk() / sbrk().");
-  }
-
-  aName.Append("/[");
-  aName.Append(aPerms);
-  aName.Append("]");
-
-  // Modify the description to include an explanation of the permissions.
-  aDesc.Append(" (");
-  if (strstr(aPerms, "rw")) {
-    aDesc.Append("read/write, ");
-  }
-  else if (strchr(aPerms, 'r')) {
-    aDesc.Append("read-only, ");
-  }
-  else if (strchr(aPerms, 'w')) {
-    aDesc.Append("write-only, ");
-  }
-  else {
-    aDesc.Append("not readable, not writable, ");
-  }
-
-  if (strchr(aPerms, 'x')) {
-    aDesc.Append("executable, ");
-  }
-  else {
-    aDesc.Append("not executable, ");
-  }
-
-  if (strchr(aPerms, 's')) {
-    aDesc.Append("shared");
-  }
-  else if (strchr(aPerms, 'p')) {
-    aDesc.Append("private");
-  }
-  else {
-    aDesc.Append("not shared or private??");
-  }
-  aDesc.Append(")");
-}
-
-nsresult
-MapsReporter::ParseMapBody(
-  FILE *aFile,
-  const nsACString &aName,
-  const nsACString &aDescription,
-  nsIMemoryReporterCallback *aCb,
-  nsISupports *aClosure,
-  CategoriesSeen *aCategoriesSeen)
-{
-  static_assert(sizeof(long long) == sizeof(int64_t),
-                "size of (long long) is expected to match (int64_t)");
-
-  const int argCount = 2;
-
-  char desc[1025];
-  unsigned long long size;
-  if (fscanf(aFile, "%1024[a-zA-Z_]: %llu kB\n",
-             desc, &size) != argCount) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // Don't report nodes with size 0.
-  if (size == 0)
-    return NS_OK;
-
-  const char* category;
-  if (strcmp(desc, "Size") == 0) {
-    category = "size";
-    aCategoriesSeen->mSeenSize = true;
-  }
-  else if (strcmp(desc, "Rss") == 0) {
-    category = "rss";
-    aCategoriesSeen->mSeenRss = true;
-  }
-  else if (strcmp(desc, "Pss") == 0) {
-    category = "pss";
-    aCategoriesSeen->mSeenPss = true;
-  }
-  else if (strcmp(desc, "Swap") == 0) {
-    category = "swap";
-    aCategoriesSeen->mSeenSwap = true;
-  }
-  else {
-    // Don't report this category.
-    return NS_OK;
-  }
-
-  nsAutoCString path;
-  path.Append(category);
-  path.Append("/");
-  path.Append(aName);
-
-  nsresult rv;
-  rv = aCb->Callback(NS_LITERAL_CSTRING(""),
-                     path,
-                     nsIMemoryReporter::KIND_NONHEAP,
-                     nsIMemoryReporter::UNITS_BYTES,
-                     int64_t(size) * 1024, // convert from kB to bytes
-                     aDescription, aClosure);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-class ResidentUniqueReporter MOZ_FINAL : public MemoryUniReporter
-{
-public:
-  ResidentUniqueReporter()
-    : MemoryUniReporter("resident-unique", KIND_OTHER, UNITS_BYTES,
-"Memory mapped by the process that is present in physical memory and not "
-"shared with any other processes.  This is also known as the process's unique "
-"set size (USS).  This is the amount of RAM we'd expect to be freed if we "
-"closed this process.")
-  {}
-
-private:
-  NS_IMETHOD GetAmount(int64_t *aAmount)
-  {
-    // You might be tempted to calculate USS by subtracting the "shared" value
-    // from the "resident" value in /proc/<pid>/statm.  But at least on Linux,
-    // statm's "shared" value actually counts pages backed by files, which has
-    // little to do with whether the pages are actually shared.  smaps on the
-    // other hand appears to give us the correct information.
-    //
-    // We could calculate this data within the smaps reporter, but the overhead
-    // of the smaps reporter is considerable (we don't even run the smaps
-    // reporter in normal about:memory operation).  Hopefully this
-    // implementation is fast enough not to matter.
-
-    *aAmount = 0;
-
-    FILE *f = fopen("/proc/self/smaps", "r");
-    NS_ENSURE_STATE(f);
-
-    int64_t total = 0;
-    char line[256];
-    while (fgets(line, sizeof(line), f)) {
-      long long val = 0;
-      if (sscanf(line, "Private_Dirty: %lld kB", &val) == 1 ||
-          sscanf(line, "Private_Clean: %lld kB", &val) == 1) {
-        total += val * 1024; // convert from kB to bytes
-      }
-    }
-    *aAmount = total;
-
-    fclose(f);
-    return NS_OK;
-  }
-};
-
-void Init()
-{
-  nsCOMPtr<nsIMemoryReporter> reporter = new MapsReporter();
-  NS_RegisterMemoryReporter(reporter);
-
-  NS_RegisterMemoryReporter(new ResidentUniqueReporter());
-}
-
-} // namespace MapsMemoryReporter
-} // namespace mozilla
deleted file mode 100644
--- a/xpcom/base/MapsMemoryReporter.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 ci et: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_MapsMemoryReporter_h_
-#define mozilla_MapsMemoryReporter_h_
-
-namespace mozilla {
-namespace MapsMemoryReporter {
-
-// This only works on Linux, but to make callers' lives easier, we stub out
-// empty functions on other platforms.
-
-#if defined(XP_LINUX)
-  void Init();
-#else
-  void Init() {}
-#endif
-
-} // namespace MapsMemoryReporter
-} // namespace mozilla
-
-#endif
--- a/xpcom/base/moz.build
+++ b/xpcom/base/moz.build
@@ -70,17 +70,16 @@ if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS += [
         'nsWindowsHelpers.h',
     ]
 
 EXPORTS.mozilla += [
     'AvailableMemoryTracker.h',
     'ClearOnShutdown.h',
     'CycleCollectedJSRuntime.h',
-    'MapsMemoryReporter.h',
     'StackWalk.h',
     'StaticMutex.h',
     'StaticPtr.h',
     'VisualEventTracer.h',
     'nsMemoryInfoDumper.h',
 ]
 
 CPP_SOURCES += [
@@ -103,21 +102,16 @@ CPP_SOURCES += [
     'nsSecurityConsoleMessage.cpp',
     'nsStackWalk.cpp',
     'nsSystemInfo.cpp',
     'nsTraceRefcntImpl.cpp',
     'nsUUIDGenerator.cpp',
     'nsVersionComparatorImpl.cpp',
 ]
 
-if CONFIG['OS_ARCH'] == 'Linux':
-    CPP_SOURCES += [
-        'MapsMemoryReporter.cpp',
-    ]
-
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     CPP_SOURCES += [
         'nsMacUtilsImpl.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     CPP_SOURCES += [
         'nsCrashOnException.cpp',
     ]
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -138,21 +138,16 @@ interface nsIMemoryReporterCallback : ns
  * - There must be an "explicit" tree.  It represents non-overlapping
  *   regions of memory that have been explicitly allocated with an
  *   OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a
  *   heap-level allocation (e.g. malloc/calloc/operator new).  Reporters
  *   in this tree must have kind HEAP or NONHEAP, units BYTES, and a
  *   description that is a sentence (i.e. starts with a capital letter and
  *   ends with a period, or similar).
  *
- * - The "size", "rss", "pss" and "swap" trees are optional.  They
- *   represent regions of virtual memory that the process has mapped.
- *   Reporters in this category must have kind NONHEAP, units BYTES, and a
- *   non-empty description.
- *
  * - The "redundant" tree is optional, and can be used for reports that are
  *   redundant w.r.t. other reports.  These are useful for telemetry, and are
  *   not shown in about:memory.  Reports in this tree are entirely
  *   unconstrained.
  *
  * - All other reports are unconstrained except that they must have a
  *   description that is a sentence.
  */
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -68,16 +68,59 @@ static nsresult GetResident(int64_t* aN)
     return GetProcSelfStatmField(1, aN);
 }
 
 static nsresult GetResidentFast(int64_t* aN)
 {
     return GetResident(aN);
 }
 
+#define HAVE_RESIDENT_UNIQUE_REPORTER
+class ResidentUniqueReporter MOZ_FINAL : public MemoryUniReporter
+{
+public:
+  ResidentUniqueReporter()
+    : MemoryUniReporter("resident-unique", KIND_OTHER, UNITS_BYTES,
+"Memory mapped by the process that is present in physical memory and not "
+"shared with any other processes.  This is also known as the process's unique "
+"set size (USS).  This is the amount of RAM we'd expect to be freed if we "
+"closed this process.")
+  {}
+
+private:
+  NS_IMETHOD GetAmount(int64_t *aAmount)
+  {
+    // You might be tempted to calculate USS by subtracting the "shared" value
+    // from the "resident" value in /proc/<pid>/statm.  But at least on Linux,
+    // statm's "shared" value actually counts pages backed by files, which has
+    // little to do with whether the pages are actually shared.
+    // /proc/self/smaps on the other hand appears to give us the correct
+    // information.
+
+    *aAmount = 0;
+
+    FILE *f = fopen("/proc/self/smaps", "r");
+    NS_ENSURE_STATE(f);
+
+    int64_t total = 0;
+    char line[256];
+    while (fgets(line, sizeof(line), f)) {
+      long long val = 0;
+      if (sscanf(line, "Private_Dirty: %lld kB", &val) == 1 ||
+          sscanf(line, "Private_Clean: %lld kB", &val) == 1) {
+        total += val * 1024; // convert from kB to bytes
+      }
+    }
+    *aAmount = total;
+
+    fclose(f);
+    return NS_OK;
+  }
+};
+
 #elif defined(__DragonFly__) || defined(__FreeBSD__) \
     || defined(__NetBSD__) || defined(__OpenBSD__)
 
 #include <sys/param.h>
 #include <sys/sysctl.h>
 #if defined(__DragonFly__) || defined(__FreeBSD__)
 #include <sys/user.h>
 #endif
@@ -713,16 +756,20 @@ nsMemoryReporterManager::Init()
 #endif
 
 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
     RegisterReporter(new VsizeReporter);
     RegisterReporter(new ResidentReporter);
     RegisterReporter(new ResidentFastReporter);
 #endif
 
+#ifdef HAVE_RESIDENT_UNIQUE_REPORTER
+    RegisterReporter(new ResidentUniqueReporter);
+#endif
+
 #ifdef HAVE_PAGE_FAULT_REPORTERS
     RegisterReporter(new PageFaultsSoftReporter);
     RegisterReporter(new PageFaultsHardReporter);
 #endif
 
 #ifdef HAVE_PRIVATE_REPORTER
     RegisterReporter(new PrivateReporter);
 #endif
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -113,17 +113,16 @@ extern nsresult nsStringInputStreamConst
 
 #include "mozilla/scache/StartupCache.h"
 
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/message_loop.h"
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
-#include "mozilla/MapsMemoryReporter.h"
 #include "mozilla/AvailableMemoryTracker.h"
 #include "mozilla/ClearOnShutdown.h"
 
 #ifdef MOZ_VISUAL_EVENT_TRACER
 #include "mozilla/VisualEventTracer.h"
 #endif
 
 #include "GeckoProfiler.h"
@@ -569,18 +568,16 @@ NS_InitXPCOM2(nsIServiceManager* *result
     // Notify observers of xpcom autoregistration start
     NS_CreateServicesFromCategory(NS_XPCOM_STARTUP_CATEGORY, 
                                   nullptr,
                                   NS_XPCOM_STARTUP_OBSERVER_ID);
 #ifdef XP_WIN
     CreateAnonTempFileRemover();
 #endif
 
-    mozilla::MapsMemoryReporter::Init();
-
     // The memory reporter manager is up and running -- register a reporter for
     // ICU's memory usage.
     NS_RegisterMemoryReporter(new ICUReporter());
 
     mozilla::Telemetry::Init();
 
     mozilla::HangMonitor::Startup();