Bug 664486 - Add page fault counts to about:memory on Linux and Mac. r=njn, sr=bz
authorJustin Lebar <justin.lebar@gmail.com>
Thu, 16 Jun 2011 14:34:09 -0400
changeset 71909 012e21b5705736afad47fca86f7925faff9bdf0d
parent 71908 f29f165edae601ef78119356954e03a0d5489515
child 71910 3b0160b38c3d452fd5f56c710820ac35f8287ab1
push id284
push userjlebar@mozilla.com
push dateTue, 28 Jun 2011 14:50:42 +0000
treeherdermozilla-inbound@a6cdfd70d6a2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn, bz
bugs664486
milestone7.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 664486 - Add page fault counts to about:memory on Linux and Mac. r=njn, sr=bz
content/canvas/src/nsCanvasRenderingContext2D.cpp
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/PMemoryReportRequest.ipdl
gfx/thebes/gfxASurface.cpp
gfx/thebes/gfxWindowsPlatform.cpp
ipc/glue/SharedMemory.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
layout/base/nsPresShell.cpp
modules/libpr0n/src/imgLoader.cpp
modules/libpr0n/test/mochitest/test_bug601470.html
storage/src/mozStorageConnection.cpp
storage/src/mozStorageService.cpp
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
toolkit/components/telemetry/TelemetryPing.js
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryReporterManager.cpp
xpcom/base/nsMemoryReporterManager.h
xpcom/reflect/xptinfo/src/xptiWorkingSet.cpp
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -163,30 +163,30 @@ static PRBool FloatValidate (double f1, 
 }
 
 #undef VALIDATE
 
 /* Memory reporter stuff */
 static nsIMemoryReporter *gCanvasMemoryReporter = nsnull;
 static PRInt64 gCanvasMemoryUsed = 0;
 
-static PRInt64 GetCanvasMemoryUsed(void *) {
+static PRInt64 GetCanvasMemoryUsed() {
     return gCanvasMemoryUsed;
 }
 
-// This is MR_OTHER because it's not always clear where in memory the pixels of
+// This is KIND_OTHER because it's not always clear where in memory the pixels of
 // a canvas are stored.  Furthermore, this memory will be tracked by the
 // underlying surface implementations.  See bug 655638 for details.
 NS_MEMORY_REPORTER_IMPLEMENT(CanvasMemory,
     "canvas-2d-pixel-bytes",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetCanvasMemoryUsed,
     "Memory used by 2D canvases. Each canvas requires (width * height * 4) "
-    "bytes.",
-    GetCanvasMemoryUsed,
-    NULL)
+    "bytes.")
 
 static void
 CopyContext(gfxContext* dest, gfxContext* src)
 {
     dest->Multiply(src->CurrentMatrix());
 
     nsRefPtr<gfxPath> path = src->CopyPath();
     dest->NewPath();
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -173,30 +173,30 @@ static PRBool FloatValidate (double f1, 
 }
 
 #undef VALIDATE
 
 /* Memory reporter stuff */
 static nsIMemoryReporter *gCanvasAzureMemoryReporter = nsnull;
 static PRInt64 gCanvasAzureMemoryUsed = 0;
 
-static PRInt64 GetCanvasAzureMemoryUsed(void *) {
+static PRInt64 GetCanvasAzureMemoryUsed() {
   return gCanvasAzureMemoryUsed;
 }
 
-// This is MR_OTHER because it's not always clear where in memory the pixels of
-// a canvas are stored.  Furthermore, this memory will be tracked by the
+// This is KIND_OTHER because it's not always clear where in memory the pixels
+// of a canvas are stored.  Furthermore, this memory will be tracked by the
 // underlying surface implementations.  See bug 655638 for details.
 NS_MEMORY_REPORTER_IMPLEMENT(CanvasAzureMemory,
   "canvas-2d-pixel-bytes",
-  MR_OTHER,
+  KIND_OTHER,
+  UNITS_BYTES,
+  GetCanvasAzureMemoryUsed,
   "Memory used by 2D canvases. Each canvas requires (width * height * 4) "
-  "bytes.",
-  GetCanvasAzureMemoryUsed,
-  nsnull)
+  "bytes.")
 
 /**
  ** nsCanvasGradientAzure
  **/
 #define NS_CANVASGRADIENTAZURE_PRIVATE_IID \
     {0x28425a6a, 0x90e0, 0x4d42, {0x9c, 0x75, 0xff, 0x60, 0x09, 0xb3, 0x10, 0xa8}}
 class nsCanvasGradientAzure : public nsIDOMCanvasGradient
 {
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -309,30 +309,33 @@ ContentChild::RecvPMemoryReportRequestCo
 
     PRBool more;
     while (NS_SUCCEEDED(r->HasMoreElements(&more)) && more) {
       nsCOMPtr<nsIMemoryReporter> report;
       r->GetNext(getter_AddRefs(report));
 
       nsCString path;
       PRInt32 kind;
+      PRInt32 units;
       nsCString desc;
-      PRInt64 memoryUsed;
+      PRInt64 amount;
       report->GetPath(getter_Copies(path));
       report->GetKind(&kind);
+      report->GetUnits(&units);
+      report->GetAmount(&amount);
       report->GetDescription(getter_Copies(desc));
-      report->GetMemoryUsed(&memoryUsed);
 
       static const int maxLength = 31;   // big enough; pid is only a few chars
       MemoryReport memreport(nsPrintfCString(maxLength, "Content (%d)",
                                              getpid()),
                              path,
                              kind,
-                             desc,
-                             memoryUsed);
+                             units,
+                             amount,
+                             desc);
 
       reports.AppendElement(memreport);
 
     }
 
     child->Send__delete__(child, reports);
     return true;
 }
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -703,35 +703,34 @@ ContentParent::DeallocPMemoryReportReque
 {
   delete actor;
   return true;
 }
 
 void
 ContentParent::SetChildMemoryReporters(const InfallibleTArray<MemoryReport>& report)
 {
-    nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
+    nsCOMPtr<nsIMemoryReporterManager> mgr =
+        do_GetService("@mozilla.org/memory-reporter-manager;1");
     for (PRInt32 i = 0; i < mMemoryReporters.Count(); i++)
         mgr->UnregisterReporter(mMemoryReporters[i]);
 
     for (PRUint32 i = 0; i < report.Length(); i++) {
-
         nsCString process  = report[i].process();
         nsCString path     = report[i].path();
         PRInt32   kind     = report[i].kind();
+        PRInt32   units    = report[i].units();
+        PRInt64   amount   = report[i].amount();
         nsCString desc     = report[i].desc();
-        PRInt64 memoryUsed = report[i].memoryUsed();
         
-        nsRefPtr<nsMemoryReporter> r = new nsMemoryReporter(process,
-                                                            path,
-                                                            kind,
-                                                            desc,
-                                                            memoryUsed);
-      mMemoryReporters.AppendObject(r);
-      mgr->RegisterReporter(r);
+        nsRefPtr<nsMemoryReporter> r =
+            new nsMemoryReporter(process, path, kind, units, amount, desc);
+
+        mMemoryReporters.AppendObject(r);
+        mgr->RegisterReporter(r);
     }
 
     nsCOMPtr<nsIObserverService> obs =
         do_GetService("@mozilla.org/observer-service;1");
     if (obs)
         obs->NotifyObservers(nsnull, "child-memory-reporter-update", nsnull);
 }
 
--- a/dom/ipc/PMemoryReportRequest.ipdl
+++ b/dom/ipc/PMemoryReportRequest.ipdl
@@ -40,18 +40,19 @@ include protocol PContent;
 
 namespace mozilla {
 namespace dom {
 
 struct MemoryReport {
   nsCString process;
   nsCString path;
   PRInt32 kind;
+  PRInt32 units;
+  PRInt64 amount;
   nsCString desc;
-  PRInt64 memoryUsed;
 };
 
 protocol PMemoryReportRequest {
   manager PContent;
 
   parent:
     __delete__(MemoryReport[] report);
 };
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -606,30 +606,35 @@ public:
     }
 
     NS_IMETHOD GetPath(char **memoryPath) {
         *memoryPath = strdup(SurfaceMemoryReporterPathForType(mType));
         return NS_OK;
     }
 
     NS_IMETHOD GetKind(PRInt32 *kind) {
-        *kind = MR_OTHER;
+        *kind = KIND_OTHER;
+        return NS_OK;
+    }
+    
+    NS_IMETHOD GetUnits(PRInt32 *units) {
+        *units = UNITS_BYTES;
+        return NS_OK;
+    }
+
+    NS_IMETHOD GetAmount(PRInt64 *amount) {
+        *amount = gSurfaceMemoryUsed[mType];
         return NS_OK;
     }
 
     NS_IMETHOD GetDescription(char **desc) {
         *desc = strdup("Memory used by gfx surface of the given type.");
         return NS_OK;
     }
 
-    NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed) {
-        *memoryUsed = gSurfaceMemoryUsed[mType];
-        return NS_OK;
-    }
-
     gfxASurface::gfxSurfaceType mType;
 };
 
 NS_IMPL_ISUPPORTS1(SurfaceMemoryReporter, nsIMemoryReporter)
 
 void
 gfxASurface::RecordMemoryUsedForSurfaceType(gfxASurface::gfxSurfaceType aType,
                                             PRInt32 aBytes)
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -84,95 +84,47 @@ using namespace mozilla::gfx;
 
 #include "nsIMemoryReporter.h"
 #include "nsMemory.h"
 #endif
 
 using namespace mozilla;
 
 #ifdef CAIRO_HAS_D2D_SURFACE
