summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorMartin Hunt <hunt@redhat.com>2008-03-28 16:20:02 -0400
committerMartin Hunt <hunt@redhat.com>2008-03-28 16:20:02 -0400
commit614ead2b7dd8ac70cd89d018b09a397be7ade371 (patch)
tree11dfb318caa7c636cd2d933905810ca5a634115e /runtime
parentfd2ef8221625866219d6fc8e99ac36520ac6017b (diff)
downloadsystemtap-steved-614ead2b7dd8ac70cd89d018b09a397be7ade371.tar.gz
systemtap-steved-614ead2b7dd8ac70cd89d018b09a397be7ade371.tar.xz
systemtap-steved-614ead2b7dd8ac70cd89d018b09a397be7ade371.zip
kretprobe trampoline fixes
Recognize when a kretprobe trampoline was hit and continue with inexact stack dump. Also some testsuite changes.
Diffstat (limited to 'runtime')
-rw-r--r--runtime/stack-i386.c22
-rw-r--r--runtime/stack-x86_64.c17
-rw-r--r--runtime/stack.c22
-rw-r--r--runtime/sym.h1
-rw-r--r--runtime/transport/symbols.c2
-rw-r--r--runtime/unwind.c86
6 files changed, 111 insertions, 39 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 != &not_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