diff --git a/js/src/gdb/gdb-tests-gdb.py.in b/js/src/gdb/gdb-tests-gdb.py.in index 37c76a8a170b..c6e4009816f5 100644 --- a/js/src/gdb/gdb-tests-gdb.py.in +++ b/js/src/gdb/gdb-tests-gdb.py.in @@ -6,3 +6,6 @@ sys.path[0:0] = [os.path.join('@topsrcdir@', 'gdb')] import mozilla.autoload mozilla.autoload.register(gdb.current_objfile()) + +import mozilla.asmjs +mozilla.asmjs.install() diff --git a/js/src/gdb/moz.build b/js/src/gdb/moz.build index 64c04b5a8387..5d795b16f565 100644 --- a/js/src/gdb/moz.build +++ b/js/src/gdb/moz.build @@ -8,6 +8,7 @@ GeckoProgram('gdb-tests', linkage=None) UNIFIED_SOURCES += [ 'gdb-tests.cpp', + 'tests/test-asmjs.cpp', 'tests/test-Interpreter.cpp', 'tests/test-jsid.cpp', 'tests/test-JSObject.cpp', diff --git a/js/src/gdb/mozilla/asmjs.py b/js/src/gdb/mozilla/asmjs.py new file mode 100644 index 000000000000..790a2f9a4af1 --- /dev/null +++ b/js/src/gdb/mozilla/asmjs.py @@ -0,0 +1,40 @@ +""" +In asm code, out-of-bounds heap accesses cause segfaults, which the engine +handles internally. Make GDB ignore them. +""" + +import gdb + +SIGSEGV = 11 + +# A sigaction buffer for each inferior process. +sigaction_buffers = {} + +def on_stop(event): + if isinstance(event, gdb.SignalEvent) and event.stop_signal == 'SIGSEGV': + # Allocate memory for sigaction, once per js shell process. + process = gdb.selected_inferior() + buf = sigaction_buffers.get(process) + if buf is None: + buf = gdb.parse_and_eval("(struct sigaction *) malloc(sizeof(struct sigaction))") + sigaction_buffers[process] = buf + + # See if AsmJSFaultHandler is installed as the SIGSEGV signal action. + sigaction_fn = gdb.parse_and_eval('__sigaction') + sigaction_fn(SIGSEGV, 0, buf) + AsmJSFaultHandler = gdb.parse_and_eval("AsmJSFaultHandler") + if buf['__sigaction_handler']['sa_handler'] == AsmJSFaultHandler: + # Advise the user that magic is happening. + print "js/src/gdb/mozilla/asmjs.py: Allowing AsmJSFaultHandler to run." + + # If AsmJSFaultHandler doesn't handle this segfault, it will unhook + # itself and re-raise. + gdb.execute("continue") + +def on_exited(event): + if event.inferior in sigaction_buffers: + del sigaction_buffers[event.inferior] + +def install(): + gdb.events.stop.connect(on_stop) + gdb.events.exited.connect(on_exited) diff --git a/js/src/gdb/tests/test-asmjs.cpp b/js/src/gdb/tests/test-asmjs.cpp new file mode 100644 index 000000000000..d6ded2304e93 --- /dev/null +++ b/js/src/gdb/tests/test-asmjs.cpp @@ -0,0 +1,38 @@ +#include "gdb-tests.h" +#include "jsapi.h" + +#include + +FRAGMENT(asmjs, segfault) { + using namespace JS; + + int line0 = __LINE__; + const char* bytes = "\n" + "function f(glob, ffi, heap) {\n" + " \"use asm\";\n" + " var f32 = new glob.Float32Array(heap);\n" + " function g(n) {\n" + " n = n | 0;\n" + " return +f32[n>>2];\n" + " }\n" + " return g;\n" + "}\n" + "\n" + "var func = f(this, null, new ArrayBuffer(0x10000));\n" + "func(0x10000 << 2);\n" + "'ok'\n"; + + CompileOptions opts(cx); + opts.setFileAndLine(__FILE__, line0 + 1); + opts.asmJSOption = true; + RootedValue rval(cx); + bool ok; + ok = false; + + ok = Evaluate(cx, opts, bytes, strlen(bytes), &rval); + + breakpoint(); + + (void) ok; + (void) rval; +} diff --git a/js/src/gdb/tests/test-asmjs.py b/js/src/gdb/tests/test-asmjs.py new file mode 100644 index 000000000000..219cb1a4de78 --- /dev/null +++ b/js/src/gdb/tests/test-asmjs.py @@ -0,0 +1,15 @@ +# Test for special asmjs SIGSEGV-handling. +# +# Expected behavior is for the asm.js code in the following fragment to trigger +# SIGSEGV. The code in js/src/gdb/mozilla/asmjs.py should prevent GDB from +# handling that signal. + +run_fragment('asmjs.segfault') + +# If SIGSEGV handling is broken, GDB would have stopped at the SIGSEGV signal. +# The breakpoint would not have hit, and run_fragment would have thrown. +# +# So if we get here, and the asm.js code actually ran, we win. + +assert_pretty('ok', 'true') +assert_pretty('rval', '$jsval("ok")') diff --git a/js/src/shell/Makefile.in b/js/src/shell/Makefile.in index 0a25dcf2a0af..ae65b7d09153 100644 --- a/js/src/shell/Makefile.in +++ b/js/src/shell/Makefile.in @@ -24,10 +24,6 @@ INSTALL_TARGETS += SHELL_INSTALL_AUTOLOAD SHELL_INSTALL_AUTOLOAD_FILES := $(CURDIR)/js-gdb.py SHELL_INSTALL_AUTOLOAD_DEST := $(DIST)/bin -INSTALL_TARGETS += SHELL_INSTALL_AUTOLOAD_SCRIPT -SHELL_INSTALL_AUTOLOAD_SCRIPT_FILES := js-gdb.gdb -SHELL_INSTALL_AUTOLOAD_SCRIPT_DEST := $(CURDIR) - include $(topsrcdir)/config/rules.mk # People expect the js shell to wind up in the top-level JS dir. diff --git a/js/src/shell/js-gdb.gdb b/js/src/shell/js-gdb.gdb deleted file mode 100644 index c87a3b21570d..000000000000 --- a/js/src/shell/js-gdb.gdb +++ /dev/null @@ -1,18 +0,0 @@ -define hookpost-run - if ($sigaction) - call free($sigaction) - set $sigaction = 0 - end -end - -catch signal SIGSEGV -commands - if !$sigaction - set $sigaction = malloc(sizeof(sigaction)) - end - set $ignored = __sigaction(11, 0, $sigaction) - set $handler = ((struct sigaction *)$sigaction)->__sigaction_handler.sa_handler - if $handler == AsmJSFaultHandler - continue - end -end diff --git a/js/src/shell/js-gdb.py.in b/js/src/shell/js-gdb.py.in index 7340e094cbac..16199bb4cc5b 100644 --- a/js/src/shell/js-gdb.py.in +++ b/js/src/shell/js-gdb.py.in @@ -6,3 +6,6 @@ sys.path[0:0] = [os.path.join('@topsrcdir@', 'gdb')] import mozilla.autoload mozilla.autoload.register(gdb.current_objfile()) + +import mozilla.asmjs +mozilla.asmjs.install()