Bug 704400 - Implement --enable-dmd. r=khuey.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 08 Dec 2011 19:09:36 -0800
changeset 83992 a8196c95d4c94f2f442f632f10aceb5108f18358
parent 83990 6bafbaa9ac33d146df75546a7a47dc1fd59b4c7a
child 83993 f137857feed3d93dbc8ee1b42c90c743472157f8
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs704400
milestone11.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 704400 - Implement --enable-dmd. r=khuey.
configure.in
dom/base/nsJSEnvironment.cpp
extensions/spellcheck/hunspell/src/mozHunspell.cpp
js/xpconnect/src/XPCJSRuntime.cpp
layout/base/nsPresShell.cpp
memory/mozalloc/Makefile.in
memory/mozalloc/mozalloc.cpp
storage/src/mozStorageService.cpp
toolkit/components/places/History.cpp
xpcom/base/Makefile.in
xpcom/base/dmd.h
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryReporterManager.cpp
--- a/configure.in
+++ b/configure.in
@@ -2109,21 +2109,35 @@ MOZ_ARG_ENABLE_BOOL(valgrind,
 [  --enable-valgrind       Enable Valgrind integration hooks (default=no)],
     MOZ_VALGRIND=1,
     MOZ_VALGRIND= )
 if test -n "$MOZ_VALGRIND"; then
     MOZ_CHECK_HEADER([valgrind/valgrind.h], [],
         AC_MSG_ERROR(
             [--enable-valgrind specified but Valgrind is not installed]))
     AC_DEFINE(MOZ_VALGRIND)
-    MOZ_VALGRIND=1
 fi
 AC_SUBST(MOZ_VALGRIND)
 
 dnl ========================================================
+dnl = Use DMD
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(dmd,
+[  --enable-dmd            Enable DMD; also disables jemalloc (default=no)],
+    MOZ_DMD=1,
+    MOZ_DMD= )
+if test -n "$MOZ_DMD"; then
+    MOZ_CHECK_HEADER([valgrind/valgrind.h], [],
+        AC_MSG_ERROR(
+            [--enable-dmd specified but Valgrind is not installed]))
+    AC_DEFINE(MOZ_DMD)
+fi
+AC_SUBST(MOZ_DMD)
+
+dnl ========================================================
 dnl jprof
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(jprof,
 [  --enable-jprof          Enable jprof profiling tool (needs mozilla/tools/jprof). Implies --enable-profiling.],
     MOZ_JPROF=1,
     MOZ_JPROF= )
 if test -n "$MOZ_JPROF"; then
     MOZ_PROFILING=1
@@ -7090,17 +7104,17 @@ if test -n "$MOZ_DEBUG"; then
     AC_DEFINE(MOZ_DUMP_PAINTING)
 fi
 
 dnl ========================================================
 dnl = Enable trace malloc
 dnl ========================================================
 NS_TRACE_MALLOC=${MOZ_TRACE_MALLOC}
 MOZ_ARG_ENABLE_BOOL(trace-malloc,
-[  --enable-trace-malloc   Enable malloc tracing],
+[  --enable-trace-malloc   Enable malloc tracing; also disables jemalloc],
     NS_TRACE_MALLOC=1,
     NS_TRACE_MALLOC= )
 if test "$NS_TRACE_MALLOC"; then
   # Please, Mr. Linker Man, don't take away our symbol names
   MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS=
   USE_ELF_DYNSTR_GC=
   AC_DEFINE(NS_TRACE_MALLOC)
 fi
@@ -7112,16 +7126,19 @@ dnl ====================================
 MOZ_ARG_ENABLE_BOOL(jemalloc,
 [  --enable-jemalloc       Replace memory allocator with jemalloc],
     MOZ_MEMORY=1,
     MOZ_MEMORY=)
 
 if test "$NS_TRACE_MALLOC"; then
     MOZ_MEMORY=
 fi
+if test "$MOZ_DMD"; then
+    MOZ_MEMORY=
+fi
 
 if test "${OS_TARGET}" = "Android"; then
   dnl On Android, we use WRAP_LDFLAGS to link everything to mozutils
   :
 elif test "${OS_TARGET}" = "WINNT" -o "${OS_TARGET}" = "Darwin" -o "${OS_TARGET}" = "OS2"; then
   dnl On Windows, OSX and OS2, we want to link all our binaries against mozutils
   MOZ_UTILS_LDFLAGS='$(call EXPAND_LIBNAME_PATH,mozutils,$(LIBXUL_DIST)/lib)'
 else
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2939,16 +2939,35 @@ static JSFunctionSpec JProfFunctions[] =
     {"JProfStopProfiling",         JProfStopProfilingJS,       0, 0},
     {"JProfClearCircular",         JProfClearCircularJS,       0, 0},
     {"JProfSaveCircular",          JProfSaveCircularJS,        0, 0},
     {nsnull,                       nsnull,                     0, 0}
 };
 
 #endif /* defined(MOZ_JPROF) */
 
