From cc1ac1eab4798df90e54b532644c3cc108dd53b7 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 17 Dec 2025 18:22:54 +0000 Subject: [PATCH 1/3] src: dump snapshot source with node:generate_default_snapshot_source This helps diffing snapshots when the reproducibility gets broken. --- src/node.cc | 14 +++++++ test/fixtures/exports.cjs | 0 test/parallel/test-snapshot-reproducible.js | 41 +++------------------ 3 files changed, 20 insertions(+), 35 deletions(-) create mode 100644 test/fixtures/exports.cjs diff --git a/src/node.cc b/src/node.cc index a18660d388be9d..52270484178571 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1374,6 +1374,20 @@ ExitCode GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr, DCHECK(snapshot_config.builder_script_path.has_value()); const std::string& builder_script = snapshot_config.builder_script_path.value(); + + // For the special builder node:generate_default_snapshot_source, generate + // the snapshot as C++ source and write it to snapshot.cc (for testing). + if (builder_script == "node:generate_default_snapshot_source") { + // Reset to empty to generate from scratch. + snapshot_config.builder_script_path = {}; + exit_code = node::SnapshotBuilder::GenerateAsSource("snapshot.cc", + args_maybe_patched, + result->exec_args(), + snapshot_config, + true /* use_array_literals */); + return exit_code; + } + // node:embedded_snapshot_main indicates that we are using the // embedded snapshot and we are not supposed to clean it up. if (builder_script == "node:embedded_snapshot_main") { diff --git a/test/fixtures/exports.cjs b/test/fixtures/exports.cjs new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/parallel/test-snapshot-reproducible.js b/test/parallel/test-snapshot-reproducible.js index f9392a7fb4adfc..f49c02a5ac8b66 100644 --- a/test/parallel/test-snapshot-reproducible.js +++ b/test/parallel/test-snapshot-reproducible.js @@ -6,12 +6,6 @@ const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const assert = require('assert'); -// When the test fails this helper can be modified to write outputs -// differently and aid debugging. -function log(line) { - console.log(line); -} - function generateSnapshot() { tmpdir.refresh(); @@ -21,7 +15,7 @@ function generateSnapshot() { '--random_seed=42', '--predictable', '--build-snapshot', - 'node:generate_default_snapshot', + 'node:generate_default_snapshot_source', ], { env: { ...process.env, NODE_DEBUG_NATIVE: 'SNAPSHOT_SERDES' }, @@ -38,33 +32,10 @@ function generateSnapshot() { }, } ); - const blobPath = tmpdir.resolve('snapshot.blob'); - return fs.readFileSync(blobPath); + const outputPath = tmpdir.resolve('snapshot.cc'); + return fs.readFileSync(outputPath, 'utf-8').split('\n'); } -const buf1 = generateSnapshot(); -const buf2 = generateSnapshot(); - -const diff = []; -let offset = 0; -const step = 16; -do { - const length = Math.min(buf1.length - offset, step); - const slice1 = buf1.slice(offset, offset + length).toString('hex'); - const slice2 = buf2.slice(offset, offset + length).toString('hex'); - if (slice1 !== slice2) { - diff.push({ offset: '0x' + (offset).toString(16), slice1, slice2 }); - } - offset += length; -} while (offset < buf1.length); - -assert.strictEqual(offset, buf1.length); -if (offset < buf2.length) { - const length = Math.min(buf2.length - offset, step); - const slice2 = buf2.slice(offset, offset + length).toString('hex'); - diff.push({ offset, slice1: '', slice2 }); - offset += length; -} while (offset < buf2.length); - -assert.deepStrictEqual(diff, []); -assert.strictEqual(buf1.length, buf2.length); +const source1 = generateSnapshot(); +const source2 = generateSnapshot(); +assert.deepStrictEqual(source1, source2); From b6ace85077ffebb1094307b7b20541e04c64f2fb Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 19 Dec 2025 19:19:21 +0100 Subject: [PATCH 2/3] fixup! src: dump snapshot source with node:generate_default_snapshot_source --- test/fixtures/exports.cjs | 0 test/parallel/test-snapshot-reproducible.js | 6 ++++++ 2 files changed, 6 insertions(+) delete mode 100644 test/fixtures/exports.cjs diff --git a/test/fixtures/exports.cjs b/test/fixtures/exports.cjs deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/test/parallel/test-snapshot-reproducible.js b/test/parallel/test-snapshot-reproducible.js index f49c02a5ac8b66..efb664d1e6dc85 100644 --- a/test/parallel/test-snapshot-reproducible.js +++ b/test/parallel/test-snapshot-reproducible.js @@ -6,6 +6,12 @@ const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const assert = require('assert'); +// When the test fails this helper can be modified to write outputs +// differently and aid debugging. +function log(line) { + console.log(line); +} + function generateSnapshot() { tmpdir.refresh(); From 09ccbcf0b06606491d1405edc8f4a0abcbb5d68a Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 19 Dec 2025 19:29:43 +0100 Subject: [PATCH 3/3] fixup! fixup! src: dump snapshot source with node:generate_default_snapshot_source --- src/node.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/node.cc b/src/node.cc index 52270484178571..8367227dd56ed4 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1380,11 +1380,12 @@ ExitCode GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr, if (builder_script == "node:generate_default_snapshot_source") { // Reset to empty to generate from scratch. snapshot_config.builder_script_path = {}; - exit_code = node::SnapshotBuilder::GenerateAsSource("snapshot.cc", - args_maybe_patched, - result->exec_args(), - snapshot_config, - true /* use_array_literals */); + exit_code = + node::SnapshotBuilder::GenerateAsSource("snapshot.cc", + args_maybe_patched, + result->exec_args(), + snapshot_config, + true /* use_array_literals */); return exit_code; }