diff options
Diffstat (limited to 'js/src/gdb/tests')
30 files changed, 876 insertions, 0 deletions
diff --git a/js/src/gdb/tests/test-ExecutableAllocator.cpp b/js/src/gdb/tests/test-ExecutableAllocator.cpp new file mode 100644 index 000000000..7cb7ad364 --- /dev/null +++ b/js/src/gdb/tests/test-ExecutableAllocator.cpp @@ -0,0 +1,44 @@ +#include "gdb-tests.h" +#include "jsapi.h" + +#include "jscntxt.h" + +#include "jit/ExecutableAllocator.h" + +FRAGMENT(ExecutableAllocator, empty) { + using namespace js::jit; + ExecutableAllocator execAlloc(cx->runtime()); + + breakpoint(); + + (void) execAlloc; +} + +FRAGMENT(ExecutableAllocator, onepool) { + using namespace js::jit; + ExecutablePool* pool = nullptr; + ExecutableAllocator execAlloc(cx->runtime()); + execAlloc.alloc(16 * 1024, &pool, BASELINE_CODE); + + breakpoint(); + + (void) pool; + (void) execAlloc; +} + +FRAGMENT(ExecutableAllocator, twopools) { + using namespace js::jit; + ExecutablePool* init = nullptr; + ExecutablePool* pool = nullptr; + ExecutableAllocator execAlloc(cx->runtime()); + + execAlloc.alloc(16 * 1024, &init, BASELINE_CODE); + + do { // Keep allocating until we get a second pool. + execAlloc.alloc(32 * 1024, &pool, ION_CODE); + } while (pool == init); + + breakpoint(); + + (void) execAlloc; +} diff --git a/js/src/gdb/tests/test-ExecutableAllocator.py b/js/src/gdb/tests/test-ExecutableAllocator.py new file mode 100644 index 000000000..b790e2d34 --- /dev/null +++ b/js/src/gdb/tests/test-ExecutableAllocator.py @@ -0,0 +1,17 @@ +# Tests for ExecutableAllocator pretty-printing + +assert_subprinter_registered('SpiderMonkey', 'JS::GCCellPtr') + +run_fragment('ExecutableAllocator.empty') + +assert_pretty('execAlloc', 'ExecutableAllocator([])') + +run_fragment('ExecutableAllocator.onepool') + +reExecPool = 'ExecutablePool [a-f0-9]{8,}-[a-f0-9]{8,}' +assert_regexp_pretty('pool', reExecPool) +assert_regexp_pretty('execAlloc', 'ExecutableAllocator\(\[' +reExecPool+ '\]\)') + +run_fragment('ExecutableAllocator.twopools') + +assert_regexp_pretty('execAlloc', 'ExecutableAllocator\(\[' + reExecPool + ', ' + reExecPool + '\]\)') diff --git a/js/src/gdb/tests/test-GCCellPtr.cpp b/js/src/gdb/tests/test-GCCellPtr.cpp new file mode 100644 index 000000000..cc5802dbe --- /dev/null +++ b/js/src/gdb/tests/test-GCCellPtr.cpp @@ -0,0 +1,23 @@ +#include "gdb-tests.h" +#include "jsapi.h" + +#include "js/HeapAPI.h" + +FRAGMENT(GCCellPtr, simple) { + JS::GCCellPtr nulll(nullptr); + + JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx)); + JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0)); + JS::Rooted<JS::Symbol*> unique(cx, JS::NewSymbol(cx, nullptr)); + + JS::GCCellPtr object(glob.get()); + JS::GCCellPtr string(empty.get()); + JS::GCCellPtr symbol(unique.get()); + + breakpoint(); + + (void) nulll; + (void) object; + (void) string; + (void) symbol; +} diff --git a/js/src/gdb/tests/test-GCCellPtr.py b/js/src/gdb/tests/test-GCCellPtr.py new file mode 100644 index 000000000..d5bb3d1e1 --- /dev/null +++ b/js/src/gdb/tests/test-GCCellPtr.py @@ -0,0 +1,10 @@ +# Tests for GCCellPtr pretty-printing + +assert_subprinter_registered('SpiderMonkey', 'JS::GCCellPtr') + +run_fragment('GCCellPtr.simple') + +assert_pretty('nulll', 'JS::GCCellPtr(nullptr)') +assert_pretty('object', 'JS::GCCellPtr((JSObject*) )') +assert_pretty('string', 'JS::GCCellPtr((JSString*) )') +assert_pretty('symbol', 'JS::GCCellPtr((JS::Symbol*) )') diff --git a/js/src/gdb/tests/test-Interpreter.cpp b/js/src/gdb/tests/test-Interpreter.cpp new file mode 100644 index 000000000..9e206ff6a --- /dev/null +++ b/js/src/gdb/tests/test-Interpreter.cpp @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gdb-tests.h" +#include "jsapi.h" + +#include "vm/Stack.h" + +namespace js { + +void +GDBTestInitInterpreterRegs(InterpreterRegs& regs, + js::InterpreterFrame* fp_, + JS::Value* sp, + uint8_t* pc) +{ + regs.fp_ = fp_; + regs.sp = sp; + regs.pc = pc; +} + +void +GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, void* ptr) +{ + MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0); + frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_ScriptFrameIterData; +} + +void +GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, InterpreterFrame* ptr) +{ + MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0); + frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_InterpreterFrame; +} + +void +GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, jit::BaselineFrame* ptr) +{ + MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0); + frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_BaselineFrame; +} + +void +GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, jit::RematerializedFrame* ptr) +{ + MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0); + frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_RematerializedFrame; +} + +} // namespace js + +FRAGMENT(Interpreter, Regs) { + struct FakeFrame { + js::InterpreterFrame frame; + JS::Value slot0; + JS::Value slot1; + JS::Value slot2; + } fakeFrame; + uint8_t fakeOpcode = JSOP_IFEQ; + + js::InterpreterRegs regs; + js::GDBTestInitInterpreterRegs(regs, &fakeFrame.frame, &fakeFrame.slot2, &fakeOpcode); + + breakpoint(); + + (void) regs; +} + +FRAGMENT(Interpreter, AbstractFramePtr) { + + js::AbstractFramePtr sfidptr; + GDBTestInitAbstractFramePtr(sfidptr, (js::ScriptFrameIter::Data*) uintptr_t(0xdeeb0)); + + js::AbstractFramePtr ifptr; + GDBTestInitAbstractFramePtr(ifptr, (js::InterpreterFrame*) uintptr_t(0x8badf00)); + + js::AbstractFramePtr bfptr; + GDBTestInitAbstractFramePtr(bfptr, (js::jit::BaselineFrame*) uintptr_t(0xbadcafe0)); + + js::AbstractFramePtr rfptr; + GDBTestInitAbstractFramePtr(rfptr, (js::jit::RematerializedFrame*) uintptr_t(0xdabbad00)); + + breakpoint(); + + (void) sfidptr; + (void) ifptr; + (void) bfptr; + (void) rfptr; +} diff --git a/js/src/gdb/tests/test-Interpreter.py b/js/src/gdb/tests/test-Interpreter.py new file mode 100644 index 000000000..c1cf87c66 --- /dev/null +++ b/js/src/gdb/tests/test-Interpreter.py @@ -0,0 +1,14 @@ +# Test printing interpreter internal data structures. + +assert_subprinter_registered('SpiderMonkey', 'js::InterpreterRegs') + +run_fragment('Interpreter.Regs') + +assert_pretty('regs', '{ fp_ = , sp = fp_.slots() + 2, pc = (JSOP_IFEQ) }') + +run_fragment('Interpreter.AbstractFramePtr') + +assert_pretty('sfidptr', 'AbstractFramePtr ((js::ScriptFrameIter::Data *) ) = {ptr_ = 913072}') +assert_pretty('ifptr', 'AbstractFramePtr ((js::InterpreterFrame *) ) = {ptr_ = 146464513}') +assert_pretty('bfptr', 'AbstractFramePtr ((js::jit::BaselineFrame *) ) = {ptr_ = 3135025122}') +assert_pretty('rfptr', 'AbstractFramePtr ((js::jit::RematerializedFrame *) ) = {ptr_ = 3669732611}') diff --git a/js/src/gdb/tests/test-JSObject-null.py b/js/src/gdb/tests/test-JSObject-null.py new file mode 100644 index 000000000..40721b2e2 --- /dev/null +++ b/js/src/gdb/tests/test-JSObject-null.py @@ -0,0 +1,6 @@ +gdb.execute('set print address on') + +run_fragment('JSObject.null') + +assert_pretty('null', '0x0') +assert_pretty('nullRaw', '0x0') diff --git a/js/src/gdb/tests/test-JSObject.cpp b/js/src/gdb/tests/test-JSObject.cpp new file mode 100644 index 000000000..2be10e89c --- /dev/null +++ b/js/src/gdb/tests/test-JSObject.cpp @@ -0,0 +1,43 @@ +#include "gdb-tests.h" +#include "jsapi.h" + +FRAGMENT(JSObject, simple) { + JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx)); + JS::Rooted<JSObject*> plain(cx, JS_NewPlainObject(cx)); + JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx)); + JS::Rooted<JSObject*> func(cx, (JSObject*) JS_NewFunction(cx, (JSNative) 1, 0, 0, + "dys")); + JS::Rooted<JSObject*> anon(cx, (JSObject*) JS_NewFunction(cx, (JSNative) 1, 0, 0, nullptr)); + JS::Rooted<JSFunction*> funcPtr(cx, JS_NewFunction(cx, (JSNative) 1, 0, 0, + "formFollows")); + + JSObject& plainRef = *plain; + JSFunction& funcRef = *funcPtr; + JSObject* plainRaw = plain; + JSObject* funcRaw = func; + + static const JSClass cls = { "\xc7X" }; + JS::RootedObject badClassName(cx, JS_NewObject(cx, &cls)); + + breakpoint(); + + (void) glob; + (void) plain; + (void) func; + (void) anon; + (void) funcPtr; + (void) &plainRef; + (void) &funcRef; + (void) plainRaw; + (void) funcRaw; +} + +FRAGMENT(JSObject, null) { + JS::Rooted<JSObject*> null(cx, nullptr); + JSObject* nullRaw = null; + + breakpoint(); + + (void) null; + (void) nullRaw; +} diff --git a/js/src/gdb/tests/test-JSObject.py b/js/src/gdb/tests/test-JSObject.py new file mode 100644 index 000000000..3d4c7c37b --- /dev/null +++ b/js/src/gdb/tests/test-JSObject.py @@ -0,0 +1,22 @@ +# Printing JSObjects. + +assert_subprinter_registered('SpiderMonkey', 'ptr-to-JSObject') +assert_subprinter_registered('SpiderMonkey', 'ref-to-JSObject') + +run_fragment('JSObject.simple') + +# These patterns look a little strange because of prologue.py's 'set print +# address off', which avoids putting varying addresses in the output. After +# the '(JSObject *) ', there is a 'void *' value printing as the empty +# string. + +assert_pretty('glob', '(JSObject *) [object global] delegate') +assert_pretty('plain', '(JSObject *) [object Object]') +assert_pretty('func', '(JSObject *) [object Function "dys"]') +assert_pretty('anon', '(JSObject *) [object Function <unnamed>]') +assert_pretty('funcPtr', '(JSFunction *) [object Function "formFollows"]') + +assert_pretty('badClassName', '(JSObject *) [object \\307X]') + +assert_pretty('plainRef', '(JSObject &) @ [object Object]') +assert_pretty('funcRef', '(JSFunction &) @ [object Function "formFollows"]') diff --git a/js/src/gdb/tests/test-JSString-null.py b/js/src/gdb/tests/test-JSString-null.py new file mode 100644 index 000000000..eec3e03ba --- /dev/null +++ b/js/src/gdb/tests/test-JSString-null.py @@ -0,0 +1,6 @@ +gdb.execute('set print address on') + +run_fragment('JSString.null') + +assert_pretty('null', '0x0') +assert_pretty('nullRaw', '0x0') diff --git a/js/src/gdb/tests/test-JSString-subclasses.py b/js/src/gdb/tests/test-JSString-subclasses.py new file mode 100644 index 000000000..4539cd145 --- /dev/null +++ b/js/src/gdb/tests/test-JSString-subclasses.py @@ -0,0 +1,5 @@ +# We can print pointers to subclasses of JSString. + +run_fragment('JSString.subclasses') + +assert_pretty('flat', '"Hi!"') diff --git a/js/src/gdb/tests/test-JSString.cpp b/js/src/gdb/tests/test-JSString.cpp new file mode 100644 index 000000000..679640bc7 --- /dev/null +++ b/js/src/gdb/tests/test-JSString.cpp @@ -0,0 +1,64 @@ +#include "gdb-tests.h" +#include "jsatom.h" +#include "jscntxt.h" + +// When JSGC_ANALYSIS is #defined, Rooted<JSFlatString*> needs the definition +// of JSFlatString in order to figure out its ThingRootKind +#include "vm/String.h" + +FRAGMENT(JSString, simple) { + JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0)); + JS::Rooted<JSString*> x(cx, JS_NewStringCopyN(cx, "x", 1)); + JS::Rooted<JSString*> z(cx, JS_NewStringCopyZ(cx, "z")); + + // I expect this will be a non-inlined string. + JS::Rooted<JSString*> stars(cx, JS_NewStringCopyZ(cx, + "*************************" + "*************************" + "*************************" + "*************************")); + + // This may well be an inlined string. + JS::Rooted<JSString*> xz(cx, JS_ConcatStrings(cx, x, z)); + + // This will probably be a rope. + JS::Rooted<JSString*> doubleStars(cx, JS_ConcatStrings(cx, stars, stars)); + + // Ensure we're not confused by typedefs for pointer types. + JSString* xRaw = x; + + breakpoint(); + + (void) empty; + (void) x; + (void) z; + (void) stars; + (void) xz; + (void) doubleStars; + (void) xRaw; +} + +FRAGMENT(JSString, null) { + JS::Rooted<JSString*> null(cx, nullptr); + JSString* nullRaw = null; + + breakpoint(); + + (void) null; + (void) nullRaw; +} + +FRAGMENT(JSString, subclasses) { + JS::Rooted<JSFlatString*> flat(cx, JS_FlattenString(cx, JS_NewStringCopyZ(cx, "Hi!"))); + + breakpoint(); + + (void) flat; +} + +FRAGMENT(JSString, atom) { + JSAtom* molybdenum = js::Atomize(cx, "molybdenum", 10); + breakpoint(); + + (void) molybdenum; +} diff --git a/js/src/gdb/tests/test-JSString.py b/js/src/gdb/tests/test-JSString.py new file mode 100644 index 000000000..3189f34a4 --- /dev/null +++ b/js/src/gdb/tests/test-JSString.py @@ -0,0 +1,23 @@ +# Printing JSStrings. + +assert_subprinter_registered('SpiderMonkey', 'ptr-to-JSString') +run_fragment('JSString.simple') + +assert_pretty('empty', '""') +assert_pretty('x', '"x"') +assert_pretty('z', '"z"') +assert_pretty('xz', '"xz"') + +stars = gdb.parse_and_eval('stars') +assert_eq(str(stars), "'*' <repeats 100 times>") + +doubleStars = gdb.parse_and_eval('doubleStars') +assert_eq(str(doubleStars), "'*' <repeats 200 times>") + +assert_pretty('xRaw', '"x"') + +# JSAtom * + +run_fragment('JSString.atom') + +assert_pretty('molybdenum', '"molybdenum"') diff --git a/js/src/gdb/tests/test-JSSymbol.cpp b/js/src/gdb/tests/test-JSSymbol.cpp new file mode 100644 index 000000000..933f3075c --- /dev/null +++ b/js/src/gdb/tests/test-JSSymbol.cpp @@ -0,0 +1,20 @@ +#include "gdb-tests.h" +#include "jsapi.h" + +FRAGMENT(JSSymbol, simple) { + using namespace JS; + + RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!")); + + Rooted<Symbol*> unique(cx, NewSymbol(cx, nullptr)); + Rooted<Symbol*> unique_with_desc(cx, NewSymbol(cx, hello)); + Rooted<Symbol*> registry(cx, GetSymbolFor(cx, hello)); + Rooted<Symbol*> well_known(cx, GetWellKnownSymbol(cx, SymbolCode::iterator)); + + breakpoint(); + + (void) unique; + (void) unique_with_desc; + (void) registry; + (void) well_known; +} diff --git a/js/src/gdb/tests/test-JSSymbol.py b/js/src/gdb/tests/test-JSSymbol.py new file mode 100644 index 000000000..a9ffb5f41 --- /dev/null +++ b/js/src/gdb/tests/test-JSSymbol.py @@ -0,0 +1,10 @@ +# Printing JS::Symbols. + +assert_subprinter_registered('SpiderMonkey', 'ptr-to-JS::Symbol') + +run_fragment('JSSymbol.simple') + +assert_pretty('unique', 'Symbol()') +assert_pretty('unique_with_desc', 'Symbol("Hello!")') +assert_pretty('registry', 'Symbol.for("Hello!")') +assert_pretty('well_known', 'Symbol.iterator') diff --git a/js/src/gdb/tests/test-Root-null.py b/js/src/gdb/tests/test-Root-null.py new file mode 100644 index 000000000..9abf2e801 --- /dev/null +++ b/js/src/gdb/tests/test-Root-null.py @@ -0,0 +1,20 @@ +# Test printing roots that refer to NULL pointers. + +# Since mozilla.prettyprinters.Pointer declines to create pretty-printers +# for null pointers, GDB built-in printing code ends up handling them. But +# as of 2012-11, GDB suppresses printing pointers in replacement values: +# see: http://sourceware.org/ml/gdb/2012-11/msg00055.html +# +# Thus, if the pretty-printer for JS::Rooted simply returns the referent as +# a replacement value (which seems reasonable enough, if you want the +# pretty-printer to be completely transparent), and the referent is a null +# pointer, it prints as nothing at all. +# +# This test ensures that the JS::Rooted pretty-printer doesn't make that +# mistake. + +gdb.execute('set print address on') + +run_fragment('Root.null') + +assert_pretty('null', '0x0') diff --git a/js/src/gdb/tests/test-Root.cpp b/js/src/gdb/tests/test-Root.cpp new file mode 100644 index 000000000..6a8d8928b --- /dev/null +++ b/js/src/gdb/tests/test-Root.cpp @@ -0,0 +1,61 @@ +#include "gdb-tests.h" + +#include "jsapi.h" +#include "jsfun.h" + +#include "gc/Barrier.h" + +FRAGMENT(Root, null) { + JS::Rooted<JSObject*> null(cx, nullptr); + + breakpoint(); + + (void) null; +} + +void callee(JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> mutableObj) +{ + // Prevent the linker from unifying this function with others that are + // equivalent in machine code but not type. + fprintf(stderr, "Called " __FILE__ ":callee\n"); + breakpoint(); +} + +FRAGMENT(Root, handle) { + JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx)); + callee(global, &global); + (void) global; +} + +FRAGMENT(Root, HeapSlot) { + JS::Rooted<JS::Value> plinth(cx, JS::StringValue(JS_NewStringCopyZ(cx, "plinth"))); + JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, JS::HandleValueArray(plinth))); + + breakpoint(); + + (void) plinth; + (void) array; +} + +FRAGMENT(Root, barriers) { + JSObject* obj = JS_NewPlainObject(cx); + js::PreBarriered<JSObject*> prebarriered(obj); + js::GCPtrObject heapptr(obj); + js::HeapPtr<JSObject*> relocatable(obj); + + JS::Value val = JS::ObjectValue(*obj); + js::PreBarrieredValue prebarrieredValue(JS::ObjectValue(*obj)); + js::GCPtrValue heapValue(JS::ObjectValue(*obj)); + js::HeapPtr<JS::Value> relocatableValue(JS::ObjectValue(*obj)); + + breakpoint(); + + (void) prebarriered; + (void) heapptr; + (void) relocatable; + (void) val; + (void) prebarrieredValue; + (void) heapValue; + (void) relocatableValue; +} + diff --git a/js/src/gdb/tests/test-Root.py b/js/src/gdb/tests/test-Root.py new file mode 100644 index 000000000..e2b215082 --- /dev/null +++ b/js/src/gdb/tests/test-Root.py @@ -0,0 +1,27 @@ +# Test printing Handles. + +assert_subprinter_registered('SpiderMonkey', 'instantiations-of-JS::Rooted') +assert_subprinter_registered('SpiderMonkey', 'instantiations-of-JS::Handle') +assert_subprinter_registered('SpiderMonkey', 'instantiations-of-JS::MutableHandle') +assert_subprinter_registered('SpiderMonkey', 'instantiations-of-js::BarrieredBase') + +run_fragment('Root.handle') + +assert_pretty('obj', '(JSObject * const) [object global] delegate') +assert_pretty('mutableObj', '(JSObject *) [object global] delegate') + +run_fragment('Root.HeapSlot') + +# This depends on implementation details of arrays, but since HeapSlot is +# not a public type, I'm not sure how to avoid doing *something* ugly. +assert_pretty('((js::NativeObject *) array.get())->elements_[0]', '$jsval("plinth")') + +run_fragment('Root.barriers'); + +assert_pretty('prebarriered', '(JSObject *) [object Object]'); +assert_pretty('heapptr', '(JSObject *) [object Object]'); +assert_pretty('relocatable', '(JSObject *) [object Object]'); +assert_pretty('val', '$jsval((JSObject *) [object Object])'); +assert_pretty('heapValue', '$jsval((JSObject *) [object Object])'); +assert_pretty('prebarrieredValue', '$jsval((JSObject *) [object Object])'); +assert_pretty('relocatableValue', '$jsval((JSObject *) [object Object])'); diff --git a/js/src/gdb/tests/test-asmjs.cpp b/js/src/gdb/tests/test-asmjs.cpp new file mode 100644 index 000000000..a9f16f1a1 --- /dev/null +++ b/js/src/gdb/tests/test-asmjs.cpp @@ -0,0 +1,38 @@ +#include "gdb-tests.h" +#include "jsapi.h" + +#include <string.h> + +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 = JS::AsmJSOption::Enabled; + 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 000000000..219cb1a4d --- /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/gdb/tests/test-jsid.cpp b/js/src/gdb/tests/test-jsid.cpp new file mode 100644 index 000000000..9dd1ff90a --- /dev/null +++ b/js/src/gdb/tests/test-jsid.cpp @@ -0,0 +1,44 @@ +#include "gdb-tests.h" +#include "jsapi.h" + +FRAGMENT(jsid, simple) { + JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, "moon")); + JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinJSString(cx, string)); + JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned)); + jsid int_id = INT_TO_JSID(1729); + JS::Rooted<jsid> unique_symbol_id( + cx, SYMBOL_TO_JSID(JS::NewSymbol(cx, interned))); + JS::Rooted<jsid> registry_symbol_id( + cx, SYMBOL_TO_JSID(JS::GetSymbolFor(cx, interned))); + JS::Rooted<jsid> well_known_symbol_id( + cx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator))); + jsid void_id = JSID_VOID; + jsid empty_id = JSID_EMPTY; + + breakpoint(); + + (void) string_id; + (void) int_id; + (void) unique_symbol_id; + (void) registry_symbol_id; + (void) well_known_symbol_id; + (void) void_id; + (void) empty_id; +} + +void +jsid_handles(JS::Handle<jsid> jsid_handle, + JS::MutableHandle<jsid> mutable_jsid_handle) +{ + // Prevent the linker from unifying this function with others that are + // equivalent in machine code but not type. + fprintf(stderr, "Called " __FILE__ ":jsid_handles\n"); + breakpoint(); +} + +FRAGMENT(jsid, handles) { + JS::Rooted<JSString*> string(cx, JS_NewStringCopyZ(cx, "shovel")); + JS::Rooted<JSString*> interned(cx, JS_AtomizeAndPinJSString(cx, string)); + JS::Rooted<jsid> string_id(cx, INTERNED_STRING_TO_JSID(cx, interned)); + jsid_handles(string_id, &string_id); +} diff --git a/js/src/gdb/tests/test-jsid.py b/js/src/gdb/tests/test-jsid.py new file mode 100644 index 000000000..265e8ac6e --- /dev/null +++ b/js/src/gdb/tests/test-jsid.py @@ -0,0 +1,19 @@ +# Tests for jsid pretty-printing + +assert_subprinter_registered('SpiderMonkey', 'jsid') + +run_fragment('jsid.simple') + +assert_pretty('string_id', '$jsid("moon")') +assert_pretty('int_id', '$jsid(1729)') +unique_symbol_pretty = str(gdb.parse_and_eval('unique_symbol_id')).split('@')[0] +assert_eq(unique_symbol_pretty, '$jsid(Symbol("moon"))') +assert_pretty('registry_symbol_id', '$jsid(Symbol.for("moon"))') +assert_pretty('well_known_symbol_id', '$jsid(Symbol.iterator)') +assert_pretty('void_id', 'JSID_VOID') +assert_pretty('empty_id', 'JSID_EMPTY') + +run_fragment('jsid.handles') + +assert_pretty('jsid_handle', '$jsid("shovel")') +assert_pretty('mutable_jsid_handle', '$jsid("shovel")') diff --git a/js/src/gdb/tests/test-jsval.cpp b/js/src/gdb/tests/test-jsval.cpp new file mode 100644 index 000000000..f3c3247e2 --- /dev/null +++ b/js/src/gdb/tests/test-jsval.cpp @@ -0,0 +1,40 @@ +#include "gdb-tests.h" +#include "jsapi.h" + +FRAGMENT(jsval, simple) { + using namespace JS; + + RootedValue fortytwo(cx, Int32Value(42)); + RootedValue negone(cx, Int32Value(-1)); + RootedValue undefined(cx, UndefinedValue()); + RootedValue null(cx, NullValue()); + RootedValue js_true(cx, BooleanValue(true)); + RootedValue js_false(cx, BooleanValue(false)); + RootedValue elements_hole(cx, js::MagicValue(JS_ELEMENTS_HOLE)); + + RootedValue empty_string(cx); + empty_string.setString(JS_NewStringCopyZ(cx, "")); + RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!")); + RootedValue friendly_string(cx, StringValue(hello)); + RootedValue symbol(cx, SymbolValue(GetSymbolFor(cx, hello))); + + RootedValue global(cx); + global.setObject(*CurrentGlobalOrNull(cx)); + + // Some interesting value that floating-point won't munge. + RootedValue onehundredthirtysevenonehundredtwentyeighths(cx, DoubleValue(137.0 / 128.0)); + + breakpoint(); + + (void) fortytwo; + (void) negone; + (void) undefined; + (void) js_true; + (void) js_false; + (void) null; + (void) elements_hole; + (void) empty_string; + (void) friendly_string; + (void) symbol; + (void) global; +} diff --git a/js/src/gdb/tests/test-jsval.py b/js/src/gdb/tests/test-jsval.py new file mode 100644 index 000000000..f39a6591f --- /dev/null +++ b/js/src/gdb/tests/test-jsval.py @@ -0,0 +1,18 @@ +# Basic unit tests for jsval pretty-printer. + +assert_subprinter_registered('SpiderMonkey', 'JS::Value') + +run_fragment('jsval.simple') + +assert_pretty('fortytwo', '$jsval(42)') +assert_pretty('negone', '$jsval(-1)') +assert_pretty('undefined', 'JSVAL_VOID') +assert_pretty('null', 'JSVAL_NULL') +assert_pretty('js_true', 'JSVAL_TRUE') +assert_pretty('js_false', 'JSVAL_FALSE') +assert_pretty('elements_hole', '$jsmagic(JS_ELEMENTS_HOLE)') +assert_pretty('empty_string', '$jsval("")') +assert_pretty('friendly_string', '$jsval("Hello!")') +assert_pretty('symbol', '$jsval(Symbol.for("Hello!"))') +assert_pretty('global', '$jsval((JSObject *) [object global] delegate)') +assert_pretty('onehundredthirtysevenonehundredtwentyeighths', '$jsval(1.0703125)') diff --git a/js/src/gdb/tests/test-prettyprinters.cpp b/js/src/gdb/tests/test-prettyprinters.cpp new file mode 100644 index 000000000..794e4fd56 --- /dev/null +++ b/js/src/gdb/tests/test-prettyprinters.cpp @@ -0,0 +1,38 @@ +#include "gdb-tests.h" + +typedef int A; +typedef A B; + +class C { }; +class D { }; +typedef C C_; +typedef D D_; +class E: C, D { }; +typedef E E_; +class F: C_, D_ { }; +class G { }; +class H: F, G { }; + +FRAGMENT(prettyprinters, implemented_types) { + int i; + A a; + B b; + C c; + C_ c_; + E e; + E_ e_; + F f; + H h; + + breakpoint(); + + (void) i; + (void) a; + (void) b; + (void) c; + (void) c_; + (void) e; + (void) e_; + (void) f; + (void) h; +} diff --git a/js/src/gdb/tests/test-prettyprinters.py b/js/src/gdb/tests/test-prettyprinters.py new file mode 100644 index 000000000..9c380fdda --- /dev/null +++ b/js/src/gdb/tests/test-prettyprinters.py @@ -0,0 +1,22 @@ +import mozilla.prettyprinters + +run_fragment('prettyprinters.implemented_types') + +def implemented_type_names(expr): + v = gdb.parse_and_eval(expr) + it = mozilla.prettyprinters.implemented_types(v.type) + return [str(_) for _ in it] + +assert_eq(implemented_type_names('i'), ['int']) +assert_eq(implemented_type_names('a'), ['A', 'int']) +assert_eq(implemented_type_names('b'), ['B', 'A', 'int']) +assert_eq(implemented_type_names('c'), ['C']) +assert_eq(implemented_type_names('c_'), ['C_', 'C']) +assert_eq(implemented_type_names('e'), ['E', 'C', 'D']) +assert_eq(implemented_type_names('e_'), ['E_', 'E', 'C', 'D']) +assert_eq(implemented_type_names('f'), ['F', 'C', 'D']) +assert_eq(implemented_type_names('h'), ['H', 'F', 'G', 'C', 'D']) + +# Check that our pretty-printers aren't interfering with printing other types. +assert_pretty('10', '10') +assert_pretty('(void*) 0', '') # Because of 'set print address off' diff --git a/js/src/gdb/tests/test-unwind.cpp b/js/src/gdb/tests/test-unwind.cpp new file mode 100644 index 000000000..6c8b7b86a --- /dev/null +++ b/js/src/gdb/tests/test-unwind.cpp @@ -0,0 +1,52 @@ +#include "gdb-tests.h" +#include "jsapi.h" +#include "jit/JitOptions.h" + +#include <string.h> + +static bool +Something(JSContext* cx, unsigned argc, JS::Value* vp) +{ + JS::CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setInt32(23); + breakpoint(); + return true; +} + +static const JSFunctionSpecWithHelp unwind_functions[] = { + JS_FN_HELP("something", Something, 0, 0, +"something()", +" Test function for test-unwind."), + JS_FS_HELP_END +}; + +FRAGMENT(unwind, simple) { + using namespace JS; + + JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx)); + if (!JS_DefineFunctionsWithHelp(cx, global, unwind_functions)) + return; + + // baseline-eager. + uint32_t saveThreshold = js::jit::JitOptions.baselineWarmUpThreshold; + js::jit::JitOptions.baselineWarmUpThreshold = 0; + + int line0 = __LINE__; + const char* bytes = "\n" + "function unwindFunctionInner() {\n" + " return something();\n" + "}\n" + "\n" + "function unwindFunctionOuter() {\n" + " return unwindFunctionInner();\n" + "}\n" + "\n" + "unwindFunctionOuter();\n"; + + CompileOptions opts(cx); + opts.setFileAndLine(__FILE__, line0 + 1); + RootedValue rval(cx); + Evaluate(cx, opts, bytes, strlen(bytes), &rval); + + js::jit::JitOptions.baselineWarmUpThreshold = saveThreshold; +} diff --git a/js/src/gdb/tests/test-unwind.py b/js/src/gdb/tests/test-unwind.py new file mode 100644 index 000000000..6ddf0cd22 --- /dev/null +++ b/js/src/gdb/tests/test-unwind.py @@ -0,0 +1,58 @@ +# Test the unwinder and the frame filter. + +import platform + +def do_unwinder_test(): + # The unwinder is disabled by default for the moment. Turn it on to check + # that the unwinder works as expected. + import gdb + gdb.execute("enable unwinder .* SpiderMonkey") + + run_fragment('unwind.simple', 'Something') + + first = True + # The unwinder is a bit flaky still but should at least be able to + # recognize one set of entry and exit frames. This also tests to + # make sure we didn't end up solely in the interpreter. + found_entry = False + found_exit = False + found_main = False + found_inner = False + found_outer = False + frames = list(gdb.frames.execute_frame_filters(gdb.newest_frame(), 0, -1)) + for frame in frames: + print("examining " + frame.function()) + if first: + assert_eq(frame.function().startswith("Something"), True) + first = False + elif frame.function() == "<<JitFrame_Exit>>": + found_exit = True + elif frame.function() == "<<JitFrame_Entry>>": + found_entry = True + elif frame.function() == "main": + found_main = True + elif "unwindFunctionInner" in frame.function(): + found_inner = True + elif "unwindFunctionOuter" in frame.function(): + found_outer = True + + # Had to have found a frame. + assert_eq(first, False) + # Had to have found main. + assert_eq(found_main, True) + # Had to have found the entry and exit frames. + assert_eq(found_exit, True) + assert_eq(found_entry, True) + # Had to have found the names of the two JS functions. + assert_eq(found_inner, True) + assert_eq(found_outer, True) + +# Only on the right platforms. +if platform.machine() == 'x86_64' and platform.system() == 'Linux': + # Only test when gdb has the unwinder feature. + try: + import gdb.unwinder + import gdb.frames + do_unwinder_test() + except: + pass diff --git a/js/src/gdb/tests/typedef-printers.cpp b/js/src/gdb/tests/typedef-printers.cpp new file mode 100644 index 000000000..7573b5de5 --- /dev/null +++ b/js/src/gdb/tests/typedef-printers.cpp @@ -0,0 +1,11 @@ +#include "gdb-tests.h" + +typedef int my_typedef; + +FRAGMENT(typedef_printers, one) { + my_typedef i = 0; + + breakpoint(); + + (void) i; +} diff --git a/js/src/gdb/tests/typedef-printers.py b/js/src/gdb/tests/typedef-printers.py new file mode 100644 index 000000000..ed37e438e --- /dev/null +++ b/js/src/gdb/tests/typedef-printers.py @@ -0,0 +1,14 @@ +# Test that we can find pretty-printers for typedef names, not just for +# struct types and templates. + +import mozilla.prettyprinters + +@mozilla.prettyprinters.pretty_printer('my_typedef') +class my_typedef(object): + def __init__(self, value, cache): + pass + def to_string(self): + return 'huzzah' + +run_fragment('typedef_printers.one') +assert_pretty('i', 'huzzah') |