diff --git a/systemtap.spec b/systemtap.spec index 73ec13c..8ba9656 100644 --- a/systemtap.spec +++ b/systemtap.spec @@ -74,7 +74,7 @@ Name: systemtap Version: 3.2 -Release: 3%{?dist} +Release: 9%{?dist} # for version, see also configure.ac Patch10: rhbz1504009.patch @@ -83,6 +83,10 @@ Patch10: rhbz1504009.patch # upstream: https://sourceware.org/bugzilla/show_bug.cgi?id=22551 Patch11: rhbz1546563.patch +# Partial fix for backstrace issues. Add a new kernel fallback unwinder. +# upstream: commit 553b6df07c9b7ab30ed468a6a4374cbdf73d1c0d +Patch13: unwind-fallback.patch + # Packaging abstract: # # systemtap empty req:-client req:-devel @@ -487,6 +491,8 @@ cd .. %patch10 -p1 %patch11 -p1 +%patch13 -p1 + %build %if %{with_bundled_elfutils} @@ -1166,6 +1172,9 @@ done # PRERELEASE %changelog +* Tue Apr 17 2018 Mark Wielaard - 3.2-9 +- Add unwind-fallback.patch. + * Thu Feb 22 2018 Sergey Avseyev - 3.2-3 - rhbz1546563 (backport fix for removed timers in kernel 4.15) diff --git a/unwind-fallback.patch b/unwind-fallback.patch new file mode 100644 index 0000000..bb57607 --- /dev/null +++ b/unwind-fallback.patch @@ -0,0 +1,249 @@ +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 +