+#ifdef MOZ_DMD
+
+// See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on
+// how to use DMD.
+
+static JSBool
+DMDCheckJS(JSContext *cx, uintN argc, jsval *vp)
+{
+  mozilla::DMDCheckAndDump();
+  return JS_TRUE;
+}
+
+static JSFunctionSpec DMDFunctions[] = {
+    {"DMD",                        DMDCheckJS,                 0, 0},
+    {nsnull,                       nsnull,                     0, 0}
+};
+
+#endif /* defined(MOZ_DMD) */
+
 nsresult
 nsJSContext::InitClasses(JSObject* aGlobalObj)
 {
   nsresult rv = InitializeExternalClasses();
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSAutoRequest ar(mContext);
 
@@ -2962,16 +2981,21 @@ nsJSContext::InitClasses(JSObject* aGlob
   ::JS_DefineFunctions(mContext, aGlobalObj, TraceMallocFunctions);
 #endif
 
 #ifdef MOZ_JPROF
   // Attempt to initialize JProf functions
   ::JS_DefineFunctions(mContext, aGlobalObj, JProfFunctions);
 #endif
 
+#ifdef MOZ_DMD
+  // Attempt to initialize DMD functions
+  ::JS_DefineFunctions(mContext, aGlobalObj, DMDFunctions);
+#endif
+
   JSOptionChangedCallback(js_options_dot_str, this);
 
   return rv;
 }
 
 void
 nsJSContext::ClearScope(void *aGlobalObj, bool aClearFromProtoChain)
 {
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp
@@ -93,25 +93,26 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTION_3(mozHunspell,
                            mPersonalDictionary,
                            mEncoder,
                            mDecoder)
 
 // Memory reporting stuff.
 static PRInt64 gHunspellAllocatedSize = 0;
 
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(HunspellMallocSizeOfForCounterInc, "hunspell")
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN_UN(HunspellMallocSizeOfForCounterDec)
+
 void HunspellReportMemoryAllocation(void* ptr) {
   // |computedSize| is zero because we don't know what it is.
-  gHunspellAllocatedSize +=
-    mozilla::MemoryReporterMallocSizeOfForCounterInc(ptr, 0);
+  gHunspellAllocatedSize += HunspellMallocSizeOfForCounterInc(ptr, 0);
 }
 void HunspellReportMemoryDeallocation(void* ptr) {
   // |computedSize| is zero because we don't know what it is.
-  gHunspellAllocatedSize -=
-    mozilla::MemoryReporterMallocSizeOfForCounterDec(ptr, 0);
+  gHunspellAllocatedSize -= HunspellMallocSizeOfForCounterDec(ptr, 0);
 }
 static PRInt64 HunspellGetCurrentAllocatedSize() {
   return gHunspellAllocatedSize;
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(Hunspell,
     "explicit/spell-check",
     KIND_HEAP,
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1230,16 +1230,18 @@ XPCJSRuntime::~XPCJSRuntime()
 #endif
     }
 
     XPCPerThreadData::ShutDown();
 }
 
 namespace {
 
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
+
 void
 CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
 {
     // Append a new CompartmentStats to the vector.
     IterateData *data = static_cast<IterateData *>(vdata);
     CompartmentStats compartmentStats(cx, compartment);
     CompartmentStats *curr =
         data->compartmentStatsVector.AppendElement(compartmentStats);
@@ -1248,19 +1250,19 @@ CompartmentCallback(JSContext *cx, void 
     // Get the compartment-level numbers.
 #ifdef JS_METHODJIT
     size_t method, regexp, unused;
     compartment->sizeOfCode(&method, &regexp, &unused);
     JS_ASSERT(regexp == 0);     /* this execAlloc is only used for method code */
     curr->mjitCode = method + unused;
 #endif
     JS_GetTypeInferenceMemoryStats(cx, compartment, &curr->typeInferenceMemory,
-                                   MemoryReporterMallocSizeOf);
+                                   JsMallocSizeOf);
     curr->shapesCompartmentTables =
-        js::GetCompartmentShapeTableSize(compartment, MemoryReporterMallocSizeOf);
+        js::GetCompartmentShapeTableSize(compartment, JsMallocSizeOf);
 }
 
 void
 ChunkCallback(JSContext *cx, void *vdata, js::gc::Chunk *chunk)
 {
     IterateData *data = static_cast<IterateData *>(vdata);
     for (uint32 i = 0; i < js::gc::ArenasPerChunk; i++)
         if (chunk->decommittedArenas.get(i))
@@ -1295,63 +1297,63 @@ CellCallback(JSContext *cx, void *vdata,
         case JSTRACE_OBJECT:
         {
             JSObject *obj = static_cast<JSObject *>(thing);
             if (JS_ObjectIsFunction(cx, obj)) {
                 curr->gcHeapObjectsFunction += thingSize;
             } else {
                 curr->gcHeapObjectsNonFunction += thingSize;
             }
-            curr->objectSlots += js::GetObjectDynamicSlotSize(obj, MemoryReporterMallocSizeOf);
+            curr->objectSlots += js::GetObjectDynamicSlotSize(obj, JsMallocSizeOf);
             break;
         }
         case JSTRACE_STRING:
         {
             JSString *str = static_cast<JSString *>(thing);
             curr->gcHeapStrings += thingSize;
-            curr->stringChars += str->charsHeapSize(MemoryReporterMallocSizeOf);
+            curr->stringChars += str->charsHeapSize(JsMallocSizeOf);
             break;
         }
         case JSTRACE_SHAPE:
         {
             js::Shape *shape = static_cast<js::Shape *>(thing);
             if (shape->inDictionary()) {
                 curr->gcHeapShapesDict += thingSize;
                 curr->shapesExtraDictTables +=
-                    shape->sizeOfPropertyTable(MemoryReporterMallocSizeOf);
+                    shape->sizeOfPropertyTable(JsMallocSizeOf);
             } else {
                 curr->gcHeapShapesTree += thingSize;
                 curr->shapesExtraTreeTables +=
-                    shape->sizeOfPropertyTable(MemoryReporterMallocSizeOf);
+                    shape->sizeOfPropertyTable(JsMallocSizeOf);
                 curr->shapesExtraTreeShapeKids +=
-                    shape->sizeOfKids(MemoryReporterMallocSizeOf);
+                    shape->sizeOfKids(JsMallocSizeOf);
             }
             break;
         }
         case JSTRACE_BASE_SHAPE:
         {
             curr->gcHeapShapesBase += thingSize;
             break;
         }
         case JSTRACE_SCRIPT:
         {
             JSScript *script = static_cast<JSScript *>(thing);
             curr->gcHeapScripts += thingSize;
-            curr->scriptData += script->dataSize(MemoryReporterMallocSizeOf);
+            curr->scriptData += script->dataSize(JsMallocSizeOf);
 #ifdef JS_METHODJIT
-            curr->mjitData += script->jitDataSize(MemoryReporterMallocSizeOf);
+            curr->mjitData += script->jitDataSize(JsMallocSizeOf);
 #endif
             break;
         }
         case JSTRACE_TYPE_OBJECT:
         {
             js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
             curr->gcHeapTypeObjects += thingSize;
             JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory,
-                                           MemoryReporterMallocSizeOf);
+                                           JsMallocSizeOf);
             break;
         }
         case JSTRACE_XML:
         {
             curr->gcHeapXML += thingSize;
             break;
         }
     }
@@ -1553,59 +1555,59 @@ CollectCompartmentStatsForRuntime(JSRunt
         data->gcHeapChunkTotal =
             PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
             js::gc::ChunkSize;
 
         js::IterateCompartmentsArenasCells(cx, data, CompartmentCallback,
                                            ArenaCallback, CellCallback);
         js::IterateChunks(cx, data, ChunkCallback);
 
-        data->runtimeObject = MemoryReporterMallocSizeOf(rt, sizeof(JSRuntime));
+        data->runtimeObject = JsMallocSizeOf(rt, sizeof(JSRuntime));
 
         // Nb: we use sizeOfExcludingThis() because atomState.atoms is within
         // JSRuntime, and so counted when JSRuntime is counted.
         data->runtimeAtomsTable =
-            rt->atomState.atoms.sizeOfExcludingThis(MemoryReporterMallocSizeOf);
+            rt->atomState.atoms.sizeOfExcludingThis(JsMallocSizeOf);
 
         {
             #ifndef JS_THREADSAFE
             #error "This code assumes JS_THREADSAFE is defined"
             #endif
 
             // Need the GC lock to call JS_ContextIteratorUnlocked() and to
             // access rt->threads.
             js::AutoLockGC lock(rt);
 
             JSContext *acx, *iter = NULL;
             while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) {
                 data->runtimeContexts +=
-                    acx->sizeOfIncludingThis(MemoryReporterMallocSizeOf);
+                    acx->sizeOfIncludingThis(JsMallocSizeOf);
             }
 
             for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
                 JSThread *thread = r.front().value;
                 size_t normal, temporary, regexpCode, stackCommitted;
-                thread->sizeOfIncludingThis(MemoryReporterMallocSizeOf,
+                thread->sizeOfIncludingThis(JsMallocSizeOf,
                                             &normal,
                                             &temporary,
                                             &regexpCode,
                                             &stackCommitted);
 
                 data->runtimeThreadsNormal         += normal;
                 data->runtimeThreadsTemporary      += temporary;
                 data->runtimeThreadsRegexpCode     += regexpCode;
                 data->runtimeThreadsStackCommitted += stackCommitted;
             }
         }
 
         XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
         data->xpconnect +=
-            xpcrt->SizeOfIncludingThis(MemoryReporterMallocSizeOf);
+            xpcrt->SizeOfIncludingThis(JsMallocSizeOf);
         data->xpconnect +=
-            XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(MemoryReporterMallocSizeOf);
+            XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JsMallocSizeOf);
     }
 
     JS_DestroyContextNoGC(cx);
 
     // This is initialized to all bytes stored in used chunks, and then we
     // subtract used space from it each time around the loop.
     data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
                                    data->gcHeapChunkCleanUnused -
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -617,16 +617,18 @@ namespace {
 struct MemoryReporterData
 {
   nsIMemoryMultiReporterCallback* callback;
   nsISupports* closure;
 };
 
 } // anonymous namespace
 
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(LayoutMallocSizeOf, "layout")
+
 /* static */ PLDHashOperator
 PresShell::MemoryReporter::SizeEnumerator(PresShellPtrKey *aEntry,
                                           void *userArg)
 {
   PresShell *aShell = static_cast<PresShell*>(aEntry->GetKey());
   MemoryReporterData *data = (MemoryReporterData*)userArg;
 
   // Build the string "explicit/layout/shell(<uri of the document>)"
@@ -661,17 +663,17 @@ PresShell::MemoryReporter::SizeEnumerato
 
   PRUint32 arenasSize;
   arenasSize = aShell->EstimateMemoryUsed();
   arenasSize += aShell->mPresContext->EstimateMemoryUsed();
 
   PRUint32 styleSize;
   styleSize = aShell->StyleSet()->SizeOf();
 
-  PRInt64 textRunsSize = aShell->SizeOfTextRuns(MemoryReporterMallocSizeOf);
+  PRInt64 textRunsSize = aShell->SizeOfTextRuns(LayoutMallocSizeOf);
 
   data->callback->
     Callback(EmptyCString(), arenaPath, nsIMemoryReporter::KIND_HEAP,
              nsIMemoryReporter::UNITS_BYTES, arenasSize, kArenaDesc,
              data->closure);
 
   data->callback->
     Callback(EmptyCString(), stylePath, nsIMemoryReporter::KIND_HEAP,
@@ -683,16 +685,18 @@ PresShell::MemoryReporter::SizeEnumerato
       Callback(EmptyCString(), textRunsPath, nsIMemoryReporter::KIND_HEAP,
                nsIMemoryReporter::UNITS_BYTES, textRunsSize, kTextRunsDesc,
                data->closure);
   }
 
   return PL_DHASH_NEXT;
 }
 
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(GfxTextrunWordCacheMallocSizeOf, "gfx/textrun-word-cache")
+
 NS_IMETHODIMP
 PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
                                           nsISupports* aClosure)
 {
   MemoryReporterData data;
   data.callback = aCb;
   data.closure = aClosure;
 
@@ -704,17 +708,17 @@ PresShell::MemoryReporter::CollectReport
   NS_NAMED_LITERAL_CSTRING(kTextRunWordCachePath,
                            "explicit/gfx/textrun-word-cache");
   NS_NAMED_LITERAL_CSTRING(kTextRunWordCacheDesc,
                            "Memory used by cached text-runs that are "
                            "not owned by a PresShell's frame tree.");
 
   // now total up cached runs that aren't otherwise accounted for
   PRInt64 textRunWordCacheSize =
-    gfxTextRunWordCache::MaybeSizeOfExcludingThis(MemoryReporterMallocSizeOf);
+    gfxTextRunWordCache::MaybeSizeOfExcludingThis(GfxTextrunWordCacheMallocSizeOf);
 
   aCb->Callback(EmptyCString(), kTextRunWordCachePath,
                 nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
                 textRunWordCacheSize, kTextRunWordCacheDesc, aClosure);
 
   return NS_OK;
 }
 