-class D2DCacheReporter :
-    public nsIMemoryReporter
-{
-public:
-    D2DCacheReporter()
-    { }
-
-    NS_DECL_ISUPPORTS
-
-    NS_IMETHOD GetProcess(char **process) {
-        *process = strdup("");
-        return NS_OK;
-    }
-
-    NS_IMETHOD GetPath(char **memoryPath) {
-        *memoryPath = strdup("gfx-d2d-surfacecache");
-        return NS_OK;
-    }
 
-    NS_IMETHOD GetKind(PRInt32 *kind) {
-        *kind = MR_OTHER;
-        return NS_OK;
-    }
-
-    NS_IMETHOD GetDescription(char **desc) {
-        *desc = strdup("Memory used by the Direct2D internal surface cache.");
-        return NS_OK;
-    }
+NS_MEMORY_REPORTER_IMPLEMENT(
+    D2DCache,
+    "gfx-d2d-surfacecache",
+    KIND_OTHER,
+    UNITS_BYTES,
+    cairo_d2d_get_image_surface_cache_usage,
+    "Memory used by the Direct2D internal surface cache.")
 
-    NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed) {
-        *memoryUsed = cairo_d2d_get_image_surface_cache_usage();
-        return NS_OK;
-    }
-}; 
-
-NS_IMPL_ISUPPORTS1(D2DCacheReporter, nsIMemoryReporter)
-
-class D2DVRAMReporter :
-    public nsIMemoryReporter
+namespace
 {
-public:
-    D2DVRAMReporter()
-    { }
 
-    NS_DECL_ISUPPORTS
-
-    NS_IMETHOD GetProcess(char **process) {
-        *process = strdup("");
-        return NS_OK;
-    }
-
-    NS_IMETHOD GetPath(char **memoryPath) {
-        *memoryPath = strdup("gfx-d2d-surfacevram");
-        return NS_OK;
-    }
+PRInt64 GetD2DSurfaceVramUsage() {
+  cairo_device_t *device =
+      gfxWindowsPlatform::GetPlatform()->GetD2DDevice();
+  if (device) {
+      return cairo_d2d_get_surface_vram_usage(device);
+  }
+  return 0;
+}
 
-    NS_IMETHOD GetKind(PRInt32 *kind) {
-        *kind = MR_OTHER;
-        return NS_OK;
-    }
-
-    NS_IMETHOD GetDescription(char **desc) {
-        *desc = strdup("Video memory used by D2D surfaces");
-        return NS_OK;
-    }
+} // anonymous namespace
 
-    NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed) {
-        cairo_device_t *device =
-            gfxWindowsPlatform::GetPlatform()->GetD2DDevice();
-        if (device) {
-            *memoryUsed = cairo_d2d_get_surface_vram_usage(device);
-        } else {
-            *memoryUsed = 0;
-        }
-        return NS_OK;
-    }
-};
+NS_MEMORY_REPORTER_IMPLEMENT(
+    D2DVram,
+    "gfx-d2d-surfacevram",
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetD2DSurfaceVramUsage,
+    "Video memory used by D2D surfaces")
 
-NS_IMPL_ISUPPORTS1(D2DVRAMReporter, nsIMemoryReporter)
 #endif
 
 #define GFX_USE_CLEARTYPE_ALWAYS "gfx.font_rendering.cleartype.always_use_for_content"
 #define GFX_DOWNLOADABLE_FONTS_USE_CLEARTYPE "gfx.font_rendering.cleartype.use_for_downloadable_fonts"
 
 #define GFX_CLEARTYPE_PARAMS           "gfx.font_rendering.cleartype_params."
 #define GFX_CLEARTYPE_PARAMS_GAMMA     "gfx.font_rendering.cleartype_params.gamma"
 #define GFX_CLEARTYPE_PARAMS_CONTRAST  "gfx.font_rendering.cleartype_params.enhanced_contrast"
@@ -222,18 +174,18 @@ gfxWindowsPlatform::gfxWindowsPlatform()
     /* 
      * Initialize COM 
      */ 
     CoInitialize(NULL); 
 
     mScreenDC = GetDC(NULL);
 
 #ifdef CAIRO_HAS_D2D_SURFACE
-    NS_RegisterMemoryReporter(new D2DCacheReporter());
-    NS_RegisterMemoryReporter(new D2DVRAMReporter());
+    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(D2DCache));
+    NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(D2DVram));
     mD2DDevice = nsnull;
 #endif
 
     UpdateRenderMode();
 }
 
 gfxWindowsPlatform::~gfxWindowsPlatform()
 {
--- a/ipc/glue/SharedMemory.cpp
+++ b/ipc/glue/SharedMemory.cpp
@@ -43,32 +43,32 @@
 #include "nsIMemoryReporter.h"
 #include "mozilla/ipc/SharedMemory.h"
 
 namespace mozilla {
 namespace ipc {
 
 static PRInt64 gShmemAllocated;
 static PRInt64 gShmemMapped;
-static PRInt64 GetShmemAllocated(void*) { return gShmemAllocated; }
-static PRInt64 GetShmemMapped(void*) { return gShmemMapped; }
+static PRInt64 GetShmemAllocated() { return gShmemAllocated; }
+static PRInt64 GetShmemMapped() { return gShmemMapped; }
 
 NS_MEMORY_REPORTER_IMPLEMENT(ShmemAllocated,
     "shmem-allocated",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetShmemAllocated,
     "Memory shared with other processes that is accessible (but not "
-    "necessarily mapped).",
-    GetShmemAllocated,
-    nsnull)
+    "necessarily mapped).")
 NS_MEMORY_REPORTER_IMPLEMENT(ShmemMapped,
     "shmem-mapped",
-    MR_OTHER,
-    "Memory shared with other processes that is mapped into the address space.",
+    KIND_OTHER,
+    UNITS_BYTES,
     GetShmemMapped,
-    nsnull)
+    "Memory shared with other processes that is mapped into the address space.")
 
 SharedMemory::SharedMemory()
   : mAllocSize(0)
   , mMappedSize(0)
 {
   // NB: SharedMemory is main-thread-only at the moment, but that may
   // change soon
   static bool registered;
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -1222,20 +1222,18 @@ XPCJSRuntime::~XPCJSRuntime()
 }
 
 class XPConnectGCChunkAllocator
     : public js::GCChunkAllocator
 {
 public:
     XPConnectGCChunkAllocator() {}
 
-    static PRInt64 GetGCChunkBytesInUse(void *data) {
-        XPConnectGCChunkAllocator *allocator =
-            static_cast<XPConnectGCChunkAllocator*>(data);
-        return allocator->mNumGCChunksInUse * js::GC_CHUNK_SIZE;
+    PRInt64 GetGCChunkBytesInUse() {
+        return mNumGCChunksInUse * js::GC_CHUNK_SIZE;
     }
 private:
     virtual void *doAlloc() {
         void *chunk;
 #ifdef MOZ_MEMORY
         // posix_memalign returns zero on success, nonzero on failure.
         if (posix_memalign(&chunk, js::GC_CHUNK_SIZE, js::GC_CHUNK_SIZE))
             chunk = 0;
@@ -1258,73 +1256,73 @@ private:
 
 protected:
     PRUint32 mNumGCChunksInUse;
 };
 
 static XPConnectGCChunkAllocator gXPCJSChunkAllocator;
 
 #ifdef MOZ_MEMORY
-#define JS_GC_HEAP_KIND  MR_HEAP
+#define JS_GC_HEAP_KIND  KIND_HEAP
 #else
-#define JS_GC_HEAP_KIND  MR_MAPPED
+#define JS_GC_HEAP_KIND  KIND_MAPPED
 #endif
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSGCHeap,
     "explicit/js/gc-heap",
     JS_GC_HEAP_KIND,
-    "Memory used by the garbage-collected JavaScript heap.",
-    XPConnectGCChunkAllocator::GetGCChunkBytesInUse,
-    &gXPCJSChunkAllocator)
+    UNITS_BYTES,
+    gXPCJSChunkAllocator.GetGCChunkBytesInUse,
+    "Memory used by the garbage-collected JavaScript heap.")
 
 static PRInt64
 GetPerCompartmentSize(PRInt64 (*f)(JSCompartment *c))
 {
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     js::AutoLockGC lock(rt);
     PRInt64 n = 0;
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
         n += f(*c);
     return n;
 }
 
 static PRInt64
-GetJSStack(void *data)
+GetJSStack()
 {
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     PRInt64 n = 0;
     for (js::ThreadDataIter i(rt); !i.empty(); i.popFront())
         n += i.threadData()->stackSpace.committedSize();
     return n;
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStack,
     "explicit/js/stack",
-    MR_MAPPED,
+    KIND_MAPPED,
+    UNITS_BYTES,
+    GetJSStack,
     "Memory used for the JavaScript stack.  This is the committed portion "
     "of the stack;  any uncommitted portion is not measured because it "
-    "hardly costs anything.",
-    GetJSStack,
-    NULL)
+    "hardly costs anything.")
 
 static PRInt64
 GetCompartmentScriptsSize(JSCompartment *c)
 {
     PRInt64 n = 0;
     for (JSScript *script = (JSScript *)c->scripts.next;
          &script->links != &c->scripts;
          script = (JSScript *)script->links.next)
     {
         n += script->totalSize(); 
     }
     return n;
 }
 
 static PRInt64
-GetJSScripts(void *data)
+GetJSScripts()
 {
     return GetPerCompartmentSize(GetCompartmentScriptsSize);
 }
 
 struct PRInt64Data {
     PRInt64Data() : n(0) { }
     PRInt64 n;
 };
@@ -1343,17 +1341,17 @@ GetJSObjectSlotsCallback(JSContext *cx, 
         data->n += obj->numSlots() * sizeof(js::Value);
     }
 }
 #ifdef _MSC_VER
 #pragma optimize("", on)
 #endif
 
 static PRInt64
-GetJSObjectSlots(void *dummy)
+GetJSObjectSlots()
 {
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     JSContext *cx = JS_NewContext(rt, 0);
     if (!cx) {
         NS_ERROR("couldn't create context for memory tracing");
         return (PRInt64) -1;
     }
 
@@ -1378,17 +1376,17 @@ GetJSStringCharsCallback(JSContext *cx, 
     PRInt64Data *data = (PRInt64Data *) v;
     data->n += str->charsHeapSize();
 }
 #ifdef _MSC_VER
 #pragma optimize("", on)
 #endif
  
 static PRInt64
-GetJSStringChars(void *dummy)
+GetJSStringChars()
 {
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     JSContext *cx = JS_NewContext(rt, 0);
     if (!cx) {
         NS_ERROR("couldn't create context for memory tracing");
         return (PRInt64) -1;
     }
 
@@ -1398,80 +1396,80 @@ GetJSStringChars(void *dummy)
 
     JS_DestroyContextNoGC(cx);
 
     return data.n;
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSScripts,
     "explicit/js/scripts",
-    MR_HEAP,
+    KIND_HEAP,
+    UNITS_BYTES,
+    GetJSScripts,
     "Memory allocated for JSScripts.  A JSScript is created for each "
     "user-defined function in a script.  One is also created for "
     "the top-level code in a script.  Each JSScript includes byte-code and "
-    "various other things.",
-    GetJSScripts,
-    NULL)
+    "various other things.")
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSObjectSlots,
     "explicit/js/object-slots",
