summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--debugger.c795
-rw-r--r--debugger.h64
-rw-r--r--helpers.c22
-rw-r--r--helpers.h5
-rw-r--r--misplays.c392
5 files changed, 905 insertions, 373 deletions
diff --git a/debugger.c b/debugger.c
index 19d9edc..bacd09c 100644
--- a/debugger.c
+++ b/debugger.c
@@ -1,282 +1,705 @@
+#include <dirent.h>
+#include <elf.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <capstone/capstone.h>
-/* #include "config.h" */
#include "debugger.h"
#include "helpers.h"
-static const int PTRACE_OPTIONS = PTRACE_O_TRACESYSGOOD;
-
-static void capture_state(struct tracee *dbg) {
- struct state *s = xmalloc(sizeof(*s));
- ptrace(PTRACE_GETREGS, dbg->id, NULL, &s->regs);
- ptrace(PTRACE_GETFPREGS, dbg->id, NULL, &s->fpregs);
- list_init(&s->maps);
- list_insert(dbg->states.end, s);
- dbg->state = s;
-
- char mapspath[128], entry[512];
- snprintf(mapspath, sizeof(mapspath), "/proc/%li/maps", (long)dbg->id);
-
- FILE *maps = fopen(mapspath, "r");
- if (maps) {
- while (fgets(entry, sizeof(entry), maps)) {
- struct map *m = xmalloc(sizeof(*m));
- sscanf(entry, "%lx-%lx ", &m->start, &m->end);
- size_t size = m->end - m->start;
- m->data = xmalloc(size);
-
- struct iovec loc = { m->data, size };
- struct iovec rem = { (void *)m->start, size };
- if (process_vm_readv(dbg->id, &loc, 1, &rem, 1, 0) < 0) {
- free(m->data);
- free(m);
- continue;
- }
+static const int PTRACE_OPTIONS =
+ PTRACE_O_TRACECLONE |
+ PTRACE_O_TRACEFORK |
+ PTRACE_O_TRACEEXEC |
+ PTRACE_O_TRACEEXIT |
+ PTRACE_O_TRACESYSGOOD;
+
+static const int PTRACE_CHILD_OPTIONS = PTRACE_OPTIONS | PTRACE_O_EXITKILL;
+//static const int STOP_ALL_ON_EVENT = 1;
+static const useconds_t SCHEDULER_DELAY = 10000;
+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) {
+ if (th->id == id) {
+ return th;
+ }
+ }
+ return NULL;
+}
+
+static void free_breakpoints(struct process *proc) {
+ while (proc->breakpoints.head != proc->breakpoints.end) {
+ struct breakpoint *b = proc->breakpoints.head;
+ list_remove(b);
+ free(b);
+ }
+}
+
+static void install_breakpoints(struct thread *th) {
+ struct list *breaks = &th->proc->breakpoints;
+ for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
+ if (!b->installed) {
+ unsigned long word;
+ word = ptrace(PTRACE_PEEKTEXT, th->id, b->address, NULL);
+ b->text = word;
+
+ word = (word & ~0xff) | BREAKPOINT_INSN;
+ ptrace(PTRACE_POKETEXT, th->id, b->address, word);
+ b->installed = 1;
+ }
+ }
+}
- list_insert(s->maps.end, m);
+static void uninstall_breakpoints(struct thread *th) {
+ struct list *breaks = &th->proc->breakpoints;
+ for (struct breakpoint *b = breaks->tail; b != breaks->end; b = b->prev) {
+ if (b->installed) {
+ ptrace(PTRACE_POKETEXT, th->id, b->address, b->text);
+ b->installed = 0;
+
+ if (b->enabled < 0) {
+ struct thread *t;
+ if (b->tid == 0 ||
+ ((t = thread_by_id(th->proc, b->tid)) && !t->doing)) {
+ struct breakpoint *del = b;
+ b = b->next;
+ list_remove(del);
+ free(del);
+ }
+ }
}
+ }
+}
+
+static int detect_breakpoint(struct thread *th, int *restart) {
+ int ret = 0;
+ *restart = 0;
- fclose(maps);
+ struct user_regs_struct regs;
+ struct iovec ivregs = { &regs, sizeof(regs) };
+ ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, &ivregs);
+
+ struct breakpoint *b = get_breakpoint(th->proc, regs.rip - 1);
+ if (b && b->installed && th->doing != PTRACE_SINGLESTEP) {
+ regs.rip--;
+ ptrace(PTRACE_SETREGSET, th->id, NT_PRSTATUS, &ivregs);
+ b->hits++; /* todo: consider whether this is firing too much */
+ ret = b->user;
+
+ if (b->stack != 0 && b->stack != regs.rsp) {
+ *restart = 1;
+ } else if (b->tid != 0 && b->tid != th->id) {
+ *restart = 1;
+ } else if (!b->enabled) {
+ *restart = 1;
+ }
}
+
+ return ret;
}
-static void clear_states(struct tracee *dbg) {
- while (dbg->states.head != dbg->states.end) {
- struct state *s = dbg->states.head;
+static void free_states(struct thread *th) {
+ while (th->states.head != th->states.end) {
+ struct state *s = th->states.head;
+
while (s->maps.head != s->maps.end) {
struct map *m = s->maps.head;
list_remove(m);
free(m->data);
free(m);
}
+
list_remove(s);
free(s);
}
- dbg->state = NULL;
+
+ th->state = NULL;
+ th->clearstates = 0;
}
-static void install_breakpoints(struct tracee *dbg) {
- struct list *breaks = &dbg->breaks;
- for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
- if (b->enabled) {
- unsigned long data;
- data = ptrace(PTRACE_PEEKTEXT, dbg->id, b->address, NULL);
- b->orig = data;
-
- data = (data & ~0xff) | BREAKPOINT_INSN;
- ptrace(PTRACE_POKETEXT, dbg->id, b->address, data);
- b->active = 1;
+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->state) {
+ if (th->clearstates) {
+ free_states(th);
+ }
+
+ struct state *s = xmalloc(sizeof(*s));
+ struct iovec regs = { &s->regs, sizeof(s->regs) };
+ ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, &regs);
+ list_init(&s->maps);
+
+ list_insert(th->states.end, s);
+ th->state = s;
+
+ char mapspath[32], entry[512];
+ snprintf(mapspath, sizeof(mapspath), "/proc/%li/maps", (long)th->id);
+
+ FILE *maps = fopen(mapspath, "r");
+ if (maps) {
+ while (fgets(entry, sizeof(entry), maps)) {
+ struct map *m = xmalloc(sizeof(*m));
+ sscanf(entry, "%lx-%lx ", &m->start, &m->end);
+ size_t size = m->end - m->start;
+ m->data = xmalloc(size);
+
+ struct iovec loc = { m->data, size };
+ struct iovec rem = { (void *)m->start, size };
+ if (process_vm_readv(th->id, &loc, 1, &rem, 1, 0) < 0) {
+ free(m->data);
+ free(m);
+ continue;
+ }
+
+ list_insert(s->maps.end, m);
+ }
+
+ fclose(maps);
+ }
}
}
}
-static int uninstall_breakpoints(struct tracee *dbg, int stop) {
- struct user_regs_struct regs;
- int ch = 0;
- int reenter = 0;
- if (stop) {
- ptrace(PTRACE_GETREGS, dbg->id, NULL, &regs);
+static char thread_state(struct thread *th) {
+ char statpath[32], stat[512];
+ snprintf(statpath, sizeof(statpath), "/proc/%li/stat", (long)th->id);
+
+ FILE *f = fopen(statpath, "r");
+ if (f) {
+ fread(stat, 1, sizeof(stat), f);
+ fclose(f);
+
+ char *c = strchr(stat, ' ');
+ c = strchr(c+1, ' ') + 1;
+ return *c;
}
- struct list *breaks = &dbg->breaks;
- for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
- if (b->active) {
- ptrace(PTRACE_POKETEXT, dbg->id, b->address, b->orig);
- b->active = 0;
-
- /* detect stop at breakpoint */
- if (stop && !ch) {
- if (regs.rip - 1 == b->address) {
- regs.rip--;
- ptrace(PTRACE_SETREGS, dbg->id, NULL, &regs);
- ch = 1;
-
- if (b->stack != 0 && b->stack != regs.rsp) {
- reenter = 1;
- }
+ return 0;
+}
+
+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);
+
+ char state;
+ do {
+ state = thread_state(th);
+ } while (state != 't' && state != 'D' && state != 'Z');
+
+ wait_thread(th);
+
+ //if (STOP_ALL_ON_EVENT) {
+ // th->cont = 0;
+ // th->shouldcont = 0;
+ //}
+ }
+ }
+}
+
+static void continue_all_threads(struct process *proc) {
+ struct list *threads = &proc->threads;
+ for (struct thread *th = threads->head; th != threads->end; th = th->next) {
+ if (th->id > 0) {
+ char state;
+ do {
+ wait_thread(th);
+
+ ptrace(PTRACE_CONT, th->id, NULL, th->signal);
+ th->stopped = 0;
+ th->signal = 0;
+ th->donext = 0;
+ th->doing = PTRACE_CONT;
+ strcpy(th->status, "RUNNING");
+ th->clearstates = 1;
+
+ state = thread_state(th);
+ } while (state == 't');
+ }
+ }
+}
+
+static void resume_threads(struct process *proc) {
+ struct list *threads = &proc->threads;
+ int once = 0;
+
+ for (struct thread *th = threads->head; th != threads->end; th = th->next) {
+ if (th->stopped && 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->stopped && th->doing && th->doing != PTRACE_SINGLESTEP) {
+ if (!once) {
+ usleep(SCHEDULER_DELAY);
+ once = 1;
+ }
+ install_breakpoints(th);
+ ptrace(th->doing, th->id, NULL, th->signal);
+ th->stopped = 0;
+ th->signal = 0;
+ strcpy(th->status, "RUNNING");
+ }
+ }
+}
+
+static int wait_thread(struct thread *th) {
+ if (th->id <= 0) {
+ 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->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 process *eventproc;
+ 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_FORK << 8):
+ eventproc = new_process(eventmsg, th->proc->child);
+ eventth = new_thread(eventproc, eventmsg);
+ list_insert(eventproc->threads.end, eventth);
+ list_insert(th->proc->next, eventproc);
+ //while (!wait_thread(eventth)) {}
+ dbg_sync(eventproc);
+
+ th->stopped = 1;
+ th->signal = 0;
+ th->doing = 0;
+ th->donext = 0;
+ strcpy(th->status, "FORK");
+ 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_EXIT << 8):
+ th->stopped = 1;
+ th->signal = 0;
+ th->doing = PTRACE_CONT;
+ th->donext = 0;
+ strcpy(th->status, "EXITING");
+ 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) {
+ struct breakpoint *b = xmalloc(sizeof(*b));
+ b->address = address;
+ b->text = 0;
+ b->installed = 0;
+ b->hits = 0;
+ b->user = 1;
+ b->stack = 0;
+ b->tid = 0;
+ b->enabled = 1;
+ list_insert(proc->breakpoints.end, b);
+ return b;
+}
+
+struct breakpoint *get_breakpoint(struct process *proc, unsigned long address) {
+ struct list *breaks = &proc->breakpoints;
+ for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
+ if (b->address == address) {
+ return b;
+ }
+ }
+ return NULL;
+}
+
+struct process *dbg_attach(pid_t pid, int child) {
+ const int OPTIONS = (child ? PTRACE_CHILD_OPTIONS : PTRACE_OPTIONS);
+
+ char taskpath[32];
+ snprintf(taskpath, sizeof(taskpath), "/proc/%li/task", (long)pid);
+
+ DIR *taskdir = opendir(taskpath);
+ if (!taskdir) {
+ return NULL;
+ }
+
+ struct dirent *task;
+ struct process *proc = new_process(pid, child);
+
+ while ((task = readdir(taskdir))) {
+ pid_t id = strict_strtoul(task->d_name, 0);
+
+ if (id != 0) {
+ if (ptrace(PTRACE_SEIZE, id, NULL, OPTIONS) < 0) {
+ closedir(taskdir);
+ dbg_detach(proc);
+ return NULL;
}
+ struct thread *th = new_thread(proc, id);
+ list_insert(proc->threads.end, th);
+ }
+ }
+
+ closedir(taskdir);
+
+ interrupt_all_threads(proc);
+ capture_state(proc);
+ return proc;
+}
+
+void dbg_detach(struct process *proc) {
+ /* ptrace requires that threads be stopped before calling PTRACE_DETACH.
+ * In some cases, a blocked thread can possibly have a pending trap which
+ * would crash the process if encountered after ptracing. We cycle an extra
+ * continue/interrupt to consume this trap event ourselves before
+ * detaching. */
+ interrupt_all_threads(proc);
+ continue_all_threads(proc);
+ interrupt_all_threads(proc);
+
+ /* Supplement to PTRACE_O_EXITKILL */
+ if (proc->child) {
+ kill(proc->id, SIGKILL);
+ }
+
+ while (proc->threads.head != proc->threads.end) {
+ struct thread *th = proc->threads.head;
+ if (th->id > 0) {
+ uninstall_breakpoints(th);
+ ptrace(PTRACE_DETACH, th->id, NULL, th->signal);
+ th->id = 0;
+ }
+ dbg_free(th);
+ }
+
+ free_breakpoints(proc);
+ list_remove(proc);
+ free(proc);
+}
+
+int dbg_free(struct thread *th) {
+ if (th->id <= 0) {
+ free_states(th);
+ list_remove(th);
+ free(th);
+ return 0;
+ }
+ return -1;
+}
+
+void dbg_sync(struct process *proc) {
+ struct thread *acted = NULL;
- if (b->enabled < 0 && !reenter) {
- struct breakpoint *del = b;
- b = b->prev;
- list_remove(del);
- free(del);
+ 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 reenter;
+ if (acted) {
+ interrupt_all_threads(proc);
+ uninstall_breakpoints(acted);
+ capture_state(proc);
+ resume_threads(proc);
+ }
}
-int dbg_process(struct tracee *dbg, pid_t pid) {
- if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
+int dbg_intr(struct thread *th) {
+ if (th->id <= 0 || th->stopped) {
return -1;
}
- list_init(&dbg->breaks);
- list_init(&dbg->states);
- dbg->state = NULL;
- dbg->id = pid;
- dbg->child = 0;
- dbg->stopped = 0;
- dbg->status = 0;
- dbg->signal = 0;
- dbg->cont = 0;
- dbg->buff = NULL;
- dbg->buffsize = 0;
-
- while (!dbg_wait(dbg)) {}
- ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_OPTIONS);
+ /* todo: move this into dbg_sync? */
+ /* probably not */
+ ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL);
+ th->doing = 0;
+ th->donext = 0;
return 0;
}
-int dbg_new_process(struct tracee *dbg, char **argv, struct console *cons) {
- pid_t pid = fork();
- if (pid < 0) {
+int dbg_cont(struct thread *th) {
+ if (th->id <= 0 || !th->stopped) {
return -1;
}
- if (pid == 0) {
- console_configslave(cons);
- setpgid(0, 0);
- ptrace(PTRACE_TRACEME, 0, NULL, NULL);
- close_range(STDERR_FILENO+1, ~0U, CLOSE_RANGE_UNSHARE);
- execvp(argv[0], argv);
- exit(-1);
- }
-
- list_init(&dbg->breaks);
- list_init(&dbg->states);
- dbg->state = NULL;
- dbg->id = pid;
- dbg->child = 1;
- dbg->stopped = 0;
- dbg->status = 0;
- dbg->signal = 0;
- dbg->cont = 0;
- dbg->buff = NULL;
- dbg->buffsize = 0;
-
- while (!dbg_wait(dbg)) {}
- ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_EXITKILL | PTRACE_OPTIONS);
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = PTRACE_CONT;
+ th->clearstates = 1;
return 0;
}
-int dbg_wait(struct tracee *dbg) {
- if (dbg->stopped) {
- return 1;
+int dbg_syscall(struct thread *th) {
+ if (th->id <= 0 || !th->stopped) {
+ return -1;
}
- if (waitpid(dbg->id, &dbg->status, WNOHANG | __WALL) <= 0) {
- return 0;
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = PTRACE_SYSCALL;
+ th->clearstates = 1;
+ return 0;
+}
+
+int dbg_stepin(struct thread *th) {
+ if (!th->stopped) {
+ return -1;
}
- if (dbg->cont != 0) {
- install_breakpoints(dbg);
- ptrace(dbg->cont, dbg->id, NULL, NULL);
- dbg->cont = 0;
+ if (th->state != th->states.tail) {
+ th->state = th->state->next;
return 0;
}
- if (uninstall_breakpoints(dbg, 1)) {
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- dbg->cont = PTRACE_CONT;
- return 0;
+ if (th->id <= 0) {
+ return -1;
}
- capture_state(dbg);
- dbg->stopped = 1;
- return 1;
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = 0;
+ th->clearstates = 0;
+ return 0;
}
-/* note: move head < state < tail checks from main into these function
- * add a dbg_stepback function */
+int dbg_stepover(struct thread *th) {
+ if (!th->stopped) {
+ return -1;
+ }
+
+ if (th->state != th->states.tail) {
+ th->state = th->state->next;
+ return 0;
+ }
-int dbg_stepin(struct tracee *dbg) {
- /* consider making this a no-op if we are not stopped on a call */
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- dbg->stopped = 0;
- dbg->cont = 0;
- return 0;
-}
+ if (th->id <= 0) {
+ return -1;
+ }
-int dbg_stepover(struct tracee *dbg) {
csh handle;
cs_open(CS_ARCH_X86, CS_MODE_64, &handle);
+ cs_insn *insn = cs_malloc(handle);
- uint64_t address = dbg->state->regs.rip;
+ uint64_t address = th->state->regs.rip;
size_t size = 128;
- const uint8_t *code = deref(dbg, address, size);
- cs_insn *insn = cs_malloc(handle);
+ const uint8_t *code = deref(th, address, size);
cs_disasm_iter(handle, &code, &size, &address, insn);
if (insn->id == X86_INS_CALL) {
- struct breakpoint *b = xmalloc(sizeof(*b));
- b->address = address;
- b->stack = dbg->state->regs.rsp;
+ struct breakpoint *b = add_breakpoint(th->proc, address);
+ b->user = 0;
+ b->stack = th->state->regs.rsp;
+ b->tid = th->id;
b->enabled = -1;
- b->active = 0;
- list_insert(dbg->breaks.end, b);
- dbg->cont = PTRACE_CONT;
- //dbg_cont(dbg, PTRACE_CONT);
+
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = PTRACE_CONT;
+ th->clearstates = 0;
} else {
- dbg->cont = 0;
- //dbg_stepin(dbg);
+ th->doing = PTRACE_SINGLESTEP;
+ th->donext = 0;
+ th->clearstates = 0;
}
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- dbg->stopped = 0;
-
cs_free(insn, 1);
cs_close(&handle);
return 0;
}
-/*
-int dbg_stepout(struct tracee *dbg) {
- unsigned long bp = dbg->state->regs.rbp;
- unsigned long ra = *(unsigned long *)deref(dbg, bp+8, sizeof(unsigned long));
+int dbg_stepback(struct thread *th) {
+ if (!th->stopped) {
+ return -1;
+ }
- struct breakpoint *b = xmalloc(sizeof(*b));
- b->address = ra;
- b->stack = 0;
- b->enabled = -1;
- b->active = 0;
- list_insert(dbg->breaks.end, b);
-
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- //clear_states(dbg); // keep this?
- dbg->stopped = 0;
- dbg->cont = PTRACE_CONT;
- return 0;
-}
-*/
+ if (th->state != th->states.head) {
+ th->state = th->state->prev;
+ return 0;
+ }
-int dbg_cont(struct tracee *dbg, int cont) {
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- clear_states(dbg);
- dbg->stopped = 0;
- dbg->cont = cont;
- return 0;
+ return -1;
}
-void *deref(struct tracee *dbg, unsigned long addr, size_t size) {
- (void)size; // todo
+void *deref(struct thread *th, unsigned long address, size_t size) {
+ /* todo: size, and mapping breaks */
+ (void)size;
+
+ if (!th->state) {
+ return NULL;
+ }
- struct list *maps = &dbg->state->maps;
+ struct list *maps = &th->state->maps;
for (struct map *m = maps->head; m != maps->end; m = m->next) {
- if (m->start <= addr && addr < m->end) {
- return (unsigned char *)m->data + (addr - m->start);
+ if (m->start <= address && address < m->end) {
+ return (unsigned char *)m->data + (address - m->start);
}
}
return NULL;
}
+
+
+
+
+
+
+//int dbg_stepout(struct tracee *dbg) {
+// unsigned long bp = dbg->state->regs.rbp;
+// unsigned long ra = *(unsigned long *)deref(dbg, bp+8, sizeof(unsigned long));
+//
+// struct breakpoint *b = xmalloc(sizeof(*b));
+// b->address = ra;
+// b->stack = 0;
+// b->enabled = -1;
+// b->active = 0;
+// list_insert(dbg->breaks.end, b);
+//
+// ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
+// //clear_states(dbg); // keep this?
+// dbg->stopped = 0;
+// dbg->cont = PTRACE_CONT;
+// return 0;
+//}
diff --git a/debugger.h b/debugger.h
index 6305e96..cea3bba 100644
--- a/debugger.h
+++ b/debugger.h
@@ -3,18 +3,20 @@
#include <sys/user.h>
#include <unistd.h>
-#include "console.h"
#include "list.h"
-#define BREAKPOINT_INSN 0xcc
-
struct breakpoint {
LINKEDLIST;
+
unsigned long address;
+ unsigned long text;
+ int installed;
+ int hits;
+ int user;
+
unsigned long stack;
- unsigned long orig;
+ pid_t tid;
int enabled;
- int active;
};
struct map {
@@ -27,29 +29,49 @@ struct map {
struct state {
LINKEDLIST;
struct user_regs_struct regs;
- struct user_fpregs_struct fpregs;
struct list maps;
};
-struct tracee {
- struct list breaks;
+struct process {
+ LINKEDLIST;
+ pid_t id;
+ int child;
+ struct list breakpoints;
+ struct list threads;
+ char status[128];
+};
+
+struct thread {
+ LINKEDLIST;
+ struct process *proc;
+
struct list states;
struct state *state;
+ int clearstates;
+
pid_t id;
- int child;
int stopped;
- int status;
int signal;
- int cont;
- void *buff;
- size_t buffsize;
+ int doing;
+ int donext;
+
+ char status[128];
};
-extern int dbg_process(struct tracee *dbg, pid_t pid);
-extern int dbg_new_process(struct tracee *dbg, char **argv, struct console *cons);
-extern int dbg_wait(struct tracee *dbg);
-extern int dbg_stepin(struct tracee *dbg);
-extern int dbg_stepover(struct tracee *dbg);
-//extern int dbg_stepout(struct tracee *dbg);
-extern int dbg_cont(struct tracee *dbg, int mode);
-extern void *deref(struct tracee *dbg, unsigned long addr, size_t size);
+extern struct breakpoint*add_breakpoint(struct process*proc,unsigned long address);
+extern struct breakpoint*get_breakpoint(struct process*proc,unsigned long address);
+
+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 void dbg_sync(struct process *proc);
+
+extern int dbg_intr(struct thread *th);
+extern int dbg_cont(struct thread *th);
+extern int dbg_syscall(struct thread *th);
+extern int dbg_stepin(struct thread *th);
+extern int dbg_stepover(struct thread *th);
+extern int dbg_stepback(struct thread *th);
+
+extern void *deref(struct thread *th, unsigned long address, size_t size);
diff --git a/helpers.c b/helpers.c
index f7cc9c2..9973093 100644
--- a/helpers.c
+++ b/helpers.c
@@ -11,6 +11,12 @@ void *xmalloc(size_t size) {
return ptr;
}
+unsigned long strict_strtoul(const char *nptr, int base) {
+ char *endptr;
+ unsigned long ret = strtoul(nptr, &endptr, base);
+ return (*endptr ? 0 : ret);
+}
+
void cursinit(void) {
setlocale(LC_ALL, "");
initscr();
@@ -20,6 +26,14 @@ void cursinit(void) {
curs_set(FALSE);
timeout(25);
refresh();
+
+ start_color();
+ use_default_colors();
+
+ init_pair(1, COLOR_GREEN, -1);
+ init_pair(2, COLOR_CYAN, -1);
+ init_pair(3, COLOR_RED, -1);
+ init_pair(4, COLOR_YELLOW, -1);
}
void cursupdate(void) {
@@ -57,3 +71,11 @@ int pprintw(PANEL *pan, const char *fmt, ...) {
int pclear(PANEL *pan) {
return wclear(panel_window(pan));
}
+
+int pattron(PANEL *pan, int attrs) {
+ return wattron(panel_window(pan), attrs);
+}
+
+int pattroff(PANEL *pan, int attrs) {
+ return wattroff(panel_window(pan), attrs);
+}
diff --git a/helpers.h b/helpers.h
index f01e024..a415a6c 100644
--- a/helpers.h
+++ b/helpers.h
@@ -4,7 +4,10 @@
#include <panel.h>
#include <stddef.h>
+#define KEY_ESCAPE 0x1b
+
extern void *xmalloc(size_t size);
+extern unsigned long strict_strtoul(const char *nptr, int base);
extern void cursinit(void);
extern void cursupdate(void);
@@ -13,3 +16,5 @@ extern void delpan(PANEL *pan);
extern void reset_panel(PANEL *pan, int h, int w, int y, int x);
extern int pprintw(PANEL *pan, const char *fmt, ...);
extern int pclear(PANEL *pan);
+extern int pattron(PANEL *pan, int attrs);
+extern int pattroff(PANEL *pan, int attrs);
diff --git a/misplays.c b/misplays.c
index 8f6e0ee..1ccf3d3 100644
--- a/misplays.c
+++ b/misplays.c
@@ -1,8 +1,8 @@
#include <signal.h>
-#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <sys/ptrace.h>
-#include <linux/ptrace.h>
#include <unistd.h>
#include <capstone/capstone.h>
@@ -10,95 +10,50 @@
#include "console.h"
#include "debugger.h"
#include "helpers.h"
+#include "list.h"
static PANEL *left, *right;
-static struct console cons;
-static int mode = 0;
-
-static void describe_status(struct tracee *_dbg, PANEL *pan) {
- struct tracee dbg = *_dbg;
- int status = dbg.status;
- struct ptrace_syscall_info info;
-
- if (WIFEXITED(status)) {
- pprintw(pan, "exited with code: %u\n", WEXITSTATUS(status));
- } else if (WIFSIGNALED(status)) {
- pprintw(pan, "terminated by signal: %s\n", strsignal(WTERMSIG(status)));
- } else {
- unsigned long msg;
- if (ptrace(PTRACE_GETEVENTMSG, dbg.id, NULL, &msg) < 0) {
- perror("PTRACE_GETEVENTMSG");
- }
-
- if (WIFSTOPPED(status)) {
- switch (status >> 8) {
- case ((PTRACE_EVENT_VFORK << 8) | SIGTRAP):
- pprintw(pan, "child vfork()'d to: %lu\n", msg);
- break;
- case ((PTRACE_EVENT_FORK << 8) | SIGTRAP):
- pprintw(pan, "child fork()'d to: %lu\n", msg);
- break;
- case ((PTRACE_EVENT_CLONE << 8) | SIGTRAP):
- pprintw(pan, "child clone()'d to: %lu\n", msg);
- break;
- case ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP):
- pprintw(pan, "finished vfork\n");
- break;
- case ((PTRACE_EVENT_EXEC << 8) | SIGTRAP):
- pprintw(pan, "child exec()'d from: %lu\n", msg);
- break;
- case ((PTRACE_EVENT_EXIT << 8) | SIGTRAP):
- pprintw(pan, "exiting with code: %lu\n", msg);
- break;
- case ((PTRACE_EVENT_STOP << 8) | SIGTRAP):
- pprintw(pan, "child stopped\n");
- break;
- case ((PTRACE_EVENT_SECCOMP << 8) | SIGTRAP):
- pprintw(pan, "child seccomp event\n");
- break;
- case (SIGTRAP | 0x80):
- ptrace(PTRACE_GET_SYSCALL_INFO, dbg.id, sizeof(info), &info);
- pprintw(pan, "child entering syscall: %llu (%llx, %llx, %llx, %llx, %llx, %llx)\n",
- info.entry.nr,
- info.entry.args[0],
- info.entry.args[1],
- info.entry.args[2],
- info.entry.args[3],
- info.entry.args[4],
- info.entry.args[5]);
- break;
+static pid_t parse_pid(const char *nptr) {
+ char *endptr;
+ pid_t pid = strtoul(nptr, &endptr, 0);
+ return (*endptr ? 0 : pid);
+}
- case SIGSTOP:
- case SIGTSTP:
- case SIGTTIN:
- case SIGTTOU:
- pprintw(pan, "child group-stopped\n");
- break;
+static pid_t dofork(char **argv, struct console *cons) {
+ pid_t pid = fork();
+ if (pid < 0) {
+ return -1;
+ }
- default:
- pprintw(pan, "received signal: %s\n", strsignal(WSTOPSIG(status)));
- break;
- }
- } else {
- pprintw(pan, "child stop event unrecognized\n");
- }
+ if (pid == 0) {
+ usleep(10000);
+ console_configslave(cons);
+ close_range(STDERR_FILENO+1, ~0U, CLOSE_RANGE_UNSHARE);
+ //raise(SIGSTOP); // ptrace(PTRACE_TRACEME, 0, NULL, NULL);
+ execvp(argv[0], argv);
+ exit(EXIT_FAILURE);
}
+
+ return pid;
}
-static void list_breakpoints(struct tracee *dbg, PANEL *pan) {
- struct list *breaks = &dbg->breaks;
+static void list_breakpoints(struct thread *dbg, PANEL *pan) {
+ struct list *breaks = &dbg->proc->breakpoints;
if (breaks->head != breaks->end) {
pprintw(pan, "---\n");
for (struct breakpoint *bp=breaks->head; bp!=breaks->end; bp=bp->next) {
- pprintw(pan, "0x%lx ", bp->address);
+ pprintw(pan, "0x%lx (%c) (%i) ",
+ bp->address,
+ (bp->installed ? '*' : ' '),
+ bp->hits);
}
pprintw(pan, "\n");
}
}
-static void describe_states(struct tracee *dbg, PANEL *pan) {
+static void describe_states(struct thread *dbg, PANEL *pan) {
struct list *states = &dbg->states;
for (struct state *s = states->head; s != states->end; s = s->next) {
pprintw(pan, "%c", (s == dbg->state ? '#' : '-'));
@@ -106,8 +61,9 @@ static void describe_states(struct tracee *dbg, PANEL *pan) {
pprintw(pan, "\n");
}
-static void dump_registers(struct tracee *dbg, PANEL *pan) {
+static void dump_registers(struct thread *dbg, PANEL *pan) {
struct user_regs_struct *regs = &dbg->state->regs;
+ pprintw(pan, "orig_rax = 0x%016llx\n", regs->orig_rax);
pprintw(pan, "rax = 0x%016llx\n", regs->rax);
pprintw(pan, "rbx = 0x%016llx\n", regs->rbx);
pprintw(pan, "rcx = 0x%016llx\n", regs->rcx);
@@ -119,7 +75,7 @@ static void dump_registers(struct tracee *dbg, PANEL *pan) {
pprintw(pan, "rip = 0x%016llx\n", regs->rip);
}
-static void dump_stack(struct tracee *dbg, PANEL *pan) {
+static void dump_stack(struct thread *dbg, PANEL *pan) {
unsigned long sp = dbg->state->regs.rsp;
for (size_t i = 0; i < 16; i++, sp += 8) {
unsigned long word = *(unsigned long *)deref(dbg, sp,sizeof(unsigned long));
@@ -127,23 +83,51 @@ static void dump_stack(struct tracee *dbg, PANEL *pan) {
}
}
-static void disasm(struct tracee *dbg, PANEL *pan) {
+static int rip_visited(struct thread *th, unsigned long rip) {
+ struct list *states = &th->states;
+ for (struct state *s = states->head; s != states->end; s = s->next) {
+ if (rip == s->regs.rip) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void disasm(struct thread *dbg, PANEL *pan) {
csh handle;
cs_insn *insn;
if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK) {
- perror("capstone open");
+ //perror("capstone open");
} else {
uint64_t address = dbg->state->regs.rip;
size_t codez = 128;
const uint8_t *code = deref(dbg, address, codez);
insn = cs_malloc(handle);
- for (size_t i = 0; i < 16; i++) {
+ for (size_t i = 0; i < 32; i++) {
if (!cs_disasm_iter(handle, &code, &codez, &address, insn)) {
break;
}
+ if (dbg->stopped) {
+ if (insn->address == dbg->state->regs.rip) {
+ pattron(pan, COLOR_PAIR(1));
+ } else if (get_breakpoint(dbg->proc, insn->address)) {
+ pattron(pan, COLOR_PAIR(3));
+ } else if (rip_visited(dbg, insn->address)) {
+ pattron(pan, COLOR_PAIR(2));
+ }
+ }
pprintw(pan, "0x%"PRIx64":\t%s %s\n", insn->address, insn->mnemonic, insn->op_str);
+ if (dbg->stopped) {
+ if (insn->address == dbg->state->regs.rip) {
+ pattroff(pan, COLOR_PAIR(1));
+ } else if (get_breakpoint(dbg->proc, insn->address)) {
+ pattroff(pan, COLOR_PAIR(3));
+ } else if (rip_visited(dbg, insn->address)) {
+ pattroff(pan, COLOR_PAIR(2));
+ }
+ }
}
cs_free(insn, 1);
@@ -151,66 +135,117 @@ static void disasm(struct tracee *dbg, PANEL *pan) {
}
}
-static void info_update(struct tracee *dbg, PANEL *pan) {
+static void info_update(struct thread *th, PANEL *pan) {
pclear(pan);
- pprintw(pan, "PID: %li\n", (long)dbg->id);
- if (!dbg->stopped) {
- pprintw(pan, "Process is running...\n");
- } else {
- describe_status(dbg, pan);
- list_breakpoints(dbg, pan);
- describe_states(dbg, pan);
- dump_registers(dbg, pan);
- pprintw(pan, "---\n");
- dump_stack(dbg, pan);
- pprintw(pan, "---\n");
- disasm(dbg, pan);
+ pprintw(pan, "TID: %li\n", (long)th->id);
+ pprintw(pan, "%s (%i: %s)\n", th->status, th->signal, strsignal(th->signal));
+ list_breakpoints(th, pan);
+ describe_states(th, pan);
+ dump_registers(th, pan);
+ pprintw(pan, "---\n");
+ dump_stack(th, pan);
+ pprintw(pan, "---\n");
+ disasm(th, pan);
+}
+
+static void wait_all_threads(struct list *processes) {
+ for (struct process *proc = processes->head; proc != processes->end; proc = proc->next) {
+ dbg_sync(proc);
}
}
-static void layout(void) {
+static void layout(struct list *processes, struct thread *th) {
int w = COLS/2;
- reset_panel(left, LINES-1, w, 0, 0);
- reset_panel(right, LINES-1, COLS-w, 0, w);
+ reset_panel(left, LINES-2, w, 1, 0);
+ reset_panel(right, LINES-2, COLS-w, 1, w);
+
+ clear();
+
+ for (struct process *proc = processes->head; proc != processes->end; proc = proc->next) {
+ if (th->proc == proc) {
+ attron(COLOR_PAIR(1));
+ printw("{ ");
+ } else {
+ attron(COLOR_PAIR(4));
+ printw("{ ");
+ attroff(COLOR_PAIR(4));
+ }
+
+ struct list *threads = &proc->threads;
+ for (struct thread *t = threads->head; t != threads->end; t = t->next) {
+ if (t == th) {
+ printw("**");
+ }
+ printw("%li (%s)", (long)t->id, t->status);
+ if (t == th) {
+ printw("**");
+ }
+
+ if (t->next != threads->end) {
+ printw(" ");
+ }
+ }
+
+
+ if (th->proc == proc) {
+ printw(" } ");
+ attroff(COLOR_PAIR(1));
+ } else {
+ attron(COLOR_PAIR(4));
+ printw(" } ");
+ attroff(COLOR_PAIR(4));
+ }
+ }
}
int main(int argc, char **argv) {
- if (argc < 3) {
- fprintf(stderr, "Usage: %s <stop> <command>\n", argv[0]);
- return 1;
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <pid | cmdline>\n", argv[0]);
+ return EXIT_FAILURE;
}
- cursinit();
- left = newpan(0, 0, 0, 0);
- right = newpan(0, 0, 0, 0);
- layout();
+ getchar();
+ struct console cons = {0};
console_init(&cons);
+ struct list processes = {0};
+ struct thread *th = NULL;
+ list_init(&processes);
+
+ int child = 0;
+ pid_t pid = parse_pid(argv[1]);
argv[argc] = NULL;
- struct tracee dbg;
- if (dbg_new_process(&dbg, argv+2, &cons)) {
- pprintw(right, "Failed to start child\n");
+
+ if (pid == 0) {
+ pid = dofork(argv+1, &cons);
+ child = 1;
+ }
+
+ struct process *proc = dbg_attach(pid, child);
+ if (!proc) {
+ fprintf(stderr, "Failed to attach to process %li\n", (long)pid);
+ return EXIT_FAILURE;
}
+ list_insert(processes.end, proc);
+ th = proc->threads.head;
- unsigned long stop = strtoul(argv[1], NULL, 0);
- if (stop != 0) {
- struct breakpoint *b = xmalloc(sizeof(*b));
- b->address = stop;
- b->stack = 0;
- b->enabled = -1;
- b->active = 0;
- list_insert(dbg.breaks.end, b);
- dbg_cont(&dbg, PTRACE_CONT);
+ if (child) {
+ dbg_cont(th);
}
+ cursinit();
+ left = newpan(0, 0, 0, 0);
+ right = newpan(0, 0, 0, 0);
+ layout(&processes, th);
+
int quit = 0;
+ int mode = 0;
while (!quit) {
- //if (dbg.stopped == NULL) {
- dbg_wait(&dbg);
- //}
+ wait_all_threads(&processes);
+ layout(&processes, th);
console_update(&cons, right);
- info_update(&dbg, left);
+ info_update(th, left);
cursupdate();
int ch = getch();
@@ -218,7 +253,7 @@ int main(int argc, char **argv) {
if (mode == 0) {
switch (ch) {
case KEY_RESIZE:
- layout();
+ layout(&processes, th);
break;
case 'q':
quit = 1;
@@ -228,59 +263,72 @@ int main(int argc, char **argv) {
console_enter(&cons, right);
break;
case 'j':
- if (dbg.stopped) {
- if (dbg.state != dbg.states.tail) {
- dbg.state = dbg.state->next;
- } else {
- dbg_stepover(&dbg);
- }
- }
+ dbg_stepover(th);
break;
case 'k':
- if (dbg.stopped) {
- if (dbg.state != dbg.states.head) {
- dbg.state = dbg.state->prev;
- }
- }
+ dbg_stepback(th);
break;
case 'l':
- if (dbg.stopped) {
- if (dbg.state != dbg.states.tail) {
- //dbg.state = dbg.state->next;
- } else {
- dbg_stepin(&dbg);
- }
- }
+ dbg_stepin(th);
+ break;
+ case 'h':
+ /* todo: step out */
break;
- //case 'h':
- // if (dbg.stopped) {
- // dbg_stepout(&dbg);
- // }
- // break;
case 'g':
- if (dbg.stopped) {
- dbg.state = dbg.states.head;
+ if (th->stopped) {
+ th->state = th->states.head;
}
break;
case 'G':
- if (dbg.stopped) {
- dbg.state = dbg.states.tail;
+ if (th->stopped) {
+ th->state = th->states.tail;
}
break;
case 's':
- if (dbg.stopped) {
- dbg_cont(&dbg, PTRACE_SYSCALL);
- }
+ dbg_syscall(th);
break;
case 'c':
- if (dbg.stopped) {
- dbg_cont(&dbg, PTRACE_CONT);
- }
+ dbg_cont(th);
break;
case 'p':
- if (!dbg.stopped) {
- tgkill(dbg.id, dbg.id, SIGSTOP);
+ dbg_intr(th);
+ break;
+ case 't':
+ proc = th->proc;
+ th = th->next;
+ if (th == proc->threads.end) {
+ do {
+ proc = proc->next;
+ } while (proc == processes.end);
+ th = proc->threads.head;
}
+ layout(&processes, th);
+ break;
+ case 'T':
+ proc = th->proc;
+ th = th->prev;
+ if (th == proc->threads.end) {
+ do {
+ proc = proc->prev;
+ } while (proc == processes.end);
+ th = proc->threads.tail;
+ }
+ layout(&processes, th);
+ break;
+ case 'd':
+ proc = th->proc;
+ if (proc->child) {
+ break;
+ }
+ if (proc->prev == proc->next) {
+ break;
+ }
+ struct process *del = proc;
+ do {
+ proc = proc->next;
+ } while (proc == processes.end);
+ th = proc->threads.head;
+ dbg_detach(del);
break;
case ':':
mvprintw(LINES-1, 0, ":");
@@ -293,27 +341,33 @@ int main(int argc, char **argv) {
noecho();
timeout(25);
clear();
- refresh();
+ layout(&processes, th);
char *t = cmd;
- struct breakpoint *b = xmalloc(sizeof(*b));
- b->enabled = 1;
- b->active = 0;
+ int en = 1;
+ pid_t tid = 0;
if (t[0] == '!') {
- b->enabled = -1;
+ en = -1;
+ t++;
+ } else if (t[0] == '#') {
+ en = 0;
+ t++;
+ } else if (t[0] == '@') {
+ tid = th->id;
t++;
}
- b->address = strtoul(t, NULL, 0);
- b->stack = 0;
- list_insert(dbg.breaks.end, b);
+ unsigned long address = strtoul(t, NULL, 0);
+ struct breakpoint *b = add_breakpoint(th->proc, address);
+ b->enabled = en;
+ b->tid = tid;
break;
}
} else {
switch (ch) {
case KEY_RESIZE:
- layout();
+ layout(&processes, th);
break;
- case 0x1b:
+ case KEY_ESCAPE:
mode = 0;
console_leave(&cons, right);
break;
@@ -326,6 +380,12 @@ int main(int argc, char **argv) {
}
}
+ for (struct process *p = processes.head; p != processes.end; p = p->next) {
+ struct process *del = p;
+ p = p->prev;
+ dbg_detach(del);
+ }
+
endwin();
- return 0;
+ return EXIT_SUCCESS;
}