systemtap/unwind-fallback.patch
2018-04-20 17:07:55 +02:00

250 lines
9.0 KiB
Diff

From 553b6df07c9b7ab30ed468a6a4374cbdf73d1c0d Mon Sep 17 00:00:00 2001
From: Mark Wielaard <mark@klomp.org>
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 <linux/sched.h>
+#include <asm/unwind.h>
+
+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 <asm/stacktrace.h>
#endif
-static void _stp_stack_print_fallback(unsigned long, int, int, int);
+#if defined(STAPCONF_KERNEL_UNWIND_STACK)
+#include <asm/unwind.h>
+#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