diff --git a/Makefile b/Makefile index a7099947c72a78..0f45cc2fe7c89d 100644 --- a/Makefile +++ b/Makefile @@ -560,6 +560,7 @@ test-with-async-hooks: .PHONY: test-v8-all .PHONY: test-v8-benchmarks .PHONY: test-v8-intl +.PHONY: test-v8-updates ifneq ("","$(wildcard deps/v8/tools/run-tests.py)") # Related CI job: node-test-commit-v8-linux test-v8: v8 ## Runs the V8 test suite on deps/v8. @@ -580,7 +581,10 @@ test-v8-benchmarks: v8 benchmarks \ $(TAP_V8_BENCHMARKS) -test-v8-all: test-v8 test-v8-intl test-v8-benchmarks +test-v8-updates: + $(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) v8-updates + +test-v8-all: test-v8 test-v8-intl test-v8-benchmarks test-v8-updates # runs all v8 tests else test-v8 test-v8-intl test-v8-benchmarks test-v8-all: diff --git a/test/fixtures/linux-perf.js b/test/fixtures/linux-perf.js new file mode 100644 index 00000000000000..011ef19777bd37 --- /dev/null +++ b/test/fixtures/linux-perf.js @@ -0,0 +1,26 @@ +'use strict'; + +const crypto = require('crypto'); + +// Functions should be complex enough for V8 to run them a few times before +// compiling, but not complex enough to always stay in interpreted mode. They +// should also take some time to run, otherwise Linux perf might miss them +// entirely even when sampling at a high frequency. +function functionOne(i) { + for (let j=i; j > 0; j--) { + crypto.createHash('md5').update(functionTwo(i, j)).digest("hex"); + } +} + +function functionTwo(x, y) { + let data = ((((x * y) + (x / y)) * y) ** (x + 1)).toString(); + if (x % 2 == 0) { + return crypto.createHash('md5').update(data.repeat((x % 100) + 1)).digest("hex"); + } else { + return crypto.createHash('md5').update(data.repeat((y % 100) + 1)).digest("hex"); + } +} + +for (let i = 0; i < 1000; i++) { + functionOne(i); +} diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index e4b757bbd3dac9..f79a3b149a8edc 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -5,8 +5,6 @@ prefix parallel # sample-test : PASS,FLAKY [true] # This section applies to all platforms -# Postmortem debugging data is prone to accidental removal during V8 updates. -test-postmortem-metadata: PASS,FLAKY [$system==win32] test-child-process-fork-net: PASS,FLAKY diff --git a/test/v8-updates/test-linux-perf.js b/test/v8-updates/test-linux-perf.js new file mode 100644 index 00000000000000..0a7f199e040d58 --- /dev/null +++ b/test/v8-updates/test-linux-perf.js @@ -0,0 +1,81 @@ +'use strict'; + +// This test verifies that JavaScript functions are being correctly sampled by +// Linux perf. The test runs a JavaScript script, sampling the execution with +// Linux perf. It then uses `perf script` to generate a human-readable output, +// and uses regular expressions to find samples of the functions defined in +// `fixtures/linux-perf.js`. + +// NOTE (mmarchini): this test is meant to run only on Linux machines with Linux +// perf installed. It will skip if those criteria are not met. + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +if (process.config.variables.node_shared) + common.skip("can't test Linux perf with shared libraries yet"); + +const perfArgs = [ + 'record', + '-F500', + '-g', + '--', + process.execPath, + '--perf-basic-prof', + '--interpreted-frames-native-stack', + '--no-turbo-inlining', // Otherwise simple functions might get inlined. + fixtures.path('linux-perf.js'), +]; + +const perfScriptArgs = [ + 'script', +]; + +const options = { + cwd: tmpdir.path, + encoding: 'utf-8', +}; + +if (!common.isLinux) + common.skip('only testing Linux for now'); + +const perf = spawnSync('perf', perfArgs, options); + +if (perf.error && perf.error.errno === 'ENOENT') + common.skip('perf not found on system'); + +if (perf.status !== 0) { + common.skip(`Failed to execute perf: ${perf.stderr}`); +} + +const perfScript = spawnSync('perf', perfScriptArgs, options); + +if (perf.error) + common.skip(`perf script aborted: ${perf.error.errno}`); + +if (perfScript.status !== 0) { + common.skip(`Failed to execute perf script: ${perfScript.stderr}`); +} + +const interpretedFunctionOneRe = /InterpretedFunction:functionOne/; +const compiledFunctionOneRe = /LazyCompile:\*functionOne/; +const interpretedFunctionTwoRe = /InterpretedFunction:functionTwo/; +const compiledFunctionTwoRe = /LazyCompile:\*functionTwo/; + +const output = perfScript.stdout; + +assert.ok(output.match(interpretedFunctionOneRe), + "Couldn't find interpreted functionOne()"); +assert.ok(output.match(compiledFunctionOneRe), + "Couldn't find compiled functionOne()"); +assert.ok(output.match(interpretedFunctionTwoRe), + "Couldn't find interpreted functionTwo()"); +assert.ok(output.match(compiledFunctionTwoRe), + "Couldn't find compiled functionTwo"); diff --git a/test/parallel/test-postmortem-metadata.js b/test/v8-updates/test-postmortem-metadata.js similarity index 100% rename from test/parallel/test-postmortem-metadata.js rename to test/v8-updates/test-postmortem-metadata.js diff --git a/test/v8-updates/testcfg.py b/test/v8-updates/testcfg.py new file mode 100644 index 00000000000000..cec2589f9b4c20 --- /dev/null +++ b/test/v8-updates/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.ParallelTestConfiguration(context, root, 'v8-updates') diff --git a/test/v8-updates/v8-updates.status b/test/v8-updates/v8-updates.status new file mode 100644 index 00000000000000..46149f4751ffb5 --- /dev/null +++ b/test/v8-updates/v8-updates.status @@ -0,0 +1,21 @@ +prefix v8-updates + +# To mark a test as flaky, list the test name in the appropriate section +# below, without ".js", followed by ": PASS,FLAKY". Example: +# sample-test : PASS,FLAKY + +[true] # This section applies to all platforms + +[$system==win32] + +[$system==linux] + +[$system==macos] + +[$arch==arm || $arch==arm64] + +[$system==solaris] # Also applies to SmartOS + +[$system==freebsd] + +[$system==aix] diff --git a/tools/test.py b/tools/test.py index e5581e8da41c44..48256e956b4f3e 100755 --- a/tools/test.py +++ b/tools/test.py @@ -1553,7 +1553,8 @@ def PrintCrashed(code): 'pummel', 'test-known-issues', 'tick-processor', - 'timers' + 'timers', + 'v8-updates' ]