In debug, try to capture a module evaluation stack

This requires setting SetCaptureStackTraceForUncaughtExceptions on the isolate,
so for now, I've only enabled it in debug.
This commit is contained in:
Karl Seguin
2026-05-27 19:10:25 +08:00
parent 4735126507
commit 158dffd543
4 changed files with 46 additions and 21 deletions

View File

@@ -392,13 +392,27 @@ fn evaluateModule(self: *Context, comptime want_result: bool, mod: js.Module, ur
// Some module-loading errors aren't handled by TryCatch. We need to
// get the error from the module itself.
const exception = mod.getException();
const message = blk: {
const e = mod.getException().toString() catch break :blk "???";
const e = exception.toString() catch break :blk "???";
break :blk e.toSlice() catch "???";
};
const stack = blk: {
if (comptime IS_DEBUG == false) {
// SetCaptureStackTraceForUncaughtExceptions is only set in Debug
break :blk "";
}
const stack_handle = v8.v8__Exception__GetStackTrace(exception.handle) orelse break :blk "";
var buf = std.Io.Writer.Allocating.init(self.call_arena);
js.writeStackTrace(self.isolate.handle, stack_handle, &buf.writer) catch break :blk "???";
break :blk buf.written();
};
log.warn(.js, "evaluate module", .{
.message = message,
.stack = stack,
.specifier = url,
.message = message,
});
return error.EvaluationError;
};

View File

@@ -135,6 +135,10 @@ pub fn init(app: *App, opts: InitOpts) !Env {
v8.v8__Isolate__SetFatalErrorHandler(isolate_handle, fatalCallback);
v8.v8__Isolate__SetOOMErrorHandler(isolate_handle, oomCallback);
if (comptime IS_DEBUG) {
v8.v8__Isolate__SetCaptureStackTraceForUncaughtExceptions(isolate_handle, true, 64);
}
isolate.enter();
errdefer isolate.exit();

View File

@@ -1342,30 +1342,19 @@ fn finalizerPtrGetter(comptime T: type, comptime FT: type) *const fn (*T) *FT {
}
pub fn stackTrace(self: *const Local) !?[]const u8 {
const isolate = self.isolate;
const isolate = self.isolate.handle;
const stack_handle = v8.v8__StackTrace__CurrentStackTrace__STATIC(isolate, 30) orelse return null;
const separator = log.separator();
var buf: std.ArrayList(u8) = .empty;
var writer = buf.writer(self.call_arena);
const stack_trace_handle = v8.v8__StackTrace__CurrentStackTrace__STATIC(isolate.handle, 30).?;
const frame_count = v8.v8__StackTrace__GetFrameCount(stack_trace_handle);
if (v8.v8__StackTrace__CurrentScriptNameOrSourceURL__STATIC(isolate.handle)) |script| {
var buf = std.Io.Writer.Allocating.init(self.call_arena);
if (v8.v8__StackTrace__CurrentScriptNameOrSourceURL__STATIC(isolate)) |script| {
const stack = js.String{ .local = self, .handle = script };
try writer.print("{s}<{f}>", .{ separator, stack });
try buf.writer.print("{s}<{f}>", .{ separator, stack });
}
for (0..@intCast(frame_count)) |i| {
const frame_handle = v8.v8__StackTrace__GetFrame(stack_trace_handle, isolate.handle, @intCast(i)).?;
if (v8.v8__StackFrame__GetFunctionName(frame_handle)) |name| {
const script = js.String{ .local = self, .handle = name };
try writer.print("{s}{f}:{d}", .{ separator, script, v8.v8__StackFrame__GetLineNumber(frame_handle) });
} else {
try writer.print("{s}<anonymous>:{d}", .{ separator, v8.v8__StackFrame__GetLineNumber(frame_handle) });
}
}
return buf.items;
try js.writeStackTrace(isolate, stack_handle, &buf.writer);
return buf.written();
}
// == Promise Helpers ==

View File

@@ -17,6 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const lp = @import("lightpanda");
pub const v8 = @import("v8").c;
const string = @import("../../string.zig");
@@ -417,3 +419,19 @@ pub const FinalizerCallback = struct {
page.releaseArena(self.arena);
}
};
pub fn writeStackTrace(isolate: *v8.Isolate, stack_handle: *const v8.StackTrace, writer: *std.Io.Writer) !void {
const separator = lp.log.separator();
const frame_count = v8.v8__StackTrace__GetFrameCount(stack_handle);
for (0..@intCast(frame_count)) |i| {
const frame_handle = v8.v8__StackTrace__GetFrame(stack_handle, isolate, @intCast(i)).?;
if (v8.v8__StackFrame__GetFunctionName(frame_handle)) |name| {
var buf: [1024]u8 = undefined;
const n = v8.v8__String__WriteUtf8(name, isolate, &buf, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
try writer.print("{s}{s}:{d}", .{ separator, buf[0..n], v8.v8__StackFrame__GetLineNumber(frame_handle) });
} else {
try writer.print("{s}<anonymous>:{d}", .{ separator, v8.v8__StackFrame__GetLineNumber(frame_handle) });
}
}
}