--- a/memory/mozalloc/Makefile.in
+++ b/memory/mozalloc/Makefile.in
@@ -54,16 +54,21 @@ LIBRARY_NAME	= mozalloc
 DIST_INSTALL 	= 1
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 FORCE_STATIC_LIB= 1
 else
 FORCE_SHARED_LIB= 1
 endif
 
+# TODO: we do this in crashreporter and storage/src too, should be centralized
+ifeq ($(OS_ARCH),Linux)
+DEFINES += -DXP_LINUX
+endif
+
 ifeq (,$(filter-out OS2,$(OS_ARCH)))
 # The strndup declaration in string.h is in an ifdef __USE_GNU section
 DEFINES		+= -D_GNU_SOURCE
 endif
 
 EXPORTS_NAMESPACES 	= mozilla
 EXPORTS_mozilla 	=			\
 	mozalloc.h		 		\
--- a/memory/mozalloc/mozalloc.cpp
+++ b/memory/mozalloc/mozalloc.cpp
@@ -254,17 +254,20 @@ moz_valloc(size_t size)
 size_t
 moz_malloc_usable_size(void *ptr)
 {
     if (!ptr)
         return 0;
 
 #if defined(XP_MACOSX)
     return malloc_size(ptr);
-#elif defined(MOZ_MEMORY)
+#elif defined(MOZ_MEMORY) || defined(XP_LINUX)
+    // XXX: the |defined(XP_LINUX)| may be too lax;  some Linux installations
+    // might use a libc that doesn't have malloc_usable_size.  Let's fix this
+    // if/when it happens.
     return malloc_usable_size(ptr);
 #elif defined(XP_WIN)
     return _msize(ptr);
 #else
     return 0;
 #endif
 }
 
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -343,17 +343,17 @@ Service::getConnections(/* inout */ nsTA
 }
 
 void
 Service::shutdown()
 {
   NS_IF_RELEASE(sXPConnect);
 }
 
