X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=handle_event.c;h=6fa7e98da6de4bbd35a2cfe1a379370a8d50d972;hb=28e4cb2f086c93d433e58faf7f1a81fb374aed4f;hp=42a7a0587390f64906e09204c6ade29330e8e1f4;hpb=689b9dbb8d7f88ab91e7741932ed000b6e49be9a;p=platform%2Fupstream%2Fltrace.git diff --git a/handle_event.c b/handle_event.c index 42a7a05..6fa7e98 100644 --- a/handle_event.c +++ b/handle_event.c @@ -1,6 +1,6 @@ /* * This file is part of ltrace. - * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc. * Copyright (C) 2010 Arnaud Patard, Mandriva SA * Copyright (C) 1998,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes * Copyright (C) 2008 Luis Machado, IBM Corporation @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "backend.h" #include "breakpoint.h" @@ -40,6 +40,8 @@ #include "fetch.h" #include "library.h" #include "proc.h" +#include "prototype.h" +#include "summary.h" #include "value_dict.h" static void handle_signal(Event *event); @@ -54,20 +56,19 @@ static void handle_exec(Event *event); static void handle_breakpoint(Event *event); static void handle_new(Event *event); -static void callstack_push_syscall(Process *proc, int sysnum); -static void callstack_push_symfunc(Process *proc, - struct library_symbol *sym); +static void callstack_push_syscall(struct process *proc, int sysnum); +static void callstack_push_symfunc(struct process *proc, struct breakpoint *bp); /* XXX Stack maintenance should be moved to a dedicated module, or to * proc.c, and push/pop should be visible outside this module. For * now, because we need this in proc.c, this is non-static. */ -void callstack_pop(struct Process *proc); +void callstack_pop(struct process *proc); -static char * shortsignal(Process *proc, int signum); -static char * sysname(Process *proc, int sysnum); -static char * arch_sysname(Process *proc, int sysnum); +static char *shortsignal(struct process *proc, int signum); +static char *sysname(struct process *proc, int sysnum); +static char *arch_sysname(struct process *proc, int sysnum); static Event * -call_handler(Process * proc, Event * event) +call_handler(struct process *proc, Event *event) { assert(proc != NULL); @@ -114,70 +115,91 @@ handle_event(Event *event) case EVENT_NONE: debug(1, "event: none"); return; + case EVENT_SIGNAL: + assert(event->proc != NULL); debug(1, "[%d] event: signal (%s [%d])", event->proc->pid, shortsignal(event->proc, event->e_un.signum), event->e_un.signum); handle_signal(event); return; + case EVENT_EXIT: + assert(event->proc != NULL); debug(1, "[%d] event: exit (%d)", event->proc->pid, event->e_un.ret_val); handle_exit(event); return; + case EVENT_EXIT_SIGNAL: + assert(event->proc != NULL); debug(1, "[%d] event: exit signal (%s [%d])", event->proc->pid, shortsignal(event->proc, event->e_un.signum), event->e_un.signum); handle_exit_signal(event); return; + case EVENT_SYSCALL: + assert(event->proc != NULL); debug(1, "[%d] event: syscall (%s [%d])", event->proc->pid, sysname(event->proc, event->e_un.sysnum), event->e_un.sysnum); handle_syscall(event); return; + case EVENT_SYSRET: + assert(event->proc != NULL); debug(1, "[%d] event: sysret (%s [%d])", event->proc->pid, sysname(event->proc, event->e_un.sysnum), event->e_un.sysnum); handle_sysret(event); return; + case EVENT_ARCH_SYSCALL: + assert(event->proc != NULL); debug(1, "[%d] event: arch_syscall (%s [%d])", event->proc->pid, arch_sysname(event->proc, event->e_un.sysnum), event->e_un.sysnum); handle_arch_syscall(event); return; + case EVENT_ARCH_SYSRET: + assert(event->proc != NULL); debug(1, "[%d] event: arch_sysret (%s [%d])", event->proc->pid, arch_sysname(event->proc, event->e_un.sysnum), event->e_un.sysnum); handle_arch_sysret(event); return; + case EVENT_CLONE: case EVENT_VFORK: + assert(event->proc != NULL); debug(1, "[%d] event: clone (%u)", event->proc->pid, event->e_un.newpid); handle_clone(event); return; + case EVENT_EXEC: + assert(event->proc != NULL); debug(1, "[%d] event: exec()", event->proc->pid); handle_exec(event); return; + case EVENT_BREAKPOINT: + assert(event->proc != NULL); debug(1, "[%d] event: breakpoint %p", event->proc->pid, event->e_un.brk_addr); handle_breakpoint(event); return; + case EVENT_NEW: debug(1, "[%d] event: new process", event->e_un.newpid); @@ -229,26 +251,18 @@ pending_new_insert(pid_t pid) { } static void -pending_new_remove(pid_t pid) { - Pending_New *p, *pred; - +pending_new_remove(pid_t pid) +{ debug(DEBUG_FUNCTION, "pending_new_remove(%d)", pid); - p = pending_news; - pred = NULL; - 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; + Pending_New **pp; + for (pp = &pending_news; *pp != NULL; pp = &(*pp)->next) + if ((*pp)->pid == pid) { + Pending_New *p = *pp; + *pp = p->next; + free(p); + return; } - } } static void @@ -256,53 +270,56 @@ handle_clone(Event *event) { debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid); - struct Process *proc = malloc(sizeof(*proc)); - if (proc == NULL) { - fail: + struct process *proc = malloc(sizeof(*proc)); + pid_t newpid = event->e_un.newpid; + if (proc == NULL + || process_clone(proc, event->proc, newpid) < 0) { free(proc); + proc = NULL; fprintf(stderr, - "Error during init of tracing process %d\n" - "This process won't be traced.\n", - event->proc->pid); - return; + "Couldn't initialize tracing of process %d.\n", + newpid); + + } else { + proc->parent = event->proc; + /* We save register values to the arch pointer, and + * these need to be per-thread. XXX arch_ptr should + * be retired in favor of fetch interface anyway. */ + proc->arch_ptr = NULL; } - if (process_clone(proc, event->proc, event->e_un.newpid) < 0) - goto fail; - proc->parent = event->proc; + if (pending_new(newpid)) { + pending_new_remove(newpid); + + if (proc != NULL) { + proc->event_handler = NULL; + if (event->proc->state == STATE_ATTACHED + && options.follow) + proc->state = STATE_ATTACHED; + else + proc->state = STATE_IGNORED; + } - /* We save register values to the arch pointer, and these need - to be per-thread. */ - proc->arch_ptr = NULL; + continue_process(newpid); - if (pending_new(proc->pid)) { - pending_new_remove(proc->pid); - /* XXX this used to be destroy_event_handler call, but - * I don't think we want to call that on a shared - * state. */ - proc->event_handler = NULL; - if (event->proc->state == STATE_ATTACHED && options.follow) - proc->state = STATE_ATTACHED; - else - proc->state = STATE_IGNORED; - continue_process(proc->pid); - } else { + } else if (proc != NULL) { proc->state = STATE_BEING_CREATED; } - if (event->type == EVENT_VFORK) + if (event->type != EVENT_VFORK) + continue_process(event->proc->pid); + else if (proc != NULL) continue_after_vfork(proc); else - continue_process(event->proc->pid); + continue_process(newpid); } static void -handle_new(Event * event) { - Process * proc; - +handle_new(Event *event) +{ debug(DEBUG_FUNCTION, "handle_new(pid=%d)", event->e_un.newpid); - proc = pid2proc(event->e_un.newpid); + struct process *proc = pid2proc(event->e_un.newpid); if (!proc) { pending_new_insert(event->e_un.newpid); } else { @@ -317,7 +334,8 @@ handle_new(Event * event) { } static char * -shortsignal(Process *proc, int signum) { +shortsignal(struct process *proc, int signum) +{ static char *signalent0[] = { #include "signalent.h" }; @@ -331,8 +349,7 @@ shortsignal(Process *proc, int signum) { debug(DEBUG_FUNCTION, "shortsignal(pid=%d, signum=%d)", proc->pid, signum); - if (proc->personality > sizeof signalents / sizeof signalents[0]) - abort(); + assert(proc->personality < sizeof signalents / sizeof signalents[0]); if (signum < 0 || signum >= nsignals[proc->personality]) { return "UNKNOWN_SIGNAL"; } else { @@ -341,53 +358,56 @@ shortsignal(Process *proc, int signum) { } static char * -sysname(Process *proc, int sysnum) { +sysname(struct process *proc, int sysnum) +{ static char result[128]; - static char *syscalent0[] = { + static char *syscallent0[] = { #include "syscallent.h" }; - static char *syscalent1[] = { + static char *syscallent1[] = { #include "syscallent1.h" }; - static char **syscalents[] = { syscalent0, syscalent1 }; - int nsyscals[] = { sizeof syscalent0 / sizeof syscalent0[0], - sizeof syscalent1 / sizeof syscalent1[0] + static char **syscallents[] = { syscallent0, syscallent1 }; + int nsyscalls[] = { + sizeof syscallent0 / sizeof syscallent0[0], + sizeof syscallent1 / sizeof syscallent1[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]) { + assert(proc->personality < sizeof syscallents / sizeof syscallents[0]); + if (sysnum < 0 || sysnum >= nsyscalls[proc->personality]) { sprintf(result, "SYS_%d", sysnum); return result; } else { - sprintf(result, "SYS_%s", - syscalents[proc->personality][sysnum]); - return result; + return syscallents[proc->personality][sysnum]; } } static char * -arch_sysname(Process *proc, int sysnum) { +arch_sysname(struct process *proc, int sysnum) +{ static char result[128]; - static char *arch_syscalent[] = { + static char *arch_syscallent[] = { #include "arch_syscallent.h" }; - int nsyscals = sizeof arch_syscalent / sizeof arch_syscalent[0]; + int nsyscalls = sizeof arch_syscallent / sizeof arch_syscallent[0]; debug(DEBUG_FUNCTION, "arch_sysname(pid=%d, sysnum=%d)", proc->pid, sysnum); - if (sysnum < 0 || sysnum >= nsyscals) { + if (sysnum < 0 || sysnum >= nsyscalls) { sprintf(result, "ARCH_%d", sysnum); return result; } else { - sprintf(result, "ARCH_%s", - arch_syscalent[sysnum]); + sprintf(result, "ARCH_%s", arch_syscallent[sysnum]); return result; } } +#ifndef HAVE_STRSIGNAL +# define strsignal(SIGNUM) "???" +#endif + static void handle_signal(Event *event) { debug(DEBUG_FUNCTION, "handle_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); @@ -399,6 +419,84 @@ handle_signal(Event *event) { continue_after_signal(event->proc->pid, event->e_un.signum); } +static int +init_syscall_symbol(struct library_symbol *libsym, const char *name) +{ + static struct library syscall_lib; + + if (syscall_lib.protolib == NULL) { + struct protolib *protolib + = protolib_cache_load(&g_protocache, "syscalls", 0, 1); + if (protolib == NULL) { + fprintf(stderr, "Couldn't load system call prototypes:" + " %s.\n", strerror(errno)); + + /* Instead, get a fake one just so we can + * carry on, limping. */ + protolib = malloc(sizeof *protolib); + if (protolib == NULL) { + fprintf(stderr, "Couldn't even allocate a fake " + "prototype library: %s.\n", + strerror(errno)); + abort(); + } + protolib_init(protolib); + } + + assert(protolib != NULL); + if (library_init(&syscall_lib, LT_LIBTYPE_SYSCALL) < 0) { + fprintf(stderr, "Couldn't initialize system call " + "library: %s.\n", strerror(errno)); + abort(); + } + + library_set_soname(&syscall_lib, "SYS", 0); + syscall_lib.protolib = protolib; + } + + if (library_symbol_init(libsym, 0, name, 0, LS_TOPLT_NONE) < 0) + return -1; + + libsym->lib = &syscall_lib; + return 0; +} + +/* Account the unfinished functions on the call stack. */ +static void +account_current_callstack(struct process *proc) +{ + if (! options.summary) + return; + + struct timedelta spent[proc->callstack_depth]; + + size_t i; + for (i = 0; i < proc->callstack_depth; ++i) { + struct callstack_element *elem = &proc->callstack[i]; + spent[i] = calc_time_spent(elem->enter_time); + } + + for (i = 0; i < proc->callstack_depth; ++i) { + struct callstack_element *elem = &proc->callstack[i]; + struct library_symbol syscall, *libsym = NULL; + if (elem->is_syscall) { + const char *name = sysname(proc, elem->c_un.syscall); + if (init_syscall_symbol(&syscall, name) >= 0) + libsym = &syscall; + + } else { + libsym = elem->c_un.libfunc; + } + + if (libsym != NULL) { + summary_account_call(libsym, spent[i]); + + if (elem->is_syscall) + library_symbol_destroy(&syscall); + } + } +} + static void handle_exit(Event *event) { debug(DEBUG_FUNCTION, "handle_exit(pid=%d, status=%d)", event->proc->pid, event->e_un.ret_val); @@ -406,6 +504,8 @@ handle_exit(Event *event) { output_line(event->proc, "+++ exited (status %d) +++", event->e_un.ret_val); } + + account_current_callstack(event->proc); remove_process(event->proc); } @@ -416,35 +516,49 @@ handle_exit_signal(Event *event) { output_line(event->proc, "+++ killed by %s +++", shortsignal(event->proc, event->e_un.signum)); } + + account_current_callstack(event->proc); remove_process(event->proc); } static void -output_syscall(struct Process *proc, const char *name, enum tof tof, - void (*output)(enum tof, struct Process *, - struct library_symbol *)) +output_syscall(struct process *proc, const char *name, enum tof tof, + bool left, struct timedelta *spent) { + if (left) + assert(spent == NULL); + struct library_symbol syscall; - if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) { - (*output)(tof, proc, &syscall); + if (init_syscall_symbol(&syscall, name) >= 0) { + if (left) { + if (! options.summary) + output_left(tof, proc, &syscall); + } else if (options.summary) { + summary_account_call(&syscall, *spent); + } else { + output_right(tof, proc, &syscall, spent); + } + library_symbol_destroy(&syscall); } } static void -output_syscall_left(struct Process *proc, const char *name) +output_syscall_left(struct process *proc, const char *name) { - output_syscall(proc, name, LT_TOF_SYSCALL, &output_left); + output_syscall(proc, name, LT_TOF_SYSCALL, true, NULL); } static void -output_syscall_right(struct Process *proc, const char *name) +output_syscall_right(struct process *proc, const char *name, + struct timedelta *spent) { - output_syscall(proc, name, LT_TOF_SYSCALLR, &output_right); + output_syscall(proc, name, LT_TOF_SYSCALLR, false, spent); } static void -handle_syscall(Event *event) { +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) { callstack_push_syscall(event->proc, event->e_un.sysnum); @@ -457,8 +571,9 @@ handle_syscall(Event *event) { } static void -handle_exec(Event * event) { - Process * proc = event->proc; +handle_exec(Event *event) +{ + struct process *proc = event->proc; /* Save the PID so that we can use it after unsuccessful * process_exec. */ @@ -473,24 +588,15 @@ handle_exec(Event * event) { } output_line(proc, "--- Called exec() ---"); + account_current_callstack(proc); + if (process_exec(proc) < 0) { fprintf(stderr, "couldn't reinitialize process %d after exec\n", pid); goto untrace; } - continue_process(proc->pid); - - /* After the exec, we expect to hit the first executable - * instruction. - * - * XXX TODO It would be nice to have this removed, but then we - * need to do that also for initial call to wait_for_proc in - * execute_program. In that case we could generate a - * EVENT_FIRST event or something, or maybe this could somehow - * be rolled into EVENT_NEW. */ - wait_for_proc(proc->pid); - continue_process(proc->pid); + continue_after_exec(proc); } static void @@ -507,77 +613,62 @@ handle_arch_syscall(Event *event) { 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]; +handle_x_sysret(Event *event, char *(*name_cb)(struct process *, int)) +{ + debug(DEBUG_FUNCTION, "handle_x_sysret(pid=%d, sysnum=%d)", + event->proc->pid, event->e_un.sysnum); - gettimeofday(&tv, &tz); + unsigned d = event->proc->callstack_depth; + assert(d > 0); + struct callstack_element *elem = &event->proc->callstack[d - 1]; + assert(elem->is_syscall); - 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); - } + struct timedelta spent = calc_time_spent(elem->enter_time); if (options.syscalls) output_syscall_right(event->proc, - sysname(event->proc, - event->e_un.sysnum)); + name_cb(event->proc, + event->e_un.sysnum), + &spent); - assert(event->proc->callstack_depth > 0); - unsigned d = event->proc->callstack_depth - 1; - assert(event->proc->callstack[d].is_syscall); callstack_pop(event->proc); } continue_after_syscall(event->proc, event->e_un.sysnum, 1); } 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); - } - if (options.syscalls) - output_syscall_right(event->proc, - arch_sysname(event->proc, - event->e_un.sysnum)); - callstack_pop(event->proc); - } - continue_process(event->proc->pid); +handle_sysret(Event *event) +{ + handle_x_sysret(event, &sysname); +} + +static void +handle_arch_sysret(Event *event) +{ + handle_x_sysret(event, &arch_sysname); } static void -output_right_tos(struct Process *proc) +output_right_tos(struct process *proc) { size_t d = proc->callstack_depth; + assert(d > 0); struct callstack_element *elem = &proc->callstack[d - 1]; - if (proc->state != STATE_IGNORED) - output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc); + assert(! elem->is_syscall); + + if (proc->state != STATE_IGNORED) { + struct timedelta spent = calc_time_spent(elem->enter_time); + if (options.summary) + summary_account_call(elem->c_un.libfunc, spent); + else + output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc, + &spent); + } } #ifndef ARCH_HAVE_SYMBOL_RET -void arch_symbol_ret(struct Process *proc, struct library_symbol *libsym) +void arch_symbol_ret(struct process *proc, struct library_symbol *libsym) { } #endif @@ -587,7 +678,7 @@ handle_breakpoint(Event *event) { int i, j; struct breakpoint *sbp; - Process *leader = event->proc->leader; + struct process *leader = event->proc->leader; void *brk_addr = event->e_un.brk_addr; /* The leader has terminated. */ @@ -602,15 +693,8 @@ handle_breakpoint(Event *event) for (i = event->proc->callstack_depth - 1; i >= 0; i--) { if (brk_addr == event->proc->callstack[i].return_addr) { - for (j = event->proc->callstack_depth - 1; j > i; j--) { + 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); - } - } - event->proc->return_addr = brk_addr; struct library_symbol *libsym = event->proc->callstack[i].c_un.libfunc; @@ -663,13 +747,14 @@ handle_breakpoint(Event *event) /* breakpoint_on_hit may delete its own breakpoint, so we have * to look it up again. */ if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) { + if (event->proc->state != STATE_IGNORED && sbp->libsym != NULL) { event->proc->stack_pointer = get_stack_pointer(event->proc); - event->proc->return_addr = - get_return_addr(event->proc, event->proc->stack_pointer); - callstack_push_symfunc(event->proc, sbp->libsym); - output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym); + callstack_push_symfunc(event->proc, sbp); + if (! options.summary) + output_left(LT_TOF_FUNCTION, event->proc, + sbp->libsym); } breakpoint_on_continue(sbp, event->proc); @@ -682,7 +767,8 @@ handle_breakpoint(Event *event) } static void -callstack_push_syscall(Process *proc, int sysnum) { +callstack_push_syscall(struct process *proc, int sysnum) +{ struct callstack_element *elem; debug(DEBUG_FUNCTION, "callstack_push_syscall(pid=%d, sysnum=%d)", proc->pid, sysnum); @@ -702,15 +788,17 @@ callstack_push_syscall(Process *proc, int sysnum) { proc->callstack_depth++; if (opt_T || options.summary) { struct timezone tz; - gettimeofday(&elem->time_spent, &tz); + gettimeofday(&elem->enter_time, &tz); } } static void -callstack_push_symfunc(Process *proc, struct library_symbol *sym) { +callstack_push_symfunc(struct process *proc, struct breakpoint *bp) +{ struct callstack_element *elem; - debug(DEBUG_FUNCTION, "callstack_push_symfunc(pid=%d, symbol=%s)", proc->pid, sym->name); + debug(DEBUG_FUNCTION, "callstack_push_symfunc(pid=%d, symbol=%s)", + proc->pid, bp->libsym->name); /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ if (proc->callstack_depth == MAX_CALLDEPTH - 1) { fprintf(stderr, "%s: Error: call nesting too deep!\n", __func__); @@ -721,28 +809,43 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { elem = &proc->callstack[proc->callstack_depth++]; *elem = (struct callstack_element){}; elem->is_syscall = 0; - elem->c_un.libfunc = sym; + elem->c_un.libfunc = bp->libsym; + + struct breakpoint *rbp = NULL; + if (breakpoint_get_return_bp(&rbp, bp, proc) == 0 + && rbp != NULL) { + struct breakpoint *ext_rbp = insert_breakpoint(proc, rbp); + if (ext_rbp != rbp) { + breakpoint_destroy(rbp); + free(rbp); + rbp = ext_rbp; + } + } - elem->return_addr = proc->return_addr; - if (elem->return_addr) - insert_breakpoint(proc, elem->return_addr, NULL); + elem->return_addr = rbp != NULL ? rbp->addr : 0; if (opt_T || options.summary) { struct timezone tz; - gettimeofday(&elem->time_spent, &tz); + gettimeofday(&elem->enter_time, &tz); } } void -callstack_pop(struct Process *proc) +callstack_pop(struct 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); + if (!elem->is_syscall && elem->return_addr) { + struct breakpoint *bp + = address2bpstruct(proc->leader, elem->return_addr); + if (bp != NULL) { + breakpoint_on_hit(bp, proc); + delete_breakpoint(proc, bp); + } + } if (elem->fetch_context != NULL) fetch_arg_done(elem->fetch_context);