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 71930 012e21b5705736afad47fca86f7925faff9bdf0d
parent 71929 f29f165edae601ef78119356954e03a0d5489515
child 71931 3b0160b38c3d452fd5f56c710820ac35f8287ab1
push idunknown
push userunknown
push dateunknown
reviewersnjn, bz
bugs664486
milestone7.0a1
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);