From 0486cea35a80be97ba43f41a29ce55f0d3a8eb01 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 16 Aug 2023 19:40:20 -0700 Subject: `bun --inspect` (#4158) * Let the debugger to pause/resume the event loop * Add initial support for Debug Adapter Protocol * Add progress * Update Worker.cpp * Fix require("console") #3820 (#4073) * Fix #3820 * Add Module (#4074) * Set exports to {} in user-constructed CommonJSModuleRecords (#4076) * feat(bun/test): Implement "toSatisfy" & "toIncludeRepeated" (fwup) (#3651) * Fix merge issues * oop * make codegen * Fix build issues --------- Co-authored-by: dave caruso * worker tests (#4058) Co-authored-by: Jarred Sumner * feat(bun:test) add support for test.each() and describe.each() (#4047) * rename callback to func * update testscope to handle function arguments * works * big cleanup * works in debug, not release * fix memory issue & update tests * catch & str test * write types for each() & switch tests to ts * rm & typo * move some code around & support describe * review changes * Fix one of the astro segfaults, also fix bun init version (#4079) * 4->16 * add assertions * fix version stuff * Remove unintentional logs from #4043 * Run prettier * Update main-worker-file.js * Update SIMDUTF (#4078) * Fix constructing buffer from a UTF16 string with the Latin1 encoding. (#4086) Close: #3914 * Add support for `bun --revision` (#4027) Co-authored-by: Yash Sharma * Updates * Update __global.zig * remove non-node node-fallbacks (#4082) * remove non-node node-fallbacks. * organize the imports * Fix test * Sync bun-polyfills branch (#4081) * bun-polyfills: initial impl. & baseline refactor * move @types/ws dep from root to /test/ * bun-types: remove ReadableStream.forEach method (this does not exist, probably added by mistake) * bun-polyfills: remove extraneous stream utils * bun-polyfills: add types syncing file * bun-polyfills: re-arrange global polyfills * bun-polyfills: fix FileBlob streams types again * bun-polyfills: sync all of @types/node * bun-polyfills: typeguard all current polyfills * bun-polyfills: fix import paths * bun-polyfills: switch to wasm impl. of farmhash * bun-polyfills: support default import of bun obj * bun-polyfills: transpiler placeholder file * bun-polyfills: loaderless import.meta polyfill * bun-polyfills: refactor import.meta polyfill * bun-polyfills: repl entrypoint & todo list index * bun-types: Add null to return type of Bun.which * bun-types: match Bun.sha with Bun.hash.SHA512_256 * bun-polyfills: new "repl" package.json script * bun-polyfills: full refactor of toplevel hashes * bun-polyfills: these are fixed * bun-types: NODE_ENV is optional * bun-polyfills: fix Bun.env types * bun-types+polyfills: fix HeapSnapshot.version type * bun-polyfills: fix some web streams type conflicts * bun-polyfills: update internal FileBlob.slice * bun-polyfills: fix subproc stdin conversions * bun-polyfills: better internal fileblob types * bun-polyfills: try to sync global performance type * bun-polyfills: working zig wasm polyfills setup * bun-polyfills: update scripts * bun-polyfills: fix wasm file location resolution * bun-polyfills: goodbye farmhash (replaced by zig) * bun-polyfills: move all Bun.hash polyfills to zig * bun-polyfills: reimpl. seeding of seeded hashes * bun-polyfills: impl. undocumented murmur32v2 * bun-polyfills: switch zighash from jsdoc to .d.ts * bun-types: partial fix of Hash types * bun-polyfills: documented Hash.murmur32v2 * bun-polyfills: misc updates * bun-polyfills: enable sourcemaps * bun-polyfills: handle empty inputs to hash funcs * bun-types: narrow down hash func types * bun-polyfills: remove unnecessary bigint casts * bun-polyfills: impl. Bun.isMainThread * bun-polyfills: impl. Bun.sleep and fix sleepSync * bun-polyfills: impl. indexOfLine * bun-polyfills: impl. Bun.peek.status * bun-types: fix hashing test --------- Co-authored-by: Jarred Sumner * Add remix guide * Fix title * add util.formatWithOptions (#4090) * Add formatWithOptions * tests and tweaks * adjust --------- Co-authored-by: Jarred Sumner * bun test: format description of test.each (#4092) * bun test: format description * add tests for tests * only --------- Co-authored-by: Jarred Sumner * Fixes #4062 (#4106) * Fixes #4062 * Update encoding.zig * Use faster C++ impl * Update wtf-bindings.cpp * undo * Fixup --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> * zig fmt * Update remix guide * fs.zig: create temp files with 0o700, not 0o007 (#4107) * Handle thundering herd of setInterval (#4109) Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> * Fix memory leak in base64url (#4111) Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> * run files without extensions (#4113) * run script without extension * process stdio write fix * don't check for trailing slash, var stream * More lazily initialize these static strings * Remove assertion * wip * Add --inspect flag * Deprecate loading `node_modules.bun` * realpath * regenerate schema * More * more * Update cli.zig * Debugger JS loads * have fun * Most of the code * Its starting to work. * more progress * win some, lose some * Update dap.ts * Async Context Tracking * untested websocket * Emit Console messages * Error handling * errors * Make profiling work better * [clap] CLI arguments with optional values ignore positional params In `bun --inspect foo.js`, `foo.js` should not be the value of `--inspect`. It is a positional parameter. `--inspect=foo` * Support multiple simultaneous clients, automatically unpause on disconnect * regenerate * Update Makefile * Update WebKit * Update cli.zig * Update InternalModuleRegistry+createInternalModuleById.h * BaseURL * Update WebKit * Add web-inspector-bun * Update build.ts * formatting, mostly * Update debugger.ts * Update InternalModuleRegistryConstants.h * wrap * Update test * Update test --------- Co-authored-by: Ashcon Partovi Co-authored-by: Dylan Conway Co-authored-by: dave caruso Co-authored-by: Tiramify (A.K. Daniel) <94789999+TiranexDev@users.noreply.github.com> Co-authored-by: Jacques <25390037+jecquas@users.noreply.github.com> Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: Ai Hoshino Co-authored-by: Yash Sharma Co-authored-by: Yash Sharma Co-authored-by: Colin McDonnell Co-authored-by: jhmaster <32803471+jhmaster2000@users.noreply.github.com> Co-authored-by: Adhityaa Chandrasekar Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> --- src/bun.js/javascript.zig | 141 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 5 deletions(-) (limited to 'src/bun.js/javascript.zig') diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 3e5ac799d..a1dd77674 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -496,6 +496,8 @@ pub const VirtualMachine = struct { gc_controller: JSC.GarbageCollectionController = .{}, worker: ?*JSC.WebWorker = null, + debugger: ?Debugger = null, + pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void; pub const OnException = fn (*ZigException) void; @@ -504,6 +506,10 @@ pub const VirtualMachine = struct { return this.worker == null; } + pub fn isInspectorEnabled(this: *const VirtualMachine) bool { + return this.debugger != null; + } + pub fn setOnException(this: *VirtualMachine, callback: *const OnException) void { this.on_exception = callback; } @@ -706,6 +712,106 @@ pub const VirtualMachine = struct { } } + pub fn nextAsyncTaskID(this: *VirtualMachine) u64 { + var debugger: *Debugger = &(this.debugger orelse return 0); + debugger.next_debugger_id +%= 1; + return debugger.next_debugger_id; + } + + pub const Debugger = struct { + path_or_port: []const u8 = "", + script_execution_context_id: u32 = 0, + next_debugger_id: u64 = 1, + poll_ref: JSC.PollRef = .{}, + auto_pause: bool = false, + const debug = Output.scoped(.DEBUGGER, false); + + extern "C" fn Bun__createJSDebugger(*JSC.JSGlobalObject) u32; + extern "C" fn Bun__ensureDebugger(u32, bool) void; + extern "C" fn Bun__startJSDebuggerThread(*JSC.JSGlobalObject, u32, *bun.String) bun.String; + var has_started_debugger_thread: bool = false; + var futex_atomic: std.atomic.Atomic(u32) = undefined; + + pub fn create(this: *VirtualMachine, globalObject: *JSGlobalObject) !void { + debug("create", .{}); + this.debugger.?.script_execution_context_id = Bun__createJSDebugger(globalObject); + if (!has_started_debugger_thread) { + has_started_debugger_thread = true; + futex_atomic = std.atomic.Atomic(u32).init(0); + var thread = try std.Thread.spawn(.{}, startJSDebuggerThread, .{this}); + thread.detach(); + } + this.eventLoop().ensureWaker(); + if (this.debugger.?.auto_pause) { + this.debugger.?.poll_ref.ref(this); + } + debug("spin", .{}); + while (futex_atomic.load(.Monotonic) > 0) std.Thread.Futex.wait(&futex_atomic, 1); + if (comptime Environment.allow_assert) + debug("waitForDebugger: {}", .{Output.ElapsedFormatter{ + .colors = Output.enable_ansi_colors_stderr, + .duration_ns = @truncate(@as(u128, @intCast(std.time.nanoTimestamp() - bun.CLI.start_time))), + }}); + + Bun__ensureDebugger(this.debugger.?.script_execution_context_id, this.debugger.?.auto_pause); + } + + pub fn startJSDebuggerThread(other_vm: *VirtualMachine) void { + var arena = bun.MimallocArena.init() catch unreachable; + Output.Source.configureNamedThread("Debugger"); + debug("startJSDebuggerThread", .{}); + + var vm = JSC.VirtualMachine.init(.{ + .allocator = arena.allocator(), + .args = std.mem.zeroes(Api.TransformOptions), + .store_fd = false, + }) catch @panic("Failed to create Debugger VM"); + vm.allocator = arena.allocator(); + vm.arena = &arena; + + vm.bundler.configureDefines() catch @panic("Failed to configure defines"); + vm.is_main_thread = false; + vm.eventLoop().ensureWaker(); + + vm.global.vm().holdAPILock(other_vm, @ptrCast(&start)); + } + + pub export var Bun__debugger_server_url: bun.String = undefined; + + fn start(other_vm: *VirtualMachine) void { + var this = VirtualMachine.get(); + var str = bun.String.create(other_vm.debugger.?.path_or_port); + Bun__debugger_server_url = Bun__startJSDebuggerThread(this.global, other_vm.debugger.?.script_execution_context_id, &str); + Bun__debugger_server_url.toThreadSafe(); + + this.global.handleRejectedPromises(); + + if (this.log.msgs.items.len > 0) { + if (Output.enable_ansi_colors) { + this.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; + } else { + this.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; + } + Output.prettyErrorln("\n", .{}); + Output.flush(); + } + + futex_atomic.store(0, .Monotonic); + std.Thread.Futex.wake(&futex_atomic, 1); + debug("wake", .{}); + this.eventLoop().tick(); + + while (true) { + while (this.eventLoop().tasks.count > 0 or this.active_tasks > 0 or this.uws_event_loop.?.active > 0) { + this.tick(); + this.eventLoop().autoTickActive(); + } + + this.eventLoop().tickPossiblyForever(); + } + } + }; + pub inline fn enqueueTask(this: *VirtualMachine, task: Task) void { this.eventLoop().enqueueTask(task); } @@ -917,6 +1023,8 @@ pub const VirtualMachine = struct { source_code_printer.?.ctx.append_null_byte = false; } + vm.configureDebugger(opts.debugger); + return vm; } @@ -928,6 +1036,7 @@ pub const VirtualMachine = struct { store_fd: bool = false, smol: bool = false, graph: ?*bun.StandaloneModuleGraph = null, + debugger: bun.CLI.Command.Debugger = .{ .unspecified = {} }, }; pub fn init(opts: Options) !*VirtualMachine { @@ -1023,9 +1132,32 @@ pub const VirtualMachine = struct { source_code_printer.?.ctx.append_null_byte = false; } + vm.configureDebugger(opts.debugger); + return vm; } + fn configureDebugger(this: *VirtualMachine, debugger: bun.CLI.Command.Debugger) void { + switch (debugger) { + .unspecified => {}, + .enable => { + this.debugger = Debugger{}; + }, + .path_or_port => { + this.debugger = Debugger{ + .path_or_port = debugger.path_or_port, + }; + }, + } + + if (debugger != .unspecified) { + this.bundler.options.minify_identifiers = false; + this.bundler.options.minify_syntax = false; + this.bundler.options.minify_whitespace = false; + this.bundler.options.debugger = true; + } + } + pub fn initWorker( worker: *WebWorker, opts: Options, @@ -1126,11 +1258,6 @@ pub const VirtualMachine = struct { return vm; } - // dynamic import - // pub fn import(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString { - - // } - pub threadlocal var source_code_printer: ?*js_printer.BufferPrinter = null; pub fn clearRefString(_: *anyopaque, ref_string: *JSC.RefString) void { @@ -1771,6 +1898,10 @@ pub const VirtualMachine = struct { var promise: *JSInternalPromise = undefined; + if (this.debugger != null) { + try Debugger.create(this, this.global); + } + if (!this.bundler.options.disable_transpilation) { { this.is_in_preload = true; -- cgit v1.2.3