diff options
-rw-r--r-- | runtime/stack-i386.c | 22 | ||||
-rw-r--r-- | runtime/stack-x86_64.c | 17 | ||||
-rw-r--r-- | runtime/stack.c | 22 | ||||
-rw-r--r-- | runtime/sym.h | 1 | ||||
-rw-r--r-- | runtime/transport/symbols.c | 2 | ||||
-rw-r--r-- | runtime/unwind.c | 86 | ||||
-rw-r--r-- | testsuite/systemtap.context/backtrace.stp | 27 | ||||
-rw-r--r-- | testsuite/systemtap.context/backtrace.tcl | 123 |
8 files changed, 194 insertions, 106 deletions
diff --git a/runtime/stack-i386.c b/runtime/stack-i386.c index a86f94be..d7c2c201 100644 --- a/runtime/stack-i386.c +++ b/runtime/stack-i386.c @@ -43,7 +43,10 @@ static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels) #endif /* STAPCONF_X86_UNIREGS */ while (_stp_valid_stack_ptr(context, (unsigned long)ebp)) { - addr = *(unsigned long *)(ebp + 4); + if (unlikely(__stp_get_user(addr, (unsigned long *)(ebp + 4)))) { + /* cannot access stack. give up. */ + return; + } if (verbose) { _stp_print_char(' '); _stp_symbol_print (addr); @@ -55,18 +58,19 @@ static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels) #else struct unwind_frame_info info; arch_unw_init_frame_info(&info, regs); + while (!arch_unw_user_mode(&info)) { int ret = unwind(&info); dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info)); - if (ret < 0) { - _stp_stack_print_fallback(context, UNW_SP(&info), verbose); - break; + if (ret == 0) { + _stp_func_print(UNW_PC(&info), verbose, 1); + continue; } - if (ret) - break; - _stp_func_print(UNW_PC(&info), verbose, 1); + /* If an error happened or we hit a kretprobe trampoline, use fallback backtrace */ + /* FIXME: is there a way to unwind across kretprobe trampolines? */ + if (ret < 0 || (ret > 0 && UNW_PC(&info) == _stp_kretprobe_trampoline)) + _stp_stack_print_fallback(UNW_SP(&info), verbose); + break; } -// _stp_printf("***********************\n"); -// _stp_stack_print_fallback(context, (unsigned long)stack, verbose); #endif /* CONFIG_FRAME_POINTER */ } diff --git a/runtime/stack-x86_64.c b/runtime/stack-x86_64.c index e2eb4aa2..846653be 100644 --- a/runtime/stack-x86_64.c +++ b/runtime/stack-x86_64.c @@ -8,13 +8,12 @@ * later version. */ -// todo: don't use unwinder for kernel if CONFIG_FRAME +// todo: don't use unwinder for kernel if CONFIG_FRAME /* DWARF unwinder failed. Just dump intereting addresses on kernel stack. */ static void _stp_stack_print_fallback(unsigned long stack, int verbose) { unsigned long addr; - while (stack & (THREAD_SIZE - 1)) { if (unlikely(__stp_get_user(addr, (unsigned long *)stack))) { /* cannot access stack. give up. */ @@ -33,12 +32,14 @@ static void __stp_stack_print(struct pt_regs *regs, int verbose, int levels) while (!arch_unw_user_mode(&info)) { int ret = unwind(&info); dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(&info), UNW_SP(&info)); - if (ret < 0) { - _stp_stack_print_fallback(UNW_SP(&info), verbose); - break; + if (ret == 0) { + _stp_func_print(UNW_PC(&info), verbose, 1); + continue; } - if (ret) - break; - _stp_func_print(UNW_PC(&info), verbose, 1); + /* If an error happened or we hit a kretprobe trampoline, use fallback backtrace */ + /* FIXME: is there a way to unwind across kretprobe trampolines? */ + if (ret < 0 || (ret > 0 && UNW_PC(&info) == _stp_kretprobe_trampoline)) + _stp_stack_print_fallback(UNW_SP(&info), verbose); + break; } } diff --git a/runtime/stack.c b/runtime/stack.c index e338f587..eefdf715 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -58,12 +58,15 @@ void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe_instan _stp_symbol_print((unsigned long)_stp_ret_addr_r(pi)); } else { _stp_print_char(' '); - _stp_symbol_print (REG_IP(regs)); + _stp_symbol_print(REG_IP(regs)); } _stp_print_char('\n'); - } else - _stp_printf ("%p ", (int64_t)REG_IP(regs)); - __stp_stack_print (regs, verbose, 0); + } else if (pi) + _stp_printf("%p %p ", (int64_t) _stp_ret_addr_r(pi), (int64_t) REG_IP(regs)); + else + _stp_printf("%p ", (int64_t) REG_IP(regs)); + + __stp_stack_print(regs, verbose, 0); } /** Writes stack backtrace to a string @@ -72,7 +75,7 @@ void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe_instan * @param regs A pointer to the struct pt_regs. * @returns void */ -void _stp_stack_snprint (char *str, int size, struct pt_regs *regs, int verbose, struct kretprobe_instance *pi) +void _stp_stack_snprint(char *str, int size, struct pt_regs *regs, int verbose, struct kretprobe_instance *pi) { /* To get a string, we use a simple trick. First flush the print buffer, */ /* then call _stp_stack_print, then copy the result into the output string */ @@ -84,19 +87,18 @@ void _stp_stack_snprint (char *str, int size, struct pt_regs *regs, int verbose, pb->len = 0; } - /** Prints the user stack backtrace * @param str string * @returns Same string as was input with trace info appended, * @note Currently limited to a depth of two. Works from jprobes and kprobes. */ #if 0 -void _stp_ustack_print (char *str) +void _stp_ustack_print(char *str) { - struct pt_regs *nregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) current->thread_info)) - 1; - _stp_printf ("%p : [user]\n", (int64_t)REG_IP(nregs)); + struct pt_regs *nregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long)current->thread_info)) - 1; + _stp_printf("%p : [user]\n", (int64_t) REG_IP(nregs)); if (REG_SP(nregs)) - _stp_printf ("%p : [user]\n", (int64_t)(*(unsigned long *)REG_SP(nregs))); + _stp_printf("%p : [user]\n", (int64_t) (*(unsigned long *)REG_SP(nregs))); } #endif /* 0 */ diff --git a/runtime/sym.h b/runtime/sym.h index 631a5bbf..0bb64c13 100644 --- a/runtime/sym.h +++ b/runtime/sym.h @@ -84,6 +84,7 @@ struct _stp_module *_stp_modules_by_addr[STP_MAX_MODULES]; /* the number of modules in the arrays */ int _stp_num_modules = 0; +static unsigned long _stp_kretprobe_trampoline = 0; unsigned long _stp_module_relocate (const char *module, const char *section, unsigned long offset); static struct _stp_module *_stp_get_unwind_info (unsigned long addr); diff --git a/runtime/transport/symbols.c b/runtime/transport/symbols.c index a81e594f..83a3a635 100644 --- a/runtime/transport/symbols.c +++ b/runtime/transport/symbols.c @@ -200,6 +200,8 @@ static int _stp_init_kernel_symbols(void) _stp_modules[0]->data = _stp_kallsyms_lookup_name("_etext"); _stp_modules[0]->text_size = _stp_modules[0]->data - _stp_modules[0]->text; _stp_modules_by_addr[0] = _stp_modules[0]; + + _stp_kretprobe_trampoline = _stp_kallsyms_lookup_name("kretprobe_trampoline"); return 0; } diff --git a/runtime/unwind.c b/runtime/unwind.c index 483c9345..e8cba72e 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -321,7 +321,7 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrTy return 0; } if ((ptrType & DW_EH_PE_indirect) - && __stp_get_user(value, (unsigned long *)value)) + && __stp_get_user(value, (unsigned long *)value)) return 0; *pLoc = ptr.p8; @@ -621,15 +621,62 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, struct _stp_module *m) return fde; } +#ifdef DEBUG_UNWIND +static const char *_stp_enc_hi_name[] = { + "", + "DW_EH_PE_pcrel", + "DW_EH_PE_textrel", + "DW_EH_PE_datarel", + "DW_EH_PE_funcrel", + "DW_EH_PE_aligned" +}; +static const char *_stp_enc_lo_name[] = { + "DW_EH_PE_absptr", + "DW_EH_PE_uleb128", + "DW_EH_PE_udata2", + "DW_EH_PE_udata4", + "DW_EH_PE_udata8", + "DW_EH_PE_sleb128", + "DW_EH_PE_sdata2", + "DW_EH_PE_sdata4", + "DW_EH_PE_sdata8" +}; +char *_stp_eh_enc_name(signed type) +{ + static char buf[64]; + int hi, low; + if (type == DW_EH_PE_omit) + return "DW_EH_PE_omit"; + + hi = (type & DW_EH_PE_ADJUST) >> 4; + low = type & DW_EH_PE_FORM; + if (hi > 5 || low > 4 || (low == 0 && (type & DW_EH_PE_signed))) { + sprintf(buf, "ERROR:encoding=0x%x", type); + return buf; + } + + buf[0] = 0; + if (type & DW_EH_PE_indirect) + strlcpy(buf, "DW_EH_PE_indirect|", sizeof(buf)); + if (hi) + strlcat(buf, _stp_enc_hi_name[hi], sizeof(buf)); + + if (type & DW_EH_PE_signed) + low += 4; + strlcat(buf, _stp_enc_lo_name[low], sizeof(buf)); + return buf; +} +#endif /* DEBUG_UNWIND */ + /* Unwind to previous to frame. Returns 0 if successful, negative - * number in case of an error. A positive return means unwinding is finished; */ -/* don't try to fallback to dumping addresses on the stack. */ + * number in case of an error. A positive return means unwinding is finished; + * don't try to fallback to dumping addresses on the stack. */ int unwind(struct unwind_frame_info *frame) { #define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) const u32 *fde, *cie = NULL; const u8 *ptr = NULL, *end = NULL; - unsigned long pc = UNW_PC(frame) - frame->call_frame; + unsigned long pc = UNW_PC(frame); unsigned long tableSize, startLoc = 0, endLoc = 0, cfa; unsigned i; signed ptrType = -1; @@ -637,6 +684,15 @@ int unwind(struct unwind_frame_info *frame) struct _stp_module *m; struct unwind_state state; + if (pc != _stp_kretprobe_trampoline) + pc -= frame->call_frame; + else { + unsigned long a1, a2, *addr = (unsigned long *)UNW_SP(frame); + __stp_get_user(a1, addr); + __stp_get_user(a2, addr+1); + dbug_unwind(1, "TRAMPOLINE: SP=%lx *SP=%lx *(SP+1)=%lx\n", UNW_SP(frame), a1, a2); + return -EINVAL; + } dbug_unwind(1, "pc=%lx, %lx", pc, UNW_PC(frame)); if (UNW_PC(frame) == 0) @@ -659,16 +715,16 @@ int unwind(struct unwind_frame_info *frame) /* found the fde, now set startLoc and endLoc */ if (fde != NULL) { cie = cie_for_fde(fde, m); - ptr = (const u8 *)(fde + 2); if (likely(cie != NULL && cie != &bad_cie && cie != ¬_fde)) { + ptr = (const u8 *)(fde + 2); ptrType = fde_pointer_type(cie); startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); + dbug_unwind(2, "startLoc=%lx, ptrType=%s", startLoc, _stp_eh_enc_name(ptrType)); if (!(ptrType & DW_EH_PE_indirect)) ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed; endLoc = startLoc + read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); if (pc > endLoc) { - dbug_unwind(1, "pc (%lx) > endLoc(%x)\n", pc, endLoc); - /* finished? */ + dbug_unwind(1, "pc (%lx) > endLoc(%lx)\n", pc, endLoc); goto done; } } else { @@ -676,6 +732,8 @@ int unwind(struct unwind_frame_info *frame) fde = NULL; } } + + /* did not a good fde find with binary search, so do slow linear search */ if (fde == NULL) { for (fde = m->unwind_data, tableSize = m->unwind_data_len; cie = NULL, tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { @@ -690,13 +748,13 @@ int unwind(struct unwind_frame_info *frame) ptr = (const u8 *)(fde + 2); startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); - dbug_unwind(3, "startLoc=%p\n", (u64)startLoc); + dbug_unwind(2, "startLoc=%lx, ptrType=%s", startLoc, _stp_eh_enc_name(ptrType)); if (!startLoc) continue; if (!(ptrType & DW_EH_PE_indirect)) ptrType &= DW_EH_PE_FORM | DW_EH_PE_signed; endLoc = startLoc + read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); - dbug_unwind(3, "endLoc=%p\n", (u64)endLoc); + dbug_unwind(3, "endLoc=%lx\n", endLoc); if (pc >= startLoc && pc < endLoc) break; } @@ -729,6 +787,7 @@ int unwind(struct unwind_frame_info *frame) case 'R': continue; case 'S': + dbug_unwind(1, "This is a signal frame\n"); frame->call_frame = 0; continue; default: @@ -785,7 +844,6 @@ int unwind(struct unwind_frame_info *frame) goto err; /* update frame */ - dbug_unwind(1, "cie=%lx fde=%lx\n", cie, fde); #ifndef CONFIG_AS_CFI_SIGNAL_FRAME if (frame->call_frame && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign)) frame->call_frame = 0; @@ -793,11 +851,11 @@ int unwind(struct unwind_frame_info *frame) cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; startLoc = min((unsigned long)UNW_SP(frame), cfa); endLoc = max((unsigned long)UNW_SP(frame), cfa); - dbug_unwind(1, "cfa=%lx SP startLoc=%lx, endLoc=%lx\n", cfa, startLoc, endLoc); + dbug_unwind(1, "cfa=%lx startLoc=%lx, endLoc=%lx\n", cfa, startLoc, endLoc); if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) { startLoc = min(STACK_LIMIT(cfa), cfa); endLoc = max(STACK_LIMIT(cfa), cfa); - dbug_unwind(1, "SP startLoc=%p, endLoc=%p\n", (u64)startLoc, (u64)endLoc); + dbug_unwind(1, "cfa startLoc=%p, endLoc=%p\n", (u64)startLoc, (u64)endLoc); } #ifndef CONFIG_64BIT # define CASES CASE(8); CASE(16); CASE(32) @@ -898,7 +956,11 @@ copy_failed: err: read_unlock(&m->lock); return -EIO; + done: + /* PC was in a range convered by a module but no unwind info */ + /* found for the specific PC. This seems to happen only for kretprobe */ + /* trampolines and at the end of interrupt backtraces. */ read_unlock(&m->lock); return 1; #undef CASES diff --git a/testsuite/systemtap.context/backtrace.stp b/testsuite/systemtap.context/backtrace.stp index c14d071c..ddd7d00a 100644 --- a/testsuite/systemtap.context/backtrace.stp +++ b/testsuite/systemtap.context/backtrace.stp @@ -1,10 +1,9 @@ -function print_all_trace_info(point:string) { +function print_all_trace_info(point) { printf("backtrace from %s:\n", pp()) print_backtrace() - print("--------\n") + printf("--- %s ---\n", point) bt = backtrace() - printf("the %s stack is %s\n", point, bt) - printf("--<%s>--\n", point) + printf("the stack is %s\n", bt) print_stack(bt); print("--------\n") } @@ -18,18 +17,20 @@ probe end { } global flag = 0 -probe module("systemtap_test_module2").function("yyy_func3").call { - print_all_trace_info("call") + +probe module("systemtap_test_module2").function("yyy_func2") { + print_all_trace_info("yyy_func2") flag ++ } -probe module("systemtap_test_module2").function("yyy_func4").return { - print_all_trace_info("return") + +probe module("systemtap_test_module2").function("yyy_func3") { + print_all_trace_info("yyy_func3") flag ++ } -probe timer.profile { - if (cpu() == 0 && flag == 2 && probemod() != "systemtap_test_module2") { - print_all_trace_info("profile") - flag ++ - } + +probe module("systemtap_test_module2").function("yyy_func4") { + print_all_trace_info("yyy_func4") + flag ++ } + diff --git a/testsuite/systemtap.context/backtrace.tcl b/testsuite/systemtap.context/backtrace.tcl index f359cd41..6c8aee40 100644 --- a/testsuite/systemtap.context/backtrace.tcl +++ b/testsuite/systemtap.context/backtrace.tcl @@ -5,13 +5,7 @@ set m4 0 set m5 0 set m6 0 -if {[istarget ia64-*-*]} { - set retexp {.*return\>--\r\n 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} -} else { - set retexp {.*return\>--\r\n 0x[a-f0-9]+ : kretprobe_trampoline_holder[^\r\n]+\r\n} -} - -spawn stap $srcdir/$subdir/backtrace.stp +spawn stap -DMAXSTRINGLEN=256 $srcdir/$subdir/backtrace.stp #exp_internal 1 expect { -timeout 240 @@ -20,69 +14,61 @@ expect { exec echo 0 > /proc/stap_test_cmd exp_continue } - -re {^backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func3@[^\r\n]+\r\n} { + + #backtrace from yyy_func2 + -re {^backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func2@[^\r\n]+\r\n} { incr m1 expect { -timeout 5 - -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m1 == 1} {incr m1} - exp_continue - } -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m1 == 2} {incr m1} + if {$m1 == 1} {incr m1} exp_continue } -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m1 == 3} {incr m1} + if {$m1 == 2} {incr m1} } } exp_continue } - -re {.*---\r\nthe call stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { + -re {.*--- yyy_func2 ---\r\nthe stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { incr m2 expect { -timeout 5 - -re {.*call\>--\r\n 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m2 == 1} {incr m2} - exp_continue - } -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m2 == 2} {incr m2} + if {$m2 == 1} {incr m2} exp_continue } -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m2 == 3} {incr m2} + if {$m2 == 2} {incr m2} } } exp_continue } - -re {.*backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func4@[^\r\n]+\r\n} { + + #backtrace from yyy_func3 + -re {.*backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func3@[^\r\n]+\r\n} { incr m3 expect { -timeout 5 - -re {^Returning from: 0x[a-f0-9]+ : yyy_func4[^\[]+\[systemtap_test_module2\]\r\n} { + -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { if {$m3 == 1} {incr m3} exp_continue - } - -re {^Returning to : 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m3 == 2} {incr m3} - exp_continue - } + } -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m3 == 3} {incr m3} + if {$m3 == 2} {incr m3} exp_continue } -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { - if {$m3 == 4} {incr m3} + if {$m3 == 3} {incr m3} } } exp_continue } - -re {.*---\r\nthe return stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { + -re {.*--- yyy_func3 ---\r\nthe stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { incr m4 expect { -timeout 5 - -re $retexp { + -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { if {$m4 == 1} {incr m4} exp_continue } @@ -96,58 +82,87 @@ expect { } exp_continue } - -re {.*backtrace from timer.profile:\r\n} { + + #backtrace from yyy_func4 + -re {.*backtrace from module\(\"systemtap_test_module2\"\)\.function\(\"yyy_func4@[^\r\n]+\r\n} { incr m5 expect { -timeout 5 - -re {^ 0x[a-f0-9]+[^\r\n]+\r\n} { + -re {^ 0x[a-f0-9]+ : yyy_func4[^\[]+\[systemtap_test_module2\]\r\n} { if {$m5 == 1} {incr m5} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m5 == 2} {incr m5} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m5 == 3} {incr m5} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m5 == 4} {incr m5} } } exp_continue } - -re {.*---\r\nthe profile stack is 0x[a-f0-9]+[^\r\n]+\r\n} { + -re {.*--- yyy_func4 ---\r\nthe stack is 0x[a-f0-9]+ [^\r\n]+\r\n} { incr m6 expect { -timeout 5 - -re {.*profile>--\r\n 0x[a-f0-9]+[^\r\n]+\r\n} { + -re {^ 0x[a-f0-9]+ : yyy_func4[^\[]+\[systemtap_test_module2\]\r\n} { if {$m6 == 1} {incr m6} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func3[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m6 == 2} {incr m6} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func2[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m6 == 3} {incr m6} + exp_continue + } + -re {^ 0x[a-f0-9]+ : yyy_func1[^\[]+\[systemtap_test_module2\]\r\n} { + if {$m6 == 4} {incr m6} } } + exp_continue } - eof {fail "backtrace of yyy_func3, yyy_func4.return and timer.profile. unexpected EOF" } + eof {fail "backtrace of yyy_func[2-4]: unexpected EOF" } } exec kill -INT -[exp_pid] -if {$m1 == 4} { - pass "backtrace of yyy_func3" +if {$m1 == 3} { + pass "backtrace of yyy_func2" } else { - fail "backtrace of yyy_func3 ($m1)" + fail "backtrace of yyy_func2 ($m1)" } -if {$m2 == 4} { - pass "print_stack of yyy_func3" +if {$m2 == 3} { + pass "print_stack of yyy_func2" } else { - fail "print_stack of yyy_func3 ($m2)" + fail "print_stack of yyy_func2 ($m2)" } -if {$m3 == 5} { - pass "backtrace of yyy_func4.return" +if {$m3 == 4} { + pass "backtrace of yyy_func3" } else { - fail "backtrace of yyy_func4.return ($m3)" + fail "backtrace of yyy_func3 ($m3)" } if {$m4 == 4} { - pass "print_stack of yyy_func4.return" + pass "print_stack of yyy_func3" } else { - fail "print_stack of yyy_func4.return ($m4)" + fail "print_stack of yyy_func3 ($m4)" } -if {$m5 == 2} { - pass "backtrace of timer.profile" +if {$m5 == 5} { + pass "backtrace of yyy_func4" } else { - fail "backtrace of timer.profile ($m5)" + fail "backtrace of yyy_func4 ($m5)" } -if {$m6 == 2} { - pass "print_stack of timer.profile" +if {$m6 == 5} { + pass "print_stack of yyy_func4" } else { - fail "print_stack of timer.profile ($m6)" + fail "print_stack of yyy_func4 ($m6)" } + + close wait |