mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 22:01:47 +00:00
Compare commits
2 Commits
jarred/win
...
cursor/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dbc9122d8 | ||
|
|
fd434e8c81 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -183,4 +183,4 @@ codegen-for-zig-team.tar.gz
|
||||
*.sock
|
||||
scratch*.{js,ts,tsx,cjs,mjs}
|
||||
|
||||
*.bun-build
|
||||
*.bun-build/bun/
|
||||
|
||||
1
bun
Submodule
1
bun
Submodule
Submodule bun added at f62940bbda
@@ -638,6 +638,9 @@ pub fn enterUWSLoop(this: *VirtualMachine) void {
|
||||
}
|
||||
|
||||
pub fn onBeforeExit(this: *VirtualMachine) void {
|
||||
@import("./trace_events.zig").addEnvironmentEvent("BeforeExit", 'B') catch {};
|
||||
defer @import("./trace_events.zig").addEnvironmentEvent("BeforeExit", 'E') catch {};
|
||||
|
||||
this.exit_handler.dispatchOnBeforeExit();
|
||||
var dispatch = false;
|
||||
while (true) {
|
||||
@@ -696,6 +699,9 @@ pub fn setEntryPointEvalResultCJS(this: *VirtualMachine, value: JSValue) callcon
|
||||
}
|
||||
|
||||
pub fn onExit(this: *VirtualMachine) void {
|
||||
@import("./trace_events.zig").addEnvironmentEvent("RunCleanup", 'B') catch {};
|
||||
defer @import("./trace_events.zig").addEnvironmentEvent("RunCleanup", 'E') catch {};
|
||||
|
||||
this.exit_handler.dispatchOnExit();
|
||||
this.is_shutting_down = true;
|
||||
|
||||
@@ -715,6 +721,14 @@ pub fn onExit(this: *VirtualMachine) void {
|
||||
extern fn Zig__GlobalObject__destructOnExit(*JSGlobalObject) void;
|
||||
|
||||
pub fn globalExit(this: *VirtualMachine) noreturn {
|
||||
@import("./trace_events.zig").addEnvironmentEvent("AtExit", 'B') catch {};
|
||||
@import("./trace_events.zig").addEnvironmentEvent("AtExit", 'E') catch {};
|
||||
|
||||
// Flush trace events to file before exiting
|
||||
var cwd_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
const cwd = std.posix.getcwd(&cwd_buf) catch ".";
|
||||
@import("./trace_events.zig").flush(cwd);
|
||||
|
||||
if (this.destruct_main_thread_on_exit and this.is_main_thread) {
|
||||
Zig__GlobalObject__destructOnExit(this.global);
|
||||
this.deinit();
|
||||
|
||||
@@ -286,6 +286,15 @@ pub const All = struct {
|
||||
// Split into a separate variable to avoid increasing the size of the timespec type.
|
||||
var has_set_now: bool = false;
|
||||
|
||||
// Add trace event for timers if any are active
|
||||
const has_timers = this.active_timer_count > 0;
|
||||
if (has_timers) {
|
||||
@import("../trace_events.zig").addEnvironmentEvent("RunTimers", 'B') catch {};
|
||||
}
|
||||
defer if (has_timers) {
|
||||
@import("../trace_events.zig").addEnvironmentEvent("RunTimers", 'E') catch {};
|
||||
};
|
||||
|
||||
while (this.next(&has_set_now, &now)) |t| {
|
||||
switch (t.fire(&now, vm)) {
|
||||
.disarm => {},
|
||||
|
||||
@@ -40,7 +40,7 @@ static StringView extractCookieName(const StringView& cookie)
|
||||
{
|
||||
auto nameEnd = cookie.find('=');
|
||||
if (nameEnd == notFound)
|
||||
return String();
|
||||
return StringView();
|
||||
return cookie.substring(0, nameEnd);
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,14 @@ pub fn tickImmediateTasks(this: *EventLoop, virtual_machine: *VirtualMachine) vo
|
||||
this.immediate_tasks = this.next_immediate_tasks;
|
||||
this.next_immediate_tasks = .{};
|
||||
|
||||
// Add trace event for immediate tasks
|
||||
if (to_run_now.items.len > 0) {
|
||||
@import("./trace_events.zig").addEnvironmentEvent("CheckImmediate", 'B') catch {};
|
||||
defer @import("./trace_events.zig").addEnvironmentEvent("CheckImmediate", 'E') catch {};
|
||||
@import("./trace_events.zig").addEnvironmentEvent("RunAndClearNativeImmediates", 'B') catch {};
|
||||
defer @import("./trace_events.zig").addEnvironmentEvent("RunAndClearNativeImmediates", 'E') catch {};
|
||||
}
|
||||
|
||||
var exception_thrown = false;
|
||||
for (to_run_now.items) |task| {
|
||||
exception_thrown = task.runImmediateTask(virtual_machine);
|
||||
|
||||
138
src/bun.js/trace_events.zig
Normal file
138
src/bun.js/trace_events.zig
Normal file
@@ -0,0 +1,138 @@
|
||||
const std = @import("std");
|
||||
const bun = @import("bun");
|
||||
const JSC = bun.JSC;
|
||||
const strings = bun.strings;
|
||||
const Output = bun.Output;
|
||||
|
||||
pub const TraceEvents = struct {
|
||||
enabled: bool = false,
|
||||
categories: []const u8 = "",
|
||||
events: std.ArrayList(Event) = undefined,
|
||||
start_time: i64 = 0,
|
||||
|
||||
const Event = struct {
|
||||
name: []const u8,
|
||||
cat: []const u8,
|
||||
ph: u8,
|
||||
pid: i32,
|
||||
tid: i32,
|
||||
ts: u64,
|
||||
|
||||
pub fn jsonStringify(self: Event, writer: anytype) !void {
|
||||
try writer.beginObject();
|
||||
try writer.objectField("name");
|
||||
try writer.write(self.name);
|
||||
try writer.objectField("cat");
|
||||
try writer.write(self.cat);
|
||||
try writer.objectField("ph");
|
||||
try writer.writeByte(self.ph);
|
||||
try writer.objectField("pid");
|
||||
try writer.write(self.pid);
|
||||
try writer.objectField("tid");
|
||||
try writer.write(self.tid);
|
||||
try writer.objectField("ts");
|
||||
try writer.write(self.ts);
|
||||
try writer.endObject();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, categories: []const u8) TraceEvents {
|
||||
const enabled = categories.len > 0;
|
||||
return .{
|
||||
.enabled = enabled,
|
||||
.categories = categories,
|
||||
.events = if (enabled) std.ArrayList(Event).init(allocator) else undefined,
|
||||
.start_time = if (enabled) std.time.microTimestamp() else 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TraceEvents) void {
|
||||
if (self.enabled) {
|
||||
self.events.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addEvent(self: *TraceEvents, name: []const u8, cat: []const u8, phase: u8) !void {
|
||||
if (!self.enabled) return;
|
||||
if (!strings.contains(self.categories, cat) and !strings.eqlComptime(self.categories, "node.environment")) return;
|
||||
|
||||
const ts = std.time.microTimestamp() - self.start_time;
|
||||
try self.events.append(.{
|
||||
.name = name,
|
||||
.cat = cat,
|
||||
.ph = phase,
|
||||
.pid = @intCast(std.process.pid()),
|
||||
.tid = @intCast(std.Thread.getCurrentId()),
|
||||
.ts = @intCast(@max(0, ts)),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn writeToFile(self: *TraceEvents, dir_path: []const u8) !void {
|
||||
if (!self.enabled) return;
|
||||
|
||||
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
const filename = try std.fmt.bufPrintZ(&buf, "{s}/node_trace.1.log", .{dir_path});
|
||||
|
||||
std.io.getStdErr().writer().print("TRACE: Writing trace events to: {s} ({} events)\n", .{ filename, self.events.items.len }) catch {};
|
||||
|
||||
const file = try std.fs.createFileAbsolute(filename, .{});
|
||||
defer file.close();
|
||||
|
||||
var writer = file.writer();
|
||||
|
||||
try writer.writeAll("{\"traceEvents\":[");
|
||||
|
||||
// Write metadata event
|
||||
try writer.writeAll("{\"pid\":");
|
||||
try writer.print("{d}", .{std.process.pid()});
|
||||
try writer.writeAll(",\"tid\":");
|
||||
try writer.print("{d}", .{std.Thread.getCurrentId()});
|
||||
try writer.writeAll(",\"ts\":0,\"ph\":\"M\",\"cat\":\"__metadata\",\"name\":\"process_name\",\"args\":{\"name\":\"node\"}}");
|
||||
|
||||
for (self.events.items) |event| {
|
||||
try writer.writeByte(',');
|
||||
try std.json.stringify(event, .{}, writer);
|
||||
}
|
||||
|
||||
try writer.writeAll("]}");
|
||||
}
|
||||
};
|
||||
|
||||
var global_trace_events: TraceEvents = undefined;
|
||||
var initialized = false;
|
||||
|
||||
pub fn initialize(allocator: std.mem.Allocator, categories: []const u8) void {
|
||||
std.io.getStdErr().writer().print("TRACE: trace_events.initialize called with categories: {s}\n", .{categories}) catch {};
|
||||
if (!initialized) {
|
||||
global_trace_events = TraceEvents.init(allocator, categories);
|
||||
initialized = true;
|
||||
|
||||
if (global_trace_events.enabled) {
|
||||
std.io.getStdErr().writer().print("TRACE: Trace events enabled with categories: {s}\n", .{categories}) catch {};
|
||||
// Add initial environment events
|
||||
addEnvironmentEvent("Environment", 'B') catch {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addEnvironmentEvent(name: []const u8, phase: u8) !void {
|
||||
if (initialized) {
|
||||
try global_trace_events.addEvent(name, "node.environment", phase);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(dir_path: []const u8) void {
|
||||
if (initialized and global_trace_events.enabled) {
|
||||
std.io.getStdErr().writer().print("TRACE: Flushing trace events to dir: {s}\n", .{dir_path}) catch {};
|
||||
global_trace_events.writeToFile(dir_path) catch |err| {
|
||||
std.io.getStdErr().writer().print("TRACE: Failed to write trace events: {s}\n", .{@errorName(err)}) catch {};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
if (initialized) {
|
||||
global_trace_events.deinit();
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,14 @@ pub const Run = struct {
|
||||
bun.JSC.initialize(false);
|
||||
bun.Analytics.Features.standalone_executable += 1;
|
||||
|
||||
// Initialize trace events if requested
|
||||
if (ctx.runtime_options.trace_event_categories.len > 0) {
|
||||
std.io.getStdErr().writer().print("TRACE: Initializing trace events with categories: {s}\n", .{ctx.runtime_options.trace_event_categories}) catch {};
|
||||
@import("./bun.js/trace_events.zig").initialize(ctx.allocator, ctx.runtime_options.trace_event_categories);
|
||||
} else {
|
||||
std.io.getStdErr().writer().print("TRACE: Not initializing trace events, categories len = {}\n", .{ctx.runtime_options.trace_event_categories.len}) catch {};
|
||||
}
|
||||
|
||||
const graph_ptr = try bun.default_allocator.create(bun.StandaloneModuleGraph);
|
||||
graph_ptr.* = graph;
|
||||
graph_ptr.set();
|
||||
@@ -177,6 +185,14 @@ pub const Run = struct {
|
||||
|
||||
bun.JSC.initialize(ctx.runtime_options.eval.eval_and_print);
|
||||
|
||||
// Initialize trace events if requested
|
||||
if (ctx.runtime_options.trace_event_categories.len > 0) {
|
||||
std.io.getStdErr().writer().print("TRACE: Initializing trace events with categories: {s}\n", .{ctx.runtime_options.trace_event_categories}) catch {};
|
||||
@import("./bun.js/trace_events.zig").initialize(ctx.allocator, ctx.runtime_options.trace_event_categories);
|
||||
} else {
|
||||
std.io.getStdErr().writer().print("TRACE: Not initializing trace events, categories len = {}\n", .{ctx.runtime_options.trace_event_categories.len}) catch {};
|
||||
}
|
||||
|
||||
js_ast.Expr.Data.Store.create();
|
||||
js_ast.Stmt.Data.Store.create();
|
||||
var arena = try Arena.init();
|
||||
@@ -284,6 +300,9 @@ pub const Run = struct {
|
||||
vm.hot_reload = this.ctx.debug.hot_reload;
|
||||
vm.onUnhandledRejection = &onUnhandledRejectionBeforeClose;
|
||||
|
||||
// Add end event for Environment trace
|
||||
@import("./bun.js/trace_events.zig").addEnvironmentEvent("Environment", 'E') catch {};
|
||||
|
||||
this.addConditionalGlobals();
|
||||
do_redis_preconnect: {
|
||||
// This must happen within the API lock, which is why it's not in the "doPreconnect" function
|
||||
|
||||
@@ -237,6 +237,7 @@ pub const Arguments = struct {
|
||||
clap.parseParam("--zero-fill-buffers Boolean to force Buffer.allocUnsafe(size) to be zero-filled.") catch unreachable,
|
||||
clap.parseParam("--redis-preconnect Preconnect to $REDIS_URL at startup") catch unreachable,
|
||||
clap.parseParam("--no-addons Throw an error if process.dlopen is called, and disable export condition \"node-addons\"") catch unreachable,
|
||||
clap.parseParam("--trace-event-categories <STR> Enable trace events for the specified categories") catch unreachable,
|
||||
};
|
||||
|
||||
const auto_or_run_params = [_]ParamType{
|
||||
@@ -851,6 +852,10 @@ pub const Arguments = struct {
|
||||
if (args.flag("--zero-fill-buffers")) {
|
||||
Bun__Node__ZeroFillBuffers = true;
|
||||
}
|
||||
|
||||
if (args.option("--trace-event-categories")) |categories| {
|
||||
ctx.runtime_options.trace_event_categories = categories;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.port != null and opts.origin == null) {
|
||||
@@ -1547,6 +1552,7 @@ pub const Command = struct {
|
||||
/// compatibility.
|
||||
expose_gc: bool = false,
|
||||
preserve_symlinks_main: bool = false,
|
||||
trace_event_categories: []const u8 = "",
|
||||
};
|
||||
|
||||
var global_cli_ctx: Context = undefined;
|
||||
|
||||
@@ -735,19 +735,28 @@ function fork(modulePath, args = [], options) {
|
||||
validateArgumentNullCheck(options.execPath, "options.execPath");
|
||||
|
||||
// Prepare arguments for fork:
|
||||
// execArgv = options.execArgv || process.execArgv;
|
||||
// validateArgumentsNullCheck(execArgv, "options.execArgv");
|
||||
const execArgv = options.execArgv || process.execArgv;
|
||||
validateArgumentsNullCheck(execArgv, "options.execArgv");
|
||||
|
||||
// if (execArgv === process.execArgv && process._eval != null) {
|
||||
// const index = ArrayPrototypeLastIndexOf.$call(execArgv, process._eval);
|
||||
// if (index > 0) {
|
||||
// // Remove the -e switch to avoid fork bombing ourselves.
|
||||
// execArgv = ArrayPrototypeSlice.$call(execArgv);
|
||||
// ArrayPrototypeSplice.$call(execArgv, index - 1, 2);
|
||||
// }
|
||||
// }
|
||||
// Store execArgv separately to pass to Bun runtime
|
||||
options.__execArgv = execArgv;
|
||||
|
||||
args = [/*...execArgv,*/ modulePath, ...args];
|
||||
if (execArgv === process.execArgv && process._eval != null) {
|
||||
const index = ArrayPrototypeLastIndexOf.$call(execArgv, process._eval);
|
||||
if (index > 0) {
|
||||
// Remove the -e switch to avoid fork bombing ourselves.
|
||||
const newExecArgv = ArrayPrototypeSlice.$call(execArgv);
|
||||
ArrayPrototypeSplice.$call(newExecArgv, index - 1, 2);
|
||||
// Only pass modulePath and args, not execArgv
|
||||
args = [modulePath, ...args];
|
||||
} else {
|
||||
// Only pass modulePath and args, not execArgv
|
||||
args = [modulePath, ...args];
|
||||
}
|
||||
} else {
|
||||
// Only pass modulePath and args, not execArgv
|
||||
args = [modulePath, ...args];
|
||||
}
|
||||
|
||||
if (typeof options.stdio === "string") {
|
||||
options.stdio = stdioStringToArray(options.stdio, "ipc");
|
||||
@@ -1285,9 +1294,19 @@ class ChildProcess extends EventEmitter {
|
||||
// Bun.spawn() expects cmd[0] to be the command to run, and argv0 to replace the first arg when running the command,
|
||||
// so we have to set argv0 to spawnargs[0] and cmd[0] to file
|
||||
|
||||
// Handle execArgv if present (from fork)
|
||||
let cmd;
|
||||
if (options.__execArgv && options.__execArgv.length > 0) {
|
||||
// When execArgv is present, we need to pass them as runtime options to Bun
|
||||
// The command array should be: [bun_executable, ...execArgv, script_file, ...args]
|
||||
cmd = [file, ...options.__execArgv, ...Array.prototype.slice.$call(spawnargs, 1)];
|
||||
} else {
|
||||
cmd = [file, ...Array.prototype.slice.$call(spawnargs, 1)];
|
||||
}
|
||||
|
||||
try {
|
||||
this.#handle = Bun.spawn({
|
||||
cmd: [file, ...Array.prototype.slice.$call(spawnargs, 1)],
|
||||
cmd: cmd,
|
||||
stdio: bunStdio,
|
||||
cwd: options.cwd || undefined,
|
||||
env: env,
|
||||
|
||||
13
test-child-simple.js
Normal file
13
test-child-simple.js
Normal file
@@ -0,0 +1,13 @@
|
||||
console.log("Child process started");
|
||||
console.log("Child argv:", process.argv);
|
||||
console.log("Child execArgv:", process.execArgv);
|
||||
|
||||
// Do some work to trigger trace events
|
||||
setImmediate(() => {
|
||||
console.log("Immediate callback");
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Timer callback");
|
||||
process.exit(0);
|
||||
}, 10);
|
||||
13
test-direct-trace.js
Normal file
13
test-direct-trace.js
Normal file
@@ -0,0 +1,13 @@
|
||||
console.log("Direct run with trace events");
|
||||
console.log("process.argv:", process.argv);
|
||||
console.log("process.execArgv:", process.execArgv);
|
||||
|
||||
// Add some events to trigger trace output
|
||||
setImmediate(() => {
|
||||
console.log("Immediate callback");
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Timer callback");
|
||||
process.exit(0);
|
||||
}, 10);
|
||||
15
test-execargv.js
Normal file
15
test-execargv.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const cp = require("child_process");
|
||||
|
||||
if (process.argv[2] === "child") {
|
||||
console.log("Child process execArgv:", process.execArgv);
|
||||
console.log("Child process argv:", process.argv);
|
||||
} else {
|
||||
console.log("Parent forking child with execArgv...");
|
||||
const child = cp.fork(__filename, ["child"], {
|
||||
execArgv: ["--trace-event-categories", "node.environment"],
|
||||
});
|
||||
|
||||
child.on("exit", code => {
|
||||
console.log("Child exited with code:", code);
|
||||
});
|
||||
}
|
||||
32
test-fork-args.js
Normal file
32
test-fork-args.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Override spawn to see what arguments are passed
|
||||
const originalSpawn = require("bun").spawn;
|
||||
require("bun").spawn = function (options) {
|
||||
console.log("Bun.spawn called with:");
|
||||
console.log(" cmd:", options.cmd);
|
||||
console.log(" cwd:", options.cwd);
|
||||
console.log(" argv0:", options.argv0);
|
||||
// Don't actually spawn
|
||||
return {
|
||||
pid: 12345,
|
||||
stdin: null,
|
||||
stdout: null,
|
||||
stderr: null,
|
||||
kill: () => {},
|
||||
ref: () => {},
|
||||
unref: () => {},
|
||||
stdio: [],
|
||||
};
|
||||
};
|
||||
|
||||
const cp = require("child_process");
|
||||
|
||||
console.log("Testing fork...");
|
||||
try {
|
||||
const child = cp.fork("./test-child-simple.js", ["arg1", "arg2"], {
|
||||
cwd: "/tmp",
|
||||
execArgv: ["--trace-event-categories", "node.environment"],
|
||||
});
|
||||
console.log("Fork returned, pid:", child.pid);
|
||||
} catch (e) {
|
||||
console.error("Fork failed:", e);
|
||||
}
|
||||
46
test-parent-fork.js
Normal file
46
test-parent-fork.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const cp = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
|
||||
const tmpdir = path.join(os.tmpdir(), "trace-test-" + Date.now());
|
||||
fs.mkdirSync(tmpdir, { recursive: true });
|
||||
|
||||
console.log("Parent process started");
|
||||
console.log("Test tmpdir:", tmpdir);
|
||||
|
||||
console.log("Forking child with execArgv...");
|
||||
const child = cp.fork("./test-child-simple.js", [], {
|
||||
cwd: tmpdir,
|
||||
execArgv: ["--trace-event-categories", "node.environment"],
|
||||
});
|
||||
|
||||
child.on("exit", code => {
|
||||
console.log("\nChild exited with code:", code);
|
||||
|
||||
// Check for trace file
|
||||
const traceFile = path.join(tmpdir, "node_trace.1.log");
|
||||
console.log("Looking for trace file:", traceFile);
|
||||
console.log("Trace file exists:", fs.existsSync(traceFile));
|
||||
|
||||
// List all files in tmpdir
|
||||
const files = fs.readdirSync(tmpdir);
|
||||
console.log("Files in tmpdir:", files);
|
||||
|
||||
if (fs.existsSync(traceFile)) {
|
||||
const content = fs.readFileSync(traceFile, "utf8");
|
||||
console.log("Trace file size:", content.length, "bytes");
|
||||
try {
|
||||
const data = JSON.parse(content);
|
||||
console.log("Trace events count:", data.traceEvents?.length || 0);
|
||||
if (data.traceEvents) {
|
||||
console.log("Event names:", data.traceEvents.map(e => e.name).join(", "));
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Failed to parse trace file:", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
fs.rmSync(tmpdir, { recursive: true });
|
||||
});
|
||||
23
test-spawn-debug.js
Normal file
23
test-spawn-debug.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const cp = require("child_process");
|
||||
const fs = require("fs");
|
||||
|
||||
// Create a test script that just prints its process info
|
||||
const testScript = `
|
||||
console.log('Child process info:');
|
||||
console.log('argv:', process.argv);
|
||||
console.log('execArgv:', process.execArgv);
|
||||
console.log('env.BUN_DEBUG_TRACE:', process.env.BUN_DEBUG_TRACE);
|
||||
`;
|
||||
|
||||
fs.writeFileSync("test-child.js", testScript);
|
||||
|
||||
console.log("Parent forking child with execArgv...");
|
||||
const child = cp.fork("./test-child.js", ["arg1", "arg2"], {
|
||||
execArgv: ["--trace-event-categories", "node.environment"],
|
||||
env: { ...process.env, BUN_DEBUG_TRACE: "1" },
|
||||
});
|
||||
|
||||
child.on("exit", code => {
|
||||
console.log("Child exited with code:", code);
|
||||
fs.unlinkSync("test-child.js");
|
||||
});
|
||||
3
test-trace-basic.js
Normal file
3
test-trace-basic.js
Normal file
@@ -0,0 +1,3 @@
|
||||
console.log("Hello from test script");
|
||||
console.log("execArgv:", process.execArgv);
|
||||
process.exit(0);
|
||||
13
test-trace-equals.js
Normal file
13
test-trace-equals.js
Normal file
@@ -0,0 +1,13 @@
|
||||
// Test with equals sign format
|
||||
console.log("Running with --trace-event-categories=node.environment");
|
||||
console.log("process.argv:", process.argv);
|
||||
console.log("process.execArgv:", process.execArgv);
|
||||
|
||||
setImmediate(() => {
|
||||
console.log("Immediate callback");
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Timer callback");
|
||||
process.exit(0);
|
||||
}, 10);
|
||||
76
test-trace-fork-debug.js
Normal file
76
test-trace-fork-debug.js
Normal file
@@ -0,0 +1,76 @@
|
||||
const cp = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
|
||||
console.log("Script started with argv:", process.argv);
|
||||
|
||||
// Look for 'child' argument anywhere in argv
|
||||
const isChild = process.argv.includes("child");
|
||||
|
||||
if (isChild) {
|
||||
console.log("=== CHILD PROCESS ===");
|
||||
console.log("Child CWD:", process.cwd());
|
||||
console.log("Child argv:", process.argv);
|
||||
console.log("Child execArgv:", process.execArgv);
|
||||
|
||||
// Do some work to trigger trace events
|
||||
setImmediate(() => {
|
||||
console.log("Immediate callback");
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Timer callback");
|
||||
|
||||
// List files before exit
|
||||
const files = fs.readdirSync(".");
|
||||
console.log(
|
||||
"Files in CWD:",
|
||||
files.filter(f => f.includes("trace")),
|
||||
);
|
||||
}, 10);
|
||||
|
||||
// Exit after a short delay
|
||||
setTimeout(() => {
|
||||
console.log("Child exiting...");
|
||||
process.exit(0);
|
||||
}, 50);
|
||||
} else {
|
||||
console.log("=== PARENT PROCESS ===");
|
||||
const tmpdir = path.join(os.tmpdir(), "trace-test-" + Date.now());
|
||||
fs.mkdirSync(tmpdir, { recursive: true });
|
||||
console.log("Test tmpdir:", tmpdir);
|
||||
|
||||
console.log("Parent forking child...");
|
||||
const child = cp.fork(__filename, ["child"], {
|
||||
cwd: tmpdir,
|
||||
execArgv: ["--trace-event-categories", "node.environment"],
|
||||
});
|
||||
|
||||
child.on("exit", code => {
|
||||
console.log("\nChild exited with code:", code);
|
||||
|
||||
// Check for trace file
|
||||
const traceFile = path.join(tmpdir, "node_trace.1.log");
|
||||
console.log("Looking for trace file:", traceFile);
|
||||
console.log("Trace file exists:", fs.existsSync(traceFile));
|
||||
|
||||
// List all files in tmpdir
|
||||
const files = fs.readdirSync(tmpdir);
|
||||
console.log("Files in tmpdir:", files);
|
||||
|
||||
if (fs.existsSync(traceFile)) {
|
||||
const content = fs.readFileSync(traceFile, "utf8");
|
||||
console.log("Trace file size:", content.length, "bytes");
|
||||
try {
|
||||
const data = JSON.parse(content);
|
||||
console.log("Trace events count:", data.traceEvents?.length || 0);
|
||||
} catch (e) {
|
||||
console.log("Failed to parse trace file:", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
fs.rmSync(tmpdir, { recursive: true });
|
||||
});
|
||||
}
|
||||
80
test-trace-fork-fixed.js
Normal file
80
test-trace-fork-fixed.js
Normal file
@@ -0,0 +1,80 @@
|
||||
const cp = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
|
||||
const tmpdir = path.join(os.tmpdir(), "trace-test-" + Date.now());
|
||||
fs.mkdirSync(tmpdir, { recursive: true });
|
||||
|
||||
console.log("Test tmpdir:", tmpdir);
|
||||
|
||||
if (process.argv[2] === "child") {
|
||||
console.log("Child process started");
|
||||
console.log("Child CWD:", process.cwd());
|
||||
console.log("Child argv:", process.argv);
|
||||
console.log("Child execArgv:", process.execArgv);
|
||||
|
||||
// Check if we have the trace flag in argv
|
||||
const hasTraceFlag = process.argv.some(arg => arg.includes("trace-event-categories"));
|
||||
console.log("Has trace flag in argv:", hasTraceFlag);
|
||||
|
||||
// Do some work to trigger trace events
|
||||
setImmediate(() => {
|
||||
console.log("Immediate callback");
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Timer callback");
|
||||
}, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Child exiting...");
|
||||
// List files in current directory before exit
|
||||
const files = fs.readdirSync(".");
|
||||
console.log("Files in CWD:", files);
|
||||
process.exit(0);
|
||||
}, 20);
|
||||
} else {
|
||||
// Test 1: Using execArgv (what the Node.js test does)
|
||||
console.log("\n=== Test 1: Using execArgv ===");
|
||||
console.log("Parent forking child with execArgv...");
|
||||
const child1 = cp.fork(__filename, ["child"], {
|
||||
cwd: tmpdir,
|
||||
execArgv: ["--trace-event-categories", "node.environment"],
|
||||
});
|
||||
|
||||
child1.on("exit", code => {
|
||||
console.log("Child1 exited with code:", code);
|
||||
|
||||
// Check for trace file
|
||||
const traceFile = path.join(tmpdir, "node_trace.1.log");
|
||||
console.log("Looking for trace file:", traceFile);
|
||||
console.log("Trace file exists:", fs.existsSync(traceFile));
|
||||
|
||||
// List all files in tmpdir
|
||||
const files = fs.readdirSync(tmpdir);
|
||||
console.log("Files in tmpdir:", files);
|
||||
|
||||
// Test 2: Using spawn with args directly
|
||||
console.log("\n=== Test 2: Using spawn directly ===");
|
||||
const child2 = cp.spawn(process.execPath, ["--trace-event-categories", "node.environment", __filename, "child"], {
|
||||
cwd: tmpdir,
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
child2.on("exit", code => {
|
||||
console.log("\nChild2 exited with code:", code);
|
||||
|
||||
// Check for trace file again
|
||||
console.log("Looking for trace file:", traceFile);
|
||||
console.log("Trace file exists:", fs.existsSync(traceFile));
|
||||
|
||||
// List all files in tmpdir
|
||||
const files2 = fs.readdirSync(tmpdir);
|
||||
console.log("Files in tmpdir:", files2);
|
||||
|
||||
// Cleanup
|
||||
fs.rmSync(tmpdir, { recursive: true });
|
||||
});
|
||||
});
|
||||
}
|
||||
53
test-trace-fork.js
Normal file
53
test-trace-fork.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const cp = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
|
||||
const tmpdir = path.join(os.tmpdir(), "trace-test-" + Date.now());
|
||||
fs.mkdirSync(tmpdir, { recursive: true });
|
||||
|
||||
console.log("Test tmpdir:", tmpdir);
|
||||
|
||||
if (process.argv[2] === "child") {
|
||||
console.log("Child process started");
|
||||
console.log("Child CWD:", process.cwd());
|
||||
console.log("Child execArgv:", process.execArgv);
|
||||
|
||||
// Do some work to trigger trace events
|
||||
setImmediate(() => {
|
||||
console.log("Immediate callback");
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Timer callback");
|
||||
}, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Child exiting...");
|
||||
// List files in current directory before exit
|
||||
const files = fs.readdirSync(".");
|
||||
console.log("Files in CWD:", files);
|
||||
}, 20);
|
||||
} else {
|
||||
console.log("Parent forking child...");
|
||||
const child = cp.fork(__filename, ["child"], {
|
||||
cwd: tmpdir,
|
||||
execArgv: ["--trace-event-categories", "node.environment"],
|
||||
});
|
||||
|
||||
child.on("exit", code => {
|
||||
console.log("Child exited with code:", code);
|
||||
|
||||
// Check for trace file
|
||||
const traceFile = path.join(tmpdir, "node_trace.1.log");
|
||||
console.log("Looking for trace file:", traceFile);
|
||||
console.log("Trace file exists:", fs.existsSync(traceFile));
|
||||
|
||||
// List all files in tmpdir
|
||||
const files = fs.readdirSync(tmpdir);
|
||||
console.log("Files in tmpdir:", files);
|
||||
|
||||
// Cleanup
|
||||
fs.rmSync(tmpdir, { recursive: true });
|
||||
});
|
||||
}
|
||||
18
test-trace-minimal.js
Normal file
18
test-trace-minimal.js
Normal file
@@ -0,0 +1,18 @@
|
||||
console.log("Starting trace test");
|
||||
console.log("Process argv:", process.argv);
|
||||
console.log("CWD:", process.cwd());
|
||||
|
||||
// Add some timers to trigger trace events
|
||||
setImmediate(() => {
|
||||
console.log("Immediate callback");
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Timer callback");
|
||||
}, 10);
|
||||
|
||||
// Exit after a short delay
|
||||
setTimeout(() => {
|
||||
console.log("Exiting...");
|
||||
process.exit(0);
|
||||
}, 50);
|
||||
68
test-trace-simple.js
Normal file
68
test-trace-simple.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const child_process = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
console.log("Starting trace test");
|
||||
console.log("Process argv:", process.argv);
|
||||
console.log("CWD:", process.cwd());
|
||||
|
||||
// Add some timers to trigger trace events
|
||||
setImmediate(() => {
|
||||
console.log("Immediate callback");
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
console.log("Timer callback");
|
||||
}, 10);
|
||||
|
||||
// Exit after a short delay
|
||||
setTimeout(() => {
|
||||
console.log("Exiting...");
|
||||
process.exit(0);
|
||||
}, 50);
|
||||
|
||||
const tmpdir = "/tmp/trace-test-" + Date.now();
|
||||
fs.mkdirSync(tmpdir);
|
||||
|
||||
console.log("Created temporary directory:", tmpdir);
|
||||
|
||||
const child = child_process.fork(process.argv[1], ["child"], {
|
||||
cwd: tmpdir,
|
||||
execArgv: ["--trace-event-categories", "node.environment"],
|
||||
});
|
||||
|
||||
if (process.argv[2] === "child") {
|
||||
console.log("Child process running with execArgv:", process.execArgv);
|
||||
setImmediate(() => {
|
||||
console.log("setImmediate callback");
|
||||
});
|
||||
setTimeout(() => {
|
||||
console.log("setTimeout callback");
|
||||
}, 10);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
child.on("exit", () => {
|
||||
console.log("Child process exited");
|
||||
const traceFile = path.join(tmpdir, "node_trace.1.log");
|
||||
console.log("Looking for trace file:", traceFile);
|
||||
|
||||
if (fs.existsSync(traceFile)) {
|
||||
console.log("Trace file found!");
|
||||
const content = fs.readFileSync(traceFile, "utf8");
|
||||
console.log("Trace file content:", content);
|
||||
|
||||
try {
|
||||
const data = JSON.parse(content);
|
||||
console.log("Parsed trace events:", data.traceEvents.length, "events");
|
||||
const eventNames = new Set(data.traceEvents.filter(e => e.cat === "node.environment").map(e => e.name));
|
||||
console.log("Event names:", Array.from(eventNames));
|
||||
} catch (e) {
|
||||
console.error("Failed to parse trace file:", e);
|
||||
}
|
||||
} else {
|
||||
console.log("Trace file NOT found");
|
||||
}
|
||||
|
||||
fs.rmSync(tmpdir, { recursive: true });
|
||||
});
|
||||
59
test/js/node/test/parallel/test-trace-events-environment.js
Normal file
59
test/js/node/test/parallel/test-trace-events-environment.js
Normal file
@@ -0,0 +1,59 @@
|
||||
// Flags: --no-warnings
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const cp = require('child_process');
|
||||
const fs = require('fs');
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
// This tests the emission of node.environment trace events
|
||||
|
||||
const names = new Set([
|
||||
'Environment',
|
||||
'RunAndClearNativeImmediates',
|
||||
'CheckImmediate',
|
||||
'RunTimers',
|
||||
'BeforeExit',
|
||||
'RunCleanup',
|
||||
'AtExit',
|
||||
]);
|
||||
|
||||
if (process.argv[2] === 'child') {
|
||||
/* eslint-disable no-unused-expressions */
|
||||
// This is just so that the child has something to do.
|
||||
1 + 1;
|
||||
// These ensure that the RunTimers, CheckImmediate, and
|
||||
// RunAndClearNativeImmediates appear in the list.
|
||||
setImmediate(() => { 1 + 1; });
|
||||
setTimeout(() => { 1 + 1; }, 1);
|
||||
/* eslint-enable no-unused-expressions */
|
||||
} else {
|
||||
tmpdir.refresh();
|
||||
|
||||
const proc = cp.fork(__filename,
|
||||
[ 'child' ], {
|
||||
cwd: tmpdir.path,
|
||||
execArgv: [
|
||||
'--trace-event-categories',
|
||||
'node.environment',
|
||||
]
|
||||
});
|
||||
|
||||
proc.once('exit', common.mustCall(async () => {
|
||||
const file = tmpdir.resolve('node_trace.1.log');
|
||||
const checkSet = new Set();
|
||||
|
||||
assert(fs.existsSync(file));
|
||||
const data = await fs.promises.readFile(file);
|
||||
JSON.parse(data.toString()).traceEvents
|
||||
.filter((trace) => trace.cat !== '__metadata')
|
||||
.forEach((trace) => {
|
||||
assert.strictEqual(trace.pid, proc.pid);
|
||||
assert(names.has(trace.name));
|
||||
checkSet.add(trace.name);
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(names, checkSet);
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user