diff options
Diffstat (limited to 'js/src/jit-test/tests/wasm/profiling.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/profiling.js | 247 |
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); +})(); |