-sqlite3_vfs* ConstructTelemetryVFS();
+sqlite3_vfs *ConstructTelemetryVFS();
 
 #ifdef MOZ_MEMORY
 
 #  if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX)
 #    include "jemalloc.h"
 #  elif defined(XP_LINUX)
 // jemalloc is directly linked into firefox-bin; libxul doesn't link
 // with it.  So if we tried to use je_malloc_usable_size_in_advance directly
@@ -380,47 +380,47 @@ namespace {
 // allocated for a given request.  SQLite uses this function before all
 // allocations, and may be able to use any excess bytes caused by the rounding.
 //
 // Note: the wrappers for moz_malloc, moz_realloc and moz_malloc_usable_size
 // are necessary because the sqlite_mem_methods type signatures differ slightly
 // from the standard ones -- they use int instead of size_t.  But we don't need
 // a wrapper for moz_free.
 
-static void* sqliteMemMalloc(int n)
+static void *sqliteMemMalloc(int n)
 {
   return ::moz_malloc(n);
 }
 
-static void* sqliteMemRealloc(void* p, int n)
+static void *sqliteMemRealloc(void *p, int n)
 {
   return ::moz_realloc(p, n);
 }
 
-static int sqliteMemSize(void* p)
+static int sqliteMemSize(void *p)
 {
   return ::moz_malloc_usable_size(p);
 }
 
 static int sqliteMemRoundup(int n)
 {
   n = je_malloc_usable_size_in_advance(n);
 
   // jemalloc can return blocks of size 2 and 4, but SQLite requires that all
   // allocations be 8-aligned.  So we round up sub-8 requests to 8.  This
   // wastes a small amount of memory but is obviously safe.
   return n <= 8 ? 8 : n;
 }
 
-static int sqliteMemInit(void* p)
+static int sqliteMemInit(void *p)
 {
   return 0;
 }
 
-static void sqliteMemShutdown(void* p)
+static void sqliteMemShutdown(void *p)
 {
 }
 
 const sqlite3_mem_methods memMethods = {
   &sqliteMemMalloc,
   &moz_free,
   &sqliteMemRealloc,
   &sqliteMemSize,
@@ -740,17 +740,17 @@ Service::SetQuotaForFilenamePattern(cons
                                data, QuotaCallbackData::Destroy);
   NS_ENSURE_TRUE(rc == SQLITE_OK, convertResultCode(rc));
 
   data.forget();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-Service::UpdateQutoaInformationForFile(nsIFile* aFile)
+Service::UpdateQutoaInformationForFile(nsIFile *aFile)
 {
   NS_ENSURE_ARG_POINTER(aFile);
 
   nsCString path;
   nsresult rv = aFile->GetNativePath(path);
   NS_ENSURE_SUCCESS(rv, rv);
 
   int rc = ::sqlite3_quota_file(PromiseFlatCString(path).get());
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -1296,22 +1296,25 @@ StoreAndNotifyEmbedVisit(VisitData& aPla
     (void)NS_ProxyRelease(mainThread, aCallback, true);
   }
 
   VisitData noReferrer;
   nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(aPlace, noReferrer);
   (void)NS_DispatchToMainThread(event);
 }
 
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(HistoryLinksHashtableMallocSizeOf,
+                                     "history-links-hashtable")
+
 PRInt64 GetHistoryObserversSize()
 {
   History* history = History::GetService();
   if (!history)
     return 0;
-  return history->SizeOfIncludingThis(MemoryReporterMallocSizeOf);
+  return history->SizeOfIncludingThis(HistoryLinksHashtableMallocSizeOf);
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(HistoryService,
     "explicit/history-links-hashtable",
     KIND_HEAP,
     UNITS_BYTES,
     GetHistoryObserversSize,
     "Memory used by the hashtable of observers Places uses to notify objects of "
--- a/xpcom/base/Makefile.in
+++ b/xpcom/base/Makefile.in
@@ -87,16 +87,17 @@ EXPORTS		= \
 		nsDebugImpl.h \
 		nsIAllocator.h \
 		nsIID.h \
 		nsISupportsObsolete.h \
 		nsStackWalk.h \
 		nsTraceRefcntImpl.h \
 		nsWeakPtr.h \
 		nsInterfaceRequestorAgg.h \
+		dmd.h \
 		$(NULL)
 
 EXPORTS_NAMESPACES = mozilla
 
 EXPORTS_mozilla = \
 	FunctionTimer.h \
 	MapsMemoryReporter.h \
 	$(NULL)
new file mode 100644
--- /dev/null
+++ b/xpcom/base/dmd.h
@@ -0,0 +1,83 @@
+/*
+   ----------------------------------------------------------------
+   The following BSD-style license applies to this one file (dmd.h) only.
+   ----------------------------------------------------------------
+
+   The Initial Developer of the Original Code is
+   the Mozilla Foundation.
+   Portions created by the Initial Developer are Copyright (C) 2011
+   the Initial Developer. All Rights Reserved.
+
+   Contributor(s):
+     Nicholas Nethercote <nnethercote@mozilla.com>
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+   2. The origin of this software must not be misrepresented; you must 
+      not claim that you wrote the original software.  If you use this 
+      software in a product, an acknowledgment in the product 
+      documentation would be appreciated but is not required.
+
+   3. Altered source versions must be plainly marked as such, and must
+      not be misrepresented as being the original software.
+
+   4. The name of the author may not be used to endorse or promote 
+      products derived from this software without specific prior written 
+      permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __DMD_H
+#define __DMD_H
+
+#include "valgrind/valgrind.h"
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! 
+   This enum comprises an ABI exported by Valgrind to programs
+   which use client requests.  DO NOT CHANGE THE ORDER OF THESE
+   ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+   enum { 
+      VG_USERREQ__DMD_REPORT = VG_USERREQ_TOOL_BASE('D','M'),
+      VG_USERREQ__DMD_UNREPORT,
+      VG_USERREQ__DMD_CHECK_REPORTING
+   } Vg_DMDClientRequest;
+
+
+/* Mark heap block at _qzz_addr as reported for _qzz_len bytes.
+ * _qzz_name is the name of the reporter. */
+#define VALGRIND_DMD_REPORT(_qzz_addr,_qzz_len,_qzz_name)        \
+    VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */,      \
+                            VG_USERREQ__DMD_REPORT,              \
+                            (_qzz_addr), (_qzz_len), (_qzz_name), 0, 0)
+
+/* Mark heap block at _qzz_addr as not reported. */
+#define VALGRIND_DMD_UNREPORT(_qzz_addr)                         \
+    VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */,      \
+                            VG_USERREQ__DMD_UNREPORT,            \
+                            (_qzz_addr), 0, 0, 0, 0)
+
+/* Do a reporting check. */
+#define VALGRIND_DMD_CHECK_REPORTING                             \
+    VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */,      \
+                            VG_USERREQ__DMD_CHECK_REPORTING,     \
+                            0, 0, 0, 0, 0)
+
+#endif
+
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -303,79 +303,114 @@ interface nsIMemoryReporterManager : nsI
 
 #define NS_MEMORY_REPORTER_IMPLEMENT(_c, _p, _k, _u, _a, _d) \
         NS_MEMORY_REPORTER_IMPLEMENT_HELPER(_c, _p, _k, _u, _a, _d, _)
 #define NS_THREADSAFE_MEMORY_REPORTER_IMPLEMENT(_c, _p, _k, _u, _a, _d) \
         NS_MEMORY_REPORTER_IMPLEMENT_HELPER(_c, _p, _k, _u, _a, _d, _THREADSAFE_)
 
 #define NS_MEMORY_REPORTER_NAME(_classname)  MemoryReporter_##_classname
 
-nsresult NS_RegisterMemoryReporter (nsIMemoryReporter *reporter);
-nsresult NS_RegisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter);
+nsresult NS_RegisterMemoryReporter(nsIMemoryReporter *reporter);
+nsresult NS_RegisterMemoryMultiReporter(nsIMemoryMultiReporter *reporter);
+
+nsresult NS_UnregisterMemoryReporter(nsIMemoryReporter *reporter);
+nsresult NS_UnregisterMemoryMultiReporter(nsIMemoryMultiReporter *reporter);
 
