From 553b6df07c9b7ab30ed468a6a4374cbdf73d1c0d Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Tue, 17 Apr 2018 14:36:13 +0200 Subject: [PATCH] linux runtime: Add support for new kernel unwind fallback. In newer kernels dump_trace got replaced by a new unwind infrastructure. Add a new autoconf-unwind-stack-trace.c to detect whether we can use it. Extend the runtime/stack.c _stp_stack_print_fallback with a new pt_regs* argument. Update all callers and add dbug_unwind output to show which fallback unwinder we are selecting (or if we are just giving up). Rename the struct unwind_state in unwind.c and unwind.h to uw_state because the old name now conflicts with the one used in the kernel. --- buildrun.cxx | 2 ++ runtime/linux/autoconf-unwind-stack-trace.c | 16 +++++++++ runtime/stack.c | 50 ++++++++++++++++++++++++++--- runtime/unwind.c | 14 ++++---- runtime/unwind/unwind.h | 4 +-- 5 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 runtime/linux/autoconf-unwind-stack-trace.c diff --git a/buildrun.cxx b/buildrun.cxx index 403fa71..59b9e88 100644 --- a/buildrun.cxx +++ b/buildrun.cxx @@ -365,6 +365,8 @@ compile_pass (systemtap_session& s) "STAPCONF_KERNEL_STACKTRACE", NULL); output_autoconf(s, o, "autoconf-save-stack-trace-no-bp.c", "STAPCONF_KERNEL_STACKTRACE_NO_BP", NULL); + output_autoconf(s, o, "autoconf-unwind-stack-trace.c", + "STAPCONF_KERNEL_UNWIND_STACK", NULL); output_autoconf(s, o, "autoconf-asm-syscall.c", "STAPCONF_ASM_SYSCALL_H", NULL); output_autoconf(s, o, "autoconf-ring_buffer-flags.c", "STAPCONF_RING_BUFFER_FLAGS", NULL); diff --git a/runtime/linux/autoconf-unwind-stack-trace.c b/runtime/linux/autoconf-unwind-stack-trace.c new file mode 100644 index 0000000..2ec399e --- /dev/null +++ b/runtime/linux/autoconf-unwind-stack-trace.c @@ -0,0 +1,16 @@ +#include +#include + +void unwind_stack_trace (void) +{ + struct unwind_state state; + unwind_start (&state, current, 0, 0); + while (! unwind_done (&state)) + { + unsigned long addr = unwind_get_return_address (&state); + if (addr == 0) + break; + unwind_next_frame (&state); + } +} + diff --git a/runtime/stack.c b/runtime/stack.c index c9d2c0c..43f98ef 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -43,7 +43,11 @@ #include #endif -static void _stp_stack_print_fallback(unsigned long, int, int, int); +#if defined(STAPCONF_KERNEL_UNWIND_STACK) +#include +#endif + +static void _stp_stack_print_fallback(unsigned long, struct pt_regs*, int, int, int); #ifdef STP_USE_DWARF_UNWINDER #ifdef STAPCONF_LINUX_UACCESS_H @@ -128,7 +132,7 @@ static const struct stacktrace_ops print_stack_ops = { }; /* Used for kernel backtrace printing when other mechanisms fail. */ -static void _stp_stack_print_fallback(unsigned long stack, +static void _stp_stack_print_fallback(unsigned long stack, struct pt_regs *regs, int sym_flags, int levels, int skip) { struct print_stack_data print_data; @@ -136,20 +140,55 @@ static void _stp_stack_print_fallback(unsigned long stack, print_data.levels = levels; print_data.skip = skip; #if defined(STAPCONF_KERNEL_STACKTRACE) + dbug_unwind(1, "fallback kernel stacktrace\n"); dump_trace(current, NULL, (long *)stack, 0, &print_stack_ops, &print_data); #else /* STAPCONF_KERNEL_STACKTRACE_NO_BP */ + dbug_unwind(1, "fallback kernel stacktrace (no bp)\n"); dump_trace(current, NULL, (long *)stack, &print_stack_ops, &print_data); #endif } #else -static void _stp_stack_print_fallback(unsigned long s, int v, int l, int k) { +#if defined(STAPCONF_KERNEL_UNWIND_STACK) +static void _stp_stack_print_fallback(unsigned long sp, struct pt_regs *regs, + int sym_flags, + int levels, int skip) { + struct unwind_state state; + unwind_start (&state, current, regs, (unsigned long *) sp); + dbug_unwind(1, "fallback kernel stacktrace (unwind)\n"); + while (levels > 0 && ! unwind_done (&state)) + { + if (skip == 0) + { + unsigned long addr = unwind_get_return_address (&state); + /* When we have frame pointers, the unwind addresses can be + (mostly) trusted, otherwise it is all guesswork. */ +#ifdef CONFIG_FRAME_POINTER + _stp_print_addr(addr, sym_flags, NULL); +#else + _stp_print_addr(addr, sym_flags | _STP_SYM_INEXACT, NULL); +#endif + if (addr == 0) + break; + levels--; + } + else + { + dbug_unwind(1, "skipping frame\n"); + skip--; + } + unwind_next_frame(&state); + } +} +#else /* no new unwind */ +static void _stp_stack_print_fallback(unsigned long s, struct pt_regs *r, int v, int l, int k) { /* Don't guess, just give up. */ + dbug_unwind(1, "no fallback kernel stacktrace (giving up)\n"); _stp_print_addr(0, v | _STP_SYM_INEXACT, NULL); } - +#endif /* new unwind */ #endif /* defined(STAPCONF_KERNEL_STACKTRACE) || defined(STAPCONF_KERNEL_STACKTRACE_NO_BP) */ @@ -382,6 +421,7 @@ static void _stp_stack_kernel_print(struct context *c, int sym_flags) if (l == 0) { remaining = MAXBACKTRACE - n; _stp_stack_print_fallback(UNW_SP(&c->uwcontext_kernel.info), + &c->uwcontext_kernel.info.regs, sym_flags, remaining, 0); break; } else { @@ -408,7 +448,7 @@ static void _stp_stack_kernel_print(struct context *c, int sym_flags) sp = 0; skip = 5; /* yes, that many framework frames. */ #endif - _stp_stack_print_fallback(sp, sym_flags, + _stp_stack_print_fallback(sp, NULL, sym_flags, MAXBACKTRACE, skip); #else if (sym_flags & _STP_SYM_SYMBOL) diff --git a/runtime/unwind.c b/runtime/unwind.c index ec7cd58..3a2d991 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -235,7 +235,7 @@ static int parse_fde_cie(const u32 *fde, const u32 *cie, #define REG_STATE state->reg[state->stackDepth] -static int advance_loc(unsigned long delta, struct unwind_state *state) +static int advance_loc(unsigned long delta, struct uw_state *state) { state->loc += delta * state->codeAlign; dbug_unwind(1, "state->loc=%lx\n", state->loc); @@ -244,7 +244,7 @@ static int advance_loc(unsigned long delta, struct unwind_state *state) /* Set Same or Nowhere rule for register. */ static void set_no_state_rule(uleb128_t reg, enum item_location where, - struct unwind_state *state) + struct uw_state *state) { dbug_unwind(1, "reg=%lx, where=%d\n", reg, where); if (reg < ARRAY_SIZE(REG_STATE.regs)) { @@ -254,7 +254,7 @@ static void set_no_state_rule(uleb128_t reg, enum item_location where, /* Memory or Value rule */ static void set_offset_rule(uleb128_t reg, enum item_location where, - sleb128_t svalue, struct unwind_state *state) + sleb128_t svalue, struct uw_state *state) { dbug_unwind(1, "reg=%lx, where=%d, svalue=%lx\n", reg, where, svalue); if (reg < ARRAY_SIZE(REG_STATE.regs)) { @@ -265,7 +265,7 @@ static void set_offset_rule(uleb128_t reg, enum item_location where, /* Register rule. */ static void set_register_rule(uleb128_t reg, uleb128_t value, - struct unwind_state *state) + struct uw_state *state) { dbug_unwind(1, "reg=%lx, value=%lx\n", reg, value); if (reg < ARRAY_SIZE(REG_STATE.regs)) { @@ -277,7 +277,7 @@ static void set_register_rule(uleb128_t reg, uleb128_t value, /* Expr or ValExpr rule. */ static void set_expr_rule(uleb128_t reg, enum item_location where, const u8 **expr, const u8 *end, - struct unwind_state *state) + struct uw_state *state) { const u8 *const start = *expr; uleb128_t len = get_uleb128(expr, end); @@ -296,7 +296,7 @@ static void set_expr_rule(uleb128_t reg, enum item_location where, #define MAX_CFI 512 static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, - signed ptrType, int user, struct unwind_state *state, int compat_task) + signed ptrType, int user, struct uw_state *state, int compat_task) { union { const u8 *p8; @@ -1169,7 +1169,7 @@ static int unwind_frame(struct unwind_context *context, unsigned i; signed ptrType = -1, call_frame = 1; uleb128_t retAddrReg = 0; - struct unwind_state *state = &context->state; + struct uw_state *state = &context->state; unsigned long addr; if (unlikely(table_len == 0)) { diff --git a/runtime/unwind/unwind.h b/runtime/unwind/unwind.h index 9d66732..b3ff786 100644 --- a/runtime/unwind/unwind.h +++ b/runtime/unwind/unwind.h @@ -492,7 +492,7 @@ struct unwind_reg_state { unsigned cfa_is_expr:1; }; -struct unwind_state { +struct uw_state { uleb128_t loc; uleb128_t codeAlign; sleb128_t dataAlign; @@ -503,7 +503,7 @@ struct unwind_state { struct unwind_context { struct unwind_frame_info info; - struct unwind_state state; + struct uw_state state; }; static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; -- 1.8.3.1