diff --git a/doc/api/module.md b/doc/api/module.md index f05726286f09..f3707765a381 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -464,10 +464,12 @@ no-op, and the directory is not created if it is missing. ### Limitations of the compile cache -Currently when using the compile cache with [V8 JavaScript code coverage][], the -coverage being collected by V8 may be less precise in functions that are -deserialized from the code cache. It's recommended to turn this off when -running tests to generate precise coverage. +V8 cannot collect block-level [V8 JavaScript code coverage][] for functions that +are deserialized from the code cache. The compile cache is therefore disabled +when coverage collection starts with the process, through +[`NODE_V8_COVERAGE=dir`][] or [`--experimental-test-coverage`][]. Coverage that +is started later, for example through the inspector, may be less precise in +functions that were already deserialized from the code cache. Compilation cache generated by one version of Node.js can not be reused by a different version of Node.js. Cache generated by different versions of Node.js will be stored @@ -525,7 +527,8 @@ The following constants are returned as the `status` field in the object returne DISABLED Node.js cannot enable the compile cache because the environment variable - NODE_DISABLE_COMPILE_CACHE=1 has been set. + NODE_DISABLE_COMPILE_CACHE=1 has been set, or because V8 + coverage is being collected. @@ -2111,6 +2114,7 @@ returned object contains the following keys: [V8 code cache]: https://v8.dev/blog/code-caching-for-devs [`"exports"`]: packages.md#exports [`--enable-source-maps`]: cli.md#--enable-source-maps +[`--experimental-test-coverage`]: cli.md#--experimental-test-coverage [`--import`]: cli.md#--importmodule [`--require`]: cli.md#-r---require-module [`NODE_COMPILE_CACHE=dir`]: cli.md#node_compile_cachedir diff --git a/src/compile_cache.h b/src/compile_cache.h index 527f72f33d42..25071de4ccea 100644 --- a/src/compile_cache.h +++ b/src/compile_cache.h @@ -47,7 +47,7 @@ struct CompileCacheEntry { V(FAILED) /* Failed to enable the cache */ \ V(ENABLED) /* Was not enabled before, and now enabled. */ \ V(ALREADY_ENABLED) /* Was already enabled. */ \ - V(DISABLED) /* Has been disabled by NODE_DISABLE_COMPILE_CACHE. */ + V(DISABLED) /* Disabled by the environment or by V8 coverage. */ enum class CompileCacheEnableStatus : uint8_t { #define V(status) status, diff --git a/src/env.cc b/src/env.cc index c30b6e2f5049..342fec1a8317 100644 --- a/src/env.cc +++ b/src/env.cc @@ -1396,10 +1396,20 @@ CompileCacheEnableResult Environment::EnableCompileCache( const std::string& cache_dir, EnableOption option) { CompileCacheEnableResult result; std::string disable_env; + const char* disabled_reason = nullptr; if (credentials::SafeGetenv( "NODE_DISABLE_COMPILE_CACHE", &disable_env, this)) { + disabled_reason = "Disabled by NODE_DISABLE_COMPILE_CACHE"; +#if HAVE_INSPECTOR + } else if (coverage_connection() != nullptr) { + // V8 cannot collect block coverage for functions deserialized from the + // cache. + disabled_reason = "Disabled because V8 coverage is being collected"; +#endif // HAVE_INSPECTOR + } + if (disabled_reason != nullptr) { result.status = CompileCacheEnableStatus::DISABLED; - result.message = "Disabled by NODE_DISABLE_COMPILE_CACHE"; + result.message = disabled_reason; Debug(this, DebugCategory::COMPILE_CACHE, "[compile cache] %s.\n", diff --git a/test/common/compile-cache.js b/test/common/compile-cache.js new file mode 100644 index 000000000000..4a96856b2fd0 --- /dev/null +++ b/test/common/compile-cache.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('./'); +const childProcess = require('./child_process'); + +// The compile cache is disabled while V8 coverage is being collected. In that +// case, check that the first child says so and skip the rest of the test. +function withCompileCache(spawn) { + return (...args) => { + if (!process.env.NODE_V8_COVERAGE) { + return spawn(...args); + } + args.pop(); + childProcess.spawnSyncAndExit(...args, { + stderr: /Disabled because V8 coverage is being collected/, + }); + common.skip('the compile cache is disabled by V8 coverage'); + }; +} + +module.exports = { + spawnSyncAndAssert: withCompileCache(childProcess.spawnSyncAndAssert), + spawnSyncAndExit: withCompileCache(childProcess.spawnSyncAndExit), +}; diff --git a/test/parallel/test-compile-cache-api-env.js b/test/parallel/test-compile-cache-api-env.js index 197f1495a886..7504d7a1fd60 100644 --- a/test/parallel/test-compile-cache-api-env.js +++ b/test/parallel/test-compile-cache-api-env.js @@ -4,7 +4,7 @@ // with a NODE_COMPILE_CACHE environment variable override. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-compile-cache-api-flush.js b/test/parallel/test-compile-cache-api-flush.js index 7e66b1fee103..fb4c26ba1bb0 100644 --- a/test/parallel/test-compile-cache-api-flush.js +++ b/test/parallel/test-compile-cache-api-flush.js @@ -3,7 +3,7 @@ // This tests module.flushCompileCache() works as expected. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-compile-cache-api-options-portable-env.js b/test/parallel/test-compile-cache-api-options-portable-env.js index 5586e3e84046..d9f196d33045 100644 --- a/test/parallel/test-compile-cache-api-options-portable-env.js +++ b/test/parallel/test-compile-cache-api-options-portable-env.js @@ -4,7 +4,7 @@ // variable NODE_COMPILE_CACHE overrides. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-compile-cache-api-portable.js b/test/parallel/test-compile-cache-api-portable.js index 620b3bdce875..3ba390503c40 100644 --- a/test/parallel/test-compile-cache-api-portable.js +++ b/test/parallel/test-compile-cache-api-portable.js @@ -4,7 +4,7 @@ // and supports portable paths across directory relocations. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fs = require('fs'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-compile-cache-api-readonly.js b/test/parallel/test-compile-cache-api-readonly.js index 8ac0248a4d53..df91ee45e4b9 100644 --- a/test/parallel/test-compile-cache-api-readonly.js +++ b/test/parallel/test-compile-cache-api-readonly.js @@ -4,7 +4,7 @@ // entries are read, nothing is written, and a missing directory is not created. const common = require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); diff --git a/test/parallel/test-compile-cache-api-success.js b/test/parallel/test-compile-cache-api-success.js index 2d4d94fd6e9d..e8a5226f7b50 100644 --- a/test/parallel/test-compile-cache-api-success.js +++ b/test/parallel/test-compile-cache-api-success.js @@ -3,7 +3,7 @@ // This tests module.enableCompileCache() and module.getCompileCacheDir() work. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-compile-cache-api-tmpdir.js b/test/parallel/test-compile-cache-api-tmpdir.js index e676893b01f5..1d4cdbb242c7 100644 --- a/test/parallel/test-compile-cache-api-tmpdir.js +++ b/test/parallel/test-compile-cache-api-tmpdir.js @@ -4,7 +4,7 @@ // the TMPDIR environment variable override. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-compile-cache-bad-syntax.js b/test/parallel/test-compile-cache-bad-syntax.js index 63f6374b2c38..b868ad385e9d 100644 --- a/test/parallel/test-compile-cache-bad-syntax.js +++ b/test/parallel/test-compile-cache-bad-syntax.js @@ -3,7 +3,7 @@ // This tests NODE_COMPILE_CACHE works. require('../common'); -const { spawnSyncAndExit } = require('../common/child_process'); +const { spawnSyncAndExit } = require('../common/compile-cache'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); const assert = require('assert'); diff --git a/test/parallel/test-compile-cache-coverage.js b/test/parallel/test-compile-cache-coverage.js new file mode 100644 index 000000000000..f2f5e9e2f77f --- /dev/null +++ b/test/parallel/test-compile-cache-coverage.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +common.skipIfInspectorDisabled(); + +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); +const cacheDir = tmpdir.resolve('compile_cache'); +const env = { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_V8_COVERAGE: tmpdir.resolve('coverage'), +}; + +spawnSyncAndAssert( + process.execPath, + [fixtures.path('empty.js')], + { env: { ...env, NODE_COMPILE_CACHE: cacheDir }, cwd: tmpdir.path }, + { stderr: /Disabled because V8 coverage is being collected/ }); +assert(!fs.existsSync(cacheDir)); + +spawnSyncAndAssert( + process.execPath, + ['-r', fixtures.path('compile-cache-wrapper.js'), fixtures.path('empty.js')], + { + env: { + ...env, + NODE_COMPILE_CACHE: undefined, + NODE_TEST_COMPILE_CACHE_DIR: cacheDir, + }, + cwd: tmpdir.path, + }, + { + stdout(output) { + assert.match(output, /Compile cache already disabled/); + assert.match(output, /dir after enableCompileCache: undefined/); + }, + stderr: /Disabled because V8 coverage is being collected/, + }); +assert(!fs.existsSync(cacheDir)); diff --git a/test/parallel/test-compile-cache-dynamic-import.js b/test/parallel/test-compile-cache-dynamic-import.js index 01731dc68225..757e992eaa88 100644 --- a/test/parallel/test-compile-cache-dynamic-import.js +++ b/test/parallel/test-compile-cache-dynamic-import.js @@ -3,7 +3,7 @@ // This tests NODE_COMPILE_CACHE works in existing directory. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-compile-cache-esm.js b/test/parallel/test-compile-cache-esm.js index 963de8bcaba2..9bddc23424c2 100644 --- a/test/parallel/test-compile-cache-esm.js +++ b/test/parallel/test-compile-cache-esm.js @@ -3,7 +3,7 @@ // This tests NODE_COMPILE_CACHE works in existing directory. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-compile-cache-existing-directory.js b/test/parallel/test-compile-cache-existing-directory.js index 0fc60f9544d5..d9c7e690fd50 100644 --- a/test/parallel/test-compile-cache-existing-directory.js +++ b/test/parallel/test-compile-cache-existing-directory.js @@ -3,7 +3,7 @@ // This tests NODE_COMPILE_CACHE works in existing directory. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fs = require('fs'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-compile-cache-portable-esm.js b/test/parallel/test-compile-cache-portable-esm.js index 18766b6810ea..bfcb6f9e2da5 100644 --- a/test/parallel/test-compile-cache-portable-esm.js +++ b/test/parallel/test-compile-cache-portable-esm.js @@ -3,7 +3,7 @@ // This tests NODE_COMPILE_CACHE works after moving directory and unusual characters in path are handled correctly. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fs = require('fs'); diff --git a/test/parallel/test-compile-cache-portable.js b/test/parallel/test-compile-cache-portable.js index b5978b5b0c62..04b7360833ca 100644 --- a/test/parallel/test-compile-cache-portable.js +++ b/test/parallel/test-compile-cache-portable.js @@ -4,7 +4,7 @@ // environment variable. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fs = require('fs'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-compile-cache-success.js b/test/parallel/test-compile-cache-success.js index c02a62432869..4d5d7a67214a 100644 --- a/test/parallel/test-compile-cache-success.js +++ b/test/parallel/test-compile-cache-success.js @@ -3,7 +3,7 @@ // This tests NODE_COMPILE_CACHE works. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-compile-cache-typescript-commonjs.js b/test/parallel/test-compile-cache-typescript-commonjs.js index 60e86c3c0f9d..5ee65a806e67 100644 --- a/test/parallel/test-compile-cache-typescript-commonjs.js +++ b/test/parallel/test-compile-cache-typescript-commonjs.js @@ -6,7 +6,7 @@ const common = require('../common'); if (!process.config.variables.node_use_amaro) { common.skip('Requires Amaro'); } -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-compile-cache-typescript-esm.js b/test/parallel/test-compile-cache-typescript-esm.js index 98473dbb2a5b..e85f442e5ac1 100644 --- a/test/parallel/test-compile-cache-typescript-esm.js +++ b/test/parallel/test-compile-cache-typescript-esm.js @@ -6,7 +6,7 @@ const common = require('../common'); if (!process.config.variables.node_use_amaro) { common.skip('Requires Amaro'); } -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js index a6c95e432b78..f229587c1f26 100644 --- a/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js +++ b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js @@ -7,7 +7,7 @@ const common = require('../common'); if (!process.config.variables.node_use_amaro) { common.skip('Requires Amaro'); } -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-compile-cache-updated-file.js b/test/parallel/test-compile-cache-updated-file.js index 68f20a6990cc..c3fecae97f0f 100644 --- a/test/parallel/test-compile-cache-updated-file.js +++ b/test/parallel/test-compile-cache-updated-file.js @@ -3,7 +3,7 @@ // This tests NODE_COMPILE_CACHE works in existing directory. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const fs = require('fs'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-compile-cache-wasm.js b/test/parallel/test-compile-cache-wasm.js index 278198378dd2..31019b33892f 100644 --- a/test/parallel/test-compile-cache-wasm.js +++ b/test/parallel/test-compile-cache-wasm.js @@ -3,7 +3,7 @@ // This tests NODE_COMPILE_CACHE works for WebAssembly modules imported as ESM. require('../common'); -const { spawnSyncAndAssert } = require('../common/child_process'); +const { spawnSyncAndAssert } = require('../common/compile-cache'); const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures');