diff --git a/doc/api/module.md b/doc/api/module.md index f05726286f0..f3707765a38 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 527f72f33d4..25071de4cce 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 c30b6e2f504..426b7375679 100644 --- a/src/env.cc +++ b/src/env.cc @@ -1398,8 +1398,16 @@ CompileCacheEnableResult Environment::EnableCompileCache( std::string disable_env; if (credentials::SafeGetenv( "NODE_DISABLE_COMPILE_CACHE", &disable_env, this)) { - result.status = CompileCacheEnableStatus::DISABLED; result.message = "Disabled by NODE_DISABLE_COMPILE_CACHE"; + } +#if HAVE_INSPECTOR + // V8 cannot collect block coverage for functions deserialized from the cache. + if (result.message.empty() && coverage_connection() != nullptr) { + result.message = "Disabled because V8 coverage is being collected"; + } +#endif // HAVE_INSPECTOR + if (!result.message.empty()) { + result.status = CompileCacheEnableStatus::DISABLED; Debug(this, DebugCategory::COMPILE_CACHE, "[compile cache] %s.\n", diff --git a/test/parallel/test-compile-cache-api-env.js b/test/parallel/test-compile-cache-api-env.js index 197f1495a88..aa7937df92a 100644 --- a/test/parallel/test-compile-cache-api-env.js +++ b/test/parallel/test-compile-cache-api-env.js @@ -10,6 +10,9 @@ const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); const fs = require('fs'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { // Test that it works with non-existent directory. tmpdir.refresh(); diff --git a/test/parallel/test-compile-cache-api-flush.js b/test/parallel/test-compile-cache-api-flush.js index 7e66b1fee10..00aee0cd0a0 100644 --- a/test/parallel/test-compile-cache-api-flush.js +++ b/test/parallel/test-compile-cache-api-flush.js @@ -8,6 +8,9 @@ const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { // Test that it works with non-existent directory. tmpdir.refresh(); 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 5586e3e8404..9922f5f80ed 100644 --- a/test/parallel/test-compile-cache-api-options-portable-env.js +++ b/test/parallel/test-compile-cache-api-options-portable-env.js @@ -11,6 +11,9 @@ const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const path = require('path'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + const wrapper = fixtures.path('compile-cache-wrapper-options.js'); tmpdir.refresh(); diff --git a/test/parallel/test-compile-cache-api-portable.js b/test/parallel/test-compile-cache-api-portable.js index 620b3bdce87..f29826d469a 100644 --- a/test/parallel/test-compile-cache-api-portable.js +++ b/test/parallel/test-compile-cache-api-portable.js @@ -11,6 +11,9 @@ const tmpdir = require('../common/tmpdir'); const path = require('path'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + tmpdir.refresh(); const workDir = path.join(tmpdir.path, 'work'); const cacheRel = '.compile_cache_dir'; diff --git a/test/parallel/test-compile-cache-api-readonly.js b/test/parallel/test-compile-cache-api-readonly.js index 8ac0248a4d5..7512c847a08 100644 --- a/test/parallel/test-compile-cache-api-readonly.js +++ b/test/parallel/test-compile-cache-api-readonly.js @@ -11,6 +11,9 @@ const path = require('path'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + tmpdir.refresh(); const wrapper = fixtures.path('compile-cache-wrapper-options.js'); const target = path.join(tmpdir.path, 'target.js'); diff --git a/test/parallel/test-compile-cache-api-success.js b/test/parallel/test-compile-cache-api-success.js index 2d4d94fd6e9..10b5dc813c2 100644 --- a/test/parallel/test-compile-cache-api-success.js +++ b/test/parallel/test-compile-cache-api-success.js @@ -10,6 +10,9 @@ const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const path = require('path'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { // Test that it works with non-existent directory. tmpdir.refresh(); diff --git a/test/parallel/test-compile-cache-api-tmpdir.js b/test/parallel/test-compile-cache-api-tmpdir.js index e676893b01f..0353aa716af 100644 --- a/test/parallel/test-compile-cache-api-tmpdir.js +++ b/test/parallel/test-compile-cache-api-tmpdir.js @@ -11,6 +11,9 @@ const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const path = require('path'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { // Test that it works with non-existent directory. tmpdir.refresh(); diff --git a/test/parallel/test-compile-cache-bad-syntax.js b/test/parallel/test-compile-cache-bad-syntax.js index 63f6374b2c3..213d295b4a0 100644 --- a/test/parallel/test-compile-cache-bad-syntax.js +++ b/test/parallel/test-compile-cache-bad-syntax.js @@ -10,6 +10,9 @@ const assert = require('assert'); const fs = require('fs'); const path = require('path'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { // Test that it throws if the script fails to parse, and no cache is created. tmpdir.refresh(); diff --git a/test/parallel/test-compile-cache-coverage.js b/test/parallel/test-compile-cache-coverage.js new file mode 100644 index 00000000000..f2f5e9e2f77 --- /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 01731dc6822..d7b1daf57dc 100644 --- a/test/parallel/test-compile-cache-dynamic-import.js +++ b/test/parallel/test-compile-cache-dynamic-import.js @@ -8,6 +8,9 @@ const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { tmpdir.refresh(); const dir = tmpdir.resolve('.compile_cache_dir'); diff --git a/test/parallel/test-compile-cache-esm.js b/test/parallel/test-compile-cache-esm.js index 963de8bcaba..0df4ba6a737 100644 --- a/test/parallel/test-compile-cache-esm.js +++ b/test/parallel/test-compile-cache-esm.js @@ -8,6 +8,9 @@ const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { tmpdir.refresh(); const dir = tmpdir.resolve('.compile_cache_dir'); diff --git a/test/parallel/test-compile-cache-existing-directory.js b/test/parallel/test-compile-cache-existing-directory.js index 0fc60f9544d..f4f51e7786a 100644 --- a/test/parallel/test-compile-cache-existing-directory.js +++ b/test/parallel/test-compile-cache-existing-directory.js @@ -9,6 +9,9 @@ const fs = require('fs'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { tmpdir.refresh(); const dir = tmpdir.resolve('.compile_cache_dir'); diff --git a/test/parallel/test-compile-cache-portable-esm.js b/test/parallel/test-compile-cache-portable-esm.js index 18766b6810e..a536602f3cf 100644 --- a/test/parallel/test-compile-cache-portable-esm.js +++ b/test/parallel/test-compile-cache-portable-esm.js @@ -9,6 +9,9 @@ const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const path = require('path'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + tmpdir.refresh(); const workDir = path.join(tmpdir.path, 'work'); diff --git a/test/parallel/test-compile-cache-portable.js b/test/parallel/test-compile-cache-portable.js index b5978b5b0c6..8ea7cf400d4 100644 --- a/test/parallel/test-compile-cache-portable.js +++ b/test/parallel/test-compile-cache-portable.js @@ -10,6 +10,9 @@ const fs = require('fs'); const tmpdir = require('../common/tmpdir'); const path = require('path'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + tmpdir.refresh(); const workDir = path.join(tmpdir.path, 'work'); const cacheRel = '.compile_cache_dir'; diff --git a/test/parallel/test-compile-cache-success.js b/test/parallel/test-compile-cache-success.js index c02a6243286..ff834501e54 100644 --- a/test/parallel/test-compile-cache-success.js +++ b/test/parallel/test-compile-cache-success.js @@ -10,6 +10,9 @@ const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const path = require('path'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { // Test that it works with non-existent directory. tmpdir.refresh(); diff --git a/test/parallel/test-compile-cache-typescript-commonjs.js b/test/parallel/test-compile-cache-typescript-commonjs.js index 60e86c3c0f9..925d9366992 100644 --- a/test/parallel/test-compile-cache-typescript-commonjs.js +++ b/test/parallel/test-compile-cache-typescript-commonjs.js @@ -11,6 +11,9 @@ const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + // Check cache for .ts files that would be run as CommonJS. { tmpdir.refresh(); diff --git a/test/parallel/test-compile-cache-typescript-esm.js b/test/parallel/test-compile-cache-typescript-esm.js index 98473dbb2a5..05142a2a8e9 100644 --- a/test/parallel/test-compile-cache-typescript-esm.js +++ b/test/parallel/test-compile-cache-typescript-esm.js @@ -11,6 +11,9 @@ const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + // Check cache for .ts files that would be run as ESM. { tmpdir.refresh(); diff --git a/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js index a6c95e432b7..8e7f9f31477 100644 --- a/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js +++ b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js @@ -12,6 +12,9 @@ const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + tmpdir.refresh(); const dir = tmpdir.resolve('.compile_cache_dir'); const script = fixtures.path('typescript', 'ts', 'test-typescript.ts'); diff --git a/test/parallel/test-compile-cache-updated-file.js b/test/parallel/test-compile-cache-updated-file.js index 68f20a6990c..60c126843f0 100644 --- a/test/parallel/test-compile-cache-updated-file.js +++ b/test/parallel/test-compile-cache-updated-file.js @@ -8,6 +8,9 @@ const assert = require('assert'); const fs = require('fs'); const tmpdir = require('../common/tmpdir'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + { tmpdir.refresh(); const dir = tmpdir.resolve('.compile_cache_dir'); diff --git a/test/parallel/test-compile-cache-wasm.js b/test/parallel/test-compile-cache-wasm.js index 278198378dd..ac84f096b00 100644 --- a/test/parallel/test-compile-cache-wasm.js +++ b/test/parallel/test-compile-cache-wasm.js @@ -8,6 +8,9 @@ const assert = require('assert'); const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); +// The compile cache is disabled while V8 coverage is being collected. +process.env.NODE_V8_COVERAGE = ''; + const script = fixtures.path('es-modules', 'test-wasm-js-string-builtins.mjs'); // V8 only serializes optimized code, so a complete cache requires eager