-    MR_HEAP,
+    KIND_HEAP,
+    UNITS_BYTES,
+    GetJSObjectSlots,
     "Memory allocated for non-fixed object slot arrays, which are used "
     "to represent object properties.  Some objects also contain a fixed "
     "number of slots which are stored on the JavaScript heap;  those slots "
-    "are not counted here.",
-    GetJSObjectSlots,
-    NULL)
+    "are not counted here.")
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStringChars,
     "explicit/js/string-chars",
-    MR_HEAP,
+    KIND_HEAP,
+    UNITS_BYTES,
+    GetJSStringChars,
     "Memory allocated to hold string characters.  Not all of this allocated "
     "memory is necessarily used to hold characters.  Each string also "
     "includes a header which is stored on the JavaScript heap;  that header "
-    "is not counted here.",
-    GetJSStringChars,
-    NULL)
-
+    "is not counted here.")
 
 #ifdef JS_METHODJIT
 
 static PRInt64
 GetCompartmentMjitCodeSize(JSCompartment *c)
 {
     return c->getMjitCodeSize();
 }
 
 static PRInt64
-GetJSMjitCode(void *data)
+GetJSMjitCode()
 {
     return GetPerCompartmentSize(GetCompartmentMjitCodeSize);
 }
 
 static PRInt64
-GetJSMJitData(void *data)
+GetJSMJitData()
 {
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     return rt->mjitDataSize;
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitCode,
     "explicit/js/mjit-code",
-    MR_MAPPED,
-    "Memory used by the method JIT to hold generated code.",
+    KIND_MAPPED,
+    UNITS_BYTES,
     GetJSMjitCode,
-    NULL)
+    "Memory used by the method JIT to hold generated code.")
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSMjitData,
     "explicit/js/mjit-data",
-    MR_HEAP,
+    KIND_HEAP,
+    UNITS_BYTES,
+    GetJSMJitData,
     "Memory used by the method JIT for the following data: "
-    "JITScripts, native maps, and inline cache structs.",
-    GetJSMJitData,
-    NULL)
+    "JITScripts, native maps, and inline cache structs.")
+
 #endif  // JS_METHODJIT
 
 #ifdef JS_TRACER
 
 static PRInt64
 GetCompartmentTjitCode(JSCompartment *c)
 {
     if (c->hasTraceMonitor()) {
@@ -1494,54 +1492,55 @@ static PRInt64
 GetCompartmentTjitDataAllocatorsReserve(JSCompartment *c)
 {
     return c->hasTraceMonitor()
          ? c->traceMonitor()->getVMAllocatorsReserveSize()
          : 0;
 }
 
 static PRInt64
-GetJSTjitCode(void *data)
+GetJSTjitCode()
 {
     return GetPerCompartmentSize(GetCompartmentTjitCode);
 }
 
 static PRInt64
-GetJSTjitDataAllocatorsMain(void *data)
+GetJSTjitDataAllocatorsMain()
 {
     return GetPerCompartmentSize(GetCompartmentTjitDataAllocatorsMain);
 }
 
 static PRInt64
-GetJSTjitDataAllocatorsReserve(void *data)
+GetJSTjitDataAllocatorsReserve()
 {
     return GetPerCompartmentSize(GetCompartmentTjitDataAllocatorsReserve);
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitCode,
     "explicit/js/tjit-code",
-    MR_MAPPED,
-    "Memory used by the trace JIT to hold generated code.",
+    KIND_MAPPED,
+    UNITS_BYTES,
     GetJSTjitCode,
-    NULL)
+    "Memory used by the trace JIT to hold generated code.")
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitDataAllocatorsMain,
     "explicit/js/tjit-data/allocators-main",
-    MR_HEAP,
-    "Memory used by the trace JIT's VMAllocators.",
+    KIND_HEAP,
+    UNITS_BYTES,
     GetJSTjitDataAllocatorsMain,
-    NULL)
+    "Memory used by the trace JIT's VMAllocators.")
 
 NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSTjitDataAllocatorsReserve,
     "explicit/js/tjit-data/allocators-reserve",
-    MR_HEAP,
+    KIND_HEAP,
+    UNITS_BYTES,
+    GetJSTjitDataAllocatorsReserve,
     "Memory used by the trace JIT and held in reserve for VMAllocators "
-    "in case of OOM.",
-    GetJSTjitDataAllocatorsReserve,
-    NULL)
+    "in case of OOM.")
+
 #endif  // JS_TRACER
 
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
  : mXPConnect(aXPConnect),
    mJSRuntime(nsnull),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1447,21 +1447,21 @@ public:
   EstimateShellsMemory(nsTHashtable<PresShellPtrKey>::Enumerator aEnumerator)
   {
     PRUint32 result = 0;
     sLiveShells->EnumerateEntries(aEnumerator, &result);
     return result;
   }
                   
                                   
-  static PRInt64 SizeOfLayoutMemoryReporter(void *) {
+  static PRInt64 SizeOfLayoutMemoryReporter() {
     return EstimateShellsMemory(LiveShellSizeEnumerator);
   }
 
-  static PRInt64 SizeOfBidiMemoryReporter(void *) {
+  static PRInt64 SizeOfBidiMemoryReporter() {
     return EstimateShellsMemory(LiveShellBidiSizeEnumerator);
   }
 
 protected:
   void QueryIsActive();
   nsresult UpdateImageLockingState();
 };
 
@@ -1665,27 +1665,27 @@ NS_NewPresShell(nsIPresShell** aInstance
   return NS_OK;
 }
 
 nsTHashtable<PresShell::PresShellPtrKey> *nsIPresShell::sLiveShells = 0;
 static PRBool sSynthMouseMove = PR_TRUE;
 
 NS_MEMORY_REPORTER_IMPLEMENT(LayoutPresShell,
     "explicit/layout/all",
-    MR_HEAP,
-    "Memory used by layout PresShell, PresContext, and other related areas.",
+    KIND_HEAP,
+    UNITS_BYTES,
     PresShell::SizeOfLayoutMemoryReporter,
-    nsnull)
+    "Memory used by layout PresShell, PresContext, and other related areas.")
 
 NS_MEMORY_REPORTER_IMPLEMENT(LayoutBidi,
     "explicit/layout/bidi",
-    MR_HEAP,
-    "Memory used by layout Bidi processor.",
+    KIND_HEAP,
+    UNITS_BYTES,
     PresShell::SizeOfBidiMemoryReporter,
-    nsnull)
+    "Memory used by layout Bidi processor.")
 
 PresShell::PresShell()
   : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
 {
   mSelection = nsnull;
 #ifdef MOZ_REFLOW_PERF
   mReflowCountMgr = new ReflowCountMgr();
   mReflowCountMgr->SetPresContext(mPresContext);
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -183,39 +183,23 @@ public:
     } else if (mType == ContentUnusedUncompressed) {
       *memoryPath = strdup("explicit/images/content/unused/uncompressed");
     }
     return NS_OK;
   }
 
   NS_IMETHOD GetKind(PRInt32 *kind)
   {
-    *kind = MR_HEAP;
+    *kind = KIND_HEAP;
     return NS_OK;
   }
 
-  NS_IMETHOD GetDescription(char **desc)
+  NS_IMETHOD GetUnits(PRInt32 *units)
   {
-    if (mType == ChromeUsedRaw) {
-      *desc = strdup("Memory used by in-use chrome images (compressed data).");
-    } else if (mType == ChromeUsedUncompressed) {
-      *desc = strdup("Memory used by in-use chrome images (uncompressed data).");
-    } else if (mType == ChromeUnusedRaw) {
-      *desc = strdup("Memory used by not in-use chrome images (compressed data).");
-    } else if (mType == ChromeUnusedUncompressed) {
-      *desc = strdup("Memory used by not in-use chrome images (uncompressed data).");
-    } else if (mType == ContentUsedRaw) {
-      *desc = strdup("Memory used by in-use content images (compressed data).");
-    } else if (mType == ContentUsedUncompressed) {
-      *desc = strdup("Memory used by in-use content images (uncompressed data).");
-    } else if (mType == ContentUnusedRaw) {
-      *desc = strdup("Memory used by not in-use content images (compressed data).");
-    } else if (mType == ContentUnusedUncompressed) {
-      *desc = strdup("Memory used by not in-use content images (uncompressed data).");
-    }
+    *units = UNITS_BYTES;
     return NS_OK;
   }
 
   struct EnumArg {
     EnumArg(ReporterType aType)
       : rtype(aType), value(0)
     { }
 
@@ -247,26 +231,48 @@ public:
       arg->value += image->GetSourceDataSize();
     } else {
       arg->value += image->GetDecodedDataSize();
     }
 
     return PL_DHASH_NEXT;
   }
 
-  NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed)
+  NS_IMETHOD GetAmount(PRInt64 *amount)
   {
     EnumArg arg(mType);
     if (mType & CHROME_BIT) {
       imgLoader::sChromeCache.EnumerateRead(EnumEntries, &arg);
     } else {
       imgLoader::sCache.EnumerateRead(EnumEntries, &arg);
     }
 
-    *memoryUsed = arg.value;
+    *amount = arg.value;
+    return NS_OK;
+  }
+
+  NS_IMETHOD GetDescription(char **desc)
+  {
+    if (mType == ChromeUsedRaw) {
+      *desc = strdup("Memory used by in-use chrome images (compressed data).");
+    } else if (mType == ChromeUsedUncompressed) {
+      *desc = strdup("Memory used by in-use chrome images (uncompressed data).");
+    } else if (mType == ChromeUnusedRaw) {
+      *desc = strdup("Memory used by not in-use chrome images (compressed data).");
+    } else if (mType == ChromeUnusedUncompressed) {
+      *desc = strdup("Memory used by not in-use chrome images (uncompressed data).");
+    } else if (mType == ContentUsedRaw) {
+      *desc = strdup("Memory used by in-use content images (compressed data).");
+    } else if (mType == ContentUsedUncompressed) {
+      *desc = strdup("Memory used by in-use content images (uncompressed data).");
+    } else if (mType == ContentUnusedRaw) {
+      *desc = strdup("Memory used by not in-use content images (compressed data).");
+    } else if (mType == ContentUnusedUncompressed) {
+      *desc = strdup("Memory used by not in-use content images (uncompressed data).");
+    }
     return NS_OK;
   }
 
   ReporterType mType;
 };
 
 NS_IMPL_ISUPPORTS1(imgMemoryReporter, nsIMemoryReporter)
 
