Bug 704400 - Implement --enable-dmd. r=khuey.

This commit is contained in:
Nicholas Nethercote 2011-12-08 19:09:36 -08:00
parent 2f644b6b1b
commit 688c469bc9
13 changed files with 306 additions and 66 deletions

View File

@ -2114,10 +2114,24 @@ if test -n "$MOZ_VALGRIND"; then
AC_MSG_ERROR( AC_MSG_ERROR(
[--enable-valgrind specified but Valgrind is not installed])) [--enable-valgrind specified but Valgrind is not installed]))
AC_DEFINE(MOZ_VALGRIND) AC_DEFINE(MOZ_VALGRIND)
MOZ_VALGRIND=1
fi fi
AC_SUBST(MOZ_VALGRIND) 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 ========================================================
dnl jprof dnl jprof
dnl ======================================================== dnl ========================================================
@ -7095,7 +7109,7 @@ dnl = Enable trace malloc
dnl ======================================================== dnl ========================================================
NS_TRACE_MALLOC=${MOZ_TRACE_MALLOC} NS_TRACE_MALLOC=${MOZ_TRACE_MALLOC}
MOZ_ARG_ENABLE_BOOL(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=1,
NS_TRACE_MALLOC= ) NS_TRACE_MALLOC= )
if test "$NS_TRACE_MALLOC"; then if test "$NS_TRACE_MALLOC"; then
@ -7117,6 +7131,9 @@ MOZ_ARG_ENABLE_BOOL(jemalloc,
if test "$NS_TRACE_MALLOC"; then if test "$NS_TRACE_MALLOC"; then
MOZ_MEMORY= MOZ_MEMORY=
fi fi
if test "$MOZ_DMD"; then
MOZ_MEMORY=
fi
if test "${OS_TARGET}" = "Android"; then if test "${OS_TARGET}" = "Android"; then
dnl On Android, we use WRAP_LDFLAGS to link everything to mozutils dnl On Android, we use WRAP_LDFLAGS to link everything to mozutils

View File

@ -2944,6 +2944,25 @@ static JSFunctionSpec JProfFunctions[] = {
#endif /* defined(MOZ_JPROF) */ #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 nsresult
nsJSContext::InitClasses(JSObject* aGlobalObj) nsJSContext::InitClasses(JSObject* aGlobalObj)
{ {
@ -2967,6 +2986,11 @@ nsJSContext::InitClasses(JSObject* aGlobalObj)
::JS_DefineFunctions(mContext, aGlobalObj, JProfFunctions); ::JS_DefineFunctions(mContext, aGlobalObj, JProfFunctions);
#endif #endif
#ifdef MOZ_DMD
// Attempt to initialize DMD functions
::JS_DefineFunctions(mContext, aGlobalObj, DMDFunctions);
#endif
JSOptionChangedCallback(js_options_dot_str, this); JSOptionChangedCallback(js_options_dot_str, this);
return rv; return rv;

View File

@ -98,15 +98,16 @@ NS_IMPL_CYCLE_COLLECTION_3(mozHunspell,
// Memory reporting stuff. // Memory reporting stuff.
static PRInt64 gHunspellAllocatedSize = 0; static PRInt64 gHunspellAllocatedSize = 0;
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(HunspellMallocSizeOfForCounterInc, "hunspell")
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN_UN(HunspellMallocSizeOfForCounterDec)
void HunspellReportMemoryAllocation(void* ptr) { void HunspellReportMemoryAllocation(void* ptr) {
// |computedSize| is zero because we don't know what it is. // |computedSize| is zero because we don't know what it is.
gHunspellAllocatedSize += gHunspellAllocatedSize += HunspellMallocSizeOfForCounterInc(ptr, 0);
mozilla::MemoryReporterMallocSizeOfForCounterInc(ptr, 0);
} }
void HunspellReportMemoryDeallocation(void* ptr) { void HunspellReportMemoryDeallocation(void* ptr) {
// |computedSize| is zero because we don't know what it is. // |computedSize| is zero because we don't know what it is.
gHunspellAllocatedSize -= gHunspellAllocatedSize -= HunspellMallocSizeOfForCounterDec(ptr, 0);
mozilla::MemoryReporterMallocSizeOfForCounterDec(ptr, 0);
} }
static PRInt64 HunspellGetCurrentAllocatedSize() { static PRInt64 HunspellGetCurrentAllocatedSize() {
return gHunspellAllocatedSize; return gHunspellAllocatedSize;

View File

@ -1235,6 +1235,8 @@ XPCJSRuntime::~XPCJSRuntime()
namespace { namespace {
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js")
void void
CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment) CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
{ {
@ -1253,9 +1255,9 @@ CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
curr->mjitCode = method + unused; curr->mjitCode = method + unused;
#endif #endif
JS_GetTypeInferenceMemoryStats(cx, compartment, &curr->typeInferenceMemory, JS_GetTypeInferenceMemoryStats(cx, compartment, &curr->typeInferenceMemory,
MemoryReporterMallocSizeOf); JsMallocSizeOf);
curr->shapesCompartmentTables = curr->shapesCompartmentTables =
js::GetCompartmentShapeTableSize(compartment, MemoryReporterMallocSizeOf); js::GetCompartmentShapeTableSize(compartment, JsMallocSizeOf);
} }
void void
@ -1300,14 +1302,14 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
} else { } else {
curr->gcHeapObjectsNonFunction += thingSize; curr->gcHeapObjectsNonFunction += thingSize;
} }
curr->objectSlots += js::GetObjectDynamicSlotSize(obj, MemoryReporterMallocSizeOf); curr->objectSlots += js::GetObjectDynamicSlotSize(obj, JsMallocSizeOf);
break; break;
} }
case JSTRACE_STRING: case JSTRACE_STRING:
{ {
JSString *str = static_cast<JSString *>(thing); JSString *str = static_cast<JSString *>(thing);
curr->gcHeapStrings += thingSize; curr->gcHeapStrings += thingSize;
curr->stringChars += str->charsHeapSize(MemoryReporterMallocSizeOf); curr->stringChars += str->charsHeapSize(JsMallocSizeOf);
break; break;
} }
case JSTRACE_SHAPE: case JSTRACE_SHAPE:
@ -1316,13 +1318,13 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
if (shape->inDictionary()) { if (shape->inDictionary()) {
curr->gcHeapShapesDict += thingSize; curr->gcHeapShapesDict += thingSize;
curr->shapesExtraDictTables += curr->shapesExtraDictTables +=
shape->sizeOfPropertyTable(MemoryReporterMallocSizeOf); shape->sizeOfPropertyTable(JsMallocSizeOf);
} else { } else {
curr->gcHeapShapesTree += thingSize; curr->gcHeapShapesTree += thingSize;
curr->shapesExtraTreeTables += curr->shapesExtraTreeTables +=
shape->sizeOfPropertyTable(MemoryReporterMallocSizeOf); shape->sizeOfPropertyTable(JsMallocSizeOf);
curr->shapesExtraTreeShapeKids += curr->shapesExtraTreeShapeKids +=
shape->sizeOfKids(MemoryReporterMallocSizeOf); shape->sizeOfKids(JsMallocSizeOf);
} }
break; break;
} }
@ -1335,9 +1337,9 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
{ {
JSScript *script = static_cast<JSScript *>(thing); JSScript *script = static_cast<JSScript *>(thing);
curr->gcHeapScripts += thingSize; curr->gcHeapScripts += thingSize;
curr->scriptData += script->dataSize(MemoryReporterMallocSizeOf); curr->scriptData += script->dataSize(JsMallocSizeOf);
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
curr->mjitData += script->jitDataSize(MemoryReporterMallocSizeOf); curr->mjitData += script->jitDataSize(JsMallocSizeOf);
#endif #endif
break; break;
} }
@ -1346,7 +1348,7 @@ CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing); js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
curr->gcHeapTypeObjects += thingSize; curr->gcHeapTypeObjects += thingSize;
JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory, JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory,
MemoryReporterMallocSizeOf); JsMallocSizeOf);
break; break;
} }
case JSTRACE_XML: case JSTRACE_XML:
@ -1558,12 +1560,12 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
ArenaCallback, CellCallback); ArenaCallback, CellCallback);
js::IterateChunks(cx, data, ChunkCallback); 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 // Nb: we use sizeOfExcludingThis() because atomState.atoms is within
// JSRuntime, and so counted when JSRuntime is counted. // JSRuntime, and so counted when JSRuntime is counted.
data->runtimeAtomsTable = data->runtimeAtomsTable =
rt->atomState.atoms.sizeOfExcludingThis(MemoryReporterMallocSizeOf); rt->atomState.atoms.sizeOfExcludingThis(JsMallocSizeOf);
{ {
#ifndef JS_THREADSAFE #ifndef JS_THREADSAFE
@ -1577,13 +1579,13 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
JSContext *acx, *iter = NULL; JSContext *acx, *iter = NULL;
while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) { while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) {
data->runtimeContexts += data->runtimeContexts +=
acx->sizeOfIncludingThis(MemoryReporterMallocSizeOf); acx->sizeOfIncludingThis(JsMallocSizeOf);
} }
for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
JSThread *thread = r.front().value; JSThread *thread = r.front().value;
size_t normal, temporary, regexpCode, stackCommitted; size_t normal, temporary, regexpCode, stackCommitted;
thread->sizeOfIncludingThis(MemoryReporterMallocSizeOf, thread->sizeOfIncludingThis(JsMallocSizeOf,
&normal, &normal,
&temporary, &temporary,
&regexpCode, &regexpCode,
@ -1598,9 +1600,9 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance(); XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
data->xpconnect += data->xpconnect +=
xpcrt->SizeOfIncludingThis(MemoryReporterMallocSizeOf); xpcrt->SizeOfIncludingThis(JsMallocSizeOf);
data->xpconnect += data->xpconnect +=
XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(MemoryReporterMallocSizeOf); XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JsMallocSizeOf);
} }
JS_DestroyContextNoGC(cx); JS_DestroyContextNoGC(cx);

