Bug 1063247: Let embeddings tell Debugger how to find the size of a malloc'd block of memory. r=terrence
☠☠ backed out by 04be894027e6 ☠ ☠
authorJim Blandy <jimb@mozilla.com>
Fri, 05 Sep 2014 10:43:36 -0700
changeset 203894 a6033944c4c1
parent 203893 c7ffa64956c6
child 203895 c3d55b8fdc58
push id48763
push userjblandy@mozilla.com
push date2014-09-05 17:45 +0000
treeherdermozilla-inbound@a6033944c4c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1063247
milestone35.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 1063247: Let embeddings tell Debugger how to find the size of a malloc'd block of memory. r=terrence
js/public/Debug.h
js/src/builtin/TestingFunctions.cpp
js/src/configure.in
js/src/shell/js.cpp
js/src/vm/DebuggerMemory.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
--- a/js/public/Debug.h
+++ b/js/public/Debug.h
@@ -6,16 +6,17 @@
 
 // Interfaces by which the embedding can interact with the Debugger API.
 
 #ifndef js_Debug_h
 #define js_Debug_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 
 #include "jspubtd.h"
 
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 
 namespace js {
@@ -244,13 +245,26 @@ class BuilderOrigin : public Builder {
   public:
     BuilderOrigin(JSContext *cx, js::Debugger *debugger_)
       : Builder(cx, debugger_)
     { }
 
     JSObject *unwrap(Object &object) { return unwrapAny(object); }
 };
 
+
+// Finding the size of blocks allocated with malloc
+// ------------------------------------------------
+//
+// Debugger.Memory wants to be able to report how many bytes items in memory are
+// consuming. To do this, it needs a function that accepts a pointer to a block,
+// and returns the number of bytes allocated to that block. SpiderMonkey itself
+// doesn't know which function is appropriate to use, but the embedding does.
+
+// Tell Debuggers in |runtime| to use |mallocSizeOf| to find the size of
+// malloc'd blocks.
+void SetDebuggerMallocSizeOf(JSRuntime *runtime, mozilla::MallocSizeOf mallocSizeOf);
+
 } // namespace dbg
 } // namespace JS
 
 
 #endif /* js_Debug_h */
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1987,16 +1987,29 @@ IsSimdAvailable(JSContext *cx, unsigned 
     bool available = false;
 #else
     bool available = cx->jitSupportsSimd();
 #endif
     args.rval().set(BooleanValue(available));
     return true;
 }
 
+static bool
+ByteSize(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
+    JS::ubi::Node node = args.get(0);
+    if (node)
+        args.rval().set(NumberValue(node.size(mallocSizeOf)));
+    else
+        args.rval().setUndefined();
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
 
     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
@@ -2307,16 +2320,21 @@ static const JSFunctionSpecWithHelp Test
     JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0,
 "getBacktrace([options])",
 "  Return the current stack as a string. Takes an optional options object,\n"
 "  which may contain any or all of the boolean properties\n"
 "    options.args - show arguments to each function\n"
 "    options.locals - show local variables in each frame\n"
 "    options.thisprops - show the properties of the 'this' object of each frame\n"),
 
+    JS_FN_HELP("byteSize", ByteSize, 1, 0,
+"byteSize(value)",
+"  Return the size in bytes occupied by |value|, or |undefined| if value\n"
+"  is not allocated in memory.\n"),
+
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3909,18 +3909,18 @@ dnl top-level configure may override thi
 MOZ_CONFIG_ICU()
 
 MOZ_SUBCONFIGURE_ICU()
 
 dnl ========================================================
 dnl JavaScript shell
 dnl ========================================================
 
-AC_HAVE_FUNCS(setlocale)
-AC_HAVE_FUNCS(localeconv)
+AC_CHECK_HEADERS(malloc.h)
+AC_CHECK_FUNCS(setlocale localeconv malloc_size malloc_usable_size)
 
 AC_SUBST(MOZILLA_VERSION)
 
 AC_SUBST(ac_configure_args)
 
 AC_SUBST(TOOLCHAIN_PREFIX)
 
 if test -n "$JS_STANDALONE"; then
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -16,16 +16,19 @@
 # include <process.h>
 #endif
 #include <errno.h>
 #include <fcntl.h>
 #if defined(XP_WIN)
 # include <io.h>     /* for isatty() */
 #endif
 #include <locale.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
 #include <math.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #ifdef XP_UNIX
@@ -51,16 +54,17 @@
 #endif
 #include "jswrapper.h"
 #include "prmjtime.h"
 
 #include "builtin/TestingFunctions.h"
 #include "frontend/Parser.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/Ion.h"
