profiling.js (11847B)
1 // |jit-test| skip-if: !WasmHelpers.isSingleStepProfilingEnabled 2 3 const Module = WebAssembly.Module; 4 const Instance = WebAssembly.Instance; 5 const Table = WebAssembly.Table; 6 7 const { 8 assertEqImpreciseStacks, 9 assertEqPreciseStacks, 10 startProfiling, 11 endProfiling 12 } = WasmHelpers; 13 14 function test(code, importObj, expectedStacks) 15 { 16 enableGeckoProfiling(); 17 18 var f = wasmEvalText(code, importObj).exports[""]; 19 startProfiling(); 20 f(); 21 assertEqImpreciseStacks(endProfiling(), expectedStacks); 22 23 disableGeckoProfiling(); 24 } 25 26 test( 27 `(module 28 (func (result i32) (i32.const 42)) 29 (export "" (func 0)) 30 )`, 31 {}, 32 ["", ">", "0,>", ">", ""]); 33 34 test( 35 `(module 36 (func (result i32) (i32.add (call 1) (i32.const 1))) 37 (func (result i32) (i32.const 42)) 38 (export "" (func 0)) 39 )`, 40 {}, 41 ["", ">", "0,>", "1,0,>", "0,>", ">", ""]); 42 43 test( 44 `(module 45 (func $foo (call_indirect (type 0) (i32.const 0))) 46 (func $bar) 47 (table funcref (elem $bar)) 48 (export "" (func $foo)) 49 )`, 50 {}, 51 ["", ">", "foo,>", "bar,foo,>", "foo,>", ">", ""]); 52 53 test( 54 `(module 55 (import "" "foo" (func $foo)) 56 (table funcref (elem $foo)) 57 (func $bar (call_indirect (type 0) (i32.const 0))) 58 (export "" (func $bar)) 59 )`, 60 {"":{foo:()=>{}}}, 61 ["", ">", "bar,>", "foo,bar,>", "<,foo,bar,>", "foo,bar,>", "bar,>", ">", ""]); 62 63 test(`(module 64 (import "Math" "sin" (func $f32 (param f32) (result f32))) 65 (func (export "") (param f32) (result f32) 66 local.get 0 67 call $f32 68 ) 69 )`, 70 this, 71 ["", ">", "1,>", "<,1,>", "1,>", ">", ""]); 72 73 if (getBuildConfiguration("arm-simulator")) { 74 // On ARM, some int64 operations are calls to C++. 75 for (let op of ['div_s', 'rem_s', 'div_u', 'rem_u']) { 76 test(`(module 77 (func (export "") (param i32) (result i32) 78 local.get 0 79 i64.extend_i32_s 80 i64.const 0x1a2b3c4d5e6f 81 i64.${op} 82 i32.wrap_i64 83 ) 84 )`, 85 this, 86 ["", ">", "0,>", "<,0,>", `i64.${op},0,>`, "<,0,>", "0,>", ">", ""], 87 ); 88 } 89 } 90 91 // memory.size is a callout. 92 test(`(module 93 (memory 1) 94 (func (export "") (result i32) 95 memory.size 96 ) 97 )`, 98 this, 99 ["", ">", "0,>", "<,0,>", "memory.size m32,0,>", "<,0,>", "0,>", ">", ""], 100 ); 101 102 // memory.grow is a callout. 103 test(`(module 104 (memory 1) 105 (func (export "") (result i32) 106 i32.const 1 107 memory.grow 108 ) 109 )`, 110 this, 111 ["", ">", "0,>", "<,0,>", "memory.grow m32,0,>", "<,0,>", "0,>", ">", ""], 112 ); 113 114 // A few math builtins. 115 for (let type of ['f32', 'f64']) { 116 for (let func of ['ceil', 'floor', 'nearest', 'trunc']) { 117 if (getBuildConfiguration("arm64")) { 118 continue; 119 } 120 test(`(module 121 (func (export "") (param ${type}) (result ${type}) 122 local.get 0 123 ${type}.${func} 124 ) 125 )`, 126 this, 127 ["", ">", "0,>", "<,0,>", `${type}.${func},0,>`, "<,0,>", "0,>", ">", ""]); 128 } 129 } 130 131 (function() { 132 // Error handling. 133 function testError(code, error, expect) 134 { 135 enableGeckoProfiling(); 136 var f = wasmEvalText(code).exports[""]; 137 enableSingleStepProfiling(); 138 assertThrowsInstanceOf(f, error); 139 assertEqImpreciseStacks(disableSingleStepProfiling(), expect); 140 disableGeckoProfiling(); 141 } 142 143 testError( 144 `(module 145 (func $foo (unreachable)) 146 (func (export "") (call $foo)) 147 )`, 148 WebAssembly.RuntimeError, 149 ["", ">", "1,>", "foo,1,>", "1,>", "", ">", ""]); 150 151 testError( 152 `(module 153 (type $good (func)) 154 (type $bad (func (param i32))) 155 (func $foo (call_indirect (type $bad) (i32.const 1) (i32.const 0))) 156 (func $bar (type $good)) 157 (table funcref (elem $bar)) 158 (export "" (func $foo)) 159 )`, 160 WebAssembly.RuntimeError, 161 ["", ">", "foo,>", "bar,foo,>", ">", "", ">", ""]); 162 })(); 163 164 (function() { 165 // Tables fun. 166 var e = wasmEvalText(` 167 (module 168 (func $foo (result i32) (i32.const 42)) 169 (export "foo" (func $foo)) 170 (func $bar (result i32) (i32.const 13)) 171 (table 10 funcref) 172 (elem (i32.const 0) $foo $bar) 173 (export "tbl" (table 0)) 174 )`).exports; 175 assertEq(e.foo(), 42); 176 assertEq(e.tbl.get(0)(), 42); 177 assertEq(e.tbl.get(1)(), 13); 178 179 enableGeckoProfiling(); 180 enableSingleStepProfiling(); 181 assertEq(e.tbl.get(0)(), 42); 182 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "foo,>", ">", ""]); 183 disableGeckoProfiling(); 184 185 assertEq(e.foo(), 42); 186 assertEq(e.tbl.get(0)(), 42); 187 assertEq(e.tbl.get(1)(), 13); 188 189 enableGeckoProfiling(); 190 enableSingleStepProfiling(); 191 assertEq(e.tbl.get(1)(), 13); 192 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "bar,>", ">", ""]); 193 disableGeckoProfiling(); 194 195 assertEq(e.tbl.get(0)(), 42); 196 assertEq(e.tbl.get(1)(), 13); 197 assertEq(e.foo(), 42); 198 199 enableGeckoProfiling(); 200 enableSingleStepProfiling(); 201 assertEq(e.foo(), 42); 202 assertEq(e.tbl.get(1)(), 13); 203 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "foo,>", ">", "", ">", "bar,>", ">", ""]); 204 disableGeckoProfiling(); 205 206 var e2 = wasmEvalText(` 207 (module 208 (type $v2i (func (result i32))) 209 (import "a" "b" (table 10 funcref)) 210 (elem (i32.const 2) $bar) 211 (func $bar (result i32) (i32.const 99)) 212 (func $baz (param $i i32) (result i32) (call_indirect (type $v2i) (local.get $i))) 213 (export "baz" (func $baz)) 214 )`, {a:{b:e.tbl}}).exports; 215 216 enableGeckoProfiling(); 217 enableSingleStepProfiling(); 218 assertEq(e2.baz(0), 42); 219 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "baz,>", "foo,baz,>", "baz,>", ">", ""]); 220 disableGeckoProfiling(); 221 222 enableGeckoProfiling(); 223 enableSingleStepProfiling(); 224 assertEq(e2.baz(1), 13); 225 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "baz,>", "bar,baz,>", "baz,>", ">", ""]); 226 disableGeckoProfiling(); 227 228 enableGeckoProfiling(); 229 enableSingleStepProfiling(); 230 assertEq(e2.baz(2), 99); 231 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "baz,>", "bar,baz,>", "baz,>", ">", ""]); 232 disableGeckoProfiling(); 233 })(); 234 235 (function() { 236 // Optimized wasm->wasm import. 237 var m1 = new Module(wasmTextToBinary(`(module 238 (func $foo (result i32) (i32.const 42)) 239 (export "foo" (func $foo)) 240 )`)); 241 var m2 = new Module(wasmTextToBinary(`(module 242 (import "a" "foo" (func $foo (result i32))) 243 (func $bar (result i32) (call $foo)) 244 (export "bar" (func $bar)) 245 )`)); 246 247 // Instantiate while not active: 248 var e1 = new Instance(m1).exports; 249 var e2 = new Instance(m2, {a:e1}).exports; 250 enableGeckoProfiling(); 251 enableSingleStepProfiling(); 252 assertEq(e2.bar(), 42); 253 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "bar,>", "foo,bar,>", "bar,>", ">", ""]); 254 disableGeckoProfiling(); 255 assertEq(e2.bar(), 42); 256 257 // Instantiate while active: 258 enableGeckoProfiling(); 259 var e3 = new Instance(m1).exports; 260 var e4 = new Instance(m2, {a:e3}).exports; 261 enableSingleStepProfiling(); 262 assertEq(e4.bar(), 42); 263 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "bar,>", "foo,bar,>", "bar,>", ">", ""]); 264 disableGeckoProfiling(); 265 assertEq(e4.bar(), 42); 266 })(); 267 268 (function() { 269 // FFIs test. 270 let prevOptions = getJitCompilerOptions(); 271 272 // Skip tests if baseline isn't enabled, since the stacks might differ by 273 // a few instructions. 274 if (prevOptions['baseline.enable'] === 0) 275 return; 276 277 setJitCompilerOption("baseline.warmup.trigger", 10); 278 279 enableGeckoProfiling(); 280 281 var m = new Module(wasmTextToBinary(`(module 282 (import "a" "ffi" (func $ffi (param i32) (result i32))) 283 284 (import "a" "sumTwo" (func $missingOneArg (param i32) (result i32))) 285 286 (func (export "foo") (param i32) (result i32) 287 local.get 0 288 call $ffi) 289 290 (func (export "id") (param i32) (result i32) 291 local.get 0 292 call $missingOneArg 293 ) 294 )`)); 295 296 var valueToConvert = 0; 297 function ffi(n) { 298 new Error().stack; // enter VM to clobber FP register. 299 if (n == 1337) { return valueToConvert }; 300 return 42; 301 } 302 303 function sumTwo(a, b) { 304 return (a|0)+(b|0)|0; 305 } 306 307 // Baseline compile ffi. 308 for (var i = 20; i --> 0;) { 309 ffi(i); 310 sumTwo(i-1, i+1); 311 } 312 313 var imports = { 314 a: { 315 ffi, 316 sumTwo 317 } 318 }; 319 320 var i = new Instance(m, imports).exports; 321 322 // Enable the jit exit. 323 assertEq(i.foo(0), 42); 324 assertEq(i.id(13), 13); 325 326 // Test normal conditions. 327 enableSingleStepProfiling(); 328 assertEq(i.foo(0), 42); 329 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "2,>", "<,2,>", 330 // Losing stack information while the JIT func prologue sets profiler 331 // virtual FP. 332 "", 333 // Callee time. 334 "<,2,>", 335 // Losing stack information while we're exiting JIT func epilogue and 336 // recovering wasm FP. 337 "", 338 // Back into the jit exit (frame info has been recovered). 339 "<,2,>", 340 // Normal unwinding. 341 "2,>", ">", ""]); 342 343 // Test rectifier frame. 344 enableSingleStepProfiling(); 345 assertEq(i.id(100), 100); 346 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "3,>", "<,3,>", 347 // Rectifier frame time is spent here (lastProfilingFrame has not been 348 // set). 349 "", 350 "<,3,>", 351 // Rectifier frame unwinding time is spent here. 352 "", 353 "<,3,>", 354 "3,>", ">", ""]); 355 356 // Test OOL coercion path. 357 valueToConvert = 2**31; 358 359 enableSingleStepProfiling(); 360 assertEq(i.foo(1337), -(2**31)); 361 assertEqImpreciseStacks(disableSingleStepProfiling(), ["", ">", "2,>", "<,2,>", "", "<,2,>", "", 362 // Back into the jit exit (frame info has been recovered). 363 // Inline conversion fails, we skip to the OOL path, call from there 364 // and get back to the jit exit. 365 "<,2,>", 366 // Normal unwinding. 367 "2,>", ">", ""]); 368 369 disableGeckoProfiling(); 370 setJitCompilerOption("baseline.warmup.trigger", prevOptions["baseline.warmup.trigger"]); 371 })(); 372 373 // Make sure it's possible to single-step through call through debug-enabled code. 374 (function() { 375 if (wasmCompileMode().includes("ion")) return; 376 377 enableGeckoProfiling(); 378 379 let g = newGlobal({newCompartment: true}); 380 let dbg = new Debugger(g); 381 dbg.onEnterFrame = () => {}; 382 enableSingleStepProfiling(); 383 g.eval(` 384 var code = wasmTextToBinary('(module (func (export "run") (result i32) i32.const 42))'); 385 var i = new WebAssembly.Instance(new WebAssembly.Module(code)); 386 assertEq(i.exports.run(), 42); 387 `); 388 389 disableSingleStepProfiling(); 390 disableGeckoProfiling(); 391 })(); 392 393 // Ion->wasm calls. 394 let func = wasmEvalText(`(module 395 (func $inner (param i32) (param i32) (result i32) 396 local.get 0 397 local.get 1 398 i32.add 399 ) 400 (func (export "add") (param i32) (param i32) (result i32) 401 local.get 0 402 local.get 1 403 call $inner 404 ) 405 )`).exports.add; 406 407 (function() { 408 enableGeckoProfiling(); 409 // 10 is enough in ion eager mode. 410 for (let i = 0; i < 10; i++) { 411 enableSingleStepProfiling(); 412 let res = func(i - 1, i + 1); 413 assertEqPreciseStacks(disableSingleStepProfiling(), [ 414 ['', '>', '1,>', 'inner,1,>' , '1,>', '>', ''], // slow entry 415 ['', '!>', '1,!>', 'inner,1,!>' , '1,!>', '!>', ''], // fast entry 416 ['', '1', 'inner,1' , '1', ''], // inlined jit call 417 ]); 418 assertEq(res, i+i); 419 } 420 disableGeckoProfiling(); 421 })();