From 9d703556a854da2b56652315f4865ed09aa9ad89 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 29 Apr 2024 19:12:06 +0200 Subject: [PATCH 1/5] tools: support != in test status files --- tools/test.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/test.py b/tools/test.py index 13ea6d12082bfc..e29b9f7ba05794 100755 --- a/tools/test.py +++ b/tools/test.py @@ -1041,6 +1041,9 @@ def Evaluate(self, env, defs): return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs) elif self.op == 'if': return False + elif self.op == '!=': + inter = self.left.GetOutcomes(env, defs) != self.right.GetOutcomes(env, defs) + return bool(inter) elif self.op == '==': inter = self.left.GetOutcomes(env, defs) & self.right.GetOutcomes(env, defs) return bool(inter) @@ -1128,6 +1131,9 @@ def Tokenize(self): elif self.Current(2) == '==': self.AddToken('==') self.Advance(2) + elif self.Current(2) == '!=': + self.AddToken('!=') + self.Advance(2) else: return None return self.tokens @@ -1180,7 +1186,7 @@ def ParseAtomicExpression(scan): return None -BINARIES = ['=='] +BINARIES = ['==', '!='] def ParseOperatorExpression(scan): left = ParseAtomicExpression(scan) if not left: return None From 7857c4bc8fc0e91992e791b8a433e480e3d686dc Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 29 Apr 2024 19:12:39 +0200 Subject: [PATCH 2/5] tools: support max_virtual_memory test configuration --- tools/test.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/tools/test.py b/tools/test.py index e29b9f7ba05794..a0238c0da93cec 100755 --- a/tools/test.py +++ b/tools/test.py @@ -46,7 +46,6 @@ import copy import io - if sys.version_info >= (3, 5): from importlib import machinery, util def get_module(name, path): @@ -572,6 +571,7 @@ def __init__(self, context, path, arch, mode): self.mode = mode self.parallel = False self.disable_core_files = False + self.max_virtual_memory = None self.serial_id = 0 self.thread_id = 0 @@ -595,7 +595,8 @@ def RunCommand(self, command, env): self.context, self.context.GetTimeout(self.mode, self.config.section), env, - disable_core_files = self.disable_core_files) + disable_core_files = self.disable_core_files, + max_virtual_memory = self.max_virtual_memory) return TestOutput(self, full_command, output, @@ -759,7 +760,8 @@ def CheckedUnlink(name): PrintError("os.unlink() " + str(e)) break -def Execute(args, context, timeout=None, env=None, disable_core_files=False, stdin=None): +def Execute(args, context, timeout=None, env=None, disable_core_files=False, + stdin=None, max_virtual_memory=None): (fd_out, outname) = tempfile.mkstemp() (fd_err, errname) = tempfile.mkstemp() @@ -781,11 +783,27 @@ def Execute(args, context, timeout=None, env=None, disable_core_files=False, std preexec_fn = None + def disableCoreFiles(): + import resource + resource.setrlimit(resource.RLIMIT_CORE, (0,0)) + if disable_core_files and not utils.IsWindows(): - def disableCoreFiles(): + preexec_fn = disableCoreFiles + + if max_virtual_memory is not None and utils.GuessOS() == 'linux': + def setMaxVirtualMemory(): import resource resource.setrlimit(resource.RLIMIT_CORE, (0,0)) - preexec_fn = disableCoreFiles + resource.setrlimit(resource.RLIMIT_AS, (max_virtual_memory,max_virtual_memory + 1)) + + if preexec_fn is not None: + prev_preexec_fn = preexec_fn + def setResourceLimits(): + setMaxVirtualMemory() + prev_preexec_fn() + preexec_fn = setResourceLimits + else: + preexec_fn = setMaxVirtualMemory (process, exit_code, timed_out) = RunProcess( context, From 3d8aa532a30e437f8fa2bdbe3d2ca199332a241d Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 2 May 2024 18:40:39 +0200 Subject: [PATCH 3/5] tools: fix get_asan_state() in tools/test.py The output of `node -p process.config.variables.asan` includes a newline character so it's never exactly "1", which means asan is always "off" for the status files. This fixes the detection by stripping whitespaces from the output. --- tools/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/test.py b/tools/test.py index a0238c0da93cec..af88bc55cc0bfb 100755 --- a/tools/test.py +++ b/tools/test.py @@ -1613,7 +1613,7 @@ def get_env_type(vm, options_type, context): def get_asan_state(vm, context): - asan = Execute([vm, '-p', 'process.config.variables.asan'], context).stdout + asan = Execute([vm, '-p', 'process.config.variables.asan'], context).stdout.strip() return "on" if asan == "1" else "off" From a0872632ed667558a7a467f38a664b0d875841ad Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 19 Apr 2024 21:35:20 +0200 Subject: [PATCH 4/5] cli: allow running wasm in limited vmemory with --disable-wasm-trap-handler By default, Node.js enables trap-handler-based WebAssembly bound checks. As a result, V8 does not need to insert inline bound checks int the code compiled from WebAssembly which may speedup WebAssembly execution significantly, but this optimization requires allocating a big virtual memory cage (currently 10GB). If the Node.js process does not have access to a large enough virtual memory address space due to system configurations or hardware limitations, users won't be able to run any WebAssembly that involves allocation in this virtual memory cage and will see an out-of-memory error. ```console $ ulimit -v 5000000 $ node -p "new WebAssembly.Memory({ initial: 10, maximum: 100 });" [eval]:1 new WebAssembly.Memory({ initial: 10, maximum: 100 }); ^ RangeError: WebAssembly.Memory(): could not allocate memory at [eval]:1:1 at runScriptInThisContext (node:internal/vm:209:10) at node:internal/process/execution:118:14 at [eval]-wrapper:6:24 at runScript (node:internal/process/execution:101:62) at evalScript (node:internal/process/execution:136:3) at node:internal/main/eval_string:49:3 ``` `--disable-wasm-trap-handler` disables this optimization so that users can at least run WebAssembly (with a less optimial performance) when the virtual memory address space available to their Node.js process is lower than what the V8 WebAssembly memory cage needs. --- doc/api/cli.md | 36 ++++++++++ src/node.cc | 69 +++++++++++--------- src/node_options.cc | 7 ++ src/node_options.h | 2 + test/testpy/__init__.py | 12 ++++ test/wasm-allocation/test-wasm-allocation.js | 7 ++ test/wasm-allocation/testcfg.py | 6 ++ test/wasm-allocation/wasm-allocation.status | 10 +++ 8 files changed, 118 insertions(+), 31 deletions(-) create mode 100644 test/wasm-allocation/test-wasm-allocation.js create mode 100644 test/wasm-allocation/testcfg.py create mode 100644 test/wasm-allocation/wasm-allocation.status diff --git a/doc/api/cli.md b/doc/api/cli.md index dd5650e6e9c272..2870d4a8e15a34 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -565,6 +565,41 @@ const vm = require('node:vm'); vm.measureMemory(); ``` +### `--disable-wasm-trap-handler` + +By default, Node.js enables trap-handler-based WebAssembly bound +checks. As a result, V8 does not need to insert inline bound checks +int the code compiled from WebAssembly which may speedup WebAssembly +execution significantly, but this optimization requires allocating +a big virtual memory cage (currently 10GB). If the Node.js process +does not have access to a large enough virtual memory address space +due to system configurations or hardware limitations, users won't +be able to run any WebAssembly that involves allocation in this +virtual memory cage and will see an out-of-memory error. + +```console +$ ulimit -v 5000000 +$ node -p "new WebAssembly.Memory({ initial: 10, maximum: 100 });" +[eval]:1 +new WebAssembly.Memory({ initial: 10, maximum: 100 }); +^ + +RangeError: WebAssembly.Memory(): could not allocate memory + at [eval]:1:1 + at runScriptInThisContext (node:internal/vm:209:10) + at node:internal/process/execution:118:14 + at [eval]-wrapper:6:24 + at runScript (node:internal/process/execution:101:62) + at evalScript (node:internal/process/execution:136:3) + at node:internal/main/eval_string:49:3 + +``` + +`--disable-wasm-trap-handler` disables this optimization so that +users can at least run WebAssembly (with less optimal performance) +when the virtual memory address space available to their Node.js +process is lower than what the V8 WebAssembly memory cage needs. + ### `--disable-proto=mode` + By default, Node.js enables trap-handler-based WebAssembly bound checks. As a result, V8 does not need to insert inline bound checks int the code compiled from WebAssembly which may speedup WebAssembly diff --git a/doc/node.1 b/doc/node.1 index c058694ac8f7e6..a1eec720325cde 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -142,6 +142,11 @@ is `delete`, the property will be removed entirely. If is `throw`, accesses to the property will throw an exception with the code `ERR_PROTO_ACCESS`. . +.It Fl -disable-wasm-trap-handler Ns = Ns Ar mode +Disable trap-handler-based WebAssembly bound checks and fall back to +inline bound checks so that WebAssembly can be run with limited virtual +memory. +. .It Fl -disallow-code-generation-from-strings Make built-in language features like `eval` and `new Function` that generate code from strings throw an exception instead. This does not affect the Node.js