Landing fix for bug 388564. Adding Dtrace probes to the JS engine. Patch by padraig.obriain@sun.com and brendan@sun.com, and some intergration work done by jst@mozilla.org. r=brendan@mozilla.org, igor@mir2.org, sayrer@gmail.com, and r+a=ted.mielczarek@gmail.com.
authorjst@mozilla.org
Fri, 19 Oct 2007 15:24:32 -0700
changeset 7058 6a54d2e59df066c00561ae6dfa0c5f4444c69202
parent 7057 ef7e2d9baee0eb275c8bab62b9b02ca6b5237870
child 7059 53fd6cf2009ec2760e1ea7657bd0dacc59c99f76
push idunknown
push userunknown
push dateunknown
reviewersbrendan
bugs388564
milestone1.9a9pre
Landing fix for bug 388564. Adding Dtrace probes to the JS engine. Patch by padraig.obriain@sun.com and brendan@sun.com, and some intergration work done by jst@mozilla.org. r=brendan@mozilla.org, igor@mir2.org, sayrer@gmail.com, and r+a=ted.mielczarek@gmail.com.
config/rules.mk
js/src/Makefile.in
js/src/javascript-trace.d
js/src/jsdtracef.c
js/src/jsdtracef.h
js/src/jsinterp.c
js/src/jsobj.c
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1043,16 +1043,25 @@ endif # OS/2
 	rm -f $@
 	$(HOST_AR) $(HOST_AR_FLAGS) $(HOST_OBJS)
 	$(HOST_RANLIB) $@
 
 ifdef NO_LD_ARCHIVE_FLAGS
 SUB_SHLOBJS = $(SUB_LOBJS)
 endif
 
+ifdef HAVE_DTRACE
+ifdef DTRACE_PROBE_OBJ
+ifndef DTRACE_LIB_DEPENDENT
+$(DTRACE_PROBE_OBJ): $(OBJS)
+	dtrace -G -C -32 -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(OBJS)
+endif
+endif
+endif
+
 # On Darwin (Mac OS X), dwarf2 debugging uses debug info left in .o files,
 # so instead of deleting .o files after repacking them into a dylib, we make
 # symlinks back to the originals. The symlinks are a no-op for stabs debugging,
 # so no need to conditionalize on OS version or debugging format.
 
 $(SHARED_LIBRARY): $(OBJS) $(LOBJS) $(DEF_FILE) $(RESFILE) $(SHARED_LIBRARY_LIBS) $(EXTRA_DEPS) $(DSO_LDOPTS_DEPS) Makefile Makefile.in
 ifndef INCREMENTAL_LINKER
 	rm -f $@
@@ -1073,35 +1082,35 @@ ifdef IS_COMPONENT
 endif
 endif
 ifdef NO_LD_ARCHIVE_FLAGS
 ifdef SHARED_LIBRARY_LIBS
 	@rm -f $(SUB_SHLOBJS)
 	@for lib in $(SHARED_LIBRARY_LIBS); do $(AR_EXTRACT) $${lib}; $(CLEANUP2); done
 endif # SHARED_LIBRARY_LIBS
 endif # NO_LD_ARCHIVE_FLAGS
-ifdef NEED_DTRACE_PROBE_OBJ
+ifdef DTRACE_LIB_DEPENDENT
 	@rm -f $(PROBE_LOBJS)
 	@for lib in $(MOZILLA_PROBE_LIBS); do $(AR_EXTRACT) $${lib}; $(CLEANUP2); done
-	dtrace -G -C -32 -s $(MOZILLA_DTRACE_SRC) -o  $(NEED_DTRACE_PROBE_OBJ) $(PROBE_LOBJS)
+	dtrace -G -C -32 -s $(MOZILLA_DTRACE_SRC) -o  $(DTRACE_PROBE_OBJ) $(PROBE_LOBJS)
 	@for lib in $(MOZILLA_PROBE_LIBS); do \
 		ofiles=`$(AR_LIST) $${lib}`; \
 		$(AR_DELETE) $${lib} $$ofiles; \
 	done
