mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-04-20 16:38:38 +03:00
crash: try to attach dimensions to the crash report
This commit is contained in:
@ -14,6 +14,14 @@ pub fn captureEvent(value: Value) ?UUID {
|
|||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setContext(key: []const u8, value: Value) void {
|
||||||
|
c.sentry_set_context_n(key.ptr, key.len, value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeContext(key: []const u8) void {
|
||||||
|
c.sentry_remove_context_n(key.ptr, key.len);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setTag(key: []const u8, value: []const u8) void {
|
pub fn setTag(key: []const u8, value: []const u8) void {
|
||||||
c.sentry_set_tag_n(key.ptr, key.len, value.ptr, value.len);
|
c.sentry_set_tag_n(key.ptr, key.len, value.ptr, value.len);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,22 @@ pub const Value = struct {
|
|||||||
) };
|
) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn initObject() Value {
|
||||||
|
return .{ .value = c.sentry_value_new_object() };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initString(value: []const u8) Value {
|
||||||
|
return .{ .value = c.sentry_value_new_string_n(value.ptr, value.len) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initBool(value: bool) Value {
|
||||||
|
return .{ .value = c.sentry_value_new_bool(@intFromBool(value)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initInt32(value: i32) Value {
|
||||||
|
return .{ .value = c.sentry_value_new_int32(value) };
|
||||||
|
}
|
||||||
|
|
||||||
pub fn decref(self: Value) void {
|
pub fn decref(self: Value) void {
|
||||||
c.sentry_value_decref(self.value);
|
c.sentry_value_decref(self.value);
|
||||||
}
|
}
|
||||||
@ -35,4 +51,14 @@ pub const Value = struct {
|
|||||||
pub fn isNull(self: Value) bool {
|
pub fn isNull(self: Value) bool {
|
||||||
return c.sentry_value_is_null(self.value) != 0;
|
return c.sentry_value_is_null(self.value) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// sentry_value_set_by_key_n
|
||||||
|
pub fn setByKey(self: Value, key: []const u8, value: Value) void {
|
||||||
|
_ = c.sentry_value_set_by_key_n(
|
||||||
|
self.value,
|
||||||
|
key.ptr,
|
||||||
|
key.len,
|
||||||
|
value.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,7 @@ const sentry = @import("sentry");
|
|||||||
const internal_os = @import("../os/main.zig");
|
const internal_os = @import("../os/main.zig");
|
||||||
const crash = @import("main.zig");
|
const crash = @import("main.zig");
|
||||||
const state = &@import("../global.zig").state;
|
const state = &@import("../global.zig").state;
|
||||||
|
const Surface = @import("../Surface.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.sentry);
|
const log = std.log.scoped(.sentry);
|
||||||
|
|
||||||
@ -14,6 +15,22 @@ const log = std.log.scoped(.sentry);
|
|||||||
/// handling is a global process-wide thing.
|
/// handling is a global process-wide thing.
|
||||||
var init_thread: ?std.Thread = null;
|
var init_thread: ?std.Thread = null;
|
||||||
|
|
||||||
|
/// Thread-local state that can be set by thread main functions so that
|
||||||
|
/// crashes have more context.
|
||||||
|
///
|
||||||
|
/// This is a hack over Sentry native SDK limitations. The native SDK has
|
||||||
|
/// one global scope for all threads and no support for thread-local scopes.
|
||||||
|
/// This means that if we want to set thread-specific data we have to do it
|
||||||
|
/// on our own in the on crash callback.
|
||||||
|
pub const ThreadState = struct {
|
||||||
|
/// The surface that this thread is attached to.
|
||||||
|
surface: *Surface,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// See ThreadState. This should only ever be set by the owner of the
|
||||||
|
/// thread entry function.
|
||||||
|
threadlocal var thread_state: ?ThreadState = null;
|
||||||
|
|
||||||
/// Process-wide initialization of our Sentry client.
|
/// Process-wide initialization of our Sentry client.
|
||||||
///
|
///
|
||||||
/// This should only be called from one thread, and deinit should be called
|
/// This should only be called from one thread, and deinit should be called
|
||||||
@ -70,6 +87,10 @@ fn initThread(gpa: Allocator) !void {
|
|||||||
);
|
);
|
||||||
sentry.c.sentry_options_set_transport(opts, @ptrCast(transport));
|
sentry.c.sentry_options_set_transport(opts, @ptrCast(transport));
|
||||||
|
|
||||||
|
// Set our crash callback. See beforeSend for more details on what we
|
||||||
|
// do here and why we use this.
|
||||||
|
sentry.c.sentry_options_set_before_send(opts, beforeSend, null);
|
||||||
|
|
||||||
// Determine the Sentry cache directory.
|
// Determine the Sentry cache directory.
|
||||||
const cache_dir = try internal_os.xdg.cache(alloc, .{ .subdir = "ghostty/sentry" });
|
const cache_dir = try internal_os.xdg.cache(alloc, .{ .subdir = "ghostty/sentry" });
|
||||||
sentry.c.sentry_options_set_database_path_n(
|
sentry.c.sentry_options_set_database_path_n(
|
||||||
@ -109,6 +130,63 @@ pub fn deinit() void {
|
|||||||
_ = sentry.c.sentry_close();
|
_ = sentry.c.sentry_close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn beforeSend(
|
||||||
|
event_val: sentry.c.sentry_value_t,
|
||||||
|
_: ?*anyopaque,
|
||||||
|
_: ?*anyopaque,
|
||||||
|
) callconv(.C) sentry.c.sentry_value_t {
|
||||||
|
// The native SDK at the time of writing doesn't support thread-local
|
||||||
|
// scopes. The full SDK has one global scope. So we use the beforeSend
|
||||||
|
// handler to set thread-specific data such as window size, grid size,
|
||||||
|
// etc. that we can use to debug crashes.
|
||||||
|
|
||||||
|
const thr_state = thread_state orelse {
|
||||||
|
// If we don't have thread state we note this in the context
|
||||||
|
// so we can see that but don't do anything else.
|
||||||
|
const obj = sentry.Value.initObject();
|
||||||
|
errdefer obj.decref();
|
||||||
|
obj.setByKey("unknown", sentry.Value.initBool(true));
|
||||||
|
sentry.setContext("surface", obj);
|
||||||
|
return event_val;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read the surface data. This is likely unsafe because on a crash
|
||||||
|
// other threads can continue running. We don't have race-safe way to
|
||||||
|
// access this data so this might be corrupted but it's most likely fine.
|
||||||
|
{
|
||||||
|
const obj = sentry.Value.initObject();
|
||||||
|
errdefer obj.decref();
|
||||||
|
const surface = thr_state.surface;
|
||||||
|
obj.setByKey(
|
||||||
|
"screen-width",
|
||||||
|
sentry.Value.initInt32(std.math.cast(i32, surface.screen_size.width) orelse -1),
|
||||||
|
);
|
||||||
|
obj.setByKey(
|
||||||
|
"screen-height",
|
||||||
|
sentry.Value.initInt32(std.math.cast(i32, surface.screen_size.height) orelse -1),
|
||||||
|
);
|
||||||
|
obj.setByKey(
|
||||||
|
"grid-columns",
|
||||||
|
sentry.Value.initInt32(std.math.cast(i32, surface.grid_size.columns) orelse -1),
|
||||||
|
);
|
||||||
|
obj.setByKey(
|
||||||
|
"grid-rows",
|
||||||
|
sentry.Value.initInt32(std.math.cast(i32, surface.grid_size.rows) orelse -1),
|
||||||
|
);
|
||||||
|
obj.setByKey(
|
||||||
|
"cell-width",
|
||||||
|
sentry.Value.initInt32(std.math.cast(i32, surface.cell_size.width) orelse -1),
|
||||||
|
);
|
||||||
|
obj.setByKey(
|
||||||
|
"cell-height",
|
||||||
|
sentry.Value.initInt32(std.math.cast(i32, surface.cell_size.height) orelse -1),
|
||||||
|
);
|
||||||
|
sentry.setContext("dimensions", obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return event_val;
|
||||||
|
}
|
||||||
|
|
||||||
pub const Transport = struct {
|
pub const Transport = struct {
|
||||||
pub fn send(envelope: *sentry.Envelope, ud: ?*anyopaque) callconv(.C) void {
|
pub fn send(envelope: *sentry.Envelope, ud: ?*anyopaque) callconv(.C) void {
|
||||||
_ = ud;
|
_ = ud;
|
||||||
|
Reference in New Issue
Block a user