summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2023-10-06 04:55:18 -0400
committerMalfurious <m@lfurio.us>2024-04-24 13:31:08 -0400
commit66db439988aa07828593aac109f5690bb48f2dc9 (patch)
tree8796e059228b9d67a677be93036305b9bd293b18
parent46f72be263cf29688f684e90f2e149e5c911016b (diff)
downloadmisplays-66db439988aa07828593aac109f5690bb48f2dc9.tar.gz
misplays-66db439988aa07828593aac109f5690bb48f2dc9.zip
Independent thread control refactor
Signed-off-by: Malfurious <m@lfurio.us>
-rw-r--r--debugger.c412
-rw-r--r--debugger.h6
-rw-r--r--misplays.c5
3 files changed, 203 insertions, 220 deletions
diff --git a/debugger.c b/debugger.c
index 6a1cb9d..a1ff940 100644
--- a/debugger.c
+++ b/debugger.c
@@ -19,10 +19,35 @@ static const int PTRACE_OPTIONS =
PTRACE_O_TRACESYSGOOD;
static const int PTRACE_CHILD_OPTIONS = PTRACE_OPTIONS | PTRACE_O_EXITKILL;
-static const int STOP_ALL_ON_EVENT = 1;
+//static const int STOP_ALL_ON_EVENT = 1;
static const useconds_t SCHEDULER_DELAY = 100000;
static const unsigned int BREAKPOINT_INSN = 0xcc;
+static struct process *new_process(pid_t id, int child) {
+ struct process *proc = xmalloc(sizeof(*proc));
+ proc->id = id;
+ proc->child = child;
+ list_init(&proc->breakpoints);
+ list_init(&proc->threads);
+ memset(proc->status, 0, sizeof(proc->status));
+ return proc;
+}
+
+static struct thread *new_thread(struct process *proc, pid_t id) {
+ struct thread *th = xmalloc(sizeof(*th));
+ th->proc = proc;
+ list_init(&th->states);
+ th->state = NULL;
+ th->clearstates = 0;
+ th->id = id;
+ th->stopped = 0;
+ th->signal = 0;
+ th->doing = 0;
+ th->donext = 0;
+ strcpy(th->status, "RUNNING");
+ return th;
+}
+
static struct thread *thread_by_id(struct process *proc, pid_t id) {
struct list *threads = &proc->threads;
for (struct thread *th = threads->head; th != threads->end; th = th->next) {
@@ -132,7 +157,7 @@ static void free_states(struct thread *th) {
static void capture_state(struct process *proc) {
struct list *threads = &proc->threads;
for (struct thread *th = threads->head; th != threads->end; th = th->next) {
- if (!th->shouldcont) {
+ if (!th->state) {
if (th->clearstates) {
free_states(th);
}
@@ -173,15 +198,17 @@ static void capture_state(struct process *proc) {
}
}
+static int wait_thread(struct thread *th);
static void interrupt_all_threads(struct process *proc) {
struct list *threads = &proc->threads;
for (struct thread *th = threads->head; th != threads->end; th = th->next) {
if (!th->stopped) {
ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL);
- while (!dbg_wait(th, 0)) {}
- }
- if (STOP_ALL_ON_EVENT) {
- th->shouldcont = 0;
+ while (!wait_thread(th)) {}
+ //if (STOP_ALL_ON_EVENT) {
+ // th->cont = 0;
+ // th->shouldcont = 0;
+ //}
}
}
}
@@ -193,53 +220,157 @@ static void continue_all_threads(struct process *proc) {
ptrace(PTRACE_CONT, th->id, NULL, th->signal);
th->stopped = 0;
th->signal = 0;
- th->cont = 0;
- th->shouldcont = 2;
+ th->donext = 0;
+ th->doing = PTRACE_CONT;
strcpy(th->status, "RUNNING");
th->clearstates = 1;
}
}
}
-/* 0: stop 1: singlestep 2: cont 3: syscall */
-/* this is simplified, surely incorrect, and needs updated once testable */
static void resume_threads(struct process *proc) {
struct list *threads = &proc->threads;
+
+ for (struct thread *th = threads->head; th != threads->end; th = th->next) {
+ if (th->doing == PTRACE_SINGLESTEP) {
+ ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal);
+ th->stopped = 0;
+ th->signal = 0;
+ strcpy(th->status, "RUNNING");
+ }
+ }
+
for (struct thread *th = threads->head; th != threads->end; th = th->next) {
- if (th->shouldcont) {
+ if (th->doing && th->doing != PTRACE_SINGLESTEP) {
install_breakpoints(th);
- ptrace(PTRACE_CONT, th->id, NULL, th->signal);
+ ptrace(th->doing, th->id, NULL, th->signal);
th->stopped = 0;
th->signal = 0;
- th->cont = 0;
strcpy(th->status, "RUNNING");
}
}
}
-static struct process *new_process(pid_t id, int child) {
- struct process *proc = xmalloc(sizeof(*proc));
- proc->id = id;
- proc->child = child;
- list_init(&proc->breakpoints);
- list_init(&proc->threads);
- memset(proc->status, 0, sizeof(proc->status));
- return proc;
-}
+static int wait_thread(struct thread *th) {
+ if (th->id <= 0) {
+ return -1;
+ } else if (th->stopped) {
+ return 1;
+ }
-static struct thread *new_thread(struct process *proc, pid_t id) {
- struct thread *th = xmalloc(sizeof(*th));
- th->proc = proc;
- list_init(&th->states);
- th->state = NULL;
- th->clearstates = 0;
- th->id = id;
- th->stopped = 0;
- th->signal = 0;
- th->cont = 0;
- th->shouldcont = 0;
- strcpy(th->status, "RUNNING");
- return th;
+ int status;
+ if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) {
+ return 0;
+ }
+
+ if (WIFEXITED(status)) {
+ th->id = 0;
+ th->stopped = 1;
+ th->signal = WEXITSTATUS(status);
+ th->doing = 0;
+ th->donext = 0;
+ strcpy(th->status, "EXITED");
+ return 1;
+ } else if (WIFSIGNALED(status)) {
+ th->id = 0;
+ th->stopped = 1;
+ th->signal = WTERMSIG(status);
+ th->doing = 0;
+ th->donext = 0;
+ strcpy(th->status, "TERMINATED");
+ return 1;
+ } else if (WIFSTOPPED(status)) {
+ struct thread *eventth;
+ unsigned long eventmsg;
+ ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg);
+
+ /* todo: process status messages */
+ switch (status >> 8) {
+ /* todo: other ptrace event stops */
+ case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
+ eventth = new_thread(th->proc, eventmsg);
+ list_insert(th->proc->threads.end, eventth);
+ while (!wait_thread(eventth)) {}
+
+ th->stopped = 1;
+ th->signal = 0;
+ th->doing = 0;
+ th->donext = 0;
+ strcpy(th->status, "CLONE");
+ th->state = NULL;
+ return 1;
+
+ case SIGTRAP | (PTRACE_EVENT_EXEC << 8):
+ eventth = thread_by_id(th->proc, eventmsg);
+ if (eventth->id != th->id) {
+ eventth->id = 0;
+ eventth->stopped = 1;
+ eventth->signal = 0;
+ eventth->doing = 0;
+ eventth->donext = 0;
+ strcpy(eventth->status, "EXITED");
+ }
+
+ th->stopped = 1;
+ th->signal = 0;
+ th->doing = 0;
+ th->donext = 0;
+ strcpy(th->status, "EXECVE");
+ th->state = NULL;
+ return 1;
+
+ case SIGTRAP | (PTRACE_EVENT_STOP << 8):
+ th->stopped = 1;
+ th->signal = 0;
+ strcpy(th->status, "STOPPED");
+ if (!th->doing) {
+ th->state = NULL;
+ }
+ return 1;
+
+ case SIGTRAP | 0x80:
+ th->stopped = 1;
+ th->signal = 0;
+ th->doing = 0;
+ th->donext = 0;
+ strcpy(th->status, "SYSCALL");
+ th->state = NULL;
+ return 1;
+
+ case SIGTRAP:
+ th->stopped = 1;
+ th->signal = 0;
+
+ int restart;
+ int bp = detect_breakpoint(th, &restart);
+ strcpy(th->status, (bp ? "BREAKPOINT" : "STEP"));
+
+ if (restart) {
+ th->donext = th->doing;
+ th->doing = (th->doing ? PTRACE_SINGLESTEP : 0);
+ } else {
+ th->doing = th->donext;
+ th->donext = 0;
+ }
+
+ if (!th->doing) {
+ th->state = NULL;
+ }
+
+ return 1;
+
+ default:
+ th->stopped = 1;
+ th->signal = WSTOPSIG(status);
+ th->doing = 0;
+ th->donext = 0;
+ strcpy(th->status, "SIGNAL DELIVERY");
+ th->state = NULL;
+ return 1;
+ }
+ }
+
+ return -1;
}
struct breakpoint *add_breakpoint(struct process *proc, unsigned long address) {
@@ -343,165 +474,26 @@ int dbg_free(struct thread *th) {
return -1;
}
-int dbg_wait(struct thread *th, int primary) {
- if (th->id <= 0) {
- return -1;
- } else if (th->stopped) {
- return 1;
- }
-
- int status;
- if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) {
- return 0;
- }
-
- if (WIFEXITED(status)) {
- th->id = 0;
- th->stopped = 1;
- th->signal = WEXITSTATUS(status);
- th->cont = 0;
- th->shouldcont = 0;
- strcpy(th->status, "EXITED");
- return 1;
- }
-
- if (WIFSIGNALED(status)) {
- th->id = 0;
- th->stopped = 1;
- th->signal = WTERMSIG(status);
- th->cont = 0;
- th->shouldcont = 0;
- strcpy(th->status, "TERMINATED");
- return 1;
- }
-
- struct thread *newth; // event thread
- unsigned long eventmsg;
- ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg);
-
- /* todo: process status messages */
- if (WIFSTOPPED(status)) {
- switch (status >> 8) {
- /* todo: other ptrace event stops */
- case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
- newth = new_thread(th->proc, eventmsg);
- list_insert(th->proc->threads.end, newth);
- while (!dbg_wait(newth, 0)) {}
-
- th->stopped = 1;
- th->signal = 0;
- th->cont = 0;
- th->shouldcont = 0;
- strcpy(th->status, "CLONE");
- th->state = NULL;
-
- if (primary) {
- interrupt_all_threads(th->proc);
- uninstall_breakpoints(th);
- capture_state(th->proc);
- resume_threads(th->proc);
- }
-
- return 1;
-
- case SIGTRAP | (PTRACE_EVENT_EXEC << 8):
- if ((pid_t)eventmsg != th->id) {
- newth = thread_by_id(th->proc, eventmsg);
- newth->id = 0;
- newth->stopped = 1;
- newth->signal = 0;
- newth->cont = 0;
- newth->shouldcont = 0;
- strcpy(newth->status, "EXITED");
- }
-
- th->stopped = 1;
- th->signal = 0;
- th->cont = 0;
- th->shouldcont = 0;
- strcpy(th->status, "EXECVE");
- th->state = NULL;
-
- if (primary) {
- interrupt_all_threads(th->proc);
- uninstall_breakpoints(th);
- capture_state(th->proc);
- resume_threads(th->proc);
- }
-
- return 1;
-
- case SIGTRAP | (PTRACE_EVENT_STOP << 8):
- th->stopped = 1;
- th->signal = 0;
- th->cont = 0;
- strcpy(th->status, "STOPPED");
-
- if (primary) {
- interrupt_all_threads(th->proc);
- uninstall_breakpoints(th);
- capture_state(th->proc);
- resume_threads(th->proc);
- }
-
- return th->stopped;
-
- case SIGTRAP | 0x80:
- th->stopped = 1;
- th->signal = 0;
- th->cont = 0;
- th->shouldcont = 0;
- strcpy(th->status, "SYSCALL");
- th->state = NULL;
-
- if (primary) {
- interrupt_all_threads(th->proc);
- uninstall_breakpoints(th);
- capture_state(th->proc);
- resume_threads(th->proc);
- }
-
- return 1;
+void dbg_sync(struct process *proc) {
+ struct thread *acted = NULL;
- case SIGTRAP:
- th->stopped = 1;
- th->signal = 0;
-
- int restart;
- int bp = detect_breakpoint(th, &restart);
- strcpy(th->status, (bp ? "BREAKPOINT" : "STEP"));
- //th->shouldcont = (b && b->enabled == 0);
- th->shouldcont = (restart ? 2 : 0);
-
- if (primary) {
- interrupt_all_threads(th->proc);
- uninstall_breakpoints(th);
- capture_state(th->proc);
- resume_threads(th->proc);
- }
-
- return th->stopped;
-
- default:
- th->stopped = 1;
- th->signal = WSTOPSIG(status);
- th->cont = 0;
- th->shouldcont = 0;
- strcpy(th->status, "SIGNAL DELIVERY");
- th->state = NULL;
-
- if (primary) {
- interrupt_all_threads(th->proc);
- uninstall_breakpoints(th);
- capture_state(th->proc);
- resume_threads(th->proc);
- }
-
- return 1;
+ struct list *threads = &proc->threads;
+ for (struct thread *th = threads->head; th != threads->end; th = th->next) {
+ if (th->id > 0) {
+ if (!th->stopped && wait_thread(th)) {
+ acted = th;
+ } else if (th->stopped && th->doing) {
+ acted = th;
+ }
}
}
- return -1;
+ if (acted) {
+ interrupt_all_threads(proc);
+ uninstall_breakpoints(acted);
+ capture_state(proc);
+ resume_threads(proc);
+ }
}
int dbg_intr(struct thread *th) {
@@ -509,8 +501,11 @@ int dbg_intr(struct thread *th) {
return -1;
}
+ /* todo: move this into dbg_sync? */
+ /* probably not */
ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL);
- th->shouldcont = 0;
+ th->doing = 0;
+ th->donext = 0;
return 0;
}
@@ -519,12 +514,8 @@ int dbg_cont(struct thread *th) {
return -1;
}
- ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal);
- th->stopped = 0;
- th->signal = 0;
- th->cont = PTRACE_CONT;
- th->shouldcont = 1;
- strcpy(th->status, "RUNNING");
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = PTRACE_CONT;
th->clearstates = 1;
return 0;
}
@@ -534,12 +525,8 @@ int dbg_syscall(struct thread *th) {
return -1;
}
- ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal);
- th->stopped = 0;
- th->signal = 0;
- th->cont = PTRACE_SYSCALL;
- th->shouldcont = 1;
- strcpy(th->status, "RUNNING");
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = PTRACE_SYSCALL;
th->clearstates = 1;
return 0;
}
@@ -558,12 +545,8 @@ int dbg_stepin(struct thread *th) {
return -1;
}
- ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal);
- th->stopped = 0;
- th->signal = 0;
- th->cont = PTRACE_SINGLESTEP;
- th->shouldcont = 1;
- strcpy(th->status, "RUNNING");
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = 0;
th->clearstates = 0;
return 0;
}
@@ -592,7 +575,6 @@ int dbg_stepover(struct thread *th) {
cs_disasm_iter(handle, &code, &size, &address, insn);
- int ret = -1;
if (insn->id == X86_INS_CALL) {
struct breakpoint *b = add_breakpoint(th->proc, address);
b->user = 0;
@@ -600,14 +582,18 @@ int dbg_stepover(struct thread *th) {
b->tid = th->id;
b->enabled = -1;
- ret = dbg_cont(th);
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = PTRACE_CONT;
+ th->clearstates = 0;
} else {
- ret = dbg_stepin(th);
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = 0;
+ th->clearstates = 0;
}
cs_free(insn, 1);
cs_close(&handle);
- return ret;
+ return 0;
}
int dbg_stepback(struct thread *th) {
diff --git a/debugger.h b/debugger.h
index ef08d43..cea3bba 100644
--- a/debugger.h
+++ b/debugger.h
@@ -52,8 +52,8 @@ struct thread {
pid_t id;
int stopped;
int signal;
- int cont;
- int shouldcont;
+ int doing;
+ int donext;
char status[128];
};
@@ -65,7 +65,7 @@ extern struct process *dbg_attach(pid_t pid, int child);
extern void dbg_detach(struct process *proc);
extern int dbg_free(struct thread *th);
-extern int dbg_wait(struct thread *th, int primary);
+extern void dbg_sync(struct process *proc);
extern int dbg_intr(struct thread *th);
extern int dbg_cont(struct thread *th);
diff --git a/misplays.c b/misplays.c
index 815e045..c064f75 100644
--- a/misplays.c
+++ b/misplays.c
@@ -149,10 +149,7 @@ static void info_update(struct thread *th, PANEL *pan) {
static void wait_all_threads(struct list *processes) {
for (struct process *proc = processes->head; proc != processes->end; proc = proc->next) {
- struct list *threads = &proc->threads;
- for (struct thread *th = threads->head; th != threads->end; th = th->next) {
- dbg_wait(th, 0);
- }
+ dbg_sync(proc);
}
}