tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 })();