commit e2f339d3eade735eba75b825ca40478aa7d2c579
parent dfc469bfd5b8989da32e1941ac3f7bfe6af84310
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 21 May 2026 10:32:39 -0700
Implement JIT session resilience via setjmp/longjmp
Diffstat:
5 files changed, 68 insertions(+), 8 deletions(-)
diff --git a/driver/dbg.c b/driver/dbg.c
@@ -769,7 +769,10 @@ static int dbg_drive(DbgState *s, DbgRunMode mode) {
if (mode == RUN_FRESH && s->has_stop) {
/* The previous session is dead (entry returned or signal landed).
- * Start a new one. */
+ * Start a new one. Try to abort it first in case it's parked in a fault. */
+ if (s->session) {
+ cfree_jit_session_resume(s->session, CFREE_RESUME_ABORT, NULL);
+ }
s->has_stop = 0;
} else if (mode != RUN_FRESH && !s->has_stop) {
driver_errf(DBG_TOOL, "no program is running; use 'r' to start");
@@ -2692,6 +2695,16 @@ static int dbg_dispatch(DbgState *s, char *line) {
dbg_drive(s, RUN_FRESH);
return 0;
}
+ if (driver_streq(cmd, "abort")) {
+ if (s->has_stop && s->session) {
+ cfree_jit_session_resume(s->session, CFREE_RESUME_ABORT, NULL);
+ s->has_stop = 0;
+ driver_errf(DBG_TOOL, "execution aborted");
+ } else {
+ driver_errf(DBG_TOOL, "nothing to abort");
+ }
+ return 0;
+ }
if (driver_streq(cmd, "c") || driver_streq(cmd, "cont") ||
driver_streq(cmd, "continue")) {
dbg_drive(s, RUN_CONTINUE);
diff --git a/driver/env.c b/driver/env.c
@@ -841,6 +841,22 @@ static CfreeStatus dbg_guarded_copy(void *user, void *dst, const void *src,
return CFREE_OK;
}
+static __thread sigjmp_buf g_dbg_abort_buf;
+
+static int dbg_call_with_catch(void *user, void (*fn)(void *), void *arg) {
+ (void)user;
+ if (sigsetjmp(g_dbg_abort_buf, 1) == 0) {
+ fn(arg);
+ return 0;
+ }
+ return 1;
+}
+
+static void dbg_thread_abort(void *user) {
+ (void)user;
+ siglongjmp(g_dbg_abort_buf, 1);
+}
+
static CfreeDbgOs g_dbg_os_posix;
/* ---------------- jit_tls (pthread-key backed) ---------------- */
@@ -1191,6 +1207,8 @@ void driver_env_init(DriverEnv *e) {
g_dbg_os_posix.code_write_end = dbg_code_write_end;
g_dbg_os_posix.flush_icache = dbg_flush_icache;
g_dbg_os_posix.guarded_copy = dbg_guarded_copy;
+ g_dbg_os_posix.call_with_catch = dbg_call_with_catch;
+ g_dbg_os_posix.thread_abort = dbg_thread_abort;
g_dbg_os_posix.user = NULL;
e->dbg_os = &g_dbg_os_posix;
diff --git a/include/cfree/dbg.h b/include/cfree/dbg.h
@@ -40,6 +40,10 @@ typedef struct CfreeDbgOs {
CfreeStatus (*guarded_copy)(void *user, void *dst, const void *src,
size_t n);
+
+ int (*call_with_catch)(void *user, void (*fn)(void *), void *arg);
+ void (*thread_abort)(void *user);
+
void *user;
} CfreeDbgOs;
@@ -68,6 +72,7 @@ typedef enum CfreeResumeMode {
CFREE_RESUME_STEP_LINE,
CFREE_RESUME_NEXT_LINE,
CFREE_RESUME_STEP_OUT,
+ CFREE_RESUME_ABORT,
} CfreeResumeMode;
typedef enum CfreeEntryKind {
diff --git a/src/dbg/session.c b/src/dbg/session.c
@@ -140,6 +140,13 @@ park:
s->os->event_signal(s->os->user, s->ev_stop);
s->os->event_wait(s->os->user, s->ev_resume);
s->os->event_reset(s->os->user, s->ev_resume);
+
+ if (s->pending_mode == CFREE_RESUME_ABORT) {
+ if (s->os->thread_abort) {
+ s->os->thread_abort(s->os->user);
+ }
+ }
+
/* Apply pending PC override (set by step_resume) before returning. */
if (s->pending_has_pc) {
regs->pc = s->pending_pc_override;
@@ -154,14 +161,9 @@ park:
/* ---- worker thread -------------------------------------------------- */
-static void worker_main(void* arg) {
+static void worker_run_entry(void* arg) {
CfreeJitSession* s = (CfreeJitSession*)arg;
- for (;;) {
- s->os->event_wait(s->os->user, s->ev_resume);
- s->os->event_reset(s->os->user, s->ev_resume);
- if (s->worker_should_exit) return;
- if (s->state == DBG_STATE_RUNNING && s->entry) {
- typedef int (*EntryIntArgv)(int, char**);
+ typedef int (*EntryIntArgv)(int, char**);
typedef uint64_t (*EntryU64_0)(void);
typedef uint64_t (*EntryU64_1)(uint64_t);
typedef uint64_t (*EntryU64_2)(uint64_t, uint64_t);
@@ -239,6 +241,26 @@ static void worker_main(void* arg) {
memset(&s->stop, 0, sizeof(s->stop));
s->stop.kind = CFREE_STOP_EXIT;
s->stop.exit_code = ret;
+}
+
+static void worker_main(void* arg) {
+ CfreeJitSession* s = (CfreeJitSession*)arg;
+ for (;;) {
+ s->os->event_wait(s->os->user, s->ev_resume);
+ s->os->event_reset(s->os->user, s->ev_resume);
+ if (s->worker_should_exit) return;
+ if (s->state == DBG_STATE_RUNNING && s->entry) {
+ int aborted = 0;
+ if (s->os->call_with_catch) {
+ aborted = s->os->call_with_catch(s->os->user, worker_run_entry, s);
+ } else {
+ worker_run_entry(s);
+ }
+ if (aborted) {
+ memset(&s->stop, 0, sizeof(s->stop));
+ s->stop.kind = CFREE_STOP_EXIT;
+ s->stop.exit_code = -1;
+ }
s->state = DBG_STATE_EXITED;
s->entry = NULL;
s->os->event_signal(s->os->user, s->ev_stop);
diff --git a/src/dbg/step.c b/src/dbg/step.c
@@ -191,6 +191,8 @@ static CfreeStatus run_next_line(CfreeJitSession* s) {
CfreeStatus dbg_step_resume(CfreeJitSession* s, CfreeResumeMode mode) {
switch (mode) {
+ case CFREE_RESUME_ABORT:
+ return CFREE_OK;
case CFREE_RESUME_CONTINUE:
if (dbg_bp_lookup_index(s, s->stop.regs.pc) != 0) {
return prepare_step_insn(s);