summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-02-14 15:54:33 -0800
committerGitHub <noreply@github.com>2024-02-14 15:54:33 -0800
commitfeb8f24128c0c3bd53862b2f39acc5116f8ae87e (patch)
treeebfe048aadde2c6cffbb7708e0b2482a66a4e5e8
parent36d20933da2267068f4e5d4bb3c5a1adb9360f26 (diff)
downloadbinaryen-feb8f24128c0c3bd53862b2f39acc5116f8ae87e.tar.gz
binaryen-feb8f24128c0c3bd53862b2f39acc5116f8ae87e.tar.bz2
binaryen-feb8f24128c0c3bd53862b2f39acc5116f8ae87e.zip
Fuzzer: Remove Asyncify integration (#6309)
Fuzzing Asyncify has a significant cost both in terms of the complexity in the fuzzer and the slowness of the fuzzing. In practice it was useful years ago when Asyncify was written but hasn't found anything for a while, and Asyncify is really deprecated given JSPI. For all those reasons, remove it from the fuzzer. We do still have lots of normal coverage of asyncify in lit tests, unit tests, and the Emscripten test suite. Removing this will also make future improvements to the fuzzer simpler.
-rwxr-xr-xscripts/fuzz_opt.py71
-rw-r--r--scripts/fuzz_shell.js123
2 files changed, 0 insertions, 194 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py
index b514c5e7e..8e80c2bb9 100755
--- a/scripts/fuzz_opt.py
+++ b/scripts/fuzz_opt.py
@@ -1071,76 +1071,6 @@ class Wasm2JS(TestCaseHandler):
return all_disallowed(['exception-handling', 'simd', 'threads', 'bulk-memory', 'nontrapping-float-to-int', 'tail-call', 'sign-ext', 'reference-types', 'multivalue', 'gc', 'multimemory'])
-class Asyncify(TestCaseHandler):
- frequency = 0.1
-
- def handle_pair(self, input, before_wasm, after_wasm, opts):
- # we must legalize in order to run in JS
- async_before_wasm = abspath('async.' + os.path.basename(before_wasm))
- async_after_wasm = abspath('async.' + os.path.basename(after_wasm))
- run([in_bin('wasm-opt'), before_wasm, '--legalize-js-interface', '-o', async_before_wasm] + FEATURE_OPTS)
- run([in_bin('wasm-opt'), after_wasm, '--legalize-js-interface', '-o', async_after_wasm] + FEATURE_OPTS)
- before_wasm = async_before_wasm
- after_wasm = async_after_wasm
- before = fix_output(run_d8_wasm(before_wasm))
- after = fix_output(run_d8_wasm(after_wasm))
-
- if STACK_LIMIT in run_bynterp(before_wasm, ['--fuzz-exec-before']):
- # Running out of stack in infinite recursion can be a problem here
- # as we compare a wasm before and after asyncify, and asyncify can
- # add a lot of locals, which could mean the host limit can be
- # reached earlier, and alter the output (less logging before we
- # reach the host limit and trap).
- # TODO This is not quite enough, as if we are just under the limit
- # then we may only hit the limit after running asyncify. But
- # then we'd also need to detect differences in the limit in
- # the JS VM's output (which can differ from Binaryen's). For
- # now, this rules out infinite recursion at least.
- print('ignoring due to stack limit being hit')
- return
-
- try:
- compare(before, after, 'Asyncify (before/after)')
- except Exception:
- # if we failed to just compare the builds before asyncify even runs,
- # then it may use NaNs or be sensitive to legalization; ignore it
- print('ignoring due to pre-asyncify difference')
- return
-
- def do_asyncify(wasm):
- cmd = [in_bin('wasm-opt'), wasm, '--asyncify', '-o', abspath('async.t.wasm')]
- # if we allow NaNs, running binaryen optimizations and then
- # executing in d8 may lead to different results due to NaN
- # nondeterminism between VMs.
- if not NANS:
- if random.random() < 0.5:
- cmd += ['--optimize-level=%d' % random.randint(1, 3)]
- if random.random() < 0.5:
- cmd += ['--shrink-level=%d' % random.randint(1, 2)]
- cmd += FEATURE_OPTS
- run(cmd)
- out = run_d8_wasm(abspath('async.t.wasm'))
- # ignore the output from the new asyncify API calls - the ones with asserts will trap, too
- for ignore in ['[fuzz-exec] calling asyncify_start_unwind\nexception!\n',
- '[fuzz-exec] calling asyncify_start_unwind\n',
- '[fuzz-exec] calling asyncify_start_rewind\nexception!\n',
- '[fuzz-exec] calling asyncify_start_rewind\n',
- '[fuzz-exec] calling asyncify_stop_rewind\n',
- '[fuzz-exec] calling asyncify_stop_unwind\n']:
- out = out.replace(ignore, '')
- out = '\n'.join([l for l in out.splitlines() if 'asyncify: ' not in l])
- return fix_output(out)
-
- before_asyncify = do_asyncify(before_wasm)
- after_asyncify = do_asyncify(after_wasm)
-
- compare(before, before_asyncify, 'Asyncify (before/before_asyncify)')
- compare(before, after_asyncify, 'Asyncify (before/after_asyncify)')
-
- def can_run_on_feature_opts(self, feature_opts):
- return all_disallowed(['exception-handling', 'simd', 'tail-call', 'reference-types', 'multivalue', 'gc', 'multimemory'])
-
-
# given a wasm and a list of exports we want to keep, remove all other exports.
def filter_exports(wasm, output, keep):
# based on
@@ -1373,7 +1303,6 @@ testcase_handlers = [
CompareVMs(),
CheckDeterminism(),
Wasm2JS(),
- Asyncify(),
TrapsNeverHappen(),
CtorEval(),
Merge(),
diff --git a/scripts/fuzz_shell.js b/scripts/fuzz_shell.js
index 08864dfc7..4a3b42d9f 100644
--- a/scripts/fuzz_shell.js
+++ b/scripts/fuzz_shell.js
@@ -38,119 +38,6 @@ var detrand = (function() {
};
})();
-// Asyncify integration.
-var Asyncify = {
- sleeping: false,
- sleepingFunction: null,
- sleeps: 0,
- maxDepth: 0,
- DATA_ADDR: 4,
- // The fuzzer emits memories of size 16 (pages). Allow us to use almost all of
- // that (we start from offset 4, so we can't use them all).
- DATA_MAX: 15 * 65536,
- savedMemory: null,
- instrumentImports: function(imports) {
- var ret = {};
- for (var module in imports) {
- ret[module] = {};
- for (var i in imports[module]) {
- if (typeof imports[module][i] === 'function') {
- (function(module, i) {
- ret[module][i] = function() {
- refreshView();
- if (!Asyncify.sleeping) {
- // Sleep if asyncify support is present (which also requires
- // that the memory be exported), and at a certain probability.
- if (exports.asyncify_start_unwind &&
- view &&
- detrand() < 0.5) {
- // We are called in order to start a sleep/unwind.
- console.log('asyncify: sleep in ' + i + '...');
- Asyncify.sleepingFunction = i;
- Asyncify.sleeps++;
- var depth = new Error().stack.split('\n').length - 6;
- Asyncify.maxDepth = Math.max(Asyncify.maxDepth, depth);
- // Save the memory we use for data, so after we restore it later, the
- // sleep/resume appears to have had no change to memory.
- Asyncify.savedMemory = new Int32Array(view.subarray(Asyncify.DATA_ADDR >> 2, Asyncify.DATA_MAX >> 2));
- // Unwinding.
- // Fill in the data structure. The first value has the stack location,
- // which for simplicity we can start right after the data structure itself.
- view[Asyncify.DATA_ADDR >> 2] = Asyncify.DATA_ADDR + 8;
- // The end of the stack will not be reached here anyhow.
- view[Asyncify.DATA_ADDR + 4 >> 2] = Asyncify.DATA_MAX;
- exports.asyncify_start_unwind(Asyncify.DATA_ADDR);
- Asyncify.sleeping = true;
- } else {
- // Don't sleep, normal execution.
- return imports[module][i].apply(null, arguments);
- }
- } else {
- // We are called as part of a resume/rewind. Stop sleeping.
- console.log('asyncify: resume in ' + i + '...');
- assert(Asyncify.sleepingFunction === i);
- exports.asyncify_stop_rewind();
- // The stack should have been all used up, and so returned to the original state.
- assert(view[Asyncify.DATA_ADDR >> 2] == Asyncify.DATA_ADDR + 8);
- assert(view[Asyncify.DATA_ADDR + 4 >> 2] == Asyncify.DATA_MAX);
- Asyncify.sleeping = false;
- // Restore the memory to the state from before we slept.
- view.set(Asyncify.savedMemory, Asyncify.DATA_ADDR >> 2);
- return imports[module][i].apply(null, arguments);
- }
- };
- })(module, i);
- } else {
- ret[module][i] = imports[module][i];
- }
- }
- }
- // Add ignored.print, which is ignored by asyncify, and allows debugging of asyncified code.
- ret['ignored'] = { 'print': function(x, y) { console.log(x, y) } };
- return ret;
- },
- instrumentExports: function(exports) {
- var ret = {};
- for (var e in exports) {
- if (typeof exports[e] === 'function' &&
- !e.startsWith('asyncify_')) {
- (function(e) {
- ret[e] = function() {
- while (1) {
- var ret = exports[e].apply(null, arguments);
- // If we are sleeping, then the stack was unwound; rewind it.
- if (Asyncify.sleeping) {
- console.log('asyncify: stop unwind; rewind');
- assert(!ret, 'results during sleep are meaningless, just 0');
- //console.log('asyncify: after unwind', view[Asyncify.DATA_ADDR >> 2], view[Asyncify.DATA_ADDR + 4 >> 2]);
- try {
- exports.asyncify_stop_unwind();
- exports.asyncify_start_rewind(Asyncify.DATA_ADDR);
- } catch (e) {
- console.log('error in unwind/rewind switch', e);
- }
- continue;
- }
- return ret;
- }
- };
- })(e);
- } else {
- ret[e] = exports[e];
- }
- }
- return ret;
- },
- check: function() {
- assert(!Asyncify.sleeping);
- },
- finish: function() {
- if (Asyncify.sleeps > 0) {
- print('asyncify:', 'sleeps:', Asyncify.sleeps, 'max depth:', Asyncify.maxDepth);
- }
- },
-};
-
// Fuzz integration.
function logValue(x, y) {
if (typeof y !== 'undefined') {
@@ -183,8 +70,6 @@ if (typeof WebAssembly.Tag !== 'undefined') {
};
}
-imports = Asyncify.instrumentImports(imports);
-
// Create the wasm.
var module = new WebAssembly.Module(binary);
@@ -198,7 +83,6 @@ try {
// Handle the exports.
var exports = instance.exports;
-exports = Asyncify.instrumentExports(exports);
var view;
@@ -215,12 +99,7 @@ for (var e in exports) {
sortedExports.push(e);
}
sortedExports.sort();
-sortedExports = sortedExports.filter(function(e) {
- // Filter special intrinsic functions.
- return !e.startsWith('asyncify_');
-});
sortedExports.forEach(function(e) {
- Asyncify.check();
if (typeof exports[e] !== 'function') return;
try {
console.log('[fuzz-exec] calling ' + e);
@@ -233,5 +112,3 @@ sortedExports.forEach(function(e) {
}
});
-// Finish up
-Asyncify.finish();