--- a/modules/libpr0n/test/mochitest/test_bug601470.html
+++ b/modules/libpr0n/test/mochitest/test_bug601470.html
@@ -28,17 +28,17 @@ window.onload = function() {
     .classes["@mozilla.org/memory-reporter-manager;1"]
     .getService(Components.interfaces.nsIMemoryReporterManager);
 
   var e = mgr.enumerateReporters();
   var memoryCounter = 0;
   while (e.hasMoreElements()) {
     var mr =
       e.getNext().QueryInterface(Components.interfaces.nsIMemoryReporter);
-    memoryCounter += mr.memoryUsed;
+    memoryCounter += mr.amount;
   }
   ok(memoryCounter > 0, "we should be using a nonzero amount of memory");
   ok(true, "yay, didn't crash!");
 
   SimpleTest.finish();
 }
 
 </script>
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -372,53 +372,60 @@ public:
     }
 
     *memoryPath = ::ToNewCString(path);
     return NS_OK;
   }
 
   NS_IMETHOD GetKind(PRInt32 *kind)
   {
-    *kind = MR_HEAP;
+    *kind = KIND_HEAP;
+    return NS_OK;
+  }
+
+  NS_IMETHOD GetUnits(PRInt32 *units)
+  {
+    *units = UNITS_BYTES;
     return NS_OK;
   }
 
+  NS_IMETHOD GetAmount(PRInt64 *amount)
+  {
+    int type = 0;
+    if (mType == Cache_Used) {
+      type = SQLITE_DBSTATUS_CACHE_USED;
+    }
+    else if (mType == Schema_Used) {
+      type = SQLITE_DBSTATUS_SCHEMA_USED;
+    }
+    else if (mType == Stmt_Used) {
+      type = SQLITE_DBSTATUS_STMT_USED;
+    }
+
+    int cur=0, max=0;
+    int rc = ::sqlite3_db_status(mDBConn, type, &cur, &max, 0);
+    *amount = cur;
+    return convertResultCode(rc);
+  }
+
   NS_IMETHOD GetDescription(char **desc)
   {
     if (mType == Cache_Used) {
       *desc = ::strdup("Memory (approximate) used by all pager caches.");
     }
     else if (mType == Schema_Used) {
       *desc = ::strdup("Memory (approximate) used to store the schema "
                        "for all databases associated with the connection");
     }
     else if (mType == Stmt_Used) {
       *desc = ::strdup("Memory (approximate) used by all prepared statements");
     }
     return NS_OK;
   }
 