View File

@ -622,6 +622,8 @@ struct MemoryReporterData
} // anonymous namespace } // anonymous namespace
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(LayoutMallocSizeOf, "layout")
/* static */ PLDHashOperator /* static */ PLDHashOperator
PresShell::MemoryReporter::SizeEnumerator(PresShellPtrKey *aEntry, PresShell::MemoryReporter::SizeEnumerator(PresShellPtrKey *aEntry,
void *userArg) void *userArg)
@ -666,7 +668,7 @@ PresShell::MemoryReporter::SizeEnumerator(PresShellPtrKey *aEntry,
PRUint32 styleSize; PRUint32 styleSize;
styleSize = aShell->StyleSet()->SizeOf(); styleSize = aShell->StyleSet()->SizeOf();
PRInt64 textRunsSize = aShell->SizeOfTextRuns(MemoryReporterMallocSizeOf); PRInt64 textRunsSize = aShell->SizeOfTextRuns(LayoutMallocSizeOf);
data->callback-> data->callback->
Callback(EmptyCString(), arenaPath, nsIMemoryReporter::KIND_HEAP, Callback(EmptyCString(), arenaPath, nsIMemoryReporter::KIND_HEAP,
@ -688,6 +690,8 @@ PresShell::MemoryReporter::SizeEnumerator(PresShellPtrKey *aEntry,
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(GfxTextrunWordCacheMallocSizeOf, "gfx/textrun-word-cache")
NS_IMETHODIMP NS_IMETHODIMP
PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
nsISupports* aClosure) nsISupports* aClosure)
@ -709,7 +713,7 @@ PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
// now total up cached runs that aren't otherwise accounted for // now total up cached runs that aren't otherwise accounted for
PRInt64 textRunWordCacheSize = PRInt64 textRunWordCacheSize =
gfxTextRunWordCache::MaybeSizeOfExcludingThis(MemoryReporterMallocSizeOf); gfxTextRunWordCache::MaybeSizeOfExcludingThis(GfxTextrunWordCacheMallocSizeOf);
aCb->Callback(EmptyCString(), kTextRunWordCachePath, aCb->Callback(EmptyCString(), kTextRunWordCachePath,
nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,

View File

@ -59,6 +59,11 @@ else
FORCE_SHARED_LIB= 1 FORCE_SHARED_LIB= 1
endif 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))) ifeq (,$(filter-out OS2,$(OS_ARCH)))
# The strndup declaration in string.h is in an ifdef __USE_GNU section # The strndup declaration in string.h is in an ifdef __USE_GNU section
DEFINES += -D_GNU_SOURCE DEFINES += -D_GNU_SOURCE

View File

@ -259,7 +259,10 @@ moz_malloc_usable_size(void *ptr)
#if defined(XP_MACOSX) #if defined(XP_MACOSX)
return malloc_size(ptr); 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); return malloc_usable_size(ptr);
#elif defined(XP_WIN) #elif defined(XP_WIN)
return _msize(ptr); return _msize(ptr);

