Bug 1063247: Let embeddings tell Debugger how to find the size of a malloc'd block of memory. r=terrence
authorJim Blandy <jimb@mozilla.com>
Fri, 19 Sep 2014 15:10:01 -0700
changeset 206276 ed38f85902f7
parent 206275 ef6d81ac0bba
child 206277 b7f417116d90
push id49394
push userjblandy@mozilla.com
push date2014-09-19 22:10 +0000
treeherdermozilla-inbound@ed38f85902f7 [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
js/src/vm/UbiNode.cpp
--- 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
@@ -2007,16 +2007,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' [, 'shrinking'])",
 "  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.\n"
 "  If 'shrinking' is passes as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
@@ -2329,16 +2342,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
@@ -1731,16 +1731,17 @@ ia64*-hpux*)
         dnl but we work around it here.
         PROFILE_USE_CFLAGS="-GL -wd4624 -wd4952"
         dnl XXX: should be -LTCG:PGOPTIMIZE, but that fails on libxul.
         dnl Probably also a compiler bug, but what can you do?
         PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE"
         LDFLAGS="$LDFLAGS -DYNAMICBASE"
     fi
     AC_DEFINE(HAVE_SNPRINTF)
+    AC_DEFINE(HAVE__MSIZE)
     AC_DEFINE(_WINDOWS)
     AC_DEFINE(WIN32)
     AC_DEFINE(XP_WIN)
     AC_DEFINE(XP_WIN32)
     AC_DEFINE(HW_THREADS)
     AC_DEFINE(STDC_HEADERS)
     AC_DEFINE(WIN32_LEAN_AND_MEAN)
     TARGET_MD_ARCH=win32
@@ -3907,18 +3908,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 malloc/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
@@ -17,16 +17,22 @@
 # 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 /* for malloc_usable_size on Linux, _msize on Windows */
+#include <malloc.h>
+#endif
+#ifdef HAVE_MALLOC_MALLOC_H
+#include <malloc/malloc.h> /* for malloc_size on OSX */
+#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
@@ -52,16 +58,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 "shell/OSObject.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Debugger.h"
@@ -5843,16 +5850,36 @@ 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 *constPtr)
+{
+    // Match the type that all the library functions we might use here expect.
+    void *ptr = (void *) constPtr;
+
+    if (!ptr)
+        return 0;
+
+#if defined(HAVE_MALLOC_USABLE_SIZE)
+    return malloc_usable_size(ptr);
+#elif defined(HAVE_MALLOC_SIZE)
+    return malloc_size(ptr);
+#elif HAVE__MSIZE
+    return _msize(ptr);
+#else
+    return 0;
+#endif
+}
+
 int
 main(int argc, char **argv, char **envp)
 {
     sArgc = argc;
     sArgv = argv;
 
     JSRuntime *rt;
     JSContext *cx;
@@ -6090,16 +6117,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
@@ -10,16 +10,17 @@
 #include "mozilla/Move.h"
 #include "mozilla/Vector.h"
 
 #include <stdlib.h>
 
 #include "jscompartment.h"
 
 #include "gc/Marking.h"
+#include "js/Debug.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeTraverse.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/SavedStacks.h"
 
 #include "vm/Debugger-inl.h"
 
@@ -244,16 +245,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
@@ -1397,16 +1397,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
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -22,16 +22,17 @@
 #include "js/Vector.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/Symbol.h"
 
 #include "jsobjinlines.h"
 
+using JS::HandleValue;
 using JS::Value;
 using JS::ubi::Concrete;
 using JS::ubi::Edge;
 using JS::ubi::EdgeRange;
 using JS::ubi::Node;
 using JS::ubi::TracerConcrete;
 using JS::ubi::TracerConcreteWithCompartment;