-  NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed)
-  {
-    int type = 0;
-    if (mType == Cache_Used) {
-      type = SQLITE_DBSTATUS_CACHE_USED;
-    }
-    else if (mType == Schema_Used) {
-      type = SQLITE_DBSTATUS_SCHEMA_USED;
-    }
-    else if (mType == Stmt_Used) {
-      type = SQLITE_DBSTATUS_STMT_USED;
-    }
-
-    int cur=0, max=0;
-    int rc = ::sqlite3_db_status(mDBConn, type, &cur, &max, 0);
-    *memoryUsed = cur;
-    return convertResultCode(rc);
-  }
   Connection &mDBConn;
   nsCString mFileName;
   ReporterType mType;
 };
 NS_IMPL_THREADSAFE_ISUPPORTS1(
   StorageMemoryReporter
 , nsIMemoryReporter
 )
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -128,27 +128,27 @@ private:
 
 namespace mozilla {
 namespace storage {
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Memory Reporting
 
 static PRInt64
-GetStorageSQLiteMemoryUsed(void *)
+GetStorageSQLiteMemoryUsed()
 {
   return ::sqlite3_memory_used();
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(StorageSQLiteMemoryUsed,
     "explicit/storage/sqlite",
-    MR_HEAP,
-    "Memory used by SQLite.",
+    KIND_HEAP,
+    UNITS_BYTES,
     GetStorageSQLiteMemoryUsed,
-    nsnull)
+    "Memory used by SQLite.")
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Helpers
 
 class ServiceMainThreadInitializer : public nsRunnable
 {
 public:
   ServiceMainThreadInitializer(nsIObserver *aObserver,
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -43,21 +43,23 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 // Must use .href here instead of .search because "about:memory" is a
 // non-standard URL.
 var gVerbose = (location.href.split(/[\?,]/).indexOf("verbose") !== -1);
 
 var gAddedObserver = false;
 
-const MR_MAPPED = Ci.nsIMemoryReporter.MR_MAPPED;
-const MR_HEAP   = Ci.nsIMemoryReporter.MR_HEAP;
-const MR_OTHER  = Ci.nsIMemoryReporter.MR_OTHER;
+const KIND_MAPPED = Ci.nsIMemoryReporter.KIND_MAPPED;
+const KIND_HEAP   = Ci.nsIMemoryReporter.KIND_HEAP;
+const KIND_OTHER  = Ci.nsIMemoryReporter.KIND_OTHER;
+const UNITS_BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
+const UNITS_COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
 
-const kUnknown = -1;    // used for _memoryUsed if a memory reporter failed
+const kUnknown = -1;    // used for _amount if a memory reporter failed
 
 function onLoad()
 {
   var os = Cc["@mozilla.org/observer-service;1"].
       getService(Ci.nsIObserverService);
   os.notifyObservers(null, "child-memory-reporter-request", null);
 
   os.addObserver(ChildMemoryListener, "child-memory-reporter-update", false);
@@ -147,46 +149,48 @@ function update()
 
   // Process each memory reporter:
   // - Make a copy of it into a sub-table indexed by its process.  Each copy
   //   looks like this:
   //
   //     interface Reporter {
   //       _path:        string;
   //       _kind:        number;
+  //       _units:       number;
+  //       _amount:      number;
   //       _description: string;
-  //       _memoryUsed:  number;
   //     }
   //
   //   After this point we never use the original memory reporter again.
   //
-  // - Note that copying rOrig.memoryUsed (which calls a C++ function under the
-  //   IDL covers) to r._memoryUsed for every reporter now means that the
+  // - Note that copying rOrig.amount (which calls a C++ function under the
+  //   IDL covers) to r._amount for every reporter now means that the
   //   results as consistent as possible -- measurements are made all at
   //   once before most of the memory required to generate this page is
   //   allocated.
   var reportersByProcess = {};
   var e = mgr.enumerateReporters();
   while (e.hasMoreElements()) {
     var rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     var process = rOrig.process === "" ? "Main" : rOrig.process;
     var r = {
       _path:        rOrig.path,
       _kind:        rOrig.kind,
-      _description: rOrig.description,
-      _memoryUsed:  rOrig.memoryUsed
+      _units:       rOrig.units,
+      _amount:      rOrig.amount,
+      _description: rOrig.description
     };
     if (!reportersByProcess[process]) {
       reportersByProcess[process] = {};
     }
     var reporters = reportersByProcess[process];
     if (reporters[r._path]) {
       // Already an entry;  must be a duplicated reporter.  This can
-      // happen legitimately.  Sum the sizes.
-      reporters[r._path]._memoryUsed += r._memoryUsed;
+      // happen legitimately.  Sum the values.
+      reporters[r._path]._amount += r._amount;
     } else {
       reporters[r._path] = r;
     }
   }
 
   // Generate output for one process at a time.  Always start with the
   // Main process.
   var text = genProcessText("Main", reportersByProcess["Main"]);
@@ -227,19 +231,25 @@ function update()
           "</div>";
 
 
   var div = document.createElement("div");
   div.innerHTML = text;
   content.appendChild(div);
 }
 
-function cmp_memoryUsed(a, b)
+// Compare two memory reporter nodes.  We want to group together measurements
+// with the same units, so sort first by the nodes' _units field, then sort by
+// the amount if the units are equal.
+function cmp_amount(a, b)
 {
-  return b._memoryUsed - a._memoryUsed
+  if (a._units != b._units)
+    return b._units - a._units;
+  else
+    return b._amount - a._amount;
 };
 
 /**
  * Generates the text for a single process.
  *
  * @param aProcess
  *        The name of the process
  * @param aReporters
@@ -251,18 +261,18 @@ function genProcessText(aProcess, aRepor
   /**
    * From a list of memory reporters, builds a tree that mirrors the tree
    * structure that will be shown as output.
    *
    * @return The built tree.  The tree nodes have this structure:
    *         interface Node {
    *           _name: string;
    *           _kind:        number;
+   *           _amount:      number;    (non-negative or 'kUnknown')
    *           _description: string;
-   *           _memoryUsed:  number;    (non-negative or 'kUnknown')
    *           _kids:        [Node];
    *           _hasReporter: boolean;   (only defined if 'true')
    *           _hasProblem:  boolean;   (only defined if 'true')
    *         }
    */
   function buildTree()
   {
     const treeName = "explicit";
@@ -278,111 +288,111 @@ function genProcessText(aProcess, aRepor
       return undefined;
     }
 
     // We want to process all reporters that begin with 'treeName'.
     // First we build the tree but only filling in '_name', '_kind', '_kids'
     // and maybe '._hasReporter'.  This is done top-down from the reporters.
     var t = {
       _name: "falseRoot",
-      _kind: MR_OTHER,
+      _kind: KIND_OTHER,
       _kids: []
     };
     for (var path in aReporters) {
       var r = aReporters[path];
       if (r._path.slice(0, treeName.length) === treeName) {
         var names = r._path.split('/');
         var u = t;
         for (var i = 0; i < names.length; i++) {
           var name = names[i];
           var uMatch = findKid(name, u._kids);
           if (uMatch) {
             u = uMatch;
           } else {
             var v = {
               _name: name,
-              _kind: MR_OTHER,
+              _kind: KIND_OTHER,
               _kids: []
             };
             u._kids.push(v);
             u = v;
           }
         }
         u._kind = r._kind;
         u._hasReporter = true;
       }
     }
     // Using falseRoot makes the above code simpler.  Now discard it, leaving
     // treeName at the root.
     t = t._kids[0];
 
-    // Next, fill in '_description' and '_memoryUsed', and maybe '_hasProblem'
+    // Next, fill in '_description' and '_amount', and maybe '_hasProblem'
     // for each node.  This is done bottom-up because for most non-leaf nodes
-    // '_memoryUsed' and '_description' are determined from the child nodes.
+    // '_amount' and '_description' are determined from the child nodes.
     function fillInTree(aT, aPrepath)
     {
       var path = aPrepath ? aPrepath + '/' + aT._name : aT._name;
       if (aT._kids.length === 0) {
         // Leaf node.  Must have a reporter.
         aT._description = getDescription(aReporters, path);
-        var memoryUsed = getBytes(aReporters, path);
-        if (memoryUsed !== kUnknown) {
-          aT._memoryUsed = memoryUsed;
+        var amount = getBytes(aReporters, path);
+        if (amount !== kUnknown) {
+          aT._amount = amount;
         } else {
-          aT._memoryUsed = 0;
+          aT._amount = 0;
           aT._hasProblem = true;
         }
       } else {
         // Non-leaf node.  Get the size of the children.
         var childrenBytes = 0;
         for (var i = 0; i < aT._kids.length; i++) {
           // Allow for kUnknown, treat it like 0.
           var b = fillInTree(aT._kids[i], path);
           childrenBytes += (b === kUnknown ? 0 : b);
         }
         if (aT._hasReporter === true) {
           aT._description = getDescription(aReporters, path);
-          var memoryUsed = getBytes(aReporters, path);
-          if (memoryUsed !== kUnknown) {
+          var amount = getBytes(aReporters, path);
+          if (amount !== kUnknown) {
             // Non-leaf node with its own reporter.  Use the reporter and add
             // an "other" child node.
-            aT._memoryUsed = memoryUsed;
+            aT._amount = amount;
             var other = {
               _name: "other",
-              _kind: MR_OTHER,
+              _kind: KIND_OTHER,
               _description: "All unclassified " + aT._name + " memory.",
-              _memoryUsed: aT._memoryUsed - childrenBytes,
+              _amount: aT._amount - childrenBytes,
               _kids: []
             };
             aT._kids.push(other);
           } else {
             // Non-leaf node with a reporter that returns kUnknown.
             // Use the sum of the children and mark it as problematic.
-            aT._memoryUsed = childrenBytes;
+            aT._amount = childrenBytes;
             aT._hasProblem = true;
           }
         } else {
           // Non-leaf node without its own reporter.  Derive its size and
           // description entirely from its children.
-          aT._memoryUsed = childrenBytes;
+          aT._amount = childrenBytes;
           aT._description = "The sum of all entries below '" + aT._name + "'.";
         }
       }
-      return aT._memoryUsed;
+      return aT._amount;
     }
     fillInTree(t, "");
 
     // Determine how many bytes are reported by heap reporters.  Be careful
     // with non-leaf reporters;  if we count a non-leaf reporter we don't want
     // to count any of its child reporters.
     var s = "";
     function getKnownHeapUsedBytes(aT)
     {
-      if (aT._kind === MR_HEAP) {
-        return aT._memoryUsed;
+      if (aT._kind === KIND_HEAP) {
+        return aT._amount;
       } else {
         var n = 0;
         for (var i = 0; i < aT._kids.length; i++) {
           n += getKnownHeapUsedBytes(aT._kids[i]);
         }
         return n;
       }
     }
@@ -394,66 +404,66 @@ function genProcessText(aProcess, aRepor
     var unknownHeapUsedBytes = 0;
     var hasProblem = true;
     if (heapUsedBytes !== kUnknown) {
       unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(t);
       hasProblem = false;
     }
     var heapUnclassified = {
       _name: "heap-unclassified",
-      _kind: MR_HEAP,
+      _kind: KIND_HEAP,
       _description:
         "Memory not classified by a more specific reporter. This includes " +
         "memory allocated by the heap allocator in excess of that requested " +
         "by the application; this can happen when the heap allocator rounds " +
         "up request sizes.",
-      _memoryUsed: unknownHeapUsedBytes,
+      _amount: unknownHeapUsedBytes,
       _hasProblem: hasProblem,
       _kids: []
     }
     t._kids.push(heapUnclassified);
-    t._memoryUsed += unknownHeapUsedBytes;
+    t._amount += unknownHeapUsedBytes;
 
     function shouldOmit(aBytes)
     {
       return !gVerbose &&
-             t._memoryUsed !== kUnknown &&
-             (100 * aBytes / t._memoryUsed) < omitThresholdPerc;
+             t._amount !== kUnknown &&
+             (100 * aBytes / t._amount) < omitThresholdPerc;
     }
 
     /**
      * Sort all kid nodes from largest to smallest and aggregate
      * insignificant nodes.
      *
      * @param aT
      *        The tree
      */
     function filterTree(aT)
     {
-      aT._kids.sort(cmp_memoryUsed);
+      aT._kids.sort(cmp_amount);
 
       for (var i = 0; i < aT._kids.length; i++) {
-        if (shouldOmit(aT._kids[i]._memoryUsed)) {
+        if (shouldOmit(aT._kids[i]._amount)) {
           // This sub-tree is below the significance threshold
           // Remove it and all remaining (smaller) sub-trees, and
           // replace them with a single aggregate node.
           var i0 = i;
           var aggBytes = 0;
           var aggNames = [];
           for ( ; i < aT._kids.length; i++) {
-            aggBytes += aT._kids[i]._memoryUsed;
+            aggBytes += aT._kids[i]._amount;
             aggNames.push(aT._kids[i]._name);
           }
           aT._kids.splice(i0);
           var n = i - i0;
           var rSub = {
             _name: "(" + n + " omitted)",
-            _kind: MR_OTHER,
+            _kind: KIND_OTHER,
             _description: "Omitted sub-trees: " + aggNames.join(", ") + ".",
-            _memoryUsed: aggBytes,
+            _amount: aggBytes,
             _kids: []
           };
           aT._kids[i0] = rSub;
           break;
         }
         filterTree(aT._kids[i]);
       }
     }
@@ -467,53 +477,77 @@ function genProcessText(aProcess, aRepor
   text += "<h1>" + aProcess + " Process</h1>\n\n";
   text += genTreeText(buildTree());
   text += genOtherText(aReporters);
   text += "<hr></hr>";
   return text;
 }
 
 /**
+ * Returns the reporter's amount formatted as a human-readable string (with
+ * units, if applicable).
+ *
+ * @param aReporter
+ *        The reporter whose usage we're formatting
+ * @return The reporter's amount formatted as a human-readable string
+ */
+function formatReporterAmount(aReporter)
+{
+  switch(aReporter._units) {
+    case UNITS_BYTES: return formatBytes(aReporter._amount);
+    case UNITS_COUNT: return formatInt(aReporter._amount);
+    default:          return "(???)"
+  }
+}
+
+/**
+ * Formats an int as a human-readable string.
+ *
+ * @param aN
+ *        The integer to format
+ * @return A human-readable string representing the int
+ */
+function formatInt(aN)
+{
+  var neg = false;
+  if (aN < 0) {
+    neg = true;
+    aN = -aN;
+  }
+  var s = "";
+  while (true) {
+    var k = aN % 1000;
+    aN = Math.floor(aN / 1000);
+    if (aN > 0) {
+      if (k < 10) {
+        s = ",00" + k + s;
+      } else if (k < 100) {
+        s = ",0" + k + s;
+      } else {
+        s = "," + k + s;
+      }
+    } else {
+      s = k + s;
+      break;
+    }
+  }
+  return neg ? "-" + s : s;
+}
+
+/**
  * Converts a byte count to an appropriate string representation.
  *
  * @param aBytes
  *        The byte count
  * @return The string representation
  */
 function formatBytes(aBytes)
 {
   var unit = gVerbose ? "B" : "MB";
 
-  function formatInt(aN)
-  {
-    var neg = false;
-    if (aN < 0) {
-      neg = true;
-      aN = -aN;
-    }
-    var s = "";
-    while (true) {
-      var k = aN % 1000;
-      aN = Math.floor(aN / 1000);
-      if (aN > 0) {
-        if (k < 10) {
-          s = ",00" + k + s;
-        } else if (k < 100) {
-          s = ",0" + k + s;
-        } else {
-          s = "," + k + s;
-        }
-      } else {
-        s = k + s;
-        break;
-      }
-    }
-    return neg ? "-" + s : s;
-  }
-
   var s;
   if (gVerbose) {
     s = formatInt(aBytes) + " " + unit;
   } else {
     var mbytes = (aBytes / (1024 * 1024)).toFixed(2);
     var a = String(mbytes).split(".");
     s = formatInt(a[0]) + "." + a[1] + " " + unit;
   }
@@ -552,17 +586,17 @@ function pad(aS, aN, aC)
  * @param aDoNotMark
  *        If set, the _done property is not set.
  * @return The byte count
  */
 function getBytes(aReporters, aPath, aDoNotMark)
 {
   var r = aReporters[aPath];
   if (r) {
-    var bytes = r._memoryUsed;
+    var bytes = r._amount;
     if (!aDoNotMark) {
       r._done = true;
     }
     return bytes;
   }
   // Nb: this should never occur; all paths have been extracted from
   // the original list of reporters and so the lookup should succeed.  Return
   // an obviously wrong number that will likely be noticed.
@@ -587,20 +621,20 @@ function getDescription(aReporters, aPat
 function genMrValueText(aValue)
 {
   return "<span class='mrValue'>" + aValue + "</span>";
 }
 
 function kindToString(aKind)
 {
   switch (aKind) {
-   case MR_MAPPED: return "(Mapped) ";
-   case MR_HEAP:   return "(Heap) ";
-   case MR_OTHER:  return "";
-   default:        return "(???) ";
+   case KIND_MAPPED: return "(Mapped) ";
+   case KIND_HEAP:   return "(Heap) ";
+   case KIND_OTHER:  return "";
+   default:          return "(???) ";
   }
 }
 
 function escapeQuotes(aStr)
 {
   return aStr.replace(/'/g, '&#39;');
 }
 
@@ -623,17 +657,17 @@ function genMrNameText(aKind, aDesc, aNa
  * Generates the text for the tree, including its heading.
  *
  * @param aT
  *        The tree
  * @return The generated text
  */
 function genTreeText(aT)
 {
-  var treeBytes = aT._memoryUsed;
+  var treeBytes = aT._amount;
   var treeBytesLength = formatBytes(treeBytes).length;
 
   /**
    * Generates the text for a particular tree, without a heading.
    *
    * @param aT
    *        The tree
    * @param aIndentGuide
@@ -672,33 +706,33 @@ function genTreeText(aT)
         indent += repeatStr(" ", aIndentGuide[i]._depth - 1);
       }
       indent += aIndentGuide[i]._isLastKid ? kUpAndRight : kVerticalAndRight;
       indent += repeatStr(kHorizontal, aIndentGuide[i]._depth - 1);
     }
 
     // Indent more if this entry is narrower than its parent, and update
     // aIndentGuide accordingly.
-    var tMemoryUsedStr = formatBytes(aT._memoryUsed);
+    var tMemoryUsedStr = formatBytes(aT._amount);
     var tBytesLength = tMemoryUsedStr.length;
     var extraIndentLength = Math.max(aParentBytesLength - tBytesLength, 0);
     if (extraIndentLength > 0) {
       for (var i = 0; i < extraIndentLength; i++) {
         indent += kHorizontal;
       }
       aIndentGuide[aIndentGuide.length - 1]._depth += extraIndentLength;
     }
     indent += "</span>";
 
     // Generate the percentage.
     var perc = "";
-    if (aT._memoryUsed === treeBytes) {
+    if (aT._amount === treeBytes) {
       perc = "100.0";
     } else {
-      perc = (100 * aT._memoryUsed / treeBytes).toFixed(2);
+      perc = (100 * aT._amount / treeBytes).toFixed(2);
       perc = pad(perc, 5, '0');
     }
     perc = "<span class='mrPerc'>(" + perc + "%)</span> ";
 
     var text = indent + genMrValueText(tMemoryUsedStr) + " " + perc +
                genMrNameText(aT._kind, aT._description, aT._name,
                              aT._hasProblem);
 
@@ -737,47 +771,48 @@ function genTreeText(aT)
  *        Table of reporters for this process, indexed by _path
  * @return The generated text
  */
 function genOtherText(aReporters)
 {
   // Generate an array of Reporter-like elements, stripping out all the
   // reporters that have already been handled.  Also find the width of the
   // widest element, so we can format things nicely.
-  var maxBytesLength = 0;
+  var maxAmountLength = 0;
   var rArray = [];
   for (var path in aReporters) {
     var r = aReporters[path];
     if (!r._done) {
       var hasProblem = false;
-      if (r._memoryUsed === kUnknown) {
+      if (r._amount === kUnknown) {
         hasProblem = true;
       }
       var elem = {
         _path:        r._path,
         _kind:        r._kind,
+        _units:       r._units,
+        _amount:  hasProblem ? 0 : r._amount,
         _description: r._description,
-        _memoryUsed:  hasProblem ? 0 : r._memoryUsed,
         _hasProblem:  hasProblem
       };
       rArray.push(elem);
-      var thisBytesLength = formatBytes(elem._memoryUsed).length;
-      if (thisBytesLength > maxBytesLength) {
-        maxBytesLength = thisBytesLength;
+      var thisAmountLength = formatReporterAmount(elem).length;
+      if (thisAmountLength > maxAmountLength) {
+        maxAmountLength = thisAmountLength;
       }
     }
   }
-  rArray.sort(cmp_memoryUsed);
+  rArray.sort(cmp_amount);
 
   // Generate text for the not-yet-printed values.
   var text = "";
   for (var i = 0; i < rArray.length; i++) {
     var elem = rArray[i];
     text += genMrValueText(
-              pad(formatBytes(elem._memoryUsed), maxBytesLength, ' ')) + " ";
+              pad(formatReporterAmount(elem), maxAmountLength, ' ')) + " ";
     text += genMrNameText(elem._kind, elem._description, elem._path,
                           elem._hasProblem);
   }
 
   // Nb: the newlines give nice spacing if we cut+paste into a text buffer.
   const desc = "This list contains other memory measurements that cross-cut " +
                "the requested memory measurements above."
   return "<h2 class='hasDesc' title='" + desc + "'>Other Measurements</h2>\n" +
--- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
@@ -30,27 +30,27 @@
     mgr.unregisterReporter(mr);
     realReporters.push(mr);
   }
 
   // Setup various fake-but-deterministic reporters.
   const KB = 1024;
   const MB = KB * KB;
   const kUnknown = -1;
-  const MAPPED = Ci.nsIMemoryReporter.MR_MAPPED;
-  const HEAP   = Ci.nsIMemoryReporter.MR_HEAP;
-  const OTHER  = Ci.nsIMemoryReporter.MR_OTHER;
+  const MAPPED = Ci.nsIMemoryReporter.KIND_MAPPED;
+  const HEAP   = Ci.nsIMemoryReporter.KIND_HEAP;
+  const OTHER  = Ci.nsIMemoryReporter.KIND_OTHER;
 
-  function f(aProcess, aPath, aKind, aMemoryUsed) {
+  function f(aProcess, aPath, aKind, aAmount) {
     return {
       process:     aProcess,
       path:        aPath,
       kind:        aKind,
       description: "(description)",
-      memoryUsed:  aMemoryUsed 
+      amount:      aAmount 
     };
   }
 
   fakeReporters = [
     f("", "heap-used",          OTHER,  500 * MB),
     f("", "heap-unused",        OTHER,  100 * MB),
     f("", "explicit/a",         HEAP,   222 * MB),
     f("", "explicit/b/a",       HEAP,    85 * MB),
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -197,29 +197,29 @@ TelemetryPing.prototype = {
       // OK to skip memory reporters in xpcshell
       return
     }
 
     let e = mgr.enumerateReporters();
     let memReporters = {};
     while (e.hasMoreElements()) {
       let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
-      //  memReporters[mr.path] = mr.memoryUsed;
+      //  memReporters[mr.path] = mr.amount;
       let id = MEM_HISTOGRAMS[mr.path];
       if (!id) {
         continue;
       }
 
       let name = "Memory:" + mr.path + " (KB)";
       let h = this._histograms[name];
       if (!h) {
         h = Telemetry.getHistogramById(id);
         this._histograms[name] = h;
       }
-      let v = Math.floor(mr.memoryUsed / 1024);
+      let v = Math.floor(mr.amount / 1024);
       h.add(v);
     }
     return memReporters;
   },
   
   /**
    * Send data to the server. Record success/send-time in histograms
    */
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -35,84 +35,115 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsISimpleEnumerator;
 
-[scriptable, uuid(d298b942-3e66-4cd3-9ff5-46abc69147a7)]
+[scriptable, uuid(37d18434-9819-4ce1-922f-15d8b63da066)]
 interface nsIMemoryReporter : nsISupports
 {
   /*
-   * The name of the process containing this reporter.  All reporters start
-   * with "", which is short-hand for the "main" process;  this is true even
-   * for reporters in child processes.  When reporters from child reporters
-   * are copied into the main process, the copies have their 'process' field
-   * set appropriately.
+   * The name of the process containing this reporter.  Each reporter initially
+   * has "" in this field, indicating that it applies to the current process.
+   * (This is true even for reporters in a child process.)  When a reporter
+   * from a child process is copied into the main process, the copy has its
+   * 'process' field set appropriately.
    */
   readonly attribute string process;
 
   /*
    * The path that this memory usage should be reported under.  Paths are
    * '/'-delimited, eg. "a/b/c".  There are two categories of paths.
    *
-   * - Paths starting with "explicit" represent non-overlapping regions of 
-   *   memory that have been explicitly allocated with an OS-level allocation
-   *   (eg. mmap/VirtualAlloc/vm_allocate) or a heap-level allocation (eg.
-   *   malloc/calloc/operator new).  Each one can be viewed as representing a
-   *   path in a tree from the root node ("explicit") to a node lower in the
-   *   tree; this lower node does not have to be a leaf node.
+   * - Paths starting with "explicit" represent regions of memory that have
+   *   been explicitly allocated with an OS-level allocation (eg.
+   *   mmap/VirtualAlloc/vm_allocate) or a heap-level allocation (eg.
+   *   malloc/calloc/operator new).
    *
-   *   So, for example, "explicit/a/b", "explicit/a/c", "explicit/d",
-   *   "explicit/d/e", and "explicit/d/f" define this tree:
+   *   Each reporter can be viewed as representing a node in a tree rooted at
+   *   "explicit".  Not all nodes of the tree need have an associated reporter.
+   *   So, for example, the reporters "explicit/a/b", "explicit/a/c",
+   *   "explicit/d", "explicit/d/e", and "explicit/d/f" define this tree:
    *
    *     explicit
    *     |--a
    *     |  |--b [*]
    *     |  \--c [*]
    *     \--d [*]
    *        |--e [*]
    *        \--f [*]
    *
-   *   Nodes marked with a [*] have a reporter.
+   *   Nodes marked with a [*] have a reporter.  Notice that "explicit/a" is
+   *   implicitly defined.
    *
-   * - All other paths represent cross-cuttings memory regions, ie. ones that
-   *   may overlap arbitrarily with regions in the "explicit" tree.
+   *   A node's children divide their parent's memory into disjoint pieces.
+   *   So in the example above, |a| may not count any allocations counted by
+   *   |d|, and vice versa.
+   *
+   * - All other paths represent cross-cutting values and may overlap with any
+   *   other reporter.
    */
   readonly attribute string path;
 
   /*
-   * Allocation kinds.  "MAPPED" means it is allocated directly by the OS, eg.
-   * by calling mmap, VirtualAlloc, vm_allocate, etc.  "HEAP" means it is
-   * allocated by the heap allocator, eg. by calling malloc, calloc, realloc,
-   * memalign, operator new, operator new[], etc.  "OTHER" means it doesn't fit
-   * into either of these categories;  such reporters should have a path that
-   * does *not* start with "explicit".
+   * There are three categories of memory reporters:
+   *
+   *  - MAPPED: memory allocated directly by the OS, e.g. by calling mmap,
+   *    VirtualAlloc, or vm_allocate.  Reporters in this category must have
+   *    units UNITS_BYTES and must have a path starting with "explicit".
+   *
+   *  - HEAP: memory allocated by the heap allocator, e.g. by calling malloc,
+   *    calloc, realloc, memalign, operator new, or operator new[].  Reporters
+   *    in this category must have units UNITS_BYTES and must have a path
+   *    starting with "explicit".
+   *
+   *  - OTHER: reporters which don't fit into either of these categories. Such
+   *    reporters must have a path that does not start with "explicit" and may
+   *    have any units.
    */
-  const PRInt32 MR_MAPPED = 0;
-  const PRInt32 MR_HEAP   = 1;
-  const PRInt32 MR_OTHER  = 2;
+  const PRInt32 KIND_MAPPED = 0;
+  const PRInt32 KIND_HEAP   = 1;
+  const PRInt32 KIND_OTHER  = 2;
 
   /*
-   * The memory kind, see MR_* above.
+   * The reporter kind.  See KIND_* above.
    */
   readonly attribute PRInt32 kind;
 
   /*
+   * The amount reported by a memory reporter may have one of the following
+   * units, but you may of course add new units as necessary:
+   *
+   *  - BYTES: The amount contains a number of bytes.
+   *
+   *  - COUNT: The amount contains the cumulative number of times some event
+   *    has occurred since the application started up.  For instance, a
+   *    reporter reporting the number of page faults since startup should have
+   *    units UNITS_COUNT.
+   */
+  const PRInt32 UNITS_BYTES = 0;
+  const PRInt32 UNITS_COUNT = 1;
+
+  /*
+   * The units on the reporter's amount.  See UNITS_* above.
+   */
+  readonly attribute PRInt32 units;
+
+  /*
+   * The numeric value reported by this memory reporter.
+   */
+  readonly attribute long long amount;
+
+  /*
    * A human-readable description of this memory usage report.
    */
   readonly attribute string description;
-
-  /*
-   * The current amount of memory in use, as reported by this memory
-   * reporter.
-   */
-  readonly attribute long long memoryUsed;
 };
 
 [scriptable, uuid(7c62de18-1edd-40f8-9da2-a8c622763074)]
 interface nsIMemoryReporterManager : nsISupports
 {
   /*
    * Return an enumerator of nsIMemoryReporters that are currently registered.
    */
@@ -137,26 +168,27 @@ interface nsIMemoryReporterManager : nsI
   void init ();
 };
 
 %{C++
 
 /*
  * Note that this defaults 'process' to "", which is usually what's desired.
  */
-#define NS_MEMORY_REPORTER_IMPLEMENT(_classname,_path,_kind,_desc,_usageFunction,_dataptr) \
-    class MemoryReporter_##_classname : public nsIMemoryReporter {      \
-    public:                                                             \
-      NS_DECL_ISUPPORTS                                                 \
-      NS_IMETHOD GetProcess(char **process) { *process = strdup(""); return NS_OK; } \
-      NS_IMETHOD GetPath(char **memoryPath) { *memoryPath = strdup(_path); return NS_OK; } \
-      NS_IMETHOD GetKind(int *kind) { *kind = _kind; return NS_OK; } \
-      NS_IMETHOD GetDescription(char **desc) { *desc = strdup(_desc); return NS_OK; } \
-      NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed) { *memoryUsed = _usageFunction(_dataptr); return NS_OK; } \
-    };                                                                  \
+#define NS_MEMORY_REPORTER_IMPLEMENT(_classname, _path, _kind, _units, _usageFunction, _desc) \
+    class MemoryReporter_##_classname : public nsIMemoryReporter {                            \
+    public:                                                                                   \
+      NS_DECL_ISUPPORTS                                                                       \
+      NS_IMETHOD GetProcess(char **process) { *process = strdup(""); return NS_OK; }          \
+      NS_IMETHOD GetPath(char **memoryPath) { *memoryPath = strdup(_path); return NS_OK; }    \
+      NS_IMETHOD GetKind(int *kind) { *kind = _kind; return NS_OK; }                          \
+      NS_IMETHOD GetUnits(int *units) { *units = _units; return NS_OK; }                      \
+      NS_IMETHOD GetAmount(PRInt64 *amount) { *amount = _usageFunction(); return NS_OK; }     \
+      NS_IMETHOD GetDescription(char **desc) { *desc = strdup(_desc); return NS_OK; }         \
+    };                                                                                        \
     NS_IMPL_ISUPPORTS1(MemoryReporter_##_classname, nsIMemoryReporter)
 
 #define NS_MEMORY_REPORTER_NAME(_classname)  MemoryReporter_##_classname
 
 NS_COM nsresult NS_RegisterMemoryReporter (nsIMemoryReporter *reporter);
 NS_COM nsresult NS_UnregisterMemoryReporter (nsIMemoryReporter *reporter);
 
 %}
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -36,16 +36,45 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsCOMPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMemoryReporterManager.h"
 #include "nsArrayEnumerator.h"
 
+#if defined(XP_LINUX) || defined(XP_MACOSX)
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+static PRInt64 GetHardPageFaults()
+{
+  struct rusage usage;
+  int err = getrusage(RUSAGE_SELF, &usage);
+  if (err != 0) {
+    return PRInt64(-1);
+  }
+
+  return usage.ru_majflt;
+}
+
+static PRInt64 GetSoftPageFaults()
+{
+  struct rusage usage;
+  int err = getrusage(RUSAGE_SELF, &usage);
+  if (err != 0) {
+    return PRInt64(-1);
+  }
+
+  return usage.ru_minflt;
+}
+
+#endif
+
 #if defined(XP_LINUX)
 
 #include <unistd.h>
 static PRInt64 GetProcSelfStatmField(int n)
 {
     // There are more than two fields, but we're only interested in the first
     // two.
     static const int MAX_FIELD = 2;
@@ -55,22 +84,22 @@ static PRInt64 GetProcSelfStatmField(int
     if (f) {
         int nread = fscanf(f, "%lu %lu", &fields[0], &fields[1]);
         fclose(f);
         return (PRInt64) ((nread == MAX_FIELD) ? fields[n]*getpagesize() : -1);
     }
     return (PRInt64) -1;
 }
 
-static PRInt64 GetVsize(void *)
+static PRInt64 GetVsize()
 {
     return GetProcSelfStatmField(0);
 }
 
-static PRInt64 GetResident(void *)
+static PRInt64 GetResident()
 {
     return GetProcSelfStatmField(1);
 }
 
 #elif defined(XP_MACOSX)
 
 #include <mach/mach_init.h>
 #include <mach/task.h>
@@ -81,103 +110,134 @@ static bool GetTaskBasicInfo(struct task
     kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO,
                                  (task_info_t)ti, &count);
     return kr == KERN_SUCCESS;
 }
 
 // The VSIZE figure on Mac includes huge amounts of shared memory and is always
 // absurdly high, eg. 2GB+ even at start-up.  But both 'top' and 'ps' report
 // it, so we might as well too.
-static PRInt64 GetVsize(void *)
+static PRInt64 GetVsize()
 {
     task_basic_info ti;
     return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.virtual_size : -1);
 }
 
-static PRInt64 GetResident(void *)
+static PRInt64 GetResident()
 {
     task_basic_info ti;
     return (PRInt64) (GetTaskBasicInfo(&ti) ? ti.resident_size : -1);
 }
 
 #elif defined(XP_WIN)
 
 #include <windows.h>
 #include <psapi.h>
 
 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
-static PRInt64 GetPrivate(void *)
+static PRInt64 GetPrivate()
 {
     PROCESS_MEMORY_COUNTERS_EX pmcex;
     pmcex.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
 
     if (!GetProcessMemoryInfo(GetCurrentProcess(),
                               (PPROCESS_MEMORY_COUNTERS) &pmcex, sizeof(pmcex)))
     return (PRInt64) -1;
 
     return pmcex.PrivateUsage;
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(Private,
     "private",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetPrivate,
     "Memory that cannot be shared with other processes, including memory that "
     "is committed and marked MEM_PRIVATE, data that is not mapped, and "
-    "executable pages that have been written to.",
-    GetPrivate,
-    NULL)
+    "executable pages that have been written to.")
 #endif
 
-static PRInt64 GetResident(void *)
+static PRInt64 GetResident()
 {
   PROCESS_MEMORY_COUNTERS pmc;
   pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
 
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
       return (PRInt64) -1;
 
   return pmc.WorkingSetSize;
 }
 
 #else
 
-static PRInt64 GetResident(void *)
+static PRInt64 GetResident()
 {
     return (PRInt64) -1;
 }
 
 #endif
 
 #if defined(XP_LINUX) || defined(XP_MACOSX)
 NS_MEMORY_REPORTER_IMPLEMENT(Vsize,
     "vsize",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetVsize,
     "Memory mapped by the process, including code and data segments, the "
     "heap, thread stacks, memory explicitly mapped by the process via mmap "
     "and similar operations, and memory shared with other processes. "
     "(Note that 'resident' is a better measure of the memory resources used "
     "by the process.) "
     "This is the vsize figure as reported by 'top' or 'ps'; on Mac the amount "
     "of memory shared with other processes is very high and so this figure is "
-    "of limited use.",
-    GetVsize,
-    NULL)
+    "of limited use.")
+
+NS_MEMORY_REPORTER_IMPLEMENT(SoftPageFaults,
+    "soft-page-faults",
+    KIND_OTHER,
+    UNITS_COUNT,
+    GetSoftPageFaults,
+    "The number of soft page faults (also known as \"minor page faults\") that "
+    "have occurred since the process started.  A soft page fault occurs when the "
+    "process tries to access a page which is present in physical memory but is "
+    "not mapped into the process's address space.  For instance, a process might "
+    "observe soft page faults when it loads a shared library which is already "
+    "present in physical memory. A process may experience many thousands of soft "
+    "page faults even when the machine has plenty of available physical memory, "
+    "and because the OS services a soft page fault without accessing the disk, "
+    "they impact performance much less than hard page faults.")
+
+NS_MEMORY_REPORTER_IMPLEMENT(HardPageFaults,
+    "hard-page-faults",
+    KIND_OTHER,
+    UNITS_COUNT,
+    GetHardPageFaults,
+    "The number of hard page faults (also known as \"major page faults\") that "
+    "have occurred since the process started.  A hard page fault occurs when a "
+    "process tries to access a page which is not present in physical memory. "
+    "The operating system must access the disk in order to fulfill a hard page "
+    "fault. When memory is plentiful, you should see very few hard page faults. "
+    "But if the process tries to use more memory than your machine has "
+    "available, you may see many thousands of hard page faults. Because "
+    "accessing the disk is up to a million times slower than accessing RAM, "
+    "the program may run very slowly when it is experiencing more than 100 or "
+    "so hard page faults a second.")
 #endif
 
 NS_MEMORY_REPORTER_IMPLEMENT(Resident,
     "resident",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetResident,
     "Memory mapped by the process that is present in physical memory, "
     "also known as the resident set size (RSS).  This is the best single "
     "figure to use when considering the memory resources used by the process, "
     "but it depends both on other processes being run and details of the OS "
     "kernel and so is best used for comparing the memory usage of a single "
-    "process at different points in time.",
-    GetResident,
-    NULL)
+    "process at different points in time.")
 
 /**
  ** memory reporter implementation for jemalloc and OSX malloc,
  ** to obtain info on total memory in use (that we know about,
  ** at least -- on OSX, there are sometimes other zones in use).
  **/
 
 #if defined(MOZ_MEMORY)
@@ -195,135 +255,135 @@ extern "C" {
 extern void jemalloc_stats(jemalloc_stats_t* stats)
   NS_VISIBILITY_DEFAULT __attribute__((weak));
 }
 #  endif  // XP_LINUX
 #endif  // MOZ_MEMORY
 
 #if HAVE_JEMALLOC_STATS
 
-static PRInt64 GetHeapUsed(void *)
+static PRInt64 GetHeapUsed()
 {
     jemalloc_stats_t stats;
     jemalloc_stats(&stats);
     return (PRInt64) stats.allocated;
 }
 
-static PRInt64 GetHeapUnused(void *)
+static PRInt64 GetHeapUnused()
 {
     jemalloc_stats_t stats;
     jemalloc_stats(&stats);
     return (PRInt64) (stats.mapped - stats.allocated);
 }
 
-static PRInt64 GetHeapCommitted(void *)
+static PRInt64 GetHeapCommitted()
 {
     jemalloc_stats_t stats;
     jemalloc_stats(&stats);
     return (PRInt64) stats.committed;
 }
 
-static PRInt64 GetHeapDirty(void *)
+static PRInt64 GetHeapDirty()
 {
     jemalloc_stats_t stats;
     jemalloc_stats(&stats);
     return (PRInt64) stats.dirty;
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(HeapCommitted,
     "heap-committed",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetHeapCommitted,
     "Memory mapped by the heap allocator that is committed, i.e. in physical "
-    "memory or paged to disk.",
-    GetHeapCommitted,
-    NULL)
+    "memory or paged to disk.")
 
 NS_MEMORY_REPORTER_IMPLEMENT(HeapDirty,
     "heap-dirty",
-    MR_OTHER,
-    "Memory mapped by the heap allocator that is committed but unused.",
+    KIND_OTHER,
+    UNITS_BYTES,
     GetHeapDirty,
-    NULL)
+    "Memory mapped by the heap allocator that is committed but unused.")
 
 #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY)
 #include <malloc/malloc.h>
 
-static PRInt64 GetHeapUsed(void *)
+static PRInt64 GetHeapUsed()
 {
     struct mstats stats = mstats();
     return (PRInt64) stats.bytes_used;
 }
 
-static PRInt64 GetHeapUnused(void *)
+static PRInt64 GetHeapUnused()
 {
     struct mstats stats = mstats();
     return (PRInt64) (stats.bytes_total - stats.bytes_used);
 }
 
-static PRInt64 GetHeapZone0Committed(void *)
+static PRInt64 GetHeapZone0Committed()
 {
     malloc_statistics_t stats;
     malloc_zone_statistics(malloc_default_zone(), &stats);
     return stats.size_in_use;
 }
 
-static PRInt64 GetHeapZone0Used(void *)
+static PRInt64 GetHeapZone0Used()
 {
     malloc_statistics_t stats;
     malloc_zone_statistics(malloc_default_zone(), &stats);
     return stats.size_allocated;
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Committed,
     "heap-zone0-committed",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetHeapZone0Committed,
     "Memory mapped by the heap allocator that is committed in the default "
-    "zone.",
-    GetHeapZone0Committed,
-    NULL)
+    "zone.")
 
 NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Used,
     "heap-zone0-used",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetHeapZone0Used,
     "Memory mapped by the heap allocator in the default zone that is "
-    "available for use by the application.",
-    GetHeapZone0Used,
-    NULL)
+    "available for use by the application.")
 #else
 
-static PRInt64 GetHeapUsed(void *)
+static PRInt64 GetHeapUsed()
 {
     return (PRInt64) -1;
 }
 
-static PRInt64 GetHeapUnused(void *)
+static PRInt64 GetHeapUnused()
 {
     return (PRInt64) -1;
 }
 
 #endif
 
 NS_MEMORY_REPORTER_IMPLEMENT(HeapUsed,
     "heap-used",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetHeapUsed,
     "Memory mapped by the heap allocator that is available for use by the "
     "application.  This may exceed the amount of memory requested by the "
     "application due to the allocator rounding up request sizes. "
-    "(The exact amount requested is not measured.) ",
-    GetHeapUsed,
-    NULL)
+    "(The exact amount requested is not measured.) ")
 
 NS_MEMORY_REPORTER_IMPLEMENT(HeapUnused,
     "heap-unused",
-    MR_OTHER,
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetHeapUnused,
     "Memory mapped by the heap allocator and not available for use by the "
     "application.  This can grow large if the heap allocator is holding onto "
-    "memory that the application has freed.",
-    GetHeapUnused,
-    NULL)
+    "memory that the application has freed.")
 
 /**
  ** nsMemoryReporterManager implementation
  **/
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsMemoryReporterManager, nsIMemoryReporterManager)
 
 NS_IMETHODIMP
@@ -337,16 +397,18 @@ nsMemoryReporterManager::Init()
 #define REGISTER(_x)  RegisterReporter(new NS_MEMORY_REPORTER_NAME(_x))
 
     REGISTER(HeapUsed);
     REGISTER(HeapUnused);
     REGISTER(Resident);
 
 #if defined(XP_LINUX) || defined(XP_MACOSX)
     REGISTER(Vsize);
+    REGISTER(SoftPageFaults);
+    REGISTER(HardPageFaults);
 #elif defined(XP_WIN) && MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
     REGISTER(Private);
 #endif
 
 #if defined(HAVE_JEMALLOC_STATS)
     REGISTER(HeapCommitted);
     REGISTER(HeapDirty);
 #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY)
@@ -396,23 +458,25 @@ nsMemoryReporterManager::UnregisterRepor
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
 
 nsMemoryReporter::nsMemoryReporter(nsCString& process,
                                    nsCString& path,
                                    PRInt32 kind,
-                                   nsCString& desc,
-                                   PRInt64 memoryUsed)
+                                   PRInt32 units,
+                                   PRInt64 amount,
+                                   nsCString& desc)
 : mProcess(process)
 , mPath(path)
 , mKind(kind)
+, mUnits(units)
 , mDesc(desc)
-, mMemoryUsed(memoryUsed)
+, mAmount(amount)
 {
 }
 
 nsMemoryReporter::~nsMemoryReporter()
 {
 }
 
 NS_IMETHODIMP nsMemoryReporter::GetProcess(char **aProcess)
@@ -428,28 +492,34 @@ NS_IMETHODIMP nsMemoryReporter::GetPath(
 }
 
 NS_IMETHODIMP nsMemoryReporter::GetKind(PRInt32 *aKind)
 {
     *aKind = mKind;
     return NS_OK;
 }
 
+NS_IMETHODIMP nsMemoryReporter::GetUnits(PRInt32 *aUnits)
+{
+  *aUnits = mUnits;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsMemoryReporter::GetAmount(PRInt64 *aAmount)
+{
+    *aAmount = mAmount;
+    return NS_OK;
+}
+
 NS_IMETHODIMP nsMemoryReporter::GetDescription(char **aDescription)
 {
     *aDescription = strdup(mDesc.get());
     return NS_OK;
 }
 
-NS_IMETHODIMP nsMemoryReporter::GetMemoryUsed(PRInt64 *aMemoryUsed)
-{
-    *aMemoryUsed = mMemoryUsed;
-    return NS_OK;
-}
-
 
 NS_COM nsresult
 NS_RegisterMemoryReporter (nsIMemoryReporter *reporter)
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
     if (mgr == nsnull)
         return NS_ERROR_FAILURE;
     return mgr->RegisterReporter(reporter);
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -9,27 +9,29 @@ class nsMemoryReporter : public nsIMemor
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
   nsMemoryReporter(nsCString& process,
                    nsCString& path, 
                    PRInt32 kind,
-                   nsCString& desc,
-                   PRInt64 memoryUsed);
+                   PRInt32 units,
+                   PRInt64 amount,
+                   nsCString& desc);
 
   ~nsMemoryReporter();
 
 protected:
   nsCString mProcess;
   nsCString mPath;
   PRInt32   mKind;
+  PRInt32   mUnits;
+  PRInt64   mAmount;
   nsCString mDesc;
-  PRInt64   mMemoryUsed;
 };
 
 
 class nsMemoryReporterManager : public nsIMemoryReporterManager
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIMEMORYREPORTERMANAGER
--- a/xpcom/reflect/xptinfo/src/xptiWorkingSet.cpp
+++ b/xpcom/reflect/xptinfo/src/xptiWorkingSet.cpp
@@ -40,27 +40,27 @@
 /* Implementation of xptiWorkingSet. */
 
 #include "xptiprivate.h"
 #include "nsString.h"
 #include "nsIMemoryReporter.h"
 
 using namespace mozilla;
 
-static PRInt64 GetXPTArenaSize(void*)
+static PRInt64 GetXPTArenaSize()
 {
   return XPT_SizeOfArena(gXPTIStructArena);
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(xptiWorkingSet,
                              "explicit/xpti-working-set",
-                             MR_HEAP,
-                             "Memory used by the XPCOM typelib system.",
+                             KIND_HEAP,
+                             UNITS_BYTES,
                              GetXPTArenaSize,
-                             nsnull)
+                             "Memory used by the XPCOM typelib system.")
 
 #define XPTI_STRUCT_ARENA_BLOCK_SIZE    (1024 * 1)
 #define XPTI_HASHTABLE_SIZE             2048
 
 xptiWorkingSet::xptiWorkingSet()
     : mTableReentrantMonitor("xptiWorkingSet::mTableReentrantMonitor")
 {
     MOZ_COUNT_CTOR(xptiWorkingSet);