View File

@ -348,7 +348,7 @@ Service::shutdown()
NS_IF_RELEASE(sXPConnect); NS_IF_RELEASE(sXPConnect);
} }
sqlite3_vfs* ConstructTelemetryVFS(); sqlite3_vfs *ConstructTelemetryVFS();
#ifdef MOZ_MEMORY #ifdef MOZ_MEMORY
@ -385,17 +385,17 @@ namespace {
// from the standard ones -- they use int instead of size_t. But we don't need // from the standard ones -- they use int instead of size_t. But we don't need
// a wrapper for moz_free. // a wrapper for moz_free.
static void* sqliteMemMalloc(int n) static void *sqliteMemMalloc(int n)
{ {
return ::moz_malloc(n); return ::moz_malloc(n);
} }
static void* sqliteMemRealloc(void* p, int n) static void *sqliteMemRealloc(void *p, int n)
{ {
return ::moz_realloc(p, n); return ::moz_realloc(p, n);
} }
static int sqliteMemSize(void* p) static int sqliteMemSize(void *p)
{ {
return ::moz_malloc_usable_size(p); return ::moz_malloc_usable_size(p);
} }
@ -410,12 +410,12 @@ static int sqliteMemRoundup(int n)
return n <= 8 ? 8 : n; return n <= 8 ? 8 : n;
} }
static int sqliteMemInit(void* p) static int sqliteMemInit(void *p)
{ {
return 0; return 0;
} }
static void sqliteMemShutdown(void* p) static void sqliteMemShutdown(void *p)
{ {
} }
@ -745,7 +745,7 @@ Service::SetQuotaForFilenamePattern(const nsACString &aPattern,
} }
NS_IMETHODIMP NS_IMETHODIMP
Service::UpdateQutoaInformationForFile(nsIFile* aFile) Service::UpdateQutoaInformationForFile(nsIFile *aFile)
{ {
NS_ENSURE_ARG_POINTER(aFile); NS_ENSURE_ARG_POINTER(aFile);

View File

@ -1301,12 +1301,15 @@ StoreAndNotifyEmbedVisit(VisitData& aPlace,
(void)NS_DispatchToMainThread(event); (void)NS_DispatchToMainThread(event);
} }
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(HistoryLinksHashtableMallocSizeOf,
"history-links-hashtable")
PRInt64 GetHistoryObserversSize() PRInt64 GetHistoryObserversSize()
{ {
History* history = History::GetService(); History* history = History::GetService();
if (!history) if (!history)
return 0; return 0;
return history->SizeOfIncludingThis(MemoryReporterMallocSizeOf); return history->SizeOfIncludingThis(HistoryLinksHashtableMallocSizeOf);
} }
NS_MEMORY_REPORTER_IMPLEMENT(HistoryService, NS_MEMORY_REPORTER_IMPLEMENT(HistoryService,

View File

@ -92,6 +92,7 @@ EXPORTS = \
nsTraceRefcntImpl.h \ nsTraceRefcntImpl.h \
nsWeakPtr.h \ nsWeakPtr.h \
nsInterfaceRequestorAgg.h \ nsInterfaceRequestorAgg.h \
dmd.h \
$(NULL) $(NULL)
EXPORTS_NAMESPACES = mozilla EXPORTS_NAMESPACES = mozilla

83
xpcom/base/dmd.h Normal file
View File

@ -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

View File

@ -308,43 +308,45 @@ interface nsIMemoryReporterManager : nsISupports
#define NS_MEMORY_REPORTER_NAME(_classname) MemoryReporter_##_classname #define NS_MEMORY_REPORTER_NAME(_classname) MemoryReporter_##_classname
nsresult NS_RegisterMemoryReporter (nsIMemoryReporter *reporter); nsresult NS_RegisterMemoryReporter(nsIMemoryReporter *reporter);
nsresult NS_RegisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter); nsresult NS_RegisterMemoryMultiReporter(nsIMemoryMultiReporter *reporter);
nsresult NS_UnregisterMemoryReporter (nsIMemoryReporter *reporter); nsresult NS_UnregisterMemoryReporter(nsIMemoryReporter *reporter);
nsresult NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *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 { 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 * - On platforms where moz_malloc_usable_size() returns 0 it just returns
* |computedSize| (this happens on platforms where malloc_usable_size() or * |computedSize|. This happens on platforms where malloc_usable_size() or
* equivalent isn't available). * equivalent isn't available.
* - Otherwise, it |returns moz_malloc_usable_size(p)|, but only after doing *
* - Otherwise, it returns |moz_malloc_usable_size(p)|, but only after doing
* some sanity checking -- it will assert if the usable size is too * some sanity checking -- it will assert if the usable size is too
* dissimilar to |computedSize|. (However, this checking is skipped if * dissimilar to |computedSize|. (However, this checking is skipped if
* |computedSize| is zero, which is useful if the computation is not worth * |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 * the effort, e.g. because it's tricky and the |computedSize| would be
* small.) * small.)
*/ *
size_t MemoryReporterMallocSizeOf(const void *ptr, size_t computedSize); * 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
* These functions are like MemoryReporterMallocSizeOf(), and should be used by * also helps with debugging and temporary ad hoc profiling. The |name| chosen
* all counter-based memory reporters when incrementing/decrementing a counter. * doesn't matter greatly, but it's best to make it similar to the path used by
*/ * the relevant memory reporter(s).
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.
*/ */
#define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(fn, name) \ #define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(fn, name) \
size_t fn(const void *ptr, size_t computedSize) \ size_t fn(const void *ptr, size_t computedSize) \
@ -353,13 +355,30 @@ size_t MemoryReporterMallocSizeOfForCounterDec(const void *ptr,
if (!usable) { \ if (!usable) { \
return computedSize; \ return computedSize; \
} \ } \
VALGRIND_DMD_REPORT(ptr, usable, name); \
NS_MEMORY_REPORTER_CHECK_SIZES(usable, computedSize); \ NS_MEMORY_REPORTER_CHECK_SIZES(usable, computedSize); \
return usable; \ return usable; \
} }
/* /*
* This is used by the MemoryReporterMallocSizeOf* functions for checking * Like NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN, but the created function sends an
* usable against computedSize. * "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) \ #define NS_MEMORY_REPORTER_CHECK_SIZES(usable, computedSize) \
do { \ do { \
@ -370,12 +389,28 @@ size_t MemoryReporterMallocSizeOfForCounterDec(const void *ptr,
/* because that's what the default allocator on Mac uses). Also, if */ \ /* because that's what the default allocator on Mac uses). Also, if */ \
/* computedSize is 0 we don't check it against usable. */ \ /* computedSize is 0 we don't check it against usable. */ \
NS_ASSERTION(usable >= computedSize, \ 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 || \ NS_ASSERTION(usable < computedSize * 2 || usable <= 16 || \
computedSize == 0, \ computedSize == 0, \
"MemoryReporterMallocSizeOf: computedSize is too small"); \ "NS_MEMORY_REPORTER_CHECK_SIZES: computedSize is too small");\
} while(0) } 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) */
} }
%} %}