-nsresult NS_UnregisterMemoryReporter (nsIMemoryReporter *reporter);
-nsresult NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter);
+// Because DMD is not a tool that comes with the standard Valgrind
+// distribution, we have to #include our own local copy of dmd.h.  Ugly but
+// unavoidable.
+#ifdef MOZ_DMD
+#if MOZ_MEMORY
+#error "--disable-jemalloc should have been forced when --enable-dmd was specified"
+#endif
+#include "dmd.h"
+#endif
 
 namespace mozilla {
 
 /*
- * This function should be used by all traversal-based memory reporters.
+ * Functions generated via this macro should be used by all traversal-based
+ * memory reporters.
+ *
  * - On platforms where moz_malloc_usable_size() returns 0 it just returns
- *   |computedSize| (this happens on platforms where malloc_usable_size() or
- *   equivalent isn't available).
- * - Otherwise, it |returns moz_malloc_usable_size(p)|, but only after doing
+ *   |computedSize|.  This happens on platforms where malloc_usable_size() or
+ *   equivalent isn't available.
+ *
+ * - Otherwise, it returns |moz_malloc_usable_size(p)|, but only after doing
  *   some sanity checking -- it will assert if the usable size is too
  *   dissimilar to |computedSize|.  (However, this checking is skipped if
  *   |computedSize| is zero, which is useful if the computation is not worth
  *   the effort, e.g. because it's tricky and the |computedSize| would be
  *   small.)
- */
-size_t MemoryReporterMallocSizeOf(const void *ptr, size_t computedSize);
-
-/*
- * These functions are like MemoryReporterMallocSizeOf(), and should be used by
- * all counter-based memory reporters when incrementing/decrementing a counter.
- */
-size_t MemoryReporterMallocSizeOfForCounterInc(const void *ptr,
-                                               size_t computedSize);
-size_t MemoryReporterMallocSizeOfForCounterDec(const void *ptr,
-                                               size_t computedSize);
-
-/*
- * For the purposes of debugging, temporary profiling, and DMD integration, it
- * is sometimes useful to temporarily create multiple variants of
- * MemoryReporterMallocSizeOf(), with each one distinguished by a string
- * |name|.  This macro makes creating such variants easy.  |name| isn't used,
- * but it will be if extra debugging code is temporarily added.
+ *
+ * You might be wondering why we have a macro that creates multiple functions
+ * distinguished only by |name|, instead of a single MemoryReporterMallocSizeOf
+ * function.  It's mostly to help with DMD integration, though it sometimes
+ * also helps with debugging and temporary ad hoc profiling.  The |name| chosen
+ * doesn't matter greatly, but it's best to make it similar to the path used by
+ * the relevant memory reporter(s).
  */
 #define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(fn, name)                        \
   size_t fn(const void *ptr, size_t computedSize)                             \
   {                                                                           \
       size_t usable = moz_malloc_usable_size((void*)ptr);                     \
       if (!usable) {                                                          \
           return computedSize;                                                \
       }                                                                       \
+      VALGRIND_DMD_REPORT(ptr, usable, name);                                 \
       NS_MEMORY_REPORTER_CHECK_SIZES(usable, computedSize);                   \
       return usable;                                                          \
   }
 
 /*
- * This is used by the MemoryReporterMallocSizeOf* functions for checking
- * usable against computedSize.
+ * Like NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN, but the created function sends an
+ * "unreport" message to DMD.
+ */
+#define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN_UN(fn)                           \
+  size_t fn(const void *ptr, size_t computedSize)                             \
+  {                                                                           \
+      size_t usable = moz_malloc_usable_size((void*)ptr);                     \
+      if (!usable) {                                                          \
+          return computedSize;                                                \
+      }                                                                       \
+      VALGRIND_DMD_UNREPORT(ptr);                                             \
+      NS_MEMORY_REPORTER_CHECK_SIZES(usable, computedSize);                   \
+      return usable;                                                          \
+  }
+
+/*
+ * This is used by the NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN* macros for
+ * checking |usable| against |computedSize|.
  */
 #define NS_MEMORY_REPORTER_CHECK_SIZES(usable, computedSize)                  \
   do {                                                                        \
     /* The factor of two is because no reasonable heap allocator will    */   \
     /* return a block more than twice the requested size.  The one       */   \
     /* exception is that a request less than N bytes may be rounded up   */   \
     /* by the allocator to N bytes (we use N = 16 in our checking        */   \
     /* because that's what the default allocator on Mac uses).  Also, if */   \
     /* computedSize is 0 we don't check it against usable.               */   \
     NS_ASSERTION(usable >= computedSize,                                      \
-                 "MemoryReporterMallocSizeOf: computedSize is too big");      \
+                 "NS_MEMORY_REPORTER_CHECK_SIZES: computedSize is too big");  \
     NS_ASSERTION(usable < computedSize * 2 || usable <= 16 ||                 \
                  computedSize == 0,                                           \
-                 "MemoryReporterMallocSizeOf: computedSize is too small");    \
+                 "NS_MEMORY_REPORTER_CHECK_SIZES: computedSize is too small");\
   } while(0)
 
