diff --git a/js/public/Debug.h b/js/public/Debug.h index e824706d6deb..d1fab06512db 100644 --- a/js/public/Debug.h +++ b/js/public/Debug.h @@ -11,6 +11,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" +#include "mozilla/MemoryReporting.h" #include "mozilla/Move.h" #include "jspubtd.h" @@ -249,6 +250,19 @@ class BuilderOrigin : public Builder { 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 diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 0a43b7a9c588..91ed3d1e38e1 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -2012,6 +2012,19 @@ IsSimdAvailable(JSContext *cx, unsigned argc, Value *vp) 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'])", @@ -2334,6 +2347,11 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = { " 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 }; diff --git a/js/src/configure.in b/js/src/configure.in index 3af4a7d20a5f..75177dd749fe 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1736,6 +1736,7 @@ ia64*-hpux*) LDFLAGS="$LDFLAGS -DYNAMICBASE" fi AC_DEFINE(HAVE_SNPRINTF) + AC_DEFINE(HAVE__MSIZE) AC_DEFINE(_WINDOWS) AC_DEFINE(WIN32) AC_DEFINE(XP_WIN) @@ -3912,8 +3913,8 @@ 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) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index d6800a752728..866522a9a991 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -22,6 +22,12 @@ # include /* for isatty() */ #endif #include +#ifdef HAVE_MALLOC_H /* for malloc_usable_size on Linux, _msize on Windows */ +#include +#endif +#ifdef HAVE_MALLOC_MALLOC_H +#include /* for malloc_size on OSX */ +#endif #include #include #include @@ -57,6 +63,7 @@ #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" @@ -5848,6 +5855,26 @@ 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) { @@ -6095,6 +6122,8 @@ main(int argc, char **argv, char **envp) JS_SetNativeStackQuota(rt, gMaxStackSize); + JS::dbg::SetDebuggerMallocSizeOf(rt, ShellMallocSizeOf); + if (!offThreadState.init()) return 1; diff --git a/js/src/vm/DebuggerMemory.cpp b/js/src/vm/DebuggerMemory.cpp index f6b4391cb5c6..a8af499fd8ba 100644 --- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -15,6 +15,7 @@ #include "jscompartment.h" #include "gc/Marking.h" +#include "js/Debug.h" #include "js/UbiNode.h" #include "js/UbiNodeTraverse.h" #include "vm/Debugger.h" @@ -249,6 +250,11 @@ DebuggerMemory::setMaxAllocationsLogLength(JSContext *cx, unsigned argc, Value * /* Debugger.Memory.prototype.takeCensus */ +void +JS::dbg::SetDebuggerMallocSizeOf(JSRuntime *rt, mozilla::MallocSizeOf mallocSizeOf) { + rt->debuggerMallocSizeOf = mallocSizeOf; +} + namespace js { namespace dbg { diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 92346a5c105b..d5c0fe5ab0fc 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -121,6 +121,12 @@ static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = { nullptr }; +static size_t +ReturnZeroSize(const void *p) +{ + return 0; +} + JSRuntime::JSRuntime(JSRuntime *parentRuntime) : JS::shadow::Runtime( #ifdef JSGC_GENERATIONAL @@ -223,7 +229,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime) enteredPolicy(nullptr), #endif largeAllocationFailureCallback(nullptr), - oomCallback(nullptr) + oomCallback(nullptr), + debuggerMallocSizeOf(ReturnZeroSize) { liveRuntimesCount++; diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 90bb614fb4c2..62670ac4b681 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1402,6 +1402,12 @@ struct JSRuntime : public JS::shadow::Runtime, } 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 { diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp index ea38d70409b7..9b4ca683be10 100644 --- a/js/src/vm/UbiNode.cpp +++ b/js/src/vm/UbiNode.cpp @@ -27,6 +27,7 @@ #include "jsobjinlines.h" +using JS::HandleValue; using JS::Value; using JS::ubi::Concrete; using JS::ubi::Edge;