-	$(MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(NEED_DTRACE_PROBE_OBJ) $(PROBE_LOBJS) $(RESFILE) $(LDFLAGS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
+	$(MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(PROBE_LOBJS) $(RESFILE) $(LDFLAGS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
 	@rm -f $(PROBE_LOBJS)
-	@rm -f $(NEED_DTRACE_PROBE_OBJ)
+	@rm -f $(DTRACE_PROBE_OBJ)
 	@for lib in $(MOZILLA_PROBE_LIBS); do \
 		if [ -L $${lib} ]; then rm -f `readlink $${lib}`; fi; \
 	done
 	@rm -f $(MOZILLA_PROBE_LIBS)
 
 else
 	$(MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(DTRACE_PROBE_OBJ) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
-endif # NEED_DTRACE_PROBE_OBJ
+endif # DTRACE_LIB_DEPENDENT
 
 ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
 ifdef MSMANIFEST_TOOL
 ifdef EMBED_MANIFEST_AT
 	@if test -f $@.manifest; then \
 		mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;$(EMBED_MANIFEST_AT); \
 		rm -f $@.manifest; \
 	fi
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -98,16 +98,21 @@ CSRCS		= \
 		jsscript.c \
 		jsstr.c \
 		jsutil.c \
 		jsxdrapi.c \
 		jsxml.c \
 		prmjtime.c \
 		$(NULL)
 
+ifdef HAVE_DTRACE
+CSRCS 		+= \
+		jsdtracef.c
+endif
+
 EXPORTS		= \
 		jsautocfg.h \
 		jsautokw.h \
 		js.msg \
 		jsapi.h \
 		jsarray.h \
 		jsarena.h \
 		jsatom.h \
@@ -147,16 +152,23 @@ EXPORTS		= \
 		jsstddef.h \
 		jsstr.h \
 		jstypes.h \
 		jsutil.h \
 		jsxdrapi.h \
 		jsxml.h \
 		$(NULL)
 
+ifdef HAVE_DTRACE
+EXPORTS 	+= \
+		jsdtracef.h \
+		javascript-trace.h \
+		$(NULL)
+endif
+
 ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH)))
 EXPORTS		+= jscpucfg.h
 endif
 
 JS_SAFE_ARENA	= 1
 
 DASH_R		= -r
 
@@ -197,16 +209,21 @@ JSJAVA_CFLAGS	= \
 		-I$(topsrcdir)/sun-java/include \
 		$(JSJAVA_STUBHEADERS)
 
 # Define keyword generator before rules.mk, see bug 323979 comment 50
 
 HOST_SIMPLE_PROGRAMS += host_jskwgen$(HOST_BIN_SUFFIX)
 GARBAGE += jsautokw.h host_jskwgen$(HOST_BIN_SUFFIX)
 
+ifdef HAVE_DTRACE
+DTRACE_PROBE_OBJ = $(LIBRARY_NAME)-dtrace.$(OBJ_SUFFIX)
+MOZILLA_DTRACE_SRC = $(srcdir)/javascript-trace.d
+endif
+
 include $(topsrcdir)/config/rules.mk
 
 DEFINES		+= -DEXPORT_JS_API 
 
 INCLUDES	+= -I$(srcdir)
 
 # MSVC '-Gy' cc flag and '/OPT:REF' linker flag cause JS_GetArgument and
 # JS_GetLocalVariable to be folded to the same address by the linker, 
@@ -387,8 +404,16 @@ endif
 
 # Extra dependancies and rules for keyword switch code
 jsscan.$(OBJ_SUFFIX): jsautokw.h jskeyword.tbl
 
 host_jskwgen.$(OBJ_SUFFIX): jsconfig.h jskeyword.tbl
 
 jsautokw.h: host_jskwgen$(HOST_BIN_SUFFIX)
 	./host_jskwgen$(HOST_BIN_SUFFIX) $@
+
+ifdef HAVE_DTRACE
+javascript-trace.h: $(srcdir)/javascript-trace.d
+	dtrace -h -s $(srcdir)/javascript-trace.d -o javascript-trace.h.in
+	sed 's/if _DTRACE_VERSION/ifdef INCLUDE_MOZILLA_DTRACE/' \
+	    javascript-trace.h.in > javascript-trace.h
+endif
+
new file mode 100644
--- /dev/null
+++ b/js/src/javascript-trace.d
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Copyright (C) 2007  Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * javascript provider probes
+ *
+ * function-entry       (filename, classname, funcname)
+ * function-info        (filename, classname, funcname, lineno,
+ *                      runfilename, runlineno)
+ * function-args        (filename, classname, funcname, argc, argv, argv0,
+ *                      argv1, argv2, argv3, argv4)
+ * function-rval        (filename, classname, funcname, lineno, rval, rval0)
+ * function-return      (filename, classname, funcname)
+ * object-create-start  (filename, classname)
+ * object-create        (filename, classname, *object, rlineno)
+ * object-create-done   (filename, classname)
+ * object-finalize      (NULL, classname, *object)
+ * execute-start        (filename, lineno)
+ * execute-done         (filename, lineno)
+ */
+
+provider javascript {
+ probe function__entry(char *, char *, char *);
+ probe function__info(char *, char *, char *, int, char *, int);
+ probe function__args(char *, char *, char *, int, void *, void *, void *,
+     void *, void *, void *);
+ probe function__rval(char *, char *, char *, int, void *, void *);
+ probe function__return(char *, char *, char *);
+ probe object__create__start(char *, char *);
+ probe object__create__done(char *, char *);
+ probe object__create(char *, char *, uintptr_t, int);
+ probe object__finalize(char *, char *, uintptr_t);
+ probe execute__start(char *, int);
+ probe execute__done(char *, int);
+};
+
+/*
+#pragma D attributes Unstable/Unstable/Common provider mozilla provider
+#pragma D attributes Private/Private/Unknown provider mozilla module
+#pragma D attributes Private/Private/Unknown provider mozilla function
+#pragma D attributes Unstable/Unstable/Common provider mozilla name
+#pragma D attributes Unstable/Unstable/Common provider mozilla args
+*/
+
new file mode 100644
--- /dev/null
+++ b/js/src/jsdtracef.c
@@ -0,0 +1,311 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Copyright (C) 2007  Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Brendan Eich <brendan@mozilla.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jsapi.h"
+#include "jsutil.h"
+#include "jsatom.h"
+#include "jscntxt.h"
+#include "jsdbgapi.h"
+#include "jsfun.h"
+#include "jsinterp.h"
+#include "jsobj.h"
+#include "jsscript.h"
+#include "jsstr.h"
+
+#include "jsdtracef.h"
+#include <sys/types.h>
+
+#define TYPEOF(cx,v)    (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
+
+static char dempty[] = "<null>";
+
+char *
+jsdtrace_filename(JSStackFrame *fp)
+{
+    while (fp && fp->script == NULL)
+        fp = fp->down;
+    return (fp && fp->script && fp->script->filename)
+           ? (char *)fp->script->filename
+           : dempty;
+}
+
+int
+jsdtrace_linenumber(JSContext *cx, JSStackFrame *fp)
+{
+    while (fp && fp->script == NULL)
+        fp = fp->down;
+    return (fp && fp->script && fp->pc)
+           ? js_PCToLineNumber(cx, fp->script, fp->pc)
+           : -1;
+}
+
+/*
+ * This function is used to convert function arguments and return value (jsval)
+ * into the following based on each value's type tag:
+ *
+ *      jsval      returned
+ *      -------------------
+ *      STRING  -> char *
+ *      INT     -> int
+ *      DOUBLE  -> double *
+ *      BOOLEAN -> int
+ *      OBJECT  -> void *
+ *
+ * All are presented as void * for DTrace consumers to use, after shifting or
+ * masking out the JavaScript type bits. This allows D scripts to use ints and
+ * booleans directly and copyinstr() for string arguments, when types are known
+ * beforehand.
+ *
+ * This is used by the function-args and function-rval probes, which also
+ * provide raw (unmasked) jsvals should type info be useful from D scripts.
+ */
+void *
+jsdtrace_jsvaltovoid(JSContext *cx, jsval argval)
+{
+    JSType type = TYPEOF(cx, argval);
+
+    switch (type) {
+      case JSTYPE_NULL:
+      case JSTYPE_VOID:
+        return JS_TYPE_STR(type);
+
+      case JSTYPE_BOOLEAN:
+        return (void *)JSVAL_TO_BOOLEAN(argval);
+
+      case JSTYPE_STRING:
+        return (void *)js_GetStringBytes(cx, JSVAL_TO_STRING(argval));
+
+      case JSTYPE_NUMBER:
+        if (JSVAL_IS_INT(argval))
+            return (void *)JSVAL_TO_INT(argval);
+        return JSVAL_TO_DOUBLE(argval);
+
+      default:
+        return JSVAL_TO_GCTHING(argval);
+    }
+    /* NOTREACHED */
+}
+
+char *
+jsdtrace_function_name(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
+{
+    JSAtom *atom;
+    JSScript *script;
+    jsbytecode *pc;
+    char *name;
+
+    atom = fun->atom;
+    if (!atom) {
+        if (fp->fun != fun || !fp->down)
+            return dempty;
+
+        script = fp->down->script;
+        pc = fp->down->pc;
+        if (!script || !pc)
+            return dempty;
+
+        /*
+         * An anonymous function called from an active script or interpreted
+         * function: try to fetch the variable or property name by which the
+         * anonymous function was invoked. First handle call ops by recovering
+         * the generating pc for the callee expression at argv[-2].
+         */
+        switch ((JSOp) *pc) {
+          case JSOP_CALL:
+          case JSOP_EVAL:
+            JS_ASSERT(fp->argv == fp->down->sp - (int)GET_ARGC(pc));
+
+            pc = (jsbytecode *) fp->argv[-2 - (int)script->depth];
+
+            /*
+             * Be paranoid about bugs to-do with generating pc storage when
+             * attempting to descend into the operand stack basement.
+             */
+            if ((uintptr_t)(pc - script->code) >= script->length)
+                return dempty;
+            break;
+        }
+
+        switch ((JSOp) *pc) {
+          case JSOP_CALLNAME:
+          case JSOP_CALLPROP:
+          case JSOP_NAME:
+          case JSOP_SETNAME:
+          case JSOP_GETPROP:
+          case JSOP_SETPROP:
+            GET_ATOM_FROM_BYTECODE(script, pc, 0, atom);
+            break;
+
+          case JSOP_CALLELEM:
+          case JSOP_GETELEM:
+          case JSOP_SETELEM:
+          case JSOP_CALLGVAR:
+          case JSOP_GETGVAR:
+          case JSOP_SETGVAR:
+          case JSOP_CALLVAR:
+          case JSOP_CALLARG:
+          case JSOP_CALLLOCAL:
+            /* FIXME: try to recover a name from these ops. */
+            /* FALL THROUGH */
+
+          default:
+            return dempty;
+        }
+    }
+
+    name = (char *)js_GetStringBytes(cx, ATOM_TO_STRING(atom));
+    return name ? name : dempty;
+}
+
+/*
+ * These functions call the DTrace macros for the JavaScript USDT probes.
+ * Originally this code was inlined in the JavaScript code; however since
+ * a number of operations are called, these have been placed into functions
+ * to reduce any negative compiler optimization effect that the addition of
+ * a number of usually unused lines of code would cause.
+ */
+void
+jsdtrace_function_entry(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
+{
+    JAVASCRIPT_FUNCTION_ENTRY(
+        jsdtrace_filename(fp),
+        fun->clasp ? (char *)fun->clasp->name : dempty,
+        jsdtrace_function_name(cx, fp, fun)
+    );
+}
+
+void
+jsdtrace_function_info(JSContext *cx, JSStackFrame *fp, JSStackFrame *dfp,
+                       JSFunction *fun)
+{
+    JAVASCRIPT_FUNCTION_INFO(
+        jsdtrace_filename(fp),
+        fun->clasp ? (char *)fun->clasp->name : dempty,
+        jsdtrace_function_name(cx, fp, fun),
+        fp->script->lineno,
+        jsdtrace_filename(dfp),
+        jsdtrace_linenumber(cx, dfp)
+    );
+}
+
+void
+jsdtrace_function_args(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
+{
+    JAVASCRIPT_FUNCTION_ARGS(
+        jsdtrace_filename(fp),
+        fun->clasp ? (char *)fun->clasp->name : dempty,
+        jsdtrace_function_name(cx, fp, fun),
+        fp->argc, (void *)fp->argv,
+        (fp->argc > 0) ? jsdtrace_jsvaltovoid(cx, fp->argv[0]) : 0,
+        (fp->argc > 1) ? jsdtrace_jsvaltovoid(cx, fp->argv[1]) : 0,
+        (fp->argc > 2) ? jsdtrace_jsvaltovoid(cx, fp->argv[2]) : 0,
+        (fp->argc > 3) ? jsdtrace_jsvaltovoid(cx, fp->argv[3]) : 0,
+        (fp->argc > 4) ? jsdtrace_jsvaltovoid(cx, fp->argv[4]) : 0
+    );
+}
+
+void
+jsdtrace_function_rval(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
+{
+    JAVASCRIPT_FUNCTION_RVAL(
+        jsdtrace_filename(fp),
+        fun->clasp ? (char *)fun->clasp->name : dempty,
+        jsdtrace_function_name(cx, fp, fun),
+        jsdtrace_linenumber(cx, fp), (void *)fp->rval,
+        jsdtrace_jsvaltovoid(cx, fp->rval)
+    );
+}
+
+void
+jsdtrace_function_return(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
+{
+    JAVASCRIPT_FUNCTION_RETURN(
+        jsdtrace_filename(fp),
+        fun->clasp ? (char *)fun->clasp->name : dempty,
+        jsdtrace_function_name(cx, fp, fun)
+    );
+}
+
+void
+jsdtrace_object_create_start(JSStackFrame *fp, JSClass *clasp)
+{
+    JAVASCRIPT_OBJECT_CREATE_START(jsdtrace_filename(fp), (char *)clasp->name);
+}
+
+void
+jsdtrace_object_create_done(JSStackFrame *fp, JSClass *clasp)
+{
+    JAVASCRIPT_OBJECT_CREATE_DONE(jsdtrace_filename(fp), (char *)clasp->name);
+}
+
+void
+jsdtrace_object_create(JSContext *cx, JSClass *clasp, JSObject *obj)
+{
+    JAVASCRIPT_OBJECT_CREATE(
+        jsdtrace_filename(cx->fp),
+        (char *)clasp->name,
+        (uintptr_t)obj,
+        jsdtrace_linenumber(cx, cx->fp)
+    );
+}
+
+void
+jsdtrace_object_finalize(JSObject *obj)
+{
+    JSClass *clasp;
+
+    clasp = LOCKED_OBJ_GET_CLASS(obj);
+
+    /* the first arg is NULL - reserved for future use (filename?) */
+    JAVASCRIPT_OBJECT_FINALIZE(NULL, (char *)clasp->name, (uintptr_t)obj);
+}
+
+void
+jsdtrace_execute_start(JSScript *script)
+{
+    JAVASCRIPT_EXECUTE_START(
+        script->filename ? (char *)script->filename : dempty,
+        script->lineno
+    );
+}
+
+void
+jsdtrace_execute_done(JSScript *script)
+{
+    JAVASCRIPT_EXECUTE_DONE(
+        script->filename ? (char *)script->filename : dempty,
+        script->lineno
+    );
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jsdtracef.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Copyright (C) 2007  Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Brendan Eich <brendan@mozilla.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "javascript-trace.h"
+#include "jspubtd.h"
+#include "jsprvtd.h"
+
+#ifndef _JSDTRACEF_H
+#define _JSDTRACEF_H
+
+extern void
+jsdtrace_function_entry(JSContext *cx, JSStackFrame *fp, JSFunction *fun);
+
+extern void
+jsdtrace_function_info(JSContext *cx, JSStackFrame *fp, JSStackFrame *dfp,
+                       JSFunction *fun);
+
+extern void
+jsdtrace_function_args(JSContext *cx, JSStackFrame *fp, JSFunction *fun);
+
+extern void
+jsdtrace_function_rval(JSContext *cx, JSStackFrame *fp, JSFunction *fun);
+
+extern void
+jsdtrace_function_return(JSContext *cx, JSStackFrame *fp, JSFunction *fun);
+
+extern void
+jsdtrace_object_create_start(JSStackFrame *fp, JSClass *clasp);
+
+extern void
+jsdtrace_object_create_done(JSStackFrame *fp, JSClass *clasp);
+
+extern void
+jsdtrace_object_create(JSContext *cx, JSClass *clasp, JSObject *obj);
+
+extern void
+jsdtrace_object_finalize(JSObject *obj);
+
+extern void
+jsdtrace_execute_start(JSScript *script);
+
+extern void
+jsdtrace_execute_done(JSScript *script);
+
+#endif /* _JSDTRACE_H */
--- a/js/src/jsinterp.c
+++ b/js/src/jsinterp.c
@@ -64,16 +64,20 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+#include "jsdtracef.h"
+#endif
+
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 /*
  * Stack macros and functions.  These all use a local variable, jsval *sp, to
  * point to the next free stack slot.  SAVE_SP must be called before any call
  * to a function that may invoke the interpreter.  RESTORE_SP must be called
@@ -1528,16 +1532,21 @@ js_Execute(JSContext *cx, JSObject *chai
            JSStackFrame *down, uintN flags, jsval *result)
 {
     JSInterpreterHook hook;
     void *hookData, *mark;
     JSStackFrame *oldfp, frame;
     JSObject *obj, *tmp;
     JSBool ok;
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_EXECUTE_START_ENABLED())
+        jsdtrace_execute_start(script);
+#endif
+
     hook = cx->debugHooks->executeHook;
     hookData = mark = NULL;
     oldfp = cx->fp;
     frame.script = script;
     if (down) {
         /* Propagate arg/var state for eval and the debugger API. */
         frame.callobj = down->callobj;
         frame.argsobj = down->argsobj;
@@ -1564,18 +1573,20 @@ js_Execute(JSContext *cx, JSObject *chai
         frame.thisp = chain;
         frame.argc = 0;
         frame.argv = NULL;
         frame.nvars = script->ngvars;
         if (script->regexpsOffset != 0)
             frame.nvars += JS_SCRIPT_REGEXPS(script)->length;
         if (frame.nvars != 0) {
             frame.vars = js_AllocRawStack(cx, frame.nvars, &mark);
-            if (!frame.vars)
-                return JS_FALSE;
+            if (!frame.vars) {
+                ok = JS_FALSE;
+                goto out;
+            }
             memset(frame.vars, 0, frame.nvars * sizeof(jsval));
         } else {
             frame.vars = NULL;
         }
         frame.annotation = NULL;
         frame.sharpArray = NULL;
     }
     frame.rval = JSVAL_VOID;
@@ -1632,16 +1643,21 @@ js_Execute(JSContext *cx, JSObject *chai
     cx->fp = oldfp;
 
     if (oldfp && oldfp != down) {
         JS_ASSERT(cx->dormantFrameChain == oldfp);
         cx->dormantFrameChain = oldfp->dormantNext;
         oldfp->dormantNext = NULL;
     }
 
+out:
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
+        jsdtrace_execute_done(script);
+#endif
     return ok;
 }
 
 #if JS_HAS_EXPORT_IMPORT
 /*
  * If id is JSVAL_VOID, import all exported properties from obj.
  */
 static JSBool
@@ -2609,16 +2625,24 @@ interrupt:
                     ok &= js_PutCallObject(cx, fp);
                 }
 
                 if (fp->argsobj) {
                     SAVE_SP_AND_PC(fp);
                     ok &= js_PutArgsObject(cx, fp);
                 }
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+                /* DTrace function return, inlines */
+                if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
+                    jsdtrace_function_rval(cx, fp, fp->fun);
+                if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
+                    jsdtrace_function_return(cx, fp, fp->fun);
+#endif
+
                 /* Restore context version only if callee hasn't set version. */
                 if (JS_LIKELY(cx->version == currentVersion)) {
                     currentVersion = ifp->callerVersion;
                     if (currentVersion != cx->version)
                         js_SetVersion(cx, currentVersion);
                 }
 
                 /* Store the return value in the caller's operand frame. */
@@ -4033,31 +4057,53 @@ interrupt:
                     cx->fp = fp = &newifp->frame;
                     pc = script->code;
 #if !JS_THREADED_INTERP
                     endpc = pc + script->length;
 #endif
                     inlineCallCount++;
                     JS_RUNTIME_METER(rt, inlineCalls);
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+                    /* DTrace function entry, inlines */
+                    if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
+                        jsdtrace_function_entry(cx, fp, fun);
+                    if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
+                        jsdtrace_function_info(cx, fp, fp->down, fun);
+                    if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
+                        jsdtrace_function_args(cx, fp, fun);
+#endif
+
                     /* Load first op and dispatch it (safe since JSOP_STOP). */
                     op = (JSOp) *pc;
                     DO_OP();
 
                   bad_inline_call:
                     RESTORE_SP(fp);
                     JS_ASSERT(fp->pc == pc);
                     script = fp->script;
                     depth = (jsint) script->depth;
                     atoms = script->atomMap.vector;
                     js_FreeRawStack(cx, newmark);
                     ok = JS_FALSE;
                     goto out;
                 }
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+                /* DTrace function entry, non-inlines */
+                if (VALUE_IS_FUNCTION(cx, lval)) {
+                    if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
+                        jsdtrace_function_entry(cx, fp, fun);
+                    if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
+                        jsdtrace_function_info(cx, fp, fp, fun);
+                    if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
+                        jsdtrace_function_args(cx, fp, fun);
+                }
+#endif
+
                 if (fun->flags & JSFUN_FAST_NATIVE) {
                     JS_ASSERT(fun->u.n.extra == 0);
                     if (argc < fun->u.n.minargs) {
                         uintN nargs;
 
                         /*
                          * If we can't fit missing args and local roots in
                          * this frame's operand stack, take the slow path.
@@ -4075,26 +4121,43 @@ interrupt:
                         } while (--nargs != 0);
                         SAVE_SP(fp);
                     }
 
                     JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]) ||
                               PRIMITIVE_THIS_TEST(fun, vp[1]));
 
                     ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp);
+#ifdef INCLUDE_MOZILLA_DTRACE
+                    if (VALUE_IS_FUNCTION(cx, lval)) {
+                        if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
+                            jsdtrace_function_rval(cx, fp, fun);
+                        if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
+                            jsdtrace_function_return(cx, fp, fun);
+                    }
+#endif
                     if (!ok)
                         goto out;
                     sp = vp + 1;
                     vp[-depth] = (jsval)pc;
                     goto end_call;
                 }
             }
 
           do_invoke:
             ok = js_Invoke(cx, argc, vp, 0);
+#ifdef INCLUDE_MOZILLA_DTRACE
+            /* DTrace function return, non-inlines */
+            if (VALUE_IS_FUNCTION(cx, lval)) {
+                if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
+                    jsdtrace_function_rval(cx, fp, fun);
+                if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
+                    jsdtrace_function_return(cx, fp, fun);
+            }
+#endif
             sp = vp + 1;
             vp[-depth] = (jsval)pc;
             LOAD_INTERRUPT_HANDLER(cx);
             if (!ok)
                 goto out;
             JS_RUNTIME_METER(rt, nonInlineCalls);
 
           end_call:
--- a/js/src/jsobj.c
+++ b/js/src/jsobj.c
@@ -78,16 +78,20 @@
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 #if JS_HAS_XDR
 #include "jsxdrapi.h"
 #endif
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+#include "jsdtracef.h"
+#endif
+
 #ifdef JS_THREADSAFE
 #define NATIVE_DROP_PROPERTY js_DropProperty
 
 extern void
 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
 #else
 #define NATIVE_DROP_PROPERTY NULL
 #endif
@@ -2506,42 +2510,47 @@ js_NewObject(JSContext *cx, JSClass *cla
     jsid id;
     JSObject *obj;
     JSObjectOps *ops;
     JSObjectMap *map;
     JSClass *protoclasp;
     uint32 nslots, i;
     JSTempValueRooter tvr;
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED())
+        jsdtrace_object_create_start(cx->fp, clasp);
+#endif
+
     /* Bootstrap the ur-object, and make it the default prototype object. */
     if (!proto) {
         if (!js_GetClassId(cx, clasp, &id))
-            return NULL;
+            goto earlybad;
         if (!js_GetClassPrototype(cx, parent, id, &proto))
-            return NULL;
+            goto earlybad;
         if (!proto &&
             !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),
                                   &proto)) {
-            return NULL;
+            goto earlybad;
         }
     }
 
     /* Always call the class's getObjectOps hook if it has one. */
     ops = clasp->getObjectOps
           ? clasp->getObjectOps(cx, clasp)
           : &js_ObjectOps;
 
     /*
      * Allocate a zeroed object from the GC heap.  Do this *after* any other
      * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps,
      * to avoid displacing the newborn root for obj.
      */
     obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
     if (!obj)
-        return NULL;
+        goto earlybad;
 
     /*
      * Initialize all JSObject fields before doing any operation that can
      * potentially trigger GC.
      */
     obj->map = NULL;
     obj->dslots = NULL;
 
@@ -2616,21 +2625,36 @@ js_NewObject(JSContext *cx, JSClass *cla
         cx->debugHooks->objectHook(cx, obj, JS_TRUE,
                                    cx->debugHooks->objectHookData);
         JS_UNKEEP_ATOMS(cx->runtime);
     }
 
 out:
     JS_POP_TEMP_ROOT(cx, &tvr);
     cx->weakRoots.newborn[GCX_OBJECT] = obj;
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_OBJECT_CREATE_ENABLED())
+        jsdtrace_object_create(cx, clasp, obj);
+    if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED())
+        jsdtrace_object_create_done(cx->fp, clasp);
+#endif
     return obj;
 
 bad:
     obj = NULL;
     goto out;
+
+earlybad:
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_OBJECT_CREATE_ENABLED())
+        jsdtrace_object_create(cx, clasp, NULL);
+    if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED())
+        jsdtrace_object_create_done(cx->fp, clasp);
+#endif
+    return NULL;
 }
 
 JS_BEGIN_EXTERN_C
 
 JS_STATIC_DLL_CALLBACK(JSObject *)
 js_InitNullClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(0);
@@ -2869,16 +2893,21 @@ js_FinalizeObject(JSContext *cx, JSObjec
     if (cx->debugHooks->objectHook) {
         cx->debugHooks->objectHook(cx, obj, JS_FALSE,
                                    cx->debugHooks->objectHookData);
     }
 
     /* Finalize obj first, in case it needs map and slots. */
     GC_AWARE_GET_CLASS(cx, obj)->finalize(cx, obj);
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED())
+        jsdtrace_object_finalize(obj);
+#endif
+
     /* Drop map and free slots. */
     js_DropObjectMap(cx, map, obj);
     FreeSlots(cx, obj);
 }
 
 /* XXXbe if one adds props, deletes earlier props, adds more, the last added
          won't recycle the deleted props' slots. */
 JSBool