+#include "js/Debug.h"
 #include "js/OldDebugAPI.h"
 #include "js/StructuredClone.h"
 #include "perf/jsperf.h"
 #include "shell/jsheaptools.h"
 #include "shell/jsoptparse.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
@@ -5990,16 +5994,31 @@ MaybeOverrideOutFileFromEnv(const char* 
 
 /* Pretend we can always preserve wrappers for dummy DOM objects. */
 static bool
 DummyPreserveWrapperCallback(JSContext *cx, JSObject *obj)
 {
     return true;
 }
 
+size_t
+ShellMallocSizeOf(const void *ptr)
+{
+    if (!ptr)
+        return 0;
+
+#if defined(HAVE_MALLOC_USABLE_SIZE)
+    return malloc_usable_size((void *) ptr);
+#elif defined(HAVE_MALLOC_SIZE)
+    return malloc_size((void *) ptr);
+#else
+    return 0;
+#endif
+}
+
 int
 main(int argc, char **argv, char **envp)
 {
     sArgc = argc;
     sArgv = argv;
 
     JSRuntime *rt;
     JSContext *cx;
@@ -6237,16 +6256,18 @@ main(int argc, char **argv, char **envp)
     JS_SetSecurityCallbacks(rt, &ShellPrincipals::securityCallbacks);
     JS_InitDestroyPrincipalsCallback(rt, ShellPrincipals::destroy);
 
     JS_SetInterruptCallback(rt, ShellInterruptCallback);
     JS::SetAsmJSCacheOps(rt, &asmJSCacheOps);
 
     JS_SetNativeStackQuota(rt, gMaxStackSize);
 
+    JS::dbg::SetDebuggerMallocSizeOf(rt, ShellMallocSizeOf);
+
     if (!offThreadState.init())
         return 1;
 
     if (!InitWatchdog(rt))
         return 1;
 
     cx = NewContext(rt);
     if (!cx)
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -244,16 +244,21 @@ DebuggerMemory::setMaxAllocationsLogLeng
     args.rval().setUndefined();
     return true;
 }
 
 
 
 /* Debugger.Memory.prototype.takeCensus */
 
+void
+JS::dbg::SetDebuggerMallocSizeOf(JSRuntime *rt, mozilla::MallocSizeOf mallocSizeOf) {
+    rt->debuggerMallocSizeOf = mallocSizeOf;
+}
+
 namespace js {
 namespace dbg {
 
 // Common data for census traversals.
 struct Census {
     JSContext * const cx;
     Zone::ZoneSet debuggeeZones;
     Zone *atomsZone;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -116,16 +116,22 @@ PerThreadData::init()
     return true;
 }
 
 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = {
     TransparentObjectWrapper,
     nullptr
 };
 
+static size_t
+ReturnZeroSize(const void *p)
+{
+    return 0;
+}
+
 JSRuntime::JSRuntime(JSRuntime *parentRuntime)
   : JS::shadow::Runtime(
 #ifdef JSGC_GENERATIONAL
         &gc.storeBuffer
 #endif
     ),
     mainThread(this),
     parentRuntime(parentRuntime),
@@ -218,17 +224,18 @@ JSRuntime::JSRuntime(JSRuntime *parentRu
     ctypesActivityCallback(nullptr),
     forkJoinWarmup(0),
     offthreadIonCompilationEnabled_(true),
     parallelParsingEnabled_(true),
 #ifdef DEBUG
     enteredPolicy(nullptr),
 #endif
     largeAllocationFailureCallback(nullptr),
-    oomCallback(nullptr)
+    oomCallback(nullptr),
+    debuggerMallocSizeOf(ReturnZeroSize)
 {
     liveRuntimesCount++;
 
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&onNewGlobalObjectWatchers);
 
     PodArrayZero(nativeStackQuota);
     PodZero(&asmJSCacheOps);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1422,16 +1422,22 @@ struct JSRuntime : public JS::shadow::Ru
         if (MOZ_LIKELY(!!p2))
             return p2;
         if (newSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
             reportAllocationOverflow();
             return nullptr;
         }
         return (T *)onOutOfMemoryCanGC(p, newSize * sizeof(T));
     }
+
+    /*
+     * Debugger.Memory functions like takeCensus use this embedding-provided
+     * function to assess the size of malloc'd blocks of memory.
+     */
+    mozilla::MallocSizeOf debuggerMallocSizeOf;
 };
 
 namespace js {
 
 // When entering JIT code, the calling JSContext* is stored into the thread's
 // PerThreadData. This function retrieves the JSContext with the pre-condition
 // that the caller is JIT code or C++ called directly from JIT code. This
 // function should not be called from arbitrary locations since the JSContext