diff options
Diffstat (limited to 'handle_event.c')
-rw-r--r-- | handle_event.c | 696 |
1 files changed, 696 insertions, 0 deletions
diff --git a/handle_event.c b/handle_event.c new file mode 100644 index 0000000..1dfb82b --- /dev/null +++ b/handle_event.c @@ -0,0 +1,696 @@ +#include "config.h" + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <assert.h> +#include <sys/time.h> + +#include "common.h" + +#ifdef __powerpc__ +#include <sys/ptrace.h> +#endif + +static void handle_signal(Event *event); +static void handle_exit(Event *event); +static void handle_exit_signal(Event *event); +static void handle_syscall(Event *event); +static void handle_arch_syscall(Event *event); +static void handle_sysret(Event *event); +static void handle_arch_sysret(Event *event); +static void handle_clone(Event *event); +static void handle_exec(Event *event); +static void handle_breakpoint(Event *event); +static void handle_new(Event *event); +static void remove_proc(Process *proc); + +static void callstack_push_syscall(Process *proc, int sysnum); +static void callstack_push_symfunc(Process *proc, + struct library_symbol *sym); +static void callstack_pop(Process *proc); + +static char * shortsignal(Process *proc, int signum); +static char * sysname(Process *proc, int sysnum); +static char * arch_sysname(Process *proc, int sysnum); + +void +handle_event(Event *event) { + debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", event->proc ? event->proc->pid : -1, event->type); + switch (event->type) { + case EVENT_NONE: + debug(1, "event: none"); + return; + case EVENT_SIGNAL: + debug(1, "event: signal (%s [%d])", + shortsignal(event->proc, event->e_un.signum), + event->e_un.signum); + handle_signal(event); + return; + case EVENT_EXIT: + debug(1, "event: exit (%d)", event->e_un.ret_val); + handle_exit(event); + return; + case EVENT_EXIT_SIGNAL: + debug(1, "event: exit signal (%s [%d])", + shortsignal(event->proc, event->e_un.signum), + event->e_un.signum); + handle_exit_signal(event); + return; + case EVENT_SYSCALL: + debug(1, "event: syscall (%s [%d])", + sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_syscall(event); + return; + case EVENT_SYSRET: + debug(1, "event: sysret (%s [%d])", + sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_sysret(event); + return; + case EVENT_ARCH_SYSCALL: + debug(1, "event: arch_syscall (%s [%d])", + arch_sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_arch_syscall(event); + return; + case EVENT_ARCH_SYSRET: + debug(1, "event: arch_sysret (%s [%d])", + arch_sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_arch_sysret(event); + return; + case EVENT_CLONE: + debug(1, "event: clone (%u)", event->e_un.newpid); + handle_clone(event); + return; + case EVENT_EXEC: + debug(1, "event: exec()"); + handle_exec(event); + return; + case EVENT_BREAKPOINT: + debug(1, "event: breakpoint"); + handle_breakpoint(event); + return; + case EVENT_NEW: + debug(1, "event: new process"); + handle_new(event); + return; + default: + fprintf(stderr, "Error! unknown event?\n"); + exit(1); + } +} + +/* TODO */ +static void * +address_clone(void * addr) { + debug(DEBUG_FUNCTION, "address_clone(%p)", addr); + return addr; +} + +static void * +breakpoint_clone(void * bp) { + Breakpoint * b; + debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); + b = malloc(sizeof(Breakpoint)); + if (!b) { + perror("malloc()"); + exit(1); + } + memcpy(b, bp, sizeof(Breakpoint)); + return b; +} + +typedef struct Pending_New Pending_New; +struct Pending_New { + pid_t pid; + Pending_New * next; +}; +static Pending_New * pending_news = NULL; + +static int +pending_new(pid_t pid) { + Pending_New * p; + + debug(DEBUG_FUNCTION, "pending_new(%d)", pid); + + p = pending_news; + while (p) { + if (p->pid == pid) { + return 1; + } + p = p->next; + } + return 0; +} + +static void +pending_new_insert(pid_t pid) { + Pending_New * p; + + debug(DEBUG_FUNCTION, "pending_new_insert(%d)", pid); + + p = malloc(sizeof(Pending_New)); + if (!p) { + perror("malloc()"); + exit(1); + } + p->pid = pid; + p->next = pending_news; + pending_news = p; +} + +static void +pending_new_remove(pid_t pid) { + Pending_New *p, *pred; + + debug(DEBUG_FUNCTION, "pending_new_remove(%d)", pid); + + p = pending_news; + if (p->pid == pid) { + pending_news = p->next; + free(p); + } else { + while (p) { + if (p->pid == pid) { + pred->next = p->next; + free(p); + } + pred = p; + p = p->next; + } + } +} + +static void +handle_clone(Event * event) { + Process *p; + + debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid); + + p = malloc(sizeof(Process)); + if (!p) { + perror("malloc()"); + exit(1); + } + memcpy(p, event->proc, sizeof(Process)); + p->breakpoints = dict_clone(event->proc->breakpoints, address_clone, breakpoint_clone); + p->pid = event->e_un.newpid; + p->parent = event->proc; + + if (pending_new(p->pid)) { + pending_new_remove(p->pid); + if (p->breakpoint_being_enabled) { + enable_breakpoint(p->pid, p->breakpoint_being_enabled); + p->breakpoint_being_enabled = NULL; + } + if (event->proc->state == STATE_ATTACHED && options.follow) { + p->state = STATE_ATTACHED; + } else { + p->state = STATE_IGNORED; + } + continue_process(p->pid); + p->next = list_of_processes; + list_of_processes = p; + } else { + p->state = STATE_BEING_CREATED; + p->next = list_of_processes; + list_of_processes = p; + } + continue_process(event->proc->pid); +} + +static void +handle_new(Event * event) { + Process * proc; + + debug(DEBUG_FUNCTION, "handle_new(pid=%d)", event->e_un.newpid); + + proc = pid2proc(event->e_un.newpid); + if (!proc) { + pending_new_insert(event->e_un.newpid); + } else { + assert(proc->state == STATE_BEING_CREATED); + if (proc->breakpoint_being_enabled) { + enable_breakpoint(proc->pid, proc->breakpoint_being_enabled); + proc->breakpoint_being_enabled = NULL; + } + if (options.follow) { + proc->state = STATE_ATTACHED; + } else { + proc->state = STATE_IGNORED; + } + continue_process(proc->pid); + } +} + +static char * +shortsignal(Process *proc, int signum) { + static char *signalent0[] = { +#include "signalent.h" + }; + static char *signalent1[] = { +#include "signalent1.h" + }; + static char **signalents[] = { signalent0, signalent1 }; + int nsignals[] = { sizeof signalent0 / sizeof signalent0[0], + sizeof signalent1 / sizeof signalent1[0] + }; + + debug(DEBUG_FUNCTION, "shortsignal(pid=%d, signum=%d)", proc->pid, signum); + + if (proc->personality > sizeof signalents / sizeof signalents[0]) + abort(); + if (signum < 0 || signum >= nsignals[proc->personality]) { + return "UNKNOWN_SIGNAL"; + } else { + return signalents[proc->personality][signum]; + } +} + +static char * +sysname(Process *proc, int sysnum) { + static char result[128]; + static char *syscalent0[] = { +#include "syscallent.h" + }; + static char *syscalent1[] = { +#include "syscallent1.h" + }; + static char **syscalents[] = { syscalent0, syscalent1 }; + int nsyscals[] = { sizeof syscalent0 / sizeof syscalent0[0], + sizeof syscalent1 / sizeof syscalent1[0] + }; + + debug(DEBUG_FUNCTION, "sysname(pid=%d, sysnum=%d)", proc->pid, sysnum); + + if (proc->personality > sizeof syscalents / sizeof syscalents[0]) + abort(); + if (sysnum < 0 || sysnum >= nsyscals[proc->personality]) { + sprintf(result, "SYS_%d", sysnum); + return result; + } else { + sprintf(result, "SYS_%s", + syscalents[proc->personality][sysnum]); + return result; + } +} + +static char * +arch_sysname(Process *proc, int sysnum) { + static char result[128]; + static char *arch_syscalent[] = { +#include "arch_syscallent.h" + }; + int nsyscals = sizeof arch_syscalent / sizeof arch_syscalent[0]; + + debug(DEBUG_FUNCTION, "arch_sysname(pid=%d, sysnum=%d)", proc->pid, sysnum); + + if (sysnum < 0 || sysnum >= nsyscals) { + sprintf(result, "ARCH_%d", sysnum); + return result; + } else { + sprintf(result, "ARCH_%s", + arch_syscalent[sysnum]); + return result; + } +} + +static void +handle_signal(Event *event) { + debug(DEBUG_FUNCTION, "handle_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); + if (exiting && event->e_un.signum == SIGSTOP) { + pid_t pid = event->proc->pid; + disable_all_breakpoints(event->proc); + untrace_pid(pid); + remove_proc(event->proc); + return; + } + if (event->proc->state != STATE_IGNORED) { + output_line(event->proc, "--- %s (%s) ---", + shortsignal(event->proc, event->e_un.signum), + strsignal(event->e_un.signum)); + } + continue_after_signal(event->proc->pid, event->e_un.signum); +} + +static void +handle_exit(Event *event) { + debug(DEBUG_FUNCTION, "handle_exit(pid=%d, status=%d)", event->proc->pid, event->e_un.ret_val); + if (event->proc->state != STATE_IGNORED) { + output_line(event->proc, "+++ exited (status %d) +++", + event->e_un.ret_val); + } + remove_proc(event->proc); +} + +static void +handle_exit_signal(Event *event) { + debug(DEBUG_FUNCTION, "handle_exit_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); + if (event->proc->state != STATE_IGNORED) { + output_line(event->proc, "+++ killed by %s +++", + shortsignal(event->proc, event->e_un.signum)); + } + remove_proc(event->proc); +} + +static void +remove_proc(Process *proc) { + Process *tmp, *tmp2; + + debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); + + if (list_of_processes == proc) { + tmp = list_of_processes; + list_of_processes = list_of_processes->next; + free(tmp); + return; + } + tmp = list_of_processes; + while (tmp->next) { + if (tmp->next == proc) { + tmp2 = tmp->next; + tmp->next = tmp->next->next; + free(tmp2); + continue; + } + tmp = tmp->next; + } +} + +static void +handle_syscall(Event *event) { + debug(DEBUG_FUNCTION, "handle_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + if (options.syscalls) { + output_left(LT_TOF_SYSCALL, event->proc, + sysname(event->proc, event->e_un.sysnum)); + } + if (event->proc->breakpoints_enabled == 0) { + enable_all_breakpoints(event->proc); + } + callstack_push_syscall(event->proc, event->e_un.sysnum); + } + continue_process(event->proc->pid); +} + +static void +handle_exec(Event * event) { + Process * proc = event->proc; + pid_t saved_pid; + + debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); + if (proc->state == STATE_IGNORED) { + untrace_pid(proc->pid); + remove_proc(proc); + return; + } + output_line(proc, "--- Called exec() ---"); + proc->mask_32bit = 0; + proc->personality = 0; + proc->arch_ptr = NULL; + free(proc->filename); + proc->filename = pid2name(proc->pid); + saved_pid = proc->pid; + proc->pid = 0; + breakpoints_init(proc); + proc->pid = saved_pid; + proc->callstack_depth = 0; + continue_process(proc->pid); +} + +static void +handle_arch_syscall(Event *event) { + debug(DEBUG_FUNCTION, "handle_arch_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + if (options.syscalls) { + output_left(LT_TOF_SYSCALL, event->proc, + arch_sysname(event->proc, event->e_un.sysnum)); + } + if (event->proc->breakpoints_enabled == 0) { + enable_all_breakpoints(event->proc); + } + callstack_push_syscall(event->proc, 0xf0000 + event->e_un.sysnum); + } + continue_process(event->proc->pid); +} + +struct timeval current_time_spent; + +static void +calc_time_spent(Process *proc) { + struct timeval tv; + struct timezone tz; + struct timeval diff; + struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "calc_time_spent(pid=%d)", proc->pid); + elem = &proc->callstack[proc->callstack_depth - 1]; + + gettimeofday(&tv, &tz); + + diff.tv_sec = tv.tv_sec - elem->time_spent.tv_sec; + if (tv.tv_usec >= elem->time_spent.tv_usec) { + diff.tv_usec = tv.tv_usec - elem->time_spent.tv_usec; + } else { + diff.tv_sec++; + diff.tv_usec = 1000000 + tv.tv_usec - elem->time_spent.tv_usec; + } + current_time_spent = diff; +} + +static void +handle_sysret(Event *event) { + debug(DEBUG_FUNCTION, "handle_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + if (opt_T || options.summary) { + calc_time_spent(event->proc); + } + callstack_pop(event->proc); + if (options.syscalls) { + output_right(LT_TOF_SYSCALLR, event->proc, + sysname(event->proc, event->e_un.sysnum)); + } + } + continue_process(event->proc->pid); +} + +static void +handle_arch_sysret(Event *event) { + debug(DEBUG_FUNCTION, "handle_arch_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + if (opt_T || options.summary) { + calc_time_spent(event->proc); + } + callstack_pop(event->proc); + if (options.syscalls) { + output_right(LT_TOF_SYSCALLR, event->proc, + arch_sysname(event->proc, event->e_un.sysnum)); + } + } + continue_process(event->proc->pid); +} + +static void +handle_breakpoint(Event *event) { + int i, j; + Breakpoint *sbp; + + debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr); + debug(2, "event: breakpoint (%p)", event->e_un.brk_addr); + +#ifdef __powerpc__ + /* Need to skip following NOP's to prevent a fake function from being stacked. */ + long stub_addr = (long) get_count_register(event->proc); + Breakpoint *stub_bp = NULL; + char nop_instruction[] = PPC_NOP; + + stub_bp = address2bpstruct (event->proc, event->e_un.brk_addr); + + if (stub_bp) { + unsigned char *bp_instruction = stub_bp->orig_value; + + if (memcmp(bp_instruction, nop_instruction, + PPC_NOP_LENGTH) == 0) { + if (stub_addr != (long) event->e_un.brk_addr) { + set_instruction_pointer (event->proc, event->e_un.brk_addr + 4); + continue_process(event->proc->pid); + return; + } + } + } +#endif + if ((sbp = event->proc->breakpoint_being_enabled) != 0) { + /* Reinsert breakpoint */ + continue_enabling_breakpoint(event->proc->pid, + event->proc-> + breakpoint_being_enabled); + event->proc->breakpoint_being_enabled = NULL; + return; + } + + for (i = event->proc->callstack_depth - 1; i >= 0; i--) { + if (event->e_un.brk_addr == + event->proc->callstack[i].return_addr) { +#ifdef __powerpc__ + /* + * PPC HACK! (XXX FIXME TODO) + * The PLT gets modified during the first call, + * so be sure to re-enable the breakpoint. + */ + unsigned long a; + struct library_symbol *libsym = + event->proc->callstack[i].c_un.libfunc; + void *addr = sym2addr(event->proc, libsym); + + if (libsym->plt_type != LS_TOPLT_POINT) { + unsigned char break_insn[] = BREAKPOINT_VALUE; + + sbp = address2bpstruct(event->proc, addr); + assert(sbp); + a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, + addr); + + if (memcmp(&a, break_insn, BREAKPOINT_LENGTH)) { + sbp->enabled--; + insert_breakpoint(event->proc, addr, + libsym); + } + } else { + sbp = dict_find_entry(event->proc->breakpoints, sym2addr(event->proc, libsym)); + assert(sbp); + if (addr != sbp->addr) { + insert_breakpoint(event->proc, addr, + libsym); + } + } +#elif defined(__mips__) + void *addr; + void *old_addr; + struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; + assert(sym); + old_addr = dict_find_entry(event->proc->breakpoints, sym2addr(event->proc, sym))->addr; + addr=sym2addr(event->proc,sym); + assert(old_addr !=0 && addr !=0); + if(addr != old_addr){ + struct library_symbol *new_sym; + new_sym=malloc(sizeof(*new_sym)); + memcpy(new_sym,sym,sizeof(*new_sym)); + new_sym->next=event->proc->list_of_symbols; + event->proc->list_of_symbols=new_sym; + insert_breakpoint(event->proc, addr, new_sym); + } +#endif + for (j = event->proc->callstack_depth - 1; j > i; j--) { + callstack_pop(event->proc); + } + if (event->proc->state != STATE_IGNORED) { + if (opt_T || options.summary) { + calc_time_spent(event->proc); + } + } + callstack_pop(event->proc); + event->proc->return_addr = event->e_un.brk_addr; + if (event->proc->state != STATE_IGNORED) { + output_right(LT_TOF_FUNCTIONR, event->proc, + event->proc->callstack[i].c_un.libfunc->name); + } + continue_after_breakpoint(event->proc, + address2bpstruct(event->proc, + event->e_un.brk_addr)); + return; + } + } + + if ((sbp = address2bpstruct(event->proc, event->e_un.brk_addr))) { + if (event->proc->state != STATE_IGNORED) { + event->proc->stack_pointer = get_stack_pointer(event->proc); + event->proc->return_addr = + get_return_addr(event->proc, event->proc->stack_pointer); + output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name); + callstack_push_symfunc(event->proc, sbp->libsym); + } +#ifdef PLT_REINITALISATION_BP + if (event->proc->need_to_reinitialize_breakpoints + && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == + 0)) + reinitialize_breakpoints(event->proc); +#endif + + continue_after_breakpoint(event->proc, sbp); + return; + } + + if (event->proc->state != STATE_IGNORED) { + output_line(event->proc, "unexpected breakpoint at %p", + (void *)event->e_un.brk_addr); + } + continue_process(event->proc->pid); +} + +static void +callstack_push_syscall(Process *proc, int sysnum) { + struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "callstack_push_syscall(pid=%d, sysnum=%d)", proc->pid, sysnum); + /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ + if (proc->callstack_depth == MAX_CALLDEPTH - 1) { + fprintf(stderr, "Error: call nesting too deep!\n"); + return; + } + + elem = &proc->callstack[proc->callstack_depth]; + elem->is_syscall = 1; + elem->c_un.syscall = sysnum; + elem->return_addr = NULL; + + proc->callstack_depth++; + if (opt_T || options.summary) { + struct timezone tz; + gettimeofday(&elem->time_spent, &tz); + } +} + +static void +callstack_push_symfunc(Process *proc, struct library_symbol *sym) { + struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "callstack_push_symfunc(pid=%d, symbol=%s)", proc->pid, sym->name); + /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ + if (proc->callstack_depth == MAX_CALLDEPTH - 1) { + fprintf(stderr, "Error: call nesting too deep!\n"); + return; + } + + elem = &proc->callstack[proc->callstack_depth]; + elem->is_syscall = 0; + elem->c_un.libfunc = sym; + + elem->return_addr = proc->return_addr; + if (elem->return_addr) { + insert_breakpoint(proc, elem->return_addr, 0); + } + + proc->callstack_depth++; + if (opt_T || options.summary) { + struct timezone tz; + gettimeofday(&elem->time_spent, &tz); + } +} + +static void +callstack_pop(Process *proc) { + struct callstack_element *elem; + assert(proc->callstack_depth > 0); + + debug(DEBUG_FUNCTION, "callstack_pop(pid=%d)", proc->pid); + elem = &proc->callstack[proc->callstack_depth - 1]; + if (!elem->is_syscall && elem->return_addr) { + delete_breakpoint(proc, elem->return_addr); + } + proc->callstack_depth--; +} |