View File

@ -423,16 +423,28 @@ static PRInt64 GetHeapAllocated()
static PRInt64 GetHeapZone0Committed() 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_statistics_t stats;
malloc_zone_statistics(malloc_default_zone(), &stats); malloc_zone_statistics(malloc_default_zone(), &stats);
return stats.size_in_use; return stats.size_in_use;
#endif
} }
static PRInt64 GetHeapZone0Used() static PRInt64 GetHeapZone0Used()
{ {
#ifdef MOZ_DMD
// See comment in GetHeapZone0Committed above.
return (PRInt64) -1;
#else
malloc_statistics_t stats; malloc_statistics_t stats;
malloc_zone_statistics(malloc_default_zone(), &stats); malloc_zone_statistics(malloc_default_zone(), &stats);
return stats.size_allocated; return stats.size_allocated;
#endif
} }
NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Committed, NS_MEMORY_REPORTER_IMPLEMENT(HeapZone0Committed,
@ -859,9 +871,59 @@ NS_UnregisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter)
namespace mozilla { namespace mozilla {
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOf, "default") #ifdef MOZ_DMD
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOfForCounterInc, "default") class NullMultiReporterCallback : public nsIMemoryMultiReporterCallback
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MemoryReporterMallocSizeOfForCounterDec, "default") {
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");
// 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) */
} }