Bug 704400 - Implement --enable-dmd. r=khuey.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 08 Dec 2011 19:09:36 -0800
changeset 82353 a8196c95d4c94f2f442f632f10aceb5108f18358
parent 82351 6bafbaa9ac33d146df75546a7a47dc1fd59b4c7a
child 82354 f137857feed3d93dbc8ee1b42c90c743472157f8
push idunknown
push userunknown
push dateunknown
reviewerskhuey
bugs704400
milestone11.0a1
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) */
 
 }