WIP: Re-apply Replay Node.js instrumentation onto Node 27 canary (V8 15.1) - #126
Open
BLamy wants to merge 40 commits into
Open
WIP: Re-apply Replay Node.js instrumentation onto Node 27 canary (V8 15.1)#126BLamy wants to merge 40 commits into
BLamy wants to merge 40 commits into
Conversation
- node_perf.{cc,h,common.h}: re-anchor timeOrigin->performance_process_start
(non-const + InitPerformance); TODO: wire InitPerformance() call site
- node_credentials.cc: SafeGetenv divergence guard (refactored to linux_at_secure)
- crypto_random.cc: AssertBytes on ncrypto::CSPRNG output
- node_platform.cc: ordered-lock arg on TaskQueue
- deps/uv/async.c: re-instrument ordered locks for C11-atomics rewrite
- deps/uv/fs.c: re-instrument uv__fs_read (uv__fs_preadv removed)
- openssl/err_prn.c: constant tid (OpenSSL 3.x rewrite)
- README.md, node_version.h: dropped (cosmetic/obsolete)
…hokepoint Covers all new randomness consumers (keygen, QUIC CIDs/tokens, DTLS, WebCrypto, randomUUID) at one point. See NEW-SURFACES.md for the full inventory of new non-deterministic subsystems (undici/fetch, QUIC, node:sqlite, FFI, fs.watch, ...) and recommended instrumentation/priority.
node:sqlite (DatabaseSync::Open), fs.watch (FSEventWrap::Start), QUIC (Endpoint::Listen/Connect): invalidate the recording rather than diverge silently. Each has a TODO(replay-port) for real support. See NEW-SURFACES.md.
… (V8 15.1/M151) Re-pathed the PR's base..head diff to deps/v8/ and applied cleanly (git apply exit 0) onto Node 27's V8 15.1.42 — only 1 build off the PR base (15.1.43). 127 files (+6126/-101), incl. the recordreplay runtime (include/replayio.h, src/replay/*). This provides the v8::recordreplay API + V8RecordReplay* C shims that every node-core/libuv/ncrypto hook depends on.
- sync V8 symbol restorations from chromium-v8 v8-m151-node-symbols into deps/v8 (OnAnnotation C++ method, AssertScriptedCaller stub, V8RecordReplayIsRecording/ IsReplaying + variadic V8RecordReplayDiagnostic) - node_platform.cc: AutoPassThroughEvents moved to v8::replayio:: (PR nodejs#282), add replayio.h include All node/libuv/ncrypto recordreplay symbols now resolve against this V8 (0 missing).
…de 27) The anchor applier had dropped InitializeRecordReplay(&argc,&argv) inside RecordReplayFinishRecording() where argc/argv aren't in scope (compile error) and before its own definition. Move it to StartInternal(), after uv_setup_args and before InitializeOncePerProcessInternal/V8 init — so the driver attaches before V8 and any Environment, and command-line args are recorded. Also wire InitPerformance() there (guarded by IsRecordingOrReplaying): Node 27 caches performance_process_start[_timestamp] into Environment at construction, so they must be re-captured deterministically before the first Environment. Resolves follow-up #1 in CONFLICTS-RESOLVED.md.
node's deps/v8 (V8 15.1.42) carries the same Replay instrumentation as the chromium-v8 fork (15.1.0) and therefore the same V8 15.1 compile bugs. Rather than re-derive, cross-ported the verified chromium-v8 fixes (PR nodejs#282) into node's deps/v8, preserving node-specific V8 patches: - Mutex(const char* ordered_name) ctor: update mutex.cc to match the ported header (was the first CI failure: "no declaration matches Mutex::Mutex()"). - Universal V8 15.1 renames: Script::WITH_OFFSET -> Script::OffsetFlag::kWithOffset; OperandType::kIdx -> kUImm + GetIndexOperand -> GetUnsignedImmediateOperand (kIdx was an upstream 10.8 type removed in 15.1); LoadRegisterTagged -> LoadRegister; GetAccumulatorTagged -> GetAccumulator. - Structural fixes cross-ported across 41 files (adversarially verified): orphaned replay statements relocated into their function scope; AutoDisallowEvents missing includes; Cast<JSAny> on Object::Get/SetProperty; MaybeDirectHandle; maglev VisitRecordReplay* moved to file scope + ReduceResult; call_head_token_position and KeyIterationParams threading; weak-ref intrinsics; dup/dead removals. - Missing backing declarations node's port had dropped: * opcodes.h: register V(IncrementAndCheckProgressCounter) (the replay opcode the simplified-operator/verifier/maglev refs require; dropped on the 15.1.42 import). * js-objects.h: add TaggedMember<Smi> record_replay_bookmark_ backing the DECL_INT_ACCESSORS + js-objects.tq field (JSMessageObject is hand-laid-out). * keys.cc: thread KeyIterationParams* params through GetFastEnumPropertyKeys and GetOwnKeysWithElements (bodies referenced params; signatures weren't updated). Brace balance verified (comment/string-aware) on all 51 touched files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…untime/exec) Second wave of node's deps/v8 fixes — the node-SPECIFIC replay hooks (not shared with chromium-v8, so not covered by the cross-port) that used removed V8 10.8 APIs. All adversarially verified. api.cc: - FLAG_xxx -> v8_flags.xxx (13; FLAG_scavenge_task -> minor_gc_task) - Isolate exception API unified: has/pending/scheduled_exception -> has_exception/ exception/clear_internal_exception/set_exception (scheduled slot folded in) - Map/Set::AsArray + Object::PreviewEntries: thread KeyIterationParams* (match hdrs) - JSMessageObject::GetIsolate removed -> EnterV8NoScriptNoExceptionScope; JavaScriptFrameIterator -> JavaScriptStackFrameIterator; Summarize() returns FrameSummaries by value; Handle<Script>::cast -> i::Cast<i::Script>. debug.cc / runtime-debug.cc / execution.cc / debug-stack-trace-iterator.cc / parse-info.cc / pending-compilation-error-handler.cc / replay-runtime-weak-refs.cc: - X::cast(y) -> i::Cast<X>(y); member .IsScript()/.IsJSFunction() -> free IsScript()/ IsJSFunction(); Tagged '.' -> '->' fixes. - RecordReplayHasRegisteredScript unified to Tagged<Script> across decl/def/calls. - runtime-debug.cc GetStackLocation: CommonFrame::Summarize(&frames) (removed out-param API) -> FrameSummaries summaries = ...Summarize(). - execution.cc: deref *function->shared() into const SharedFunctionInfo&; add file-scope externs (gProgressCounter, gRecordReplayEnableDependencyGraph, RecordReplayIsDivergentUserJSWithoutPause). - weak-refs.h/.cc: ReplayWeakRefPins::Pin/Unpin take Tagged<HeapObject>. Brace balance verified (delta vs HEAD) on all 11 files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compiler::GetWrappedFunction's 'replaced source contents while replaying' block (ReplayingReplaceScriptContents) referenced 10.8-era locals that don't exist in 15.1 (parsing_while_debugging/restriction/parameters_end_pos/outer_info, context->IsNativeContext, and built a JSFunction into a DirectHandle<SFI>). Rewrote it to mirror the function's own 15.1 wrapped-compile path (NewScript + CompileToplevel, IsNativeContext(*context), v8_flags.lazy, kWrapped syntax) and replace |result| with the recompiled SharedFunctionInfo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Execution::Call now takes base::Vector<const DirectHandle<Object>> (not
isolate,fn,recv,argc,argv): callArgs arrays -> DirectHandle<Object>,
calls -> base::VectorOf(callArgs) / {} for the 0-arg case. Add missing
json-parser.h + json-stringifier.h includes (JsonParser<uint8_t>::Parse and
JsonStringify calls were already 15.1-correct, just undeclared).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e.cc The anonymous namespace opened at the stack-walk helpers (VisitStack / VisitStack_ForCallSiteBuilder) was never closed, so Isolate::RecordReplayInvokeApiInterruptCallbacksAtProgress (and the rest of the file's Isolate members) ended up defined inside it -> 'definition not in namespace enclosing Isolate'. Add the missing '} // namespace' before the first Isolate:: member. (depth check: final brace depth 1 -> 0.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Followup to the prior anon-namespace fix: closing the anon namespace before
RecordReplayInvokeApiInterruptCallbacksAtProgress left the trailing stack-trace
helpers + the original '} // namespace' (now over-closing 'internal', dropping
CaptureAndSetErrorStack et al into public 'namespace v8'). Reopen 'namespace {'
after the RecordReplay member so the helpers stay in the anon namespace and the
existing close balances. Verified: CaptureAndSetErrorStack back at v8::internal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e link) V8 fully COMPILED but torque (a libbase-only build tool) failed to LINK: 'Undefined symbols: v8::recordreplay::IsARMRecording()'. The libbase platform files reference recordreplay:: functions defined in v8_base (api.cc), which torque/mksnapshot don't link. Reference the C ABI (V8RecordReplayIsARM / V8IsRecordingOrReplaying) weakly (V8_WEAK) so those build tools resolve them to null (a build tool never records); libv8 still binds the strong api.cc defs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previous commit put 'extern "C" V8_WEAK ...' inside function bodies, which is
illegal ('expected unqualified-id before string constant'). Move the weak C-ABI
declarations (V8RecordReplayIsARM / V8IsRecordingOrReplaying) to namespace scope
in platform-darwin.cc / platform-linux.cc; the call sites keep the weak null-check.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aybeHandle 15.1 MaybeHandle<T> has no implicit DirectHandle<T> conversion; 'return keys;' (keys is DirectHandle<FixedArray>, fn returns MaybeHandle<FixedArray>) failed. Use indirect_handle(keys, isolate). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d in 15.1) V8 15.1 removed src/base/optional.h (migrated to std::optional). The replay AutoMaybeDisallowEvents helper included it + used base::Optional. Switch to <optional> / std::optional. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The promise-adoption hook (RecordReplayShouldCallOnPromiseHook / AddPromiseDependencyGraphAdoption) was relocated into JSPromise::Resolve, but the forward decls sit ~600 lines later -> 'not declared in scope'. Add the decls before JSPromise::Resolve. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ved in 15.1) The WriteString two-byte-widening replay hook used base::ScopedVector<uc16>, removed in V8 15.1. Use base::OwnedVector<uc16>::New(n) (.size(), .as_vector()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ries/internalProperties defs
The .h declarations + callers carry 'const v8::KeyIterationParams* params' but
the .cc definitions of V8Debugger::collectionsEntries/internalProperties were
still 2-arg ('no declaration matches'). Add the param to both defs and thread it
(PreviewEntries(&isKeyValue, params); collectionsEntries(context, value, params)).
(chromium-v8 already had this; node's port was behind.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Uint The baseline VisitRecordReplay* handlers used the 10.8 SaveAccumulatorScope(&basm_) ctor (now (this, &basm_)) and Index(0) (now Uint(0), since the RR bytecodes use kUImm). Fix both across the 5 handlers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tubs
bytecode-graph-builder: JSGraph::Constant(uint32_t) was removed in 15.1;
the RecordReplay instrumentation visitors now use ConstantNoHole (resolves
to the double overload, matching sibling coverage/offset constants).
platform-{darwin,linux}: replace weak *declarations* of the recordreplay
C-ABI hooks with weak *definitions* returning false — a weak undefined ref
links on ELF but not on macOS Mach-O ('symbol not found' from torque/
mksnapshot, which link libbase only and never record). The strong api.cc
definition still overrides these for libv8.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 3-way patch apply stranded RecordReplayAssertFromC calls at file scope: - o_str.c: the openssl_strerror_r assert landed between two functions; moved it inside openssl_strerror_r (its target function still exists). - rand_lib.c: 4 asserts for rand_drbg_get_entropy were dumped at file scope referencing undefined drbg/buffer. That function was removed in OpenSSL 3.x's DRBG->providers refactor (no equivalent drbg->parent path in ossl_prov_get_entropy), so the asserts never compiled in this port. Retired them (and the now-unused extern decl) -- RNG determinism is enforced by the driver intercepting the OS entropy source; these were diagnostic asserts for the old 1.1.1 code structure. Matches the port plan's 'retire obsolete hooks' provision. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
node_js2c (and other host tools) link libuv.a but not libv8, leaving libuv's V8RecordReplay* hook calls undefined at link. Add weak no-op definitions of all 14 referenced hooks to uv-common.c (compiled into every libuv.a); the strong libv8 (api.cc) defs are statically linked into the real node binary and override them. Tools never record, so no-ops are correct. Guarded to GCC/Clang non-Windows where these unix hooks are used. Also add the missing DLLEXPORT to api.cc V8RecordReplayAssert so it matches its 13 siblings (export consistency for shared/embedder builds). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RecordReplayAssertFromC was only declared+called in o_str.c; its canonical definition lived in OpenSSL 1.1.1's crypto/rand/rand_unix.c (+ drbg_lib.c), both of which moved into providers/ or were removed in node 27's OpenSSL 3.x, so the definition hunk never landed and the symbol was undefined everywhere. This broke linking of every target that pulls in libcrypto's o_str.o without the recordreplay runtime -- the node binary AND standalone tools like openssl-cli. Restore the canonical Replay shim (dlsym RTLD_DEFAULT 'RecordReplayAssert' at runtime, no-op when the driver isn't loaded) next to its sole remaining caller. Always defined, so all targets link; forwards only when recording. Windows-guarded since o_str.c (unlike the original unix-only home) is cross-platform. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… alloc)
node_errors.h: the v8::recordreplay::Assert("ThrowException %s", message)
hook was stranded at namespace scope between the error-message list and the
THROW_##code macro -- 'message' is undeclared there and a qualified
recordreplay::Assert can't be declared inside namespace node, which broke
EVERY TU including this header (environment.cc, exceptions.cc, etc). Moved it
inside the THROW_##code(isolate) macro body, matching the printf-style
THROW_##code template that already places the same assert correctly.
environment.cc: node 27 refactored NodeArrayBufferAllocator::Allocate to
always zero-fill (dropping the old zero_fill_field_/zero_fill_all_buffers
conditional), so the ported hook referenced a removed member and left a
malformed if (no else, possibly-uninitialized ret, missing COUNT_GENERIC_
USAGE). Restored Allocate to pristine and relocated the determinism hook to
AllocateUninitialized -- the only remaining uninitialized path -- where it
zero-fills during record/replay so recordings stay deterministic.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mechanical hook apply landed two v8::recordreplay::Assert calls at a bad line within multi-line constructs: - cares_wrap.cc AfterGetNameInfo: the assert was injected into the middle of the function's parameter list (between 'int status,' and the next param), so it parsed as a declaration -> 'no type named Assert in v8::recordreplay'. Moved it to the first statement of the body. - env.cc RunAndClearNativeImmediates: the assert was injected between 'while (auto head = queue.Shift())' and its body 'head->Call(this);', making the assert the loop body and pushing head->Call() out of head's scope. Moved it to a statement just before the loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 'ordered mutex' determinism hook added
native_immediates_threadsafe_mutex_(/* ordered */ true) to the
Environment::Environment member-initializer list but terminated it with
' {', prematurely opening the constructor body and orphaning the real final
initializer thread_name_(thread_name). The parser then read
thread_name_(thread_name) as a statement calling a std::string, cascading
into ~20 'function definition not allowed here' errors through the rest of
env.cc. Change the stray ' {' to ',' so the mutex initializer is a normal
list member and thread_name_(thread_name) remains the body opener. (This
also resolves the file's real 308/307 brace imbalance.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Environment ctor: list thread_name_ before native_immediates_threadsafe_ mutex_ to match declaration order (env.h 1188 vs 1228), silencing -Werror,-Wreorder-ctor. - The Environment::GetNow time-determinism hooks (Assert + RecordReplayBytes recording 'now') had been dropped into the middle of a MakeCallback(...) argument list in CheckImmediate, where 'now' is undeclared. Removed them from there (restoring the clean MakeCallback call) and placed them in Environment::GetNow() right after 'uint64_t now = GetNowUint64();', so the clock value is recorded/replayed deterministically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When removing the misplaced GetNow hooks from this MakeCallback arg list in the previous commit, I also dropped the 5th argument (argv = nullptr) -- the botched hooks had overwritten that line. node 27's MakeCallback takes (isolate, recv, fn, argc, argv, async_context); restore the nullptr argv to match the pristine call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
node.cc: the entire Replay definitions block (gRecordReplay* fn-pointer
decls, 'namespace recordreplay {}', RecordReplayFinishRecording,
OpenDriverHandle, InitializeRecordReplay) had been inserted INSIDE
InitializeNodeWithArgsInternal -- between the 'node_is_initialized' comment
and the node_is_initialized=true epilogue -- so 'namespace recordreplay'
was being defined in function scope (error) and gBuildId/InitializeRecord
Replay appeared undeclared. Moved the function's 3-line epilogue above the
block so the function closes after the comment and the Replay defs sit at
namespace scope (InitializeRecordReplay is still called later at startup).
node_api.cc (napi_module_register_by_symbol): restored the missing
'napi_value _exports = nullptr;' declaration, removed the leftover duplicate
env->CallIntoModule() (the replay if/else replaces it), and updated the two
v8::recordreplay::RegisterPointer calls to M151's (name, ptr) signature
(the fork's original 1-arg form predates the named driver API).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Replay print_build_id block used the old result.exit_code = 0 / result.early_return = true form. In node 27 'result' is a shared_ptr<InitializationResultImpl> with renamed members and an ExitCode enum, so use result->exit_code_ = ExitCode::kNoFailure / result->early_ return_ = true, matching the sibling print_version block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pile The 3-way apply mis-anchored this hook onto AddExternalizedBuiltin, wrapping its source-loading block in if(!IsRecordingOrReplaying()) -- which left 'resource' uninitialized on the R/R path (-Werror,-Wsometimes-uninitialized). That hook belongs on the code-cache path (originally node_native_module.cc:: LookupAndCompile, renamed to node_builtins.cc in node 27): reverted AddExternalizedBuiltin to pristine and wrapped BuiltinLoader::LookupAndCompile's cached_data lookup block instead. There cached_data stays default-constructed during R/R (has_cache=false), so the builtin compiles fresh -- the safe, intended behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ce::Run The hook had been dropped into the middle of the assignment '*exit_code = SpinEventLoopInternal(env).FromMaybe(...)', so it parsed as '*exit_code = RecordReplayFinishRecording()' (void -> 'assigning to ExitCode from void'). Per the original fork, it belongs after the if(*exit_code == kNoFailure) block (right where the old code called it before ResetStdio). Restored the assignment and moved the call there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
node_os.cc: three 'if (MaybeMarkUnavailable(args)) return;' entry guards had been dumped together at namespace scope after GetCPUInfo. Per the kit each OS-info accessor gets one; the three belong to GetFreeMemory, GetTotalMemory and GetUptime (the three that were left without a guard). Removed the stranded block and added a guard to the top of each. node_platform.cc: the non-deterministic-task check + Assert (both referencing 'task') were stranded between PostIdleTaskImpl and PostTaskImpl; 'task' is a param of PostTaskImpl, so moved them to the top of PostTaskImpl's body. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mechanical apply badly interleaved two Replay blocks with node 27's refactored code in node_process_methods.cc: - a block of env->SetMethod(target, "recordReplay*", ...) calls was stranded at namespace scope after HrtimeBigIntImpl; - a block of registry->Register(recordReplay*) calls was dumped inside BindingData::SlowHrtime's body, leaving that function unclosed. Removed both stranded blocks (restoring SlowHrtime/SlowHrtimeBigInt to pristine) and re-added the 14 record/replay methods to their correct node 27 homes: CreatePerIsolateProperties (converted env->SetMethod(target, ...) to the SetMethod(isolate, target, ...) API node 27 uses there) and the free RegisterExternalReferences (registry->Register). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mechanical apply inserted the RecordReplayDouble helper and the Replay '#define V' over SetHeapSnapshotNearHeapLimit's function signature, orphaning its body at namespace scope (while loop/'args' undeclared cascade), and left UpdateHeapStatisticsBuffer with the pristine (non-instrumented) V macro. Restored SetHeapSnapshotNearHeapLimit's signature+body, kept RecordReplayDouble between it and UpdateHeapStatisticsBuffer, and gave UpdateHeapStatisticsBuffer the RecordReplayDouble V macro (matching the already-correct Space/Code statistics siblings) so heap-statistics doubles are recorded deterministically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hook declared 'Worker* worker' inside an early if(!IsRecordingOrReplaying())
block (with a stale int Exit(1,...) call), leaving 'worker' out of scope for
the rest of the function ('unexpected namespace name worker') and the body's
worker->Exit(ExitCode,...) unreachable-by-name. Restored 'worker' to function
scope per pristine, dropped the stale early Exit(1,...), and wrapped the single
OOM worker->Exit(ExitCode::kGenericUserError, ...) in the R/R guard -- which is
the hook's actual intent (don't force workers to exit at non-deterministic
points while recording/replaying).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
inspector/node_string.cc had a stranded function body (the istringstream-> strtod 'workaround when replaying') with no signature, referencing undefined buffer/length/ok. The StringUtil::toDouble/parseDouble it instrumented was removed in node 27's inspector refactor (pristine goes straight from fromUTF16 to fromUTF8, and no such method is declared anywhere), so the body never compiled. Retired it, restoring pristine structure. (If inspector double-parse determinism is still needed, it belongs against node 27's actual crdtp parsing, as a separate change.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…idation
This file is normally GENERATED (and .gitignored) by the Replay build flow,
which embeds the downloaded driver binary + build id. node's standard GitHub
Actions CI (coverage / shared-lib matrix) doesn't run that generation step, so
the libnode_base.a archive was failing on the missing node_record_replay_
driver.o ('ar: ... No such file or directory').
Commit a force-added placeholder defining the symbols node.cc references
(gRecordReplayDriver / gRecordReplayDriverSize / gBuildId) with empty values so
the standard CI can link and the rest of the port stays compile-validated. The
real build flow overwrites this file with the embedded driver, so functional
record/replay builds are unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WIP: Re-apply Replay Node.js instrumentation onto Node 27 canary (V8 15.1)
replayio/nodeforked at v16.14.2 (442e84a3). This re-applies Replay's Node forkinstrumentation onto nodejs/node-v8
canary(d746551f= Node 27.0.0-canary / V8 15.1.42).Why the canary? To match the V8 the Chromium/V8 port targets (V8 15.1, M151). No released or
nightly Node ships V8 15.1 (Node 26.3 = V8 14.6); node-v8's canary floats on V8 tip at 15.1.42 —
one patch off
replayio/chromium-v8PR nodejs#282's 15.1.43 — sodeps/v8lines up between the two.Landed (
replay-port→node27-m151, ~240 files, +8.8k/-2.3k)lib/internal/recordreplay/*.js) +163 modified. 171/184 hunks auto-placed (93%) via the kit's anchor applier; 13 drifted
conflicts hand-resolved (see below). 0 unanchored.
replayio/chromium-v8PR build: Warn when using snapshot on Armv6 nodejs/node#282, plusthe node↔V8 recordreplay contract completed for Node 27.
ncrypto::CSPRNGrandomness chokepoint; unsupportednew surfaces guarded with
InvalidateRecording.Hand-resolved drift (highlights — full table in CONFLICTS-RESOLVED.md)
node_perf:timeOrigin*globals →performance_process_start*, now cached inEnvironmentctor.node_credentials.cc:SafeGetenvrefactor → wrapped uid/gid check in!HasDivergedFromRecording().crypto_random.cc:DeriveBitsnow usesncrypto::CSPRNG→ capture rv +AssertBytes.async.c/fs.c: rewritten to C11 atomics / new iovec paths → re-instrumented ordered locks.err_prn.ctid handling re-done; provider-model RNG hooks deferred.Open follow-ups (need Replay-team judgment — flagged with
TODO(replay-port))InitPerformance()call site — must run before the firstEnvironmentis constructed whenrecording/replaying; the fn + writable globals are in place but the call isn't wired.
drbg_lib.c/rand_unix.cgone in the provider model; decide whether tore-instrument at the provider layer or rely on higher-level
crypto_random.ccdeterminism.semantics need review.
Base is
node27-canary-base(d746551f); diff is exactly the replay re-application. WIP —auto-placed hooks need build/replay validation.