+#ifdef MOZ_DMD
+
+/*
+ * This runs all the memory reporters but does nothing with the results;  i.e.
+ * it does the minimal amount of work possible for DMD to do its thing.  Then
+ * it dumps the DMD output to stderr (or somewhere else, if one of
+ * DMD/Valgrind's logging options was used).
+ */
+void DMDCheckAndDump();
+
+#else
+
+#define VALGRIND_DMD_REPORT(ptr, usable, name)
+#define VALGRIND_DMD_UNREPORT(ptr)
+
+#endif  /* defined(MOZ_DMD) */
 }
 
 %}
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -418,26 +418,38 @@ static PRInt64 GetHeapUnallocated()
 static PRInt64 GetHeapAllocated()
 {
     struct mstats stats = mstats();
     return (PRInt64) stats.bytes_used;
 }
 
 static PRInt64 GetHeapZone0Committed()
 {
+#ifdef MOZ_DMD
+    // malloc_zone_statistics() crashes when run under DMD because Valgrind
+    // doesn't intercept it.  This measurement isn't important for DMD, so
+    // don't even try.
+    return (PRInt64) -1;
+#else
     malloc_statistics_t stats;
     malloc_zone_statistics(malloc_default_zone(), &stats);
     return stats.size_in_use;
+#endif
 }
 
 static PRInt64 GetHeapZone0Used()
 {
+#ifdef MOZ_DMD
+    // See comment in GetHeapZone0Committed above.
+    return (PRInt64) -1;
+#else
     malloc_statistics_t stats;
     malloc_zone_statistics(malloc_default_zone(), &stats);
     return stats.size_allocated;
+#endif
 }
 
 NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Committed,
     "heap-zone0-committed",
     KIND_OTHER,
     UNITS_BYTES,
     GetHeapZone0Committed,
     "Memory mapped by the heap allocator that is committed in the default "
