summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/profiling.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/profiling.js')
-rw-r--r--js/src/jit-test/tests/wasm/profiling.js247
1 files changed, 247 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/profiling.js b/js/src/jit-test/tests/wasm/profiling.js
new file mode 100644
index 000000000..c8bf8467e
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/profiling.js
@@ -0,0 +1,247 @@
+load(libdir + "wasm.js");
+
+// Single-step profiling currently only works in the ARM simulator
+if (!getBuildConfiguration()["arm-simulator"])
+ quit();
+
+const Module = WebAssembly.Module;
+const Instance = WebAssembly.Instance;
+const Table = WebAssembly.Table;
+
+function normalize(stack)
+{
+ var wasmFrameTypes = [
+ {re:/^entry trampoline \(in asm.js\)$/, sub:">"},
+ {re:/^wasm-function\[(\d+)\] \(.*\)$/, sub:"$1"},
+ {re:/^(fast|slow) FFI trampoline \(in asm.js\)$/, sub:"<"},
+ {re:/ \(in asm.js\)$/, sub:""}
+ ];
+
+ var framesIn = stack.split(',');
+ var framesOut = [];
+ for (let frame of framesIn) {
+ for (let {re, sub} of wasmFrameTypes) {
+ if (re.test(frame)) {
+ framesOut.push(frame.replace(re, sub));
+ break;
+ }
+ }
+ }
+
+ return framesOut.join(',');
+}
+
+function removeAdjacentDuplicates(array) {
+ if (array.length < 2)
+ return;
+ let i = 0;
+ for (let j = 1; j < array.length; j++) {
+ if (array[i] !== array[j])
+ array[++i] = array[j];
+ }
+ array.length = i + 1;
+}
+
+function assertEqStacks(got, expect)
+{
+ for (let i = 0; i < got.length; i++)
+ got[i] = normalize(got[i]);
+
+ removeAdjacentDuplicates(got);
+
+ if (got.length != expect.length) {
+ print(`Got:\n${got.toSource()}\nExpect:\n${expect.toSource()}`);
+ assertEq(got.length, expect.length);
+ }
+
+ for (let i = 0; i < got.length; i++) {
+ if (got[i] !== expect[i]) {
+ print(`On stack ${i}, Got:\n${got[i]}\nExpect:\n${expect[i]}`);
+ assertEq(got[i], expect[i]);
+ }
+ }
+}
+
+function test(code, importObj, expect)
+{
+ enableSPSProfiling();
+
+ var f = wasmEvalText(code, importObj).exports[""];
+ enableSingleStepProfiling();
+ f();
+ assertEqStacks(disableSingleStepProfiling(), expect);
+
+ disableSPSProfiling();
+}
+
+test(
+`(module
+ (func (result i32) (i32.const 42))
+ (export "" 0)
+)`,
+{},
+["", ">", "0,>", ">", ""]);
+
+test(
+`(module
+ (func (result i32) (i32.add (call 1) (i32.const 1)))
+ (func (result i32) (i32.const 42))
+ (export "" 0)
+)`,
+{},
+["", ">", "0,>", "1,0,>", "0,>", ">", ""]);
+
+test(
+`(module
+ (func $foo (call_indirect 0 (i32.const 0)))
+ (func $bar)
+ (table anyfunc (elem $bar))
+ (export "" $foo)
+)`,
+{},
+["", ">", "0,>", "1,0,>", "0,>", ">", ""]);
+
+test(
+`(module
+ (import $foo "" "foo")
+ (table anyfunc (elem $foo))
+ (func $bar (call_indirect 0 (i32.const 0)))
+ (export "" $bar)
+)`,
+{"":{foo:()=>{}}},
+["", ">", "1,>", "0,1,>", "<,0,1,>", "0,1,>", "1,>", ">", ""]);
+
+function testError(code, error, expect)
+{
+ enableSPSProfiling();
+ var f = wasmEvalText(code).exports[""];
+ enableSingleStepProfiling();
+ assertThrowsInstanceOf(f, error);
+ assertEqStacks(disableSingleStepProfiling(), expect);
+ disableSPSProfiling();
+}
+
+testError(
+`(module
+ (func $foo (unreachable))
+ (func (export "") (call $foo))
+)`,
+WebAssembly.RuntimeError,
+["", ">", "1,>", "0,1,>", "trap handling,0,1,>", "inline stub,0,1,>", ""]);
+
+testError(
+`(module
+ (type $good (func))
+ (type $bad (func (param i32)))
+ (func $foo (call_indirect $bad (i32.const 1) (i32.const 0)))
+ (func $bar (type $good))
+ (table anyfunc (elem $bar))
+ (export "" $foo)
+)`,
+WebAssembly.RuntimeError,
+// Technically we have this one *one-instruction* interval where
+// the caller is lost (the stack with "1,>"). It's annoying to fix and shouldn't
+// mess up profiles in practice so we ignore it.
+["", ">", "0,>", "1,0,>", "1,>", "trap handling,0,>", "inline stub,0,>", ""]);
+
+(function() {
+ var e = wasmEvalText(`
+ (module
+ (func $foo (result i32) (i32.const 42))
+ (export "foo" $foo)
+ (func $bar (result i32) (i32.const 13))
+ (table 10 anyfunc)
+ (elem (i32.const 0) $foo $bar)
+ (export "tbl" table)
+ )`).exports;
+ assertEq(e.foo(), 42);
+ assertEq(e.tbl.get(0)(), 42);
+ assertEq(e.tbl.get(1)(), 13);
+
+ enableSPSProfiling();
+ enableSingleStepProfiling();
+ assertEq(e.tbl.get(0)(), 42);
+ assertEqStacks(disableSingleStepProfiling(), ["", ">", "0,>", ">", ""]);
+ disableSPSProfiling();
+
+ assertEq(e.foo(), 42);
+ assertEq(e.tbl.get(0)(), 42);
+ assertEq(e.tbl.get(1)(), 13);
+
+ enableSPSProfiling();
+ enableSingleStepProfiling();
+ assertEq(e.tbl.get(1)(), 13);
+ assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", ">", ""]);
+ disableSPSProfiling();
+
+ assertEq(e.tbl.get(0)(), 42);
+ assertEq(e.tbl.get(1)(), 13);
+ assertEq(e.foo(), 42);
+
+ enableSPSProfiling();
+ enableSingleStepProfiling();
+ assertEq(e.foo(), 42);
+ assertEq(e.tbl.get(1)(), 13);
+ assertEqStacks(disableSingleStepProfiling(), ["", ">", "0,>", ">", "", ">", "1,>", ">", ""]);
+ disableSPSProfiling();
+
+ var e2 = wasmEvalText(`
+ (module
+ (type $v2i (func (result i32)))
+ (import "a" "b" (table 10 anyfunc))
+ (elem (i32.const 2) $bar)
+ (func $bar (result i32) (i32.const 99))
+ (func $baz (param $i i32) (result i32) (call_indirect $v2i (get_local $i)))
+ (export "baz" $baz)
+ )`, {a:{b:e.tbl}}).exports;
+
+ enableSPSProfiling();
+ enableSingleStepProfiling();
+ assertEq(e2.baz(0), 42);
+ assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]);
+ disableSPSProfiling();
+
+ enableSPSProfiling();
+ enableSingleStepProfiling();
+ assertEq(e2.baz(1), 13);
+ assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", "1,1,>", "1,>", ">", ""]);
+ disableSPSProfiling();
+
+ enableSPSProfiling();
+ enableSingleStepProfiling();
+ assertEq(e2.baz(2), 99);
+ assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]);
+ disableSPSProfiling();
+})();
+
+(function() {
+ var m1 = new Module(wasmTextToBinary(`(module
+ (func $foo (result i32) (i32.const 42))
+ (export "foo" $foo)
+ )`));
+ var m2 = new Module(wasmTextToBinary(`(module
+ (import $foo "a" "foo" (result i32))
+ (func $bar (result i32) (call $foo))
+ (export "bar" $bar)
+ )`));
+
+ // Instantiate while not active:
+ var e1 = new Instance(m1).exports;
+ var e2 = new Instance(m2, {a:e1}).exports;
+ enableSPSProfiling();
+ enableSingleStepProfiling();
+ assertEq(e2.bar(), 42);
+ assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]);
+ disableSPSProfiling();
+ assertEq(e2.bar(), 42);
+
+ // Instantiate while active:
+ enableSPSProfiling();
+ var e3 = new Instance(m1).exports;
+ var e4 = new Instance(m2, {a:e3}).exports;
+ enableSingleStepProfiling();
+ assertEq(e4.bar(), 42);
+ assertEqStacks(disableSingleStepProfiling(), ["", ">", "1,>", "0,1,>", "1,>", ">", ""]);
+ disableSPSProfiling();
+ assertEq(e4.bar(), 42);
+})();