ptrace: support tracing from non-parent process
This commit is contained in:
parent
41ba264cdc
commit
aa8fdab570
116
apps/dbg.c
116
apps/dbg.c
@ -30,6 +30,8 @@
|
||||
static char * last_command = NULL;
|
||||
static char * binary_path = NULL;
|
||||
static FILE * binary_obj = NULL;
|
||||
static pid_t binary_pid = 0;
|
||||
static int binary_is_child = 0;
|
||||
|
||||
struct regs {
|
||||
uintptr_t r15, r14, r13, r12;
|
||||
@ -484,8 +486,10 @@ static void show_commandline(pid_t pid, int status, struct regs * regs) {
|
||||
}
|
||||
|
||||
_exitDebugger:
|
||||
ptrace(PTRACE_CONT, pid, NULL, (void*)(uintptr_t)SIGKILL);
|
||||
fprintf(stderr, "Exiting.\n");
|
||||
if (binary_is_child) {
|
||||
fprintf(stderr, "Terminating child process '%d'.\n", pid);
|
||||
ptrace(PTRACE_CONT, pid, NULL, (void*)(uintptr_t)SIGKILL);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -544,10 +548,19 @@ static char * sig_to_str(int signum) {
|
||||
return _buf;
|
||||
}
|
||||
|
||||
static void pass_sig(int sig) {
|
||||
kill(binary_pid, sig);
|
||||
signal(SIGINT, pass_sig);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
pid_t target_pid = 0;
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "o:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "o:p:h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'p':
|
||||
target_pid = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
return (usage(argv), 0);
|
||||
case '?':
|
||||
@ -580,63 +593,74 @@ int main(int argc, char * argv[]) {
|
||||
|
||||
/* Attempt to load symbol information... */
|
||||
|
||||
pid_t p = fork();
|
||||
if (!p) {
|
||||
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
|
||||
if (target_pid) {
|
||||
binary_pid = target_pid;
|
||||
if (ptrace(PTRACE_ATTACH, binary_pid, NULL, NULL) < 0) {
|
||||
fprintf(stderr, "%s: ptrace: %s\n", argv[0], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
execv(binary_path, &argv[optind]);
|
||||
return 1;
|
||||
signal(SIGINT, pass_sig);
|
||||
} else {
|
||||
binary_is_child = 1;
|
||||
binary_pid = fork();
|
||||
if (!binary_pid) {
|
||||
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
|
||||
fprintf(stderr, "%s: ptrace: %s\n", argv[0], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
execv(binary_path, &argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
signal(SIGINT, SIG_IGN);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int status = 0;
|
||||
pid_t res = waitpid(p, &status, WSTOPPED);
|
||||
while (1) {
|
||||
int status = 0;
|
||||
pid_t res = waitpid(binary_pid, &status, WSTOPPED);
|
||||
|
||||
if (res == 0) continue;
|
||||
if (res == 0) continue;
|
||||
|
||||
if (res < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
fprintf(stderr, "%s: waitpid: %s\n", argv[0], strerror(errno));
|
||||
} else {
|
||||
if (WIFSTOPPED(status)) {
|
||||
if (WSTOPSIG(status) == SIGTRAP) {
|
||||
/* Don't care about TRAP right now */
|
||||
int event = (status >> 16) & 0xFF;
|
||||
switch (event) {
|
||||
case PTRACE_EVENT_SINGLESTEP: {
|
||||
struct regs regs;
|
||||
ptrace(PTRACE_GETREGS, res, NULL, ®s);
|
||||
show_commandline(res, status, ®s);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//ptrace(PTRACE_SIGNALS_ONLY_PLZ, p, NULL, NULL);
|
||||
ptrace(PTRACE_CONT, p, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
printf("Program received signal %s.\n", sig_to_str(WSTOPSIG(status)));
|
||||
|
||||
struct regs regs;
|
||||
ptrace(PTRACE_GETREGS, res, NULL, ®s);
|
||||
|
||||
show_commandline(res, status, ®s);
|
||||
if (res < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
fprintf(stderr, "%s: waitpid: %s\n", argv[0], strerror(errno));
|
||||
} else {
|
||||
if (WIFSTOPPED(status)) {
|
||||
if (WSTOPSIG(status) == SIGTRAP) {
|
||||
/* Don't care about TRAP right now */
|
||||
int event = (status >> 16) & 0xFF;
|
||||
switch (event) {
|
||||
case PTRACE_EVENT_SINGLESTEP: {
|
||||
struct regs regs;
|
||||
ptrace(PTRACE_GETREGS, res, NULL, ®s);
|
||||
show_commandline(res, status, ®s);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//ptrace(PTRACE_SIGNALS_ONLY_PLZ, p, NULL, NULL);
|
||||
ptrace(PTRACE_CONT, res, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
fprintf(stderr, "Process %d was killed by %s.\n", res, signal_names[WTERMSIG(status)]);
|
||||
return 0;
|
||||
} else if (WIFEXITED(status)) {
|
||||
fprintf(stderr, "Process %d exited normally.\n", res);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown state?\n");
|
||||
printf("Program received signal %s.\n", sig_to_str(WSTOPSIG(status)));
|
||||
|
||||
struct regs regs;
|
||||
ptrace(PTRACE_GETREGS, res, NULL, ®s);
|
||||
|
||||
show_commandline(res, status, ®s);
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
fprintf(stderr, "Process %d was killed by %s.\n", res, signal_names[WTERMSIG(status)]);
|
||||
return 0;
|
||||
} else if (WIFEXITED(status)) {
|
||||
fprintf(stderr, "Process %d exited normally.\n", res);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown state?\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
102
apps/strace.c
102
apps/strace.c
@ -821,10 +821,11 @@ static void finish_syscall(pid_t pid, int syscall, struct regs * r) {
|
||||
static int usage(char * argv[]) {
|
||||
#define T_I "\033[3m"
|
||||
#define T_O "\033[0m"
|
||||
fprintf(stderr, "usage: %s [-o logfile] [-e trace=...] command...\n"
|
||||
fprintf(stderr, "usage: %s [-o logfile] [-e trace=...] [-p PID] [command...]\n"
|
||||
" -o logfile " T_I "Write tracing output to a file." T_O "\n"
|
||||
" -h " T_I "Show this help text." T_O "\n"
|
||||
" -e trace=... " T_I "Set tracing options." T_O "\n",
|
||||
" -e trace=... " T_I "Set tracing options." T_O "\n"
|
||||
" -p PID " T_I "Trace an existing process." T_O "\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
@ -832,9 +833,13 @@ static int usage(char * argv[]) {
|
||||
int main(int argc, char * argv[]) {
|
||||
logfile = stdout;
|
||||
|
||||
pid_t p = 0;
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "ho:e:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "ho:e:p:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'p':
|
||||
p = atoi(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
logfile = fopen(optarg, "w");
|
||||
if (!logfile) {
|
||||
@ -960,59 +965,66 @@ int main(int argc, char * argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (optind == argc) {
|
||||
if (!p && optind == argc) {
|
||||
return usage(argv);
|
||||
}
|
||||
|
||||
pid_t p = fork();
|
||||
if (!p) {
|
||||
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
|
||||
p = fork();
|
||||
if (!p) {
|
||||
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
|
||||
fprintf(stderr, "%s: ptrace: %s\n", argv[0], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
return 1;
|
||||
}
|
||||
signal(SIGINT, SIG_IGN);
|
||||
} else {
|
||||
if (ptrace(PTRACE_ATTACH, p, NULL, NULL) < 0) {
|
||||
fprintf(stderr, "%s: ptrace: %s\n", argv[0], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
execvp(argv[optind], &argv[optind]);
|
||||
return 1;
|
||||
} else {
|
||||
signal(SIGINT, SIG_IGN);
|
||||
int previous_syscall = -1;
|
||||
while (1) {
|
||||
int status = 0;
|
||||
pid_t res = waitpid(p, &status, WSTOPPED);
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
fprintf(stderr, "%s: waitpid: %s\n", argv[0], strerror(errno));
|
||||
} else {
|
||||
if (WIFSTOPPED(status)) {
|
||||
if (WSTOPSIG(status) == SIGTRAP) {
|
||||
struct regs regs;
|
||||
ptrace(PTRACE_GETREGS, p, NULL, ®s);
|
||||
int previous_syscall = -1;
|
||||
while (1) {
|
||||
int status = 0;
|
||||
pid_t res = waitpid(p, &status, WSTOPPED);
|
||||
|
||||
/* Event type */
|
||||
int event = (status >> 16) & 0xFF;
|
||||
switch (event) {
|
||||
case PTRACE_EVENT_SYSCALL_ENTER:
|
||||
previous_syscall = regs.rax;
|
||||
handle_syscall(p, ®s);
|
||||
break;
|
||||
case PTRACE_EVENT_SYSCALL_EXIT:
|
||||
finish_syscall(p, previous_syscall, ®s);
|
||||
break;
|
||||
default:
|
||||
fprintf(logfile, "Unknown event.\n");
|
||||
break;
|
||||
}
|
||||
ptrace(PTRACE_CONT, p, NULL, NULL);
|
||||
} else {
|
||||
fprintf(logfile, "--- %s ---\n", signal_names[WSTOPSIG(status)]);
|
||||
ptrace(PTRACE_CONT, p, NULL, (void*)(uintptr_t)WSTOPSIG(status));
|
||||
if (res < 0) {
|
||||
fprintf(stderr, "%s: waitpid: %s\n", argv[0], strerror(errno));
|
||||
} else {
|
||||
if (WIFSTOPPED(status)) {
|
||||
if (WSTOPSIG(status) == SIGTRAP) {
|
||||
struct regs regs;
|
||||
ptrace(PTRACE_GETREGS, p, NULL, ®s);
|
||||
|
||||
/* Event type */
|
||||
int event = (status >> 16) & 0xFF;
|
||||
switch (event) {
|
||||
case PTRACE_EVENT_SYSCALL_ENTER:
|
||||
previous_syscall = regs.rax;
|
||||
handle_syscall(p, ®s);
|
||||
break;
|
||||
case PTRACE_EVENT_SYSCALL_EXIT:
|
||||
finish_syscall(p, previous_syscall, ®s);
|
||||
break;
|
||||
default:
|
||||
fprintf(logfile, "Unknown event.\n");
|
||||
break;
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
fprintf(logfile, "+++ killed by %s +++\n", signal_names[WTERMSIG(status)]);
|
||||
return 0;
|
||||
} else if (WIFEXITED(status)) {
|
||||
fprintf(logfile, "+++ exited with %d +++\n", WEXITSTATUS(status));
|
||||
return 0;
|
||||
ptrace(PTRACE_CONT, p, NULL, NULL);
|
||||
} else {
|
||||
fprintf(logfile, "--- %s ---\n", signal_names[WSTOPSIG(status)]);
|
||||
ptrace(PTRACE_CONT, p, NULL, (void*)(uintptr_t)WSTOPSIG(status));
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
fprintf(logfile, "+++ killed by %s +++\n", signal_names[WTERMSIG(status)]);
|
||||
return 0;
|
||||
} else if (WIFEXITED(status)) {
|
||||
fprintf(logfile, "+++ exited with %d +++\n", WEXITSTATUS(status));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +137,7 @@ typedef struct process {
|
||||
/* Tracing */
|
||||
pid_t tracer;
|
||||
spin_lock_t wait_lock;
|
||||
list_t * tracees;
|
||||
} process_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -527,6 +527,13 @@ void process_reap(process_t * proc) {
|
||||
free(proc->signal_kstack);
|
||||
}
|
||||
|
||||
if (proc->tracees) {
|
||||
while (proc->tracees->length) {
|
||||
free(list_pop(proc->tracees));
|
||||
}
|
||||
free(proc->tracees);
|
||||
}
|
||||
|
||||
/* Unmark the stack bottom's fault detector */
|
||||
mmu_frame_allocate(
|
||||
mmu_get_page(proc->image.stack - KERNEL_STACK_SIZE, 0),
|
||||
@ -963,6 +970,7 @@ int waitpid(int pid, int * status, int options) {
|
||||
do {
|
||||
volatile process_t * candidate = NULL;
|
||||
int has_children = 0;
|
||||
int is_parent = 0;
|
||||
|
||||
spin_lock(proc->wait_lock);
|
||||
|
||||
@ -975,6 +983,7 @@ int waitpid(int pid, int * status, int options) {
|
||||
|
||||
if (wait_candidate(proc, pid, options, child)) {
|
||||
has_children = 1;
|
||||
is_parent = 1;
|
||||
if (child->flags & PROC_FLAG_FINISHED) {
|
||||
candidate = child;
|
||||
break;
|
||||
@ -986,6 +995,19 @@ int waitpid(int pid, int * status, int options) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidate && proc->tracees) {
|
||||
foreach(node, proc->tracees) {
|
||||
process_t * child = node->value;
|
||||
if (wait_candidate(proc,pid,options,child)) {
|
||||
has_children = 1;
|
||||
if (child->flags & PROC_FLAG_SUSPENDED) {
|
||||
candidate = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_children) {
|
||||
/* No valid children matching this description */
|
||||
spin_unlock(proc->wait_lock);
|
||||
@ -998,7 +1020,7 @@ int waitpid(int pid, int * status, int options) {
|
||||
*status = candidate->status;
|
||||
}
|
||||
int pid = candidate->id;
|
||||
if (candidate->flags & PROC_FLAG_FINISHED) {
|
||||
if (is_parent && (candidate->flags & PROC_FLAG_FINISHED)) {
|
||||
while (*((volatile int *)&candidate->flags) & PROC_FLAG_RUNNING);
|
||||
proc->time_children += candidate->time_children + candidate->time_total;
|
||||
proc->time_sys_children += candidate->time_sys_children + candidate->time_sys;
|
||||
@ -1211,6 +1233,25 @@ void task_exit(int retval) {
|
||||
}
|
||||
}
|
||||
|
||||
if (this_core->current_process->tracees) {
|
||||
spin_lock(this_core->current_process->wait_lock);
|
||||
while (this_core->current_process->tracees->length) {
|
||||
node_t * n = list_pop(this_core->current_process->tracees);
|
||||
process_t * tracee = n->value;
|
||||
free(n);
|
||||
if (is_valid_process(tracee)) {
|
||||
tracee->tracer = 0;
|
||||
__sync_and_and_fetch(&tracee->flags, ~(PROC_FLAG_TRACE_SIGNALS | PROC_FLAG_TRACE_SYSCALLS));
|
||||
if (tracee->flags & PROC_FLAG_SUSPENDED) {
|
||||
tracee->status = 0;
|
||||
__sync_and_and_fetch(&tracee->flags, ~(PROC_FLAG_SUSPENDED));
|
||||
make_process_ready(tracee);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(this_core->current_process->wait_lock);
|
||||
}
|
||||
|
||||
update_process_times(1);
|
||||
|
||||
process_t * parent = process_get_parent((process_t *)this_core->current_process);
|
||||
|
@ -12,22 +12,38 @@
|
||||
#include <kernel/arch/x86_64/regs.h>
|
||||
#include <kernel/arch/x86_64/mmu.h>
|
||||
|
||||
static void _ptrace_trace(process_t * tracer, process_t * tracee) {
|
||||
spin_lock(tracer->wait_lock);
|
||||
__sync_or_and_fetch(&tracee->flags, (PROC_FLAG_TRACE_SYSCALLS | PROC_FLAG_TRACE_SIGNALS));
|
||||
|
||||
if (!tracer->tracees) {
|
||||
tracer->tracees = list_create("debug tracees", tracer);
|
||||
}
|
||||
|
||||
list_insert(tracer->tracees, tracee);
|
||||
|
||||
tracee->tracer = tracer->id;
|
||||
|
||||
spin_unlock(tracer->wait_lock);
|
||||
}
|
||||
|
||||
long ptrace_attach(pid_t pid) {
|
||||
process_t * tracer = (process_t *)this_core->current_process;
|
||||
process_t * tracee = process_from_pid(pid);
|
||||
if (!tracee) return -ESRCH;
|
||||
if (this_core->current_process->user != 0 && this_core->current_process->user != tracee->user) return -EPERM;
|
||||
__sync_or_and_fetch(&tracee->flags, (PROC_FLAG_TRACE_SYSCALLS | PROC_FLAG_TRACE_SIGNALS));
|
||||
tracee->tracer = this_core->current_process->id;
|
||||
send_signal(pid, SIGSTOP, 1);
|
||||
if (tracer->user != 0 && tracer->user != tracee->user) return -EPERM;
|
||||
|
||||
_ptrace_trace(tracer, tracee);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long ptrace_self(void) {
|
||||
process_t * parent = process_get_parent((process_t*)this_core->current_process);
|
||||
if (!parent) return -EINVAL;
|
||||
process_t * tracee = (process_t*)this_core->current_process;
|
||||
process_t * tracer = process_get_parent(tracee);
|
||||
if (!tracer) return -EINVAL;
|
||||
|
||||
__sync_or_and_fetch(&this_core->current_process->flags, (PROC_FLAG_TRACE_SYSCALLS | PROC_FLAG_TRACE_SIGNALS));
|
||||
this_core->current_process->tracer = parent->id;
|
||||
_ptrace_trace(tracer, tracee);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user