@@ -854,14 +866,64 @@ NS_UnregisterMemoryMultiReporter (nsIMem
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
     if (mgr == nsnull)
         return NS_ERROR_FAILURE;
     return mgr->UnregisterMultiReporter(reporter);
 }
 
 namespace mozilla {
 
-NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOf, "default")
+#ifdef MOZ_DMD
+
+class NullMultiReporterCallback : public nsIMemoryMultiReporterCallback
+{
+public:
+    NS_DECL_ISUPPORTS
+
+    NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
+                        PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount,
+                        const nsACString &aDescription,
+                        nsISupports *aData)
+    {
+        // Do nothing;  the reporter has already reported to DMD.
+        return NS_OK;
+    }
+};
+NS_IMPL_ISUPPORTS1(
+  NullMultiReporterCallback
+, nsIMemoryMultiReporterCallback
+)
+
+void
+DMDCheckAndDump()
+{
+    nsCOMPtr<nsIMemoryReporterManager> mgr =
+        do_GetService("@mozilla.org/memory-reporter-manager;1");
 
-NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOfForCounterInc, "default")
-NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOfForCounterDec, "default")
+    // Do vanilla reporters.
+    nsCOMPtr<nsISimpleEnumerator> e;
+    mgr->EnumerateReporters(getter_AddRefs(e));
+    bool more;
+    while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
+        nsCOMPtr<nsIMemoryReporter> r;
+        e->GetNext(getter_AddRefs(r));
+
+        // Just getting the amount is enough for the reporter to report to DMD.
+        PRInt64 amount;
+        (void)r->GetAmount(&amount);
+    }
+
+    // Do multi-reporters.
+    nsCOMPtr<nsISimpleEnumerator> e2;
+    mgr->EnumerateMultiReporters(getter_AddRefs(e2));
+    nsRefPtr<NullMultiReporterCallback> cb = new NullMultiReporterCallback();
+    while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
+      nsCOMPtr<nsIMemoryMultiReporter> r;
+      e2->GetNext(getter_AddRefs(r));
+      r->CollectReports(cb, nsnull);
+    }
+
+    VALGRIND_DMD_CHECK_REPORTING;
+}
+
+#endif  /* defined(MOZ_DMD) */
 
 }