From 87da5bc0473e5a8021021e73f7f40a5c0996269b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 2 Jan 2019 10:10:51 +0900 Subject: [PATCH 1/9] mcount: Skip restoring for mcount return functions In mcount_rstack_restore() it saves original return address in the stack. So no need to save our functions there. Signed-off-by: Namhyung Kim --- libmcount/misc.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libmcount/misc.c b/libmcount/misc.c index 807ccb727..a36012640 100644 --- a/libmcount/misc.c +++ b/libmcount/misc.c @@ -133,8 +133,15 @@ void mcount_rstack_restore(struct mcount_thread_data *mtdp) int idx; /* reverse order due to tail calls */ - for (idx = mtdp->idx - 1; idx >= 0; idx--) - *mtdp->rstack[idx].parent_loc = mtdp->rstack[idx].parent_ip; + for (idx = mtdp->idx - 1; idx >= 0; idx--) { + unsigned long parent_ip = mtdp->rstack[idx].parent_ip; + + if (parent_ip == (unsigned long)mcount_return || + parent_ip == (unsigned long)plthook_return) + continue; + + *mtdp->rstack[idx].parent_loc = parent_ip; + } } /* hook return address again (used after mcount_rstack_restore) */ From 348b8e5a86fb5a6ccafb132b22148c7cf823f0c0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 3 Jan 2019 16:45:21 +0900 Subject: [PATCH 2/9] mcount: Defer finish trigger in mcount_exit() It's subtle that finish can be triggered by signal so that it can see in the middle of mcount routine. Howevery we can delay the processing to the end when it sees the trigger in mcount_exit(). The mcount_exit() needs to know where to return and it requires rstack to be remained during the routine. Once it figured out the return address it can process the finish trigger to free the rstacks. Same for plthook_exit(). One subtlety is tail-call, which needs to return to itself from the mcount_exit(). However after rstack is destroyed it should not return to itself. To know the real return address, save the pointer where return address is saved, and reload the pointer. It's different for cygprof and xray routines since their _exit() can be called regardless entry() succeeded or not so no need to hook return addresses. This effectively reverts commit 368b2b4d ("mcount: Make mcount/plthook_ exit() return 0 when trace finished"). Signed-off-by: Namhyung Kim --- libmcount/internal.h | 3 +++ libmcount/mcount.c | 43 +++++++++++++++++++++++++++++++++---------- libmcount/plthook.c | 27 ++++++++++++++++++++------- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/libmcount/internal.h b/libmcount/internal.h index 73285e33f..7ddf9461f 100644 --- a/libmcount/internal.h +++ b/libmcount/internal.h @@ -111,6 +111,7 @@ struct mcount_thread_data { int record_idx; bool recursion_marker; bool in_exception; + bool dead; unsigned long cygprof_dummy; struct mcount_ret_stack *rstack; void *argbuf; @@ -144,6 +145,8 @@ static inline void mcount_restore_arch_context(struct mcount_arch_context *ctx) extern TLS struct mcount_thread_data mtd; +void __mcount_guard_recursion(struct mcount_thread_data *mtdp); +void __mcount_unguard_recursion(struct mcount_thread_data *mtdp); bool mcount_guard_recursion(struct mcount_thread_data *mtdp); void mcount_unguard_recursion(struct mcount_thread_data *mtdp); diff --git a/libmcount/mcount.c b/libmcount/mcount.c index 4f9b63e1e..effde80f4 100644 --- a/libmcount/mcount.c +++ b/libmcount/mcount.c @@ -327,12 +327,12 @@ static void mcount_trace_finish(bool send_msg) } /* to be used by pthread_create_key() */ -static void mtd_dtor(void *arg) +void mtd_dtor(void *arg) { struct mcount_thread_data *mtdp = arg; struct uftrace_msg_task tmsg; - if (mtdp->rstack == NULL) + if (mtdp->dead) return; if (mcount_should_stop()) @@ -340,11 +340,13 @@ static void mtd_dtor(void *arg) /* this thread is done, do not enter anymore */ mtdp->recursion_marker = true; + mtdp->dead = true; mcount_rstack_restore(mtdp); free(mtdp->rstack); mtdp->rstack = NULL; + mtdp->idx = 0; mcount_filter_release(mtdp); mcount_watch_release(mtdp); @@ -358,6 +360,16 @@ static void mtd_dtor(void *arg) uftrace_send_message(UFTRACE_MSG_TASK_END, &tmsg, sizeof(tmsg)); } +void __mcount_guard_recursion(struct mcount_thread_data *mtdp) +{ + mtdp->recursion_marker = true; +} + +void __mcount_unguard_recursion(struct mcount_thread_data *mtdp) +{ + mtdp->recursion_marker = false; +} + bool mcount_guard_recursion(struct mcount_thread_data *mtdp) { if (unlikely(mtdp->recursion_marker)) @@ -376,7 +388,7 @@ void mcount_unguard_recursion(struct mcount_thread_data *mtdp) { mtdp->recursion_marker = false; - if (mcount_should_stop()) + if (unlikely(mcount_should_stop())) mtd_dtor(mtdp); } @@ -999,33 +1011,44 @@ unsigned long mcount_exit(long *retval) { struct mcount_thread_data *mtdp; struct mcount_ret_stack *rstack; + unsigned long *ret_loc; unsigned long retaddr; mtdp = get_thread_data(); assert(mtdp != NULL); + assert(!mtdp->dead); /* - * if finish trigger was fired during the call, it already - * restored the original return address for us so just return. + * it's only called when mcount_entry() was succeeded + * no need to check recursion here. But still needs to + * prevent recursion during this call. */ - if (!mcount_guard_recursion(mtdp)) - return 0; + __mcount_guard_recursion(mtdp); rstack = &mtdp->rstack[mtdp->idx - 1]; rstack->end_time = mcount_gettime(); mcount_exit_filter_record(mtdp, rstack, retval); + ret_loc = rstack->parent_loc; retaddr = rstack->parent_ip; /* re-hijack return address of parent */ if (mcount_auto_recover) mcount_auto_reset(mtdp); - mcount_unguard_recursion(mtdp); + __mcount_unguard_recursion(mtdp); - if (unlikely(mcount_should_stop())) - retaddr = 0; + if (unlikely(mcount_should_stop())) { + mtd_dtor(mtdp); + /* + * mtd_dtor() will free rstack but current ret_addr + * might be plthook_return() when it was a tailcall. + * reload the return address after mtd_dtor() restored + * all the parent locations. + */ + retaddr = *ret_loc; + } compiler_barrier(); diff --git a/libmcount/plthook.c b/libmcount/plthook.c index 6b9b9918c..d538d218e 100644 --- a/libmcount/plthook.c +++ b/libmcount/plthook.c @@ -815,22 +815,26 @@ unsigned long plthook_entry(unsigned long *ret_addr, unsigned long child_idx, return real_addr; } +void mtd_dtor(void *arg); + unsigned long plthook_exit(long *retval) { unsigned dyn_idx; struct mcount_thread_data *mtdp; struct mcount_ret_stack *rstack; + unsigned long *ret_loc; unsigned long ret_addr = 0; mtdp = get_thread_data(); assert(mtdp != NULL); + assert(!mtdp->dead); /* - * there's a race with mcount_finish(), if it wins it already - * restored the original return address for us so just return. + * it's only called when mcount_entry() was succeeded + * no need to check recursion here. But still needs to + * prevent recursion during this call. */ - if (!mcount_guard_recursion(mtdp)) - return 0; + __mcount_guard_recursion(mtdp); again: if (likely(mtdp->idx > 0)) @@ -867,16 +871,25 @@ unsigned long plthook_exit(long *retval) mcount_exit_filter_record(mtdp, rstack, retval); update_pltgot(mtdp, rstack->pd, dyn_idx); + ret_loc = rstack->parent_loc; ret_addr = rstack->parent_ip; /* re-hijack return address of parent */ if (mcount_auto_recover) mcount_auto_reset(mtdp); - mcount_unguard_recursion(mtdp); + __mcount_unguard_recursion(mtdp); - if (unlikely(mcount_should_stop())) - ret_addr = 0; + if (unlikely(mcount_should_stop())) { + mtd_dtor(mtdp); + /* + * mtd_dtor() will free rstack but current ret_addr + * might be plthook_return() when it was a tailcall. + * reload the return address after mtd_dtor() restored + * all the parent locations. + */ + ret_addr = *ret_loc; + } compiler_barrier(); From 961518c8c22fe69e26b642c950b4260b0b159b29 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 3 Jan 2019 16:55:08 +0900 Subject: [PATCH 3/9] Revert "Handle mcount/plthook_return() return 0" This reverts following commits do same thing for each arch: * 09b8005c61f416decee1d84d5d176bf34e41c5f4 * 85ccd8106dcb16450eb7c5acf4f05c6b782d45f9 * 666040a9969cb2bd126d997316515d10252a8e79 * 977f4afdc4592a3624a9e7483a5d3a3ded88484b Signed-off-by: Namhyung Kim --- arch/aarch64/mcount.S | 9 +-------- arch/aarch64/plthook.S | 9 +-------- arch/arm/mcount.S | 6 ------ arch/arm/plthook.S | 6 ------ arch/i386/mcount.S | 5 +---- arch/i386/plthook.S | 5 +---- arch/x86_64/mcount.S | 5 +---- arch/x86_64/plthook.S | 5 +---- 8 files changed, 6 insertions(+), 44 deletions(-) diff --git a/arch/aarch64/mcount.S b/arch/aarch64/mcount.S index 867769204..fcb913a1b 100644 --- a/arch/aarch64/mcount.S +++ b/arch/aarch64/mcount.S @@ -41,16 +41,9 @@ ENTRY(mcount_return) add x0, sp, #16 bl mcount_exit - - cmp x0, #0 - b.eq 1f mov x16, x0 - b 2f - -1: /* if finish trigger fired, use restored address */ - ldr x16, [sp, #40] -2: /* restore return values */ + /* restore return values */ ldr d0, [sp], #16 ldp x0, x8, [sp], #16 /* restore frame pointer */ diff --git a/arch/aarch64/plthook.S b/arch/aarch64/plthook.S index 9a0aff150..15cc905db 100644 --- a/arch/aarch64/plthook.S +++ b/arch/aarch64/plthook.S @@ -81,16 +81,9 @@ ENTRY(plthook_return) add x0, sp, #16 bl plthook_exit - - cmp x0, #0 - b.eq 1f mov x16, x0 - b 2f - -1: /* if finish trigger fired, use restored address */ - ldr x16, [sp, #40] -2: /* restore return values */ + /* restore return values */ ldr d0, [sp], #16 ldp x0, x8, [sp], #16 /* restore frame pointer */ diff --git a/arch/arm/mcount.S b/arch/arm/mcount.S index 5b47eea2c..118bad8a3 100644 --- a/arch/arm/mcount.S +++ b/arch/arm/mcount.S @@ -73,13 +73,7 @@ ENTRY(mcount_return) #if HAVE_ARM_HARDFP vpop {d0} #endif - - /* if finish trigger fired, return address already updated */ - cmp r0, #0 - beq 1f - /* update return address (pc) in the stack */ str r0, [sp, #12] -1: pop {r0-r1, lr, pc} END(mcount_return) diff --git a/arch/arm/plthook.S b/arch/arm/plthook.S index 251369ef6..25712433f 100644 --- a/arch/arm/plthook.S +++ b/arch/arm/plthook.S @@ -65,13 +65,7 @@ ENTRY(plthook_return) #ifdef HAVE_ARM_HARDFP vpop {d0} #endif - - /* if finish trigger fired, return address already updated */ - cmp r0, #0 - beq 1f - /* update return address (pc) in the stack */ str r0, [sp, #12] -1: pop {r0-r1, lr, pc} END(plthook_return) diff --git a/arch/i386/mcount.S b/arch/i386/mcount.S index e78a37653..7734c0c07 100644 --- a/arch/i386/mcount.S +++ b/arch/i386/mcount.S @@ -43,11 +43,8 @@ ENTRY(mcount_return) /* returns original parent address */ call mcount_exit - - test %eax, %eax - jz 1f movl %eax, 12(%esp) -1: + movl 4(%esp), %eax movl 8(%esp), %edx add $12, %esp diff --git a/arch/i386/plthook.S b/arch/i386/plthook.S index 7ff790280..ef150ed8e 100644 --- a/arch/i386/plthook.S +++ b/arch/i386/plthook.S @@ -56,11 +56,8 @@ ENTRY(plthook_return) movl %eax, 0(%esp) call plthook_exit - - test %eax, %eax - jz 1f movl %eax, 12(%esp) -1: + movl 4(%esp), %eax movl 8(%esp), %edx add $12, %esp diff --git a/arch/x86_64/mcount.S b/arch/x86_64/mcount.S index d13788632..283b9c270 100644 --- a/arch/x86_64/mcount.S +++ b/arch/x86_64/mcount.S @@ -96,11 +96,8 @@ ENTRY(mcount_return) /* returns original parent address */ call mcount_exit + movq %rax, 40(%rsp) - test %rax, %rax - jz 1f - movq %rax, 40(%rsp) -1: movq 0(%rsp), %rax movq 8(%rsp), %rdx movdqu 16(%rsp), %xmm0 diff --git a/arch/x86_64/plthook.S b/arch/x86_64/plthook.S index 8d6f009ae..7e9c9df19 100644 --- a/arch/x86_64/plthook.S +++ b/arch/x86_64/plthook.S @@ -83,11 +83,8 @@ ENTRY(plthook_return) movq %rsp, %rdi call plthook_exit + movq %rax, 40(%rsp) - test %rax, %rax - jz 1f - movq %rax, 40(%rsp) -1: movq 0(%rsp), %rax movq 8(%rsp), %rdx movdqu 16(%rsp), %xmm0 From 22e7b8e48e309e49f8f6a3085b1de289a1ad3b83 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 4 Jan 2019 14:09:10 +0900 Subject: [PATCH 4/9] mcount: Fix possible segfault in segv_handler If finish trigger is fired, mtd might be freed so accessing rstack will cause another segfault. Also it's possible that it didn't handle any functions due to filters. Check the index before accessing rstack. Signed-off-by: Namhyung Kim --- libmcount/mcount.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libmcount/mcount.c b/libmcount/mcount.c index effde80f4..5ec762a80 100644 --- a/libmcount/mcount.c +++ b/libmcount/mcount.c @@ -421,6 +421,9 @@ static void segv_handler(int sig, siginfo_t *si, void *ctx) if (check_thread_data(mtdp)) goto out; + if (mtdp->idx <= 0) + goto out; + mcount_rstack_restore(mtdp); idx = mtdp->idx - 1; From 46a73d635748577e95614c4dd9516f54839582fe Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 14 Jan 2019 18:11:10 +0900 Subject: [PATCH 5/9] mcount: Fix backtrace in segv_handler The idx variable was reused for checking sigsegv_codes which resulted in invalid indices for backtrace print. Signed-off-by: Namhyung Kim --- libmcount/mcount.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libmcount/mcount.c b/libmcount/mcount.c index 5ec762a80..17f1d400e 100644 --- a/libmcount/mcount.c +++ b/libmcount/mcount.c @@ -432,17 +432,19 @@ static void segv_handler(int sig, siginfo_t *si, void *ctx) record_trace_data(mtdp, rstack, NULL); if (dbg_domain[PR_DOMAIN]) { - for (idx = 0; idx < (int)ARRAY_SIZE(sigsegv_codes); idx++) { + int i; + + for (i = 0; i < (int)ARRAY_SIZE(sigsegv_codes); i++) { if (sig != SIGSEGV) break; - if (si->si_code == sigsegv_codes[idx].code) { + if (si->si_code == sigsegv_codes[i].code) { pr_red("Segmentation fault: %s (addr: %p)\n", - sigsegv_codes[idx].msg, si->si_addr); + sigsegv_codes[i].msg, si->si_addr); break; } } - if (sig != SIGSEGV || idx == (int)ARRAY_SIZE(sigsegv_codes)) { + if (sig != SIGSEGV || i == (int)ARRAY_SIZE(sigsegv_codes)) { pr_red("process crashed by signal %d: %s (si_code: %d)\n", sig, strsignal(sig), si->si_code); } From 5362c64ca557c6910215fb984e4292af0c8d8876 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 30 Jun 2016 16:42:26 +0900 Subject: [PATCH 6/9] mcount: Implement signal trigger The signal trigger is to do some action when given signal is received. Currently 'trace-on', 'trace-off' and 'finish' actions are supported. Signed-off-by: Namhyung Kim --- libmcount/mcount.c | 257 +++++++++++++++++++++++++++++++++++++++++++-- utils/filter.c | 4 +- utils/filter.h | 7 ++ 3 files changed, 256 insertions(+), 12 deletions(-) diff --git a/libmcount/mcount.c b/libmcount/mcount.c index 17f1d400e..a1a68235c 100644 --- a/libmcount/mcount.c +++ b/libmcount/mcount.c @@ -132,6 +132,250 @@ static void prepare_pmu_trigger(struct rb_root *root) } } +/* be careful: this can be called from signal handler */ +static void mcount_finish_trigger(void) +{ + if (mcount_global_flags & MCOUNT_GFL_FINISH) + return; + + /* mark other threads can see the finish flag */ + mcount_global_flags |= MCOUNT_GFL_FINISH; +} + +static LIST_HEAD(siglist); + +struct signal_trigger_item { + struct list_head list; + int sig; + struct uftrace_trigger tr; +}; + +static struct uftrace_trigger * get_signal_trigger(int sig) +{ + struct signal_trigger_item *item; + + list_for_each_entry(item, &siglist, list) { + if (item->sig == sig) + return &item->tr; + } + + return NULL; +} + +static void add_signal_trigger(int sig, const char *name, + struct uftrace_trigger *tr) +{ + struct signal_trigger_item *item; + + item = xmalloc(sizeof(*item)); + item->sig = sig; + memcpy(&item->tr, tr, sizeof(*tr)); + + pr_dbg("add signal trigger: %s (%d), flags = %lx\n", + name, sig, tr->flags); + + list_add(&item->list, &siglist); +} + +static void mcount_signal_trigger(int sig) +{ + struct uftrace_trigger *tr; + + tr = get_signal_trigger(sig); + if (tr == NULL) + return; + + pr_dbg("got signal %d\n", sig); + + if (tr->flags & TRIGGER_FL_TRACE_ON) { + mcount_enabled = true; + } + if (tr->flags & TRIGGER_FL_TRACE_OFF) { + mcount_enabled = false; + } + if (tr->flags & TRIGGER_FL_FINISH) { + mcount_finish_trigger(); + } +} + +#define SIGTABLE_ENTRY(s) { #s, s } + +static const struct sigtable { + const char *name; + int sig; +} sigtable[] = { + SIGTABLE_ENTRY(SIGHUP), + SIGTABLE_ENTRY(SIGINT), + SIGTABLE_ENTRY(SIGQUIT), + SIGTABLE_ENTRY(SIGILL), + SIGTABLE_ENTRY(SIGTRAP), + SIGTABLE_ENTRY(SIGABRT), + SIGTABLE_ENTRY(SIGBUS), + SIGTABLE_ENTRY(SIGFPE), + SIGTABLE_ENTRY(SIGKILL), + SIGTABLE_ENTRY(SIGUSR1), + SIGTABLE_ENTRY(SIGSEGV), + SIGTABLE_ENTRY(SIGUSR2), + SIGTABLE_ENTRY(SIGPIPE), + SIGTABLE_ENTRY(SIGALRM), + SIGTABLE_ENTRY(SIGTERM), + SIGTABLE_ENTRY(SIGSTKFLT), + SIGTABLE_ENTRY(SIGCHLD), + SIGTABLE_ENTRY(SIGCONT), + SIGTABLE_ENTRY(SIGSTOP), + SIGTABLE_ENTRY(SIGTSTP), + SIGTABLE_ENTRY(SIGTTIN), + SIGTABLE_ENTRY(SIGTTOU), + SIGTABLE_ENTRY(SIGURG), + SIGTABLE_ENTRY(SIGXCPU), + SIGTABLE_ENTRY(SIGXFSZ), + SIGTABLE_ENTRY(SIGVTALRM), + SIGTABLE_ENTRY(SIGPROF), + SIGTABLE_ENTRY(SIGWINCH), + SIGTABLE_ENTRY(SIGIO), + SIGTABLE_ENTRY(SIGPWR), + SIGTABLE_ENTRY(SIGSYS), +}; + +#undef SIGTABLE_ENTRY + +static int parse_sigspec(char *spec, struct uftrace_filter_setting *setting) +{ + char *pos, *tmp; + unsigned i; + int sig = -1; + int off = 0; + const char *signame = NULL; + bool num_spec = false; + char num_spec_str[16]; + struct uftrace_trigger tr = { + .flags = 0, + }; + struct sigaction old_sa; + struct sigaction sa = { + .sa_handler = mcount_signal_trigger, + .sa_flags = SA_RESTART, + }; + + pos = strchr(spec, '@'); + if (pos == NULL) + return -1; + *pos = '\0'; + + if (isdigit(spec[0])) + num_spec = true; + else if (strncmp(spec, "SIG", 3)) + off = 3; /* skip "SIG" prefix */ + + for (i = 0; i < ARRAY_SIZE(sigtable); i++) { + if (num_spec) { + int num = strtol(spec, &tmp, 0); + + if (num == sigtable[i].sig) { + sig = num; + signame = sigtable[i].name; + break; + } + + continue; + } + + if (!strcmp(sigtable[i].name + off, spec)) { + sig = sigtable[i].sig; + signame = sigtable[i].name; + break; + } + } + + /* real-time signals */ + if (!strncmp(spec, "SIGRTM" + off, 6 - off)) { + if (!strncmp(spec, "SIGRTMIN" + off, 8 - off)) + sig = SIGRTMIN + strtol(&spec[8 - off], NULL, 0); + if (!strncmp(spec, "SIGRTMAX" + off, 8 - off)) + sig = SIGRTMAX + strtol(&spec[8 - off], NULL, 0); + signame = spec; + } + + if (sig == -1 && num_spec) { + int sigrtmid = (SIGRTMIN + SIGRTMAX) / 2; + + sig = strtol(spec, &tmp, 0); + + /* SIGRTMIN/MAX might not be constant, avoid switch/case */ + if (sig == SIGRTMIN) { + strcpy(num_spec_str, "SIGRTMIN"); + } + else if (SIGRTMIN < sig && sig <= sigrtmid) { + snprintf(num_spec_str, sizeof(num_spec_str), "%s+%d", + "SIGRTMIN", sig - SIGRTMIN); + } + else if (sigrtmid < sig && sig < SIGRTMAX) { + snprintf(num_spec_str, sizeof(num_spec_str), "%s-%d", + "SIGRTMAX", SIGRTMAX - sig); + } + else if (sig == SIGRTMAX) { + strcpy(num_spec_str, "SIGRTMAX"); + } + else { + sig = -1; + } + signame = num_spec_str; + } + + if (sig == -1) { + pr_use("failed to parse signal: %s\n", spec); + return -1; + } + + /* setup_trigger_action() requires the '@' sign */ + *pos = '@'; + + if (setup_trigger_action(spec, &tr, &tmp, TRIGGER_FL_SIGNAL, setting) < 0) + return -1; + + add_signal_trigger(sig, signame, &tr); + if (sigaction(sig, &sa, &old_sa) < 0) { + pr_warn("cannot overwrite signal handler for %s\n", spec); + sigaction(sig, &old_sa, NULL); + return -1; + } + + return 0; +} + +static int mcount_signal_init(char *sigspec, + struct uftrace_filter_setting *setting) +{ + struct strv strv = STRV_INIT; + char *spec; + int i; + int ret = 0; + + if (sigspec == NULL) + return 0; + + strv_split(&strv, sigspec, ";"); + + strv_for_each(&strv, spec, i) { + if (parse_sigspec(spec, setting) < 0) + ret = -1; + } + strv_free(&strv); + + return ret; +} + +static void mcount_signal_finish(void) +{ + struct signal_trigger_item *item; + + while (!list_empty(&siglist)) { + item = list_first_entry(&siglist, typeof(*item), list); + list_del(&item->list); + free(item); + } +} + static void mcount_filter_init(enum uftrace_pattern_type ptype, char *dirname, bool force) { @@ -152,6 +396,8 @@ static void mcount_filter_init(enum uftrace_pattern_type ptype, char *dirname, load_module_symtabs(&symtabs); + mcount_signal_init(getenv("UFTRACE_SIGNAL"), &filter_setting); + /* setup auto-args only if argument/return value is used */ if (argument_str || retval_str || autoargs_str || (trigger_str && (strstr(trigger_str, "arg") || @@ -232,6 +478,7 @@ static void mcount_filter_finish(void) finish_debug_info(&symtabs); finish_pmu_event(); + mcount_signal_finish(); } static void mcount_watch_init(void) @@ -701,16 +948,6 @@ static void script_hook_exit(struct mcount_thread_data *mtdp, symbol_putname(sym, symname); } -/* be careful: this can be called from signal handler */ -static void mcount_finish_trigger(void) -{ - if (mcount_global_flags & MCOUNT_GFL_FINISH) - return; - - /* mark other threads can see the finish flag */ - mcount_global_flags |= MCOUNT_GFL_FINISH; -} - /* save current filter state to rstack */ void mcount_entry_filter_record(struct mcount_thread_data *mtdp, struct mcount_ret_stack *rstack, diff --git a/utils/filter.c b/utils/filter.c index 58d4c8ee8..2452d2a1e 100644 --- a/utils/filter.c +++ b/utils/filter.c @@ -844,12 +844,12 @@ static const struct trigger_action_parser actions[] = { { "depth=", parse_depth_action, TRIGGER_FL_FILTER, }, { "time=", parse_time_action, TRIGGER_FL_FILTER, }, { "caller", parse_caller_action, TRIGGER_FL_FILTER, }, + { "trace", parse_trace_action, TRIGGER_FL_SIGNAL, }, + { "finish", parse_finish_action, TRIGGER_FL_SIGNAL, }, { "read=", parse_read_action, }, { "color=", parse_color_action, }, - { "trace", parse_trace_action, }, { "backtrace", parse_backtrace_action, }, { "recover", parse_recover_action, }, - { "finish", parse_finish_action, }, { "auto-args", parse_auto_args_action, }, }; diff --git a/utils/filter.h b/utils/filter.h index 25e6c98ca..b455bb56c 100644 --- a/utils/filter.h +++ b/utils/filter.h @@ -33,6 +33,7 @@ enum trigger_flag { TRIGGER_FL_FINISH = (1U << 13), TRIGGER_FL_AUTO_ARGS = (1U << 14), TRIGGER_FL_CALLER = (1U << 15), + TRIGGER_FL_SIGNAL = (1U << 16), }; enum filter_mode { @@ -225,4 +226,10 @@ void release_enum_def(struct rb_root *root); extern struct rb_root dwarf_enum; +void add_trigger(struct uftrace_filter *filter, struct uftrace_trigger *tr, + bool exact_match); +int setup_trigger_action(char *str, struct uftrace_trigger *tr, char **module, + unsigned long orig_flags, + struct uftrace_filter_setting *setting); + #endif /* UFTRACE_FILTER_H */ From 0ba73d0b324d82946095d898c9fadb977eeb7853 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 7 Jan 2019 11:46:18 +0900 Subject: [PATCH 7/9] mcount: Add ARCH_CAN_RESTORE_PLTHOOK logic Some arch like ARM/AArch64 call PLT functions before setting return address in the stack as they have the linker register to save it. So plt_hooker() sees an intermediate address it sets up during the hooking and the address cannot be used to restore real return address after plt_hooker() returns. That means it cannot restore all return stacks (rstack) from the finish trigger and needs to keep up the rstack for PLT functions to be returned to the real callers after plthook_exit(). Signed-off-by: Namhyung Kim --- arch/i386/mcount-arch.h | 2 ++ arch/x86_64/mcount-arch.h | 1 + libmcount/internal.h | 11 +++++++++++ libmcount/mcount.c | 13 +++++-------- libmcount/misc.c | 34 +++++++++++++++++++++++++++++----- libmcount/plthook.c | 28 ++++++++++++++++++++++------ 6 files changed, 70 insertions(+), 19 deletions(-) diff --git a/arch/i386/mcount-arch.h b/arch/i386/mcount-arch.h index 535801597..a331648f8 100644 --- a/arch/i386/mcount-arch.h +++ b/arch/i386/mcount-arch.h @@ -29,4 +29,6 @@ unsigned long * mcount_arch_parent_location(struct symtabs *symtabs, #define ARCH_PLT0_SIZE 16 #define ARCH_PLTHOOK_ADDR_OFFSET 6 +#define ARCH_CAN_RESTORE_PLTHOOK 1 + #endif /* __MCOUNT_ARCH_H__ */ diff --git a/arch/x86_64/mcount-arch.h b/arch/x86_64/mcount-arch.h index c9e4ef704..3a616f514 100644 --- a/arch/x86_64/mcount-arch.h +++ b/arch/x86_64/mcount-arch.h @@ -33,5 +33,6 @@ struct mcount_arch_context { #define ARCH_PLTHOOK_ADDR_OFFSET 6 #define ARCH_SUPPORT_AUTO_RECOVER 1 +#define ARCH_CAN_RESTORE_PLTHOOK 1 #endif /* MCOUNT_ARCH_H */ diff --git a/libmcount/internal.h b/libmcount/internal.h index 7ddf9461f..6cb804e6e 100644 --- a/libmcount/internal.h +++ b/libmcount/internal.h @@ -24,6 +24,16 @@ #include "utils/filter.h" #include "utils/compiler.h" +/* could be defined in mcount-arch.h */ +#ifndef ARCH_SUPPORT_AUTO_RECOVER +# define ARCH_SUPPORT_AUTO_RECOVER 0 +#endif + +/* plt_hooker has return address to hook */ +#ifndef ARCH_CAN_RESTORE_PLTHOOK +# define ARCH_CAN_RESTORE_PLTHOOK 0 +#endif + enum filter_result { FILTER_RSTACK = -1, FILTER_OUT, @@ -251,6 +261,7 @@ extern void mcount_rstack_reset_exception(struct mcount_thread_data *mtdp, unsigned long frame_addr); extern void mcount_auto_restore(struct mcount_thread_data *mtdp); extern void mcount_auto_reset(struct mcount_thread_data *mtdp); +extern bool mcount_rstack_has_plthook(struct mcount_thread_data *mtdp); extern void prepare_shmem_buffer(struct mcount_thread_data *mtdp); extern void clear_shmem_buffer(struct mcount_thread_data *mtdp); diff --git a/libmcount/mcount.c b/libmcount/mcount.c index a1a68235c..87426d7a0 100644 --- a/libmcount/mcount.c +++ b/libmcount/mcount.c @@ -30,11 +30,6 @@ #include "utils/filter.h" #include "utils/script.h" -/* could be defined in mcount-arch.h */ -#ifndef ARCH_SUPPORT_AUTO_RECOVER -# define ARCH_SUPPORT_AUTO_RECOVER 0 -#endif - /* time filter in nsec */ uint64_t mcount_threshold; @@ -591,9 +586,11 @@ void mtd_dtor(void *arg) mcount_rstack_restore(mtdp); - free(mtdp->rstack); - mtdp->rstack = NULL; - mtdp->idx = 0; + if (ARCH_CAN_RESTORE_PLTHOOK || !mcount_rstack_has_plthook(mtdp)) { + free(mtdp->rstack); + mtdp->rstack = NULL; + mtdp->idx = 0; + } mcount_filter_release(mtdp); mcount_watch_release(mtdp); diff --git a/libmcount/misc.c b/libmcount/misc.c index a36012640..d919c6a6a 100644 --- a/libmcount/misc.c +++ b/libmcount/misc.c @@ -127,20 +127,36 @@ void build_debug_domain(char *dbg_domain_str) } } +bool mcount_rstack_has_plthook(struct mcount_thread_data *mtdp) +{ + int idx; + + for (idx = 0; idx < mtdp->idx; idx++) { + if (mtdp->rstack[idx].dyn_idx != MCOUNT_INVALID_DYNIDX) + return true; + } + return false; +} + /* restore saved original return address */ void mcount_rstack_restore(struct mcount_thread_data *mtdp) { int idx; + struct mcount_ret_stack *rstack; /* reverse order due to tail calls */ for (idx = mtdp->idx - 1; idx >= 0; idx--) { - unsigned long parent_ip = mtdp->rstack[idx].parent_ip; + rstack = &mtdp->rstack[idx]; - if (parent_ip == (unsigned long)mcount_return || - parent_ip == (unsigned long)plthook_return) + if (rstack->parent_ip == (unsigned long)mcount_return || + rstack->parent_ip == (unsigned long)plthook_return) + continue; + + if (!ARCH_CAN_RESTORE_PLTHOOK && + rstack->dyn_idx != MCOUNT_INVALID_DYNIDX) continue; - *mtdp->rstack[idx].parent_loc = parent_ip; + *rstack->parent_loc = rstack->parent_ip; } } @@ -155,7 +171,7 @@ void mcount_rstack_reset(struct mcount_thread_data *mtdp) if (rstack->dyn_idx == MCOUNT_INVALID_DYNIDX) *rstack->parent_loc = (unsigned long)mcount_return; - else + else if (ARCH_CAN_RESTORE_PLTHOOK) *rstack->parent_loc = (unsigned long)plthook_return; } } @@ -175,6 +191,10 @@ void mcount_auto_restore(struct mcount_thread_data *mtdp) curr_rstack = &mtdp->rstack[mtdp->idx - 1]; prev_rstack = &mtdp->rstack[mtdp->idx - 2]; + if (!ARCH_CAN_RESTORE_PLTHOOK && + prev_rstack->dyn_idx != MCOUNT_INVALID_DYNIDX) + return; + /* ignore tail calls */ if (curr_rstack->parent_loc == prev_rstack->parent_loc) return; @@ -209,6 +229,10 @@ void mcount_auto_reset(struct mcount_thread_data *mtdp) curr_rstack = &mtdp->rstack[mtdp->idx - 1]; prev_rstack = &mtdp->rstack[mtdp->idx - 2]; + if (!ARCH_CAN_RESTORE_PLTHOOK && + prev_rstack->dyn_idx != MCOUNT_INVALID_DYNIDX) + return; + /* ignore tail calls */ if (curr_rstack->parent_loc == prev_rstack->parent_loc) return; diff --git a/libmcount/plthook.c b/libmcount/plthook.c index d538d218e..dcd437b9c 100644 --- a/libmcount/plthook.c +++ b/libmcount/plthook.c @@ -728,7 +728,7 @@ unsigned long plthook_entry(unsigned long *ret_addr, unsigned long child_idx, if (unlikely(special_flag & PLT_FL_SKIP)) goto out; - if (pd->dsymtab.nr_sym && child_idx < pd->dsymtab.nr_sym) { + if (likely(child_idx < pd->dsymtab.nr_sym)) { sym = &pd->dsymtab.sym[child_idx]; pr_dbg2("[mod: %lx, idx: %d] enter %lx: %s\n", module_id, child_idx, sym->addr, sym->name); @@ -806,7 +806,8 @@ unsigned long plthook_entry(unsigned long *ret_addr, unsigned long child_idx, } out: - if (pd && pd->resolved_addr[child_idx]) + if (likely(pd && child_idx < pd->dsymtab.nr_sym) && + pd->resolved_addr[child_idx] != 0) real_addr = pd->resolved_addr[child_idx]; if (!recursion) @@ -826,8 +827,7 @@ unsigned long plthook_exit(long *retval) unsigned long ret_addr = 0; mtdp = get_thread_data(); - assert(mtdp != NULL); - assert(!mtdp->dead); + assert(!check_thread_data(mtdp)); /* * it's only called when mcount_entry() was succeeded @@ -858,9 +858,24 @@ unsigned long plthook_exit(long *retval) rstack = restore_vfork(mtdp, rstack); dyn_idx = rstack->dyn_idx; - if (dyn_idx == MCOUNT_INVALID_DYNIDX) + if (unlikely(dyn_idx == MCOUNT_INVALID_DYNIDX || + dyn_idx >= rstack->pd->dsymtab.nr_sym)) pr_err_ns("<%d> invalid dynsym idx: %d\n", mtdp->idx, dyn_idx); + if (!ARCH_CAN_RESTORE_PLTHOOK && unlikely(mtdp->dead)) { + ret_addr = rstack->parent_ip; + + /* make sure it doesn't have plthook below */ + mtdp->idx--; + + if (!mcount_rstack_has_plthook(mtdp)) { + free(mtdp->rstack); + mtdp->rstack = NULL; + mtdp->idx = 0; + } + return ret_addr; + } + pr_dbg3("[%d] exit %lx: %s\n", dyn_idx, rstack->pd->resolved_addr[dyn_idx], rstack->pd->dsymtab.sym[dyn_idx].name); @@ -888,7 +903,8 @@ unsigned long plthook_exit(long *retval) * reload the return address after mtd_dtor() restored * all the parent locations. */ - ret_addr = *ret_loc; + if (ARCH_CAN_RESTORE_PLTHOOK) + ret_addr = *ret_loc; } compiler_barrier(); From 33a2496c5e8468ff2b9469aad1c21b4ac5ee221a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 30 Jun 2016 16:50:58 +0900 Subject: [PATCH 8/9] record: Add --signal option The --signal option is to setup triggers on signal. This can be useful to control long-running programs like servers/daemons. Signed-off-by: Namhyung Kim --- cmds/info.c | 8 ++++---- cmds/record.c | 14 ++++++++++++-- doc/uftrace-live.md | 19 +++++++++++++++---- doc/uftrace-record.md | 21 ++++++++++++++++----- uftrace.c | 6 ++++++ uftrace.h | 3 ++- 6 files changed, 55 insertions(+), 16 deletions(-) diff --git a/cmds/info.c b/cmds/info.c index af9dae104..1f3ea1835 100644 --- a/cmds/info.c +++ b/cmds/info.c @@ -1096,7 +1096,10 @@ void process_uftrace_info(struct uftrace_data *handle, struct opts *opts, if (info_mask & (1UL << EXIT_STATUS)) { int status = info->exit_status; - if (WIFEXITED(status)) { + if (status == UFTRACE_EXIT_FINISHED) { + snprintf(buf, sizeof(buf), "terminated by finish trigger"); + } + else if (WIFEXITED(status)) { snprintf(buf, sizeof(buf), "exited with code: %d", WEXITSTATUS(status)); } @@ -1104,9 +1107,6 @@ void process_uftrace_info(struct uftrace_data *handle, struct opts *opts, snprintf(buf, sizeof(buf), "terminated by signal: %d", WTERMSIG(status)); } - else if (status == UFTRACE_EXIT_FINISHED) { - snprintf(buf, sizeof(buf), "terminated by finish trigger"); - } else { snprintf(buf, sizeof(buf), "unknown exit status: %d", status); diff --git a/cmds/record.c b/cmds/record.c index 4738e6dab..031ed0846 100644 --- a/cmds/record.c +++ b/cmds/record.c @@ -70,7 +70,7 @@ static bool can_use_fast_libmcount(struct opts *opts) getenv("UFTRACE_ARGUMENT") || getenv("UFTRACE_RETVAL") || getenv("UFTRACE_PATCH") || getenv("UFTRACE_SCRIPT") || getenv("UFTRACE_AUTO_ARGS") || getenv("UFTRACE_WATCH") || - getenv("UFTRACE_CALLER")) + getenv("UFTRACE_CALLER") || getenv("UFTRACE_SIGNAL")) return false; return true; } @@ -306,6 +306,9 @@ static void setup_child_environ(struct opts *opts, int pfd, if (opts->patt_type != PATT_REGEX) setenv("UFTRACE_PATTERN", get_filter_pattern(opts->patt_type), 1); + if (opts->sig_trigger) + setenv("UFTRACE_SIGNAL", opts->sig_trigger, 1); + if (argc > 0) { char *args = NULL; int i; @@ -317,6 +320,10 @@ static void setup_child_environ(struct opts *opts, int pfd, free(args); } + /* + * ----- end of option processing ----- + */ + libpath = get_libmcount_path(opts); if (libpath == NULL) pr_err_ns("cannot found libmcount.so\n"); @@ -1748,7 +1755,7 @@ static int stop_tracing(struct writer_data *wd, struct opts *opts) break; if (finish_received) { - ret = UFTRACE_EXIT_FINISHED; + status = UFTRACE_EXIT_FINISHED; break; } @@ -1888,6 +1895,9 @@ int do_main_loop(int pfd[2], int ready, struct opts *opts, int pid) wd.pipefd = pfd[0]; close(pfd[1]); + if (opts->sig_trigger) + pr_out("uftrace: install signal handlers to task %d\n", pid); + setup_writers(&wd, opts); start_tracing(&wd, opts, ready); close(ready); diff --git a/doc/uftrace-live.md b/doc/uftrace-live.md index 292bb5972..964b0666e 100644 --- a/doc/uftrace-live.md +++ b/doc/uftrace-live.md @@ -214,6 +214,12 @@ OPTIONS -W, \--watch=*POINT* : Add watch point to display POINT if the value is changed. See *WATCH POINT*. +\--signal=*TRG* +: Set trigger on selected signals rather than functions. But there are + restrictions so only a few of trigger actions are support for signals. + The available actions are: traceon, traceoff, finish. + This option can be used more than once. See *TRIGGERS*. + FILTERS ======= @@ -319,10 +325,9 @@ for details. TRIGGERS ======== -The uftrace tool supports triggering actions on selected function calls with or -without filters. Currently supported triggers are `depth` (for record and -replay) and `backtrace` (for replay only). The BNF for trigger specifications -is like below: +The uftrace tool supports triggering actions on selected function calls (with or +without filters) and/or signals. Currently supported triggers are listed below. +The BNF for trigger specifications is like below: := "@" := | "," @@ -400,6 +405,12 @@ The 'filter' and 'notrace' triggers have same effect as -F/\--filter and Triggers only work for user-level functions for now. +The trigger can be used for signals as well. This is done by signal trigger +with \--signal option. The syntax is similar to function trigger but only +"trace_on", "trace_off" and "finish" trigger actions are supported. + + $ uftrace live --signal 'SIGUSR1@finish' ./some-daemon + ARGUMENTS ========= diff --git a/doc/uftrace-record.md b/doc/uftrace-record.md index 0d68932e3..62b28ae69 100644 --- a/doc/uftrace-record.md +++ b/doc/uftrace-record.md @@ -174,6 +174,12 @@ OPTIONS -W, \--watch=*POINT* : Add watch point to display POINT if the value is changed. See *WATCH POINT*. +\--signal=*TRG* +: Set trigger on selected signals rather than functions. But there are + restrictions so only a few of trigger actions are support for signals. + The available actions are: traceon, traceoff, finish. + This option can be used more than once. See *TRIGGERS*. + FILTERS ======= @@ -325,8 +331,8 @@ example will show all user functions and the (kernel) page fault handler. TRIGGERS ======== -The uftrace tool supports triggering actions on selected function calls with or -without filters. Currently supported triggers are listed below. +The uftrace tool supports triggering actions on selected function calls (with or +without filters) and/or signals. Currently supported triggers are listed below. The BNF for trigger specification is: := "@" @@ -335,9 +341,8 @@ The BNF for trigger specification is: "time=" | "read=" | "finish" | "filter" | "notrace" | "recover" := [ ] - := "ns" | "us" | "ms" | "s" - := "proc/statm" | "page-fault" | "pmu-cycle" | "pmu-cache" | - "pmu-branch" + := "ns" | "nsec" | "us" | "usec" | "ms" | "msec" | "s" | "sec" | "m" | "min" + := "proc/statm" | "page-fault" | "pmu-cycle" | "pmu-cache" | "pmu-branch" The `depth` trigger is to change filter depth during execution of the function. It can be used to apply different filter depths for different functions. @@ -408,6 +413,12 @@ The 'filter' and 'notrace' triggers have same effect as `-F`/`--filter` and Triggers only work for user-level functions for now. +The trigger can be used for signals as well. This is done by signal trigger +with \--signal option. The syntax is similar to function trigger but only +"trace_on", "trace_off" and "finish" trigger actions are supported. + + $ uftrace record --signal 'SIGUSR1@finish' ./some-daemon + ARGUMENTS ========= diff --git a/uftrace.c b/uftrace.c index 1dbe9f817..3b2ff5863 100644 --- a/uftrace.c +++ b/uftrace.c @@ -97,6 +97,7 @@ enum options { OPT_match_type, OPT_no_randomize_addr, OPT_no_event, + OPT_signal, }; static struct argp_option uftrace_options[] = { @@ -174,6 +175,7 @@ static struct argp_option uftrace_options[] = { { "no-randomize-addr", OPT_no_randomize_addr, 0, 0, "Disable ASLR (Address Space Layout Randomization)" }, { "no-event", OPT_no_event, 0, 0, "Disable (default) events" }, { "watch", 'W', "POINT", 0, "Watch and report POINT if it's changed" }, + { "signal", OPT_signal, "SIG@act[,act,...]", 0, "Trigger action on those SIGnal" }, { "help", 'h', 0, 0, "Give this help list" }, { 0 } }; @@ -756,6 +758,10 @@ static error_t parse_option(int key, char *arg, struct argp_state *state) opts->no_event = true; break; + case OPT_signal: + opts->sig_trigger = opt_add_string(opts->sig_trigger, arg); + break; + case ARGP_KEY_ARG: if (state->arg_num) { /* diff --git a/uftrace.h b/uftrace.h index 3e15e1cc8..fb298f8a2 100644 --- a/uftrace.h +++ b/uftrace.h @@ -132,8 +132,8 @@ enum { UFTRACE_EXIT_SUCCESS = 0, UFTRACE_EXIT_FAILURE, UFTRACE_EXIT_SIGNALED, - UFTRACE_EXIT_FINISHED, UFTRACE_EXIT_UNKNOWN, + UFTRACE_EXIT_FINISHED = 1 << 16, }; struct kbuffer; @@ -194,6 +194,7 @@ struct opts { char *lib_path; char *filter; char *trigger; + char *sig_trigger; char *tid; char *exename; char *dirname; From 770faff161f0397842fdf3bb455775e44673af28 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 14 Nov 2018 16:13:28 +0900 Subject: [PATCH 9/9] test: Add a test case for signal trigger $ uftrace -a --signal SIGUSR1@finish tests/t-signal # DURATION TID FUNCTION [ 13635] | main(1, 0x7ffd5c074428) { 0.245 us [ 13635] | foo() = 0; 1.572 us [ 13635] | signal(SIGUSR1, &sighandler) = 0x7f20827b9fd0; [ 13635] | raise() { [ 13635] | sighandler(10) { 0.102 us [ 13635] | bar(10); [ 13635] | /* linux:task-exit */ 4.376 us [ 13635] | } /* sighandler */ uftrace stopped tracing with remaining functions ================================================ task: 13635 [2] sighandler [1] raise [0] main Signed-off-by: Namhyung Kim --- libmcount/mcount.c | 30 ++++++++++++++++++++++++++++++ tests/runtest.py | 2 +- tests/s-signal.c | 15 +++++++++------ tests/t214_signal_trigger.py | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 tests/t214_signal_trigger.py diff --git a/libmcount/mcount.c b/libmcount/mcount.c index 87426d7a0..675e36498 100644 --- a/libmcount/mcount.c +++ b/libmcount/mcount.c @@ -1844,4 +1844,34 @@ TEST_CASE(mcount_thread_data) return TEST_OK; } +TEST_CASE(mcount_signal_setup) +{ + struct signal_trigger_item *item; + struct uftrace_filter_setting setting = { + .ptype = PATT_NONE, + }; + + /* it signal triggers are maintained in a stack (LIFO) */ + mcount_signal_init("SIGUSR1@traceon;USR2@traceoff;RTMIN+3@finish", + &setting); + + item = list_first_entry(&siglist, typeof(*item), list); + TEST_EQ(item->sig, SIGRTMIN + 3); + TEST_EQ(item->tr.flags, TRIGGER_FL_FINISH); + + item = list_next_entry(item, list); + TEST_EQ(item->sig, SIGUSR2); + TEST_EQ(item->tr.flags, TRIGGER_FL_TRACE_OFF); + + item = list_next_entry(item, list); + TEST_EQ(item->sig, SIGUSR1); + TEST_EQ(item->tr.flags, TRIGGER_FL_TRACE_ON); + + mcount_signal_finish(); + + TEST_EQ(list_empty(&siglist), true); + + return TEST_OK; +} + #endif /* UNIT_TEST */ diff --git a/tests/runtest.py b/tests/runtest.py index aa7ccd7e0..466eada5d 100755 --- a/tests/runtest.py +++ b/tests/runtest.py @@ -166,7 +166,7 @@ def task_sort(self, output, ignore_children=False): # ignore result of remaining functions which follows a blank line if ln.strip() == '': break - pid_patt = re.compile('[^[]*\[ *(\d+)\] |') + pid_patt = re.compile('[^[]+\[ *(\d+)\] |') m = pid_patt.match(ln) try: pid = int(m.group(1)) diff --git a/tests/s-signal.c b/tests/s-signal.c index e62885df1..524acabe6 100644 --- a/tests/s-signal.c +++ b/tests/s-signal.c @@ -3,12 +3,12 @@ volatile int dummy; -void foo(void) +typedef void (*sighandler_t)(int sig); +sighandler_t old_handler; + +int foo(void) { - if (dummy == 0) - dummy = 1; - else - dummy = 0; + return dummy; } void bar(int n) @@ -19,6 +19,9 @@ void bar(int n) void sighandler(int sig) { bar(sig); + + if (old_handler != SIG_DFL) + old_handler(sig); } int main(int argc, char *argv[]) @@ -29,7 +32,7 @@ int main(int argc, char *argv[]) sig = atoi(argv[1]); foo(); - signal(sig, sighandler); + old_handler = signal(sig, sighandler); raise(sig); foo(); diff --git a/tests/t214_signal_trigger.py b/tests/t214_signal_trigger.py new file mode 100644 index 000000000..ebe7061f8 --- /dev/null +++ b/tests/t214_signal_trigger.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +from runtest import TestBase + +class TestCase(TestBase): + def __init__(self): + TestBase.__init__(self, 'signal', """ +# DURATION TID FUNCTION + [ 11892] | main() { + 0.241 us [ 11892] | foo(); + 1.611 us [ 11892] | signal(); + [ 11892] | raise() { + [ 11892] | sighandler() { + 0.120 us [ 11892] | bar(); + 2.315 us [ 11892] | } /* sighandler */ + +uftrace stopped tracing with remaining functions +================================================ +task: 11892 +[2] sighandler +[1] raise +[0] main +""") + + def runcmd(self): + uftrace = TestBase.uftrace_cmd + options = "--signal SIGUSR1@finish" + program = 't-' + self.name + + return "%s %s %s" % (uftrace, options, program) + + def fixup(self, cflags, result): + return result.replace(""" } /* sighandler */ +""", "")