This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[PR6580; patch for review] incremental unwind enhancement
- From: Serguei Makarov <smakarov at redhat dot com>
- To: systemtap at sourceware dot org
- Date: Thu, 13 Sep 2012 16:01:30 -0400 (EDT)
- Subject: [PR6580; patch for review] incremental unwind enhancement
Updated version of the backtrace/unwind patch based on feedback. This one makes the cache *optional* (enabled with -DSTP_FULL_BACKTRACE_CACHE); otherwise, only the current unwind state for each of user / kernel space is retained, which is sufficient to implement PR6580 tapset functions efficiently so long as they call stack() / ustack() with increasing consecutive numbers.
Added initialization overhead for a probe prologue is now only two assignments (to set depth = 0 on both unwind states). This is probably tolerable; I could modify one of the visitors to search for /* pragma:unwind */ in tapset functions used by a probe and generate the code conditionally, but that would be severe overkill.
The patch is robust enough to pass the exelib.exp stress test, among other things. Any feedback/nitpicking would be very welcome, though.
----- Original Message -----
From: "Serguei Makarov" <smakarov@redhat.com>
To: systemtap@sourceware.org
Cc: "Frank Ch. Eigler" <fche@redhat.com>, "Mark Wielaard" <mjw@redhat.com>
Sent: Wednesday, September 5, 2012 11:43:07 AM
Subject: [PR6580; patch for review] first step of unwind_cache enhancement
The first incremental step to the runtime tweaks required to sanely implement PR6580. Namely, the kernel-side backtrace can be unwound one step at a time using _stp_stack_kernel_get, with the already unwound portion of the backtrace cached within the probe context.
This allows us to implement stack(n) (which returns the PC n levels deep within the stack) without having to resort to tokenizing a backtrace string each time.
The patch itself seems to be sound, but testing it drew attention to memory corruption errors in the existing DWARF unwinder (see PR14546).
diff --git a/runtime/common_probe_context.h b/runtime/common_probe_context.h
index df07b90..062bec1 100644
--- a/runtime/common_probe_context.h
+++ b/runtime/common_probe_context.h
@@ -118,5 +118,15 @@ cycles_t cycles_sum;
/* Current state of the unwinder (as used in the unwind.c dwarf unwinder). */
#if defined(STP_NEED_UNWIND_DATA)
-struct unwind_context uwcontext;
+// Invariants (for both _kernel and _user unwind states):
+// uwcache.depth == 0 --> uwcontext needs to be initialized
+// uwcache.depth > 0 --> uwcontext is state after uwcache.depth unwind
+// steps, where fetching current PC counts as a step
+// uwcache.depth < 0 --> unwind finished after -uwcache.depth steps
+// ... and when STP_FULL_BACKTRACE_CACHE is enabled, we have:
+// uwcache.depth > n --> uwcache.data[n] contains PC at depth n
+struct unwind_cache uwcache_user;
+struct unwind_cache uwcache_kernel;
+struct unwind_context uwcontext_user;
+struct unwind_context uwcontext_kernel;
#endif
diff --git a/runtime/stack-dwarf.c b/runtime/stack-dwarf.c
deleted file mode 100644
index 9c55997..0000000
--- a/runtime/stack-dwarf.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- linux-c -*-
- * x86 stack tracing functions
- * Copyright (C) 2005-2011 Red Hat Inc.
- *
- * This file is part of systemtap, and is free software. You can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License (GPL); either version 2, or (at your option) any
- * later version.
- */
-
-#ifdef STAPCONF_LINUX_UACCESS_H
-#include <linux/uaccess.h>
-#else
-#include <asm/uaccess.h>
-#endif
-#include <linux/types.h>
-#define intptr_t long
-#define uintptr_t unsigned long
-
-static int _stp_valid_pc_addr(unsigned long addr, struct task_struct *tsk)
-{
- /* Just a simple check of whether the the address can be accessed
- as a user space address. Zero is always bad. */
-
-/* FIXME for s390x PR13350. */
-#if defined (__s390__) || defined (__s390x__)
- return addr != 0L;
-#else
- int ok;
- mm_segment_t oldfs = get_fs();
- set_fs(USER_DS);
- ok = access_ok(VERIFY_READ, (long *) (intptr_t) addr, sizeof(long));
- set_fs(oldfs);
- return addr != 0L && tsk != NULL ? ok : ! ok;
-#endif
-}
-
-static void __stp_dwarf_stack_kernel_print(struct pt_regs *regs, int verbose,
- int levels, struct unwind_context *uwcontext)
-{
- struct unwind_frame_info *info = &uwcontext->info;
- arch_unw_init_frame_info(info, regs, 0);
-
- while (levels) {
- int ret = unwind(uwcontext, 0);
- dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(info), UNW_SP(info));
- if (ret == 0 && _stp_valid_pc_addr(UNW_PC(info), NULL)) {
- _stp_print_addr(UNW_PC(info), verbose, NULL);
- levels--;
- if (UNW_PC(info) != _stp_kretprobe_trampoline)
- continue;
- }
- /* If an error happened or we hit a kretprobe trampoline,
- * and the current pc frame address is still valid kernel
- * address use fallback backtrace, unless user task backtrace.
- * FIXME: is there a way to unwind across kretprobe
- * trampolines? PR9999. */
- if ((ret < 0 || UNW_PC(info) == _stp_kretprobe_trampoline))
- _stp_stack_print_fallback(UNW_SP(info),
- verbose, levels, 0);
- return;
- }
-}
-
-
-static void __stp_dwarf_stack_user_print(struct pt_regs *regs, int verbose,
- int levels, struct unwind_context *uwcontext,
- struct uretprobe_instance *ri, int uregs_valid)
-{
- struct unwind_frame_info *info = &uwcontext->info;
- arch_unw_init_frame_info(info, regs, ! uregs_valid);
-
- while (levels) {
- int ret = unwind(uwcontext, 1);
-#ifdef STAPCONF_UPROBE_GET_PC
- unsigned long maybe_pc = 0;
- if (ri) {
- maybe_pc = uprobe_get_pc(ri, UNW_PC(info),
- UNW_SP(info));
- if (!maybe_pc)
- printk("SYSTEMTAP ERROR: uprobe_get_return returned 0\n");
- else
- UNW_PC(info) = maybe_pc;
- }
-#endif
- dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(info), UNW_SP(info));
- if (ret == 0 && _stp_valid_pc_addr(UNW_PC(info), current)) {
- _stp_print_addr(UNW_PC(info), verbose, current);
- levels--;
- continue;
- }
-
- /* If an error happened or the PC becomes invalid, then
- * we are done, _stp_stack_print_fallback is only for
- * kernel space. */
- return;
- }
-}
diff --git a/runtime/stack.c b/runtime/stack.c
index 413ef1e..410698f 100644
--- a/runtime/stack.c
+++ b/runtime/stack.c
@@ -46,7 +46,32 @@
static void _stp_stack_print_fallback(unsigned long, int, int, int);
#ifdef STP_USE_DWARF_UNWINDER
-#include "stack-dwarf.c"
+#ifdef STAPCONF_LINUX_UACCESS_H
+#include <linux/uaccess.h>
+#else
+#include <asm/uaccess.h>
+#endif
+#include <linux/types.h>
+#define intptr_t long
+#define uintptr_t unsigned long
+
+static int _stp_valid_pc_addr(unsigned long addr, struct task_struct *tsk)
+{
+ /* Just a simple check of whether the the address can be accessed
+ as a user space address. Zero is always bad. */
+
+/* FIXME for s390x PR13350. */
+#if defined (__s390__) || defined (__s390x__)
+ return addr != 0L;
+#else
+ int ok;
+ mm_segment_t oldfs = get_fs();
+ set_fs(USER_DS);
+ ok = access_ok(VERIFY_READ, (long *) (intptr_t) addr, sizeof(long));
+ set_fs(oldfs);
+ return addr != 0L && tsk != NULL ? ok : ! ok;
+#endif
+}
#endif
#if defined (__ia64__)
@@ -153,6 +178,7 @@ static struct pt_regs *_stp_get_uregs(struct context *c)
c->probe_flags |= _STP_PROBE_STATE_FULL_UREGS;
else if (c->uregs == NULL)
{
+ dbug_unwind(1, "computing uregs\n");
/* First try simple recovery through task_pt_regs,
on some platforms that already provides complete uregs. */
c->uregs = _stp_current_pt_regs();
@@ -165,7 +191,7 @@ static struct pt_regs *_stp_get_uregs(struct context *c)
else if (c->uregs != NULL && c->kregs != NULL
&& ! (c->probe_flags & _STP_PROBE_STATE_USER_MODE))
{
- struct unwind_frame_info *info = &c->uwcontext.info;
+ struct unwind_frame_info *info = &c->uwcontext_user.info;
int ret = 0;
int levels;
@@ -189,7 +215,7 @@ static struct pt_regs *_stp_get_uregs(struct context *c)
while (levels > 0 && ret == 0 && UNW_PC(info) != REG_IP(c->uregs))
{
levels--;
- ret = unwind(&c->uwcontext, 0);
+ ret = unwind(&c->uwcontext_user, 0);
dbug_unwind(1, "unwind levels: %d, ret: %d, pc=0x%lx\n",
levels, ret, UNW_PC(info));
}
@@ -213,6 +239,118 @@ static struct pt_regs *_stp_get_uregs(struct context *c)
return c->uregs;
}
+
+static unsigned long _stp_stack_unwind_one_kernel(struct context *c, unsigned depth)
+{
+ struct pt_regs *regs = NULL;
+ struct unwind_frame_info *info = NULL;
+ int ret;
+
+ if (depth == 0) { /* Start by fetching the current PC. */
+ dbug_unwind(1, "STARTING kernel unwind\n");
+
+ if (! c->kregs) {
+ /* Even the current PC is unknown; so we have
+ * absolutely no data at any depth.
+ *
+ * Note that unlike _stp_stack_kernel_print(),
+ * we can't fall back to calling dump_trace()
+ * to obtain the backtrace -- since that
+ * returns a string, which we would have to
+ * tokenize. Callers that want to use the
+ * dump_trace() fallback should call
+ * _stp_stack_kernel_print() and do their own
+ * tokenization of the result. */
+ return 0;
+ } else if (c->probe_type == _STP_PROBE_HANDLER_KRETPROBE
+ && c->ips.krp.pi) {
+ return (unsigned long)_stp_ret_addr_r(c->ips.krp.pi);
+ } else {
+ return REG_IP(c->kregs);
+ }
+ }
+
+#ifdef STP_USE_DWARF_UNWINDER
+ /* Otherwise, use the DWARF unwinder to unwind one step. */
+ if (! c->kregs) {
+ return 0;
+ }
+ regs = c->kregs;
+
+ info = &c->uwcontext_kernel.info;
+
+ dbug_unwind(1, "CONTINUING kernel unwind to depth %d\n", depth);
+
+ if (depth == 1) { /* need to clear uregs & set up uwcontext->info */
+ if (c->uregs == &c->uwcontext_kernel.info.regs) {
+ dbug_unwind(1, "clearing uregs\n");
+ /* Unwinder needs the reg state, clear uregs ref. */
+ c->uregs = NULL;
+ c->probe_flags &= ~_STP_PROBE_STATE_FULL_UREGS;
+ }
+
+ arch_unw_init_frame_info(info, regs, 0);
+ }
+
+ ret = unwind(&c->uwcontext_kernel, 0);
+ dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(info), UNW_SP(info));
+
+ /* check if unwind hit an error */
+ if (ret || ! _stp_valid_pc_addr(UNW_PC(info), NULL)) {
+ return 0;
+ }
+
+ return UNW_PC(info);
+#else
+ return 0;
+#endif
+}
+
+static unsigned long _stp_stack_kernel_get(struct context *c, unsigned depth)
+{
+ if (unlikely(depth >= MAXBACKTRACE)) {
+ return 0;
+ }
+
+ /* Reset uwcontext if necessary */
+ if (c->uwcache_kernel.depth < 0) { /* unwind finished earlier */
+ if (depth < 0 - c->uwcache_kernel.depth) {
+#ifdef STP_FULL_BACKTRACE_CACHE
+ return c->uwcache_kernel.data[depth];
+#else
+ c->uwcache_kernel.depth = 0;
+#endif
+ } else {
+ return 0; /* unwind does not reach this far */
+ }
+ } else if (c->uwcache_kernel.depth > depth) {
+#ifdef STP_FULL_BACKTRACE_CACHE
+ return c->uwcache_kernel.data[depth];
+#else
+ c->uwcache_kernel.depth = 0;
+#endif
+ }
+
+ /* Advance uwcontext to the required depth. */
+ while (c->uwcache_kernel.depth <= depth) {
+ c->uwcache_kernel.pc = _stp_stack_unwind_one_kernel(c, c->uwcache_kernel.depth);
+#ifdef STP_FULL_BACKTRACE_CACHE
+ c->uwcache_kernel.data[c->uwcache_kernel.depth] = c->uwcache_kernel.pc;
+#endif
+ if (c->uwcache_kernel.pc == 0
+ || c->uwcache_kernel.pc == _stp_kretprobe_trampoline) {
+ /* Mark unwind completed. */
+ c->uwcache_kernel.depth = -c->uwcache_kernel.depth;
+ break;
+ /* XXX: is there a way to unwind across kretprobe trampolines? PR9999 */
+ }
+ c->uwcache_kernel.depth ++;
+ }
+
+ /* Return the program counter at the current depth. */
+ return c->uwcache_kernel.pc;
+}
+
/** Prints the stack backtrace
* @param regs A pointer to the struct pt_regs.
* @param verbose _STP_SYM_FULL or _STP_SYM_BRIEF
@@ -220,14 +358,18 @@ static struct pt_regs *_stp_get_uregs(struct context *c)
static void _stp_stack_kernel_print(struct context *c, int sym_flags)
{
- struct pt_regs *regs = NULL;
+ unsigned n, remaining;
+ unsigned long l;
if (! c->kregs) {
- /* For the kernel we can use an inexact fallback.
- When compiled with frame pointers we can do
- a pretty good guess at the stack value,
- otherwise let dump_stack guess it
- (and skip some framework frames). */
+ /* This is a fatal block for _stp_stack_kernel_get,
+ * but when printing a backtrace we can use this
+ * inexact fallback.
+ *
+ * When compiled with frame pointers we can do
+ * a pretty good guess at the stack value,
+ * otherwise let dump_stack guess it
+ * (and skip some framework frames). */
#if defined(STAPCONF_KERNEL_STACKTRACE) || defined(STAPCONF_KERNEL_STACKTRACE_NO_BP)
unsigned long sp;
int skip;
@@ -248,44 +390,46 @@ static void _stp_stack_kernel_print(struct context *c, int sym_flags)
_stp_print("\n");
#endif
return;
- } else {
- regs = c->kregs;
}
/* print the current address */
- if (c->probe_type == _STP_PROBE_HANDLER_KRETPROBE && c->ips.krp.pi) {
- if ((sym_flags & _STP_SYM_FULL) == _STP_SYM_FULL) {
- _stp_print("Returning from: ");
- _stp_print_addr((unsigned long)_stp_probe_addr_r(c->ips.krp.pi),
- sym_flags, NULL);
- _stp_print("Returning to : ");
- }
- _stp_print_addr((unsigned long)_stp_ret_addr_r(c->ips.krp.pi),
+ if (c->probe_type == _STP_PROBE_HANDLER_KRETPROBE && c->ips.krp.pi
+ && (sym_flags & _STP_SYM_FULL) == _STP_SYM_FULL) {
+ _stp_print("Returning from: ");
+ _stp_print_addr((unsigned long)_stp_probe_addr_r(c->ips.krp.pi),
sym_flags, NULL);
- } else {
- _stp_print_addr(REG_IP(regs), sym_flags, NULL);
+ _stp_print("Returning to : ");
}
+ _stp_print_addr(_stp_stack_kernel_get(c, 0), sym_flags, NULL);
- /* print rest of stack... */
#ifdef STP_USE_DWARF_UNWINDER
- if (c->uregs == &c->uwcontext.info.regs) {
- /* Unwinder needs the reg state, clear uregs ref. */
- c->uregs = NULL;
- c->probe_flags &= ~_STP_PROBE_STATE_FULL_UREGS;
+ for (n = 1; n < MAXBACKTRACE; n++) {
+ l = _stp_stack_kernel_get(c, n);
+ if (l == 0) {
+ remaining = MAXBACKTRACE - n;
+ _stp_stack_print_fallback(UNW_SP(&c->uwcontext_kernel.info),
+ sym_flags, remaining, 0);
+ break;
+ } else {
+ _stp_print_addr(l, sym_flags, NULL);
+ }
}
- __stp_dwarf_stack_kernel_print(regs, sym_flags, MAXBACKTRACE,
- &c->uwcontext);
#else
/* Arch specific fallback for kernel backtraces. */
__stp_stack_print(regs, sym_flags, MAXBACKTRACE);
#endif
}
-static void _stp_stack_user_print(struct context *c, int sym_flags)
+static unsigned long _stp_stack_unwind_one_user(struct context *c, unsigned depth)
{
struct pt_regs *regs = NULL;
int uregs_valid = 0;
struct uretprobe_instance *ri = NULL;
+ struct unwind_frame_info *info = NULL;
+ int ret;
+#ifdef STAPCONF_UPROBE_GET_PC
+ unsigned long maybe_pc;
+#endif
if (c->probe_type == _STP_PROBE_HANDLER_URETPROBE)
ri = c->ips.ri;
@@ -294,9 +438,127 @@ static void _stp_stack_user_print(struct context *c, int sym_flags)
ri = GET_PC_URETPROBE_NONE;
#endif
+ /* XXX: The computation that gives this is cached, so calling
+ * _stp_get_uregs multiple times is okay... probably. */
regs = _stp_get_uregs(c);
uregs_valid = c->probe_flags & _STP_PROBE_STATE_FULL_UREGS;
+ if (! current->mm || ! regs)
+ return 0; // no user backtrace at this probe point
+
+ if (depth == 0) { /* Start by fetching the current PC. */
+ dbug_unwind(1, "STARTING user unwind\n");
+
+#ifdef STAPCONF_UPROBE_GET_PC
+ if (c->probe_type == _STP_PROBE_HANDLER_URETPROBE && ri) {
+ return ri->ret_addr;
+ } else {
+ return REG_IP(regs);
+ }
+#else
+ return REG_IP(regs);
+#endif
+ }
+
+#ifdef STP_USE_DWARF_UNWINDER
+ info = &c->uwcontext_user.info;
+
+ dbug_unwind(1, "CONTINUING user unwind to depth %d\n", depth);
+
+ if (depth == 1) { /* need to clear uregs & set up uwcontext->info */
+ if (c->uregs == &c->uwcontext_user.info.regs) {
+ dbug_unwind(1, "clearing uregs\n");
+ /* Unwinder needs the reg state, clear uregs ref. */
+ c->uregs = NULL;
+ c->probe_flags &= ~_STP_PROBE_STATE_FULL_UREGS;
+ }
+
+ arch_unw_init_frame_info(info, regs, 0);
+ }
+
+ ret = unwind(&c->uwcontext_user, 1);
+#ifdef STAPCONF_UPROBE_GET_PC
+ maybe_pc = 0;
+ if (ri) {
+ maybe_pc = uprobe_get_pc(ri, UNW_PC(info), UNW_SP(info));
+ if (!maybe_pc)
+ printk("SYSTEMTAP ERROR: uprobe_get_return returned 0\n");
+ else
+ UNW_PC(info) = maybe_pc;
+ }
+#endif
+ dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(info), UNW_SP(info));
+
+ /* check if unwind hit an error */
+ if (ret || ! _stp_valid_pc_addr(UNW_PC(info), current)) {
+ return 0;
+ }
+
+ return UNW_PC(info);
+#else
+ /* User stack traces only supported for arches with dwarf unwinder. */
+ return 0;
+#endif
+}
+
+static unsigned long _stp_stack_user_get(struct context *c, unsigned depth)
+{
+ if (unlikely(depth >= MAXBACKTRACE)) {
+ return 0;
+ }
+
+ /* Reset uwcontext if necessary */
+ if (c->uwcache_user.depth < 0) { /* unwind finished earlier */
+ if (depth < 0 - c->uwcache_user.depth) {
+#ifdef STP_FULL_BACKTRACE_CACHE
+ return c->uwcache_user.data[depth];
+#else
+ c->uwcache_user.depth = 0;
+#endif
+ } else {
+ return 0; /* unwind does not reach this far */
+ }
+ } else if (c->uwcache_user.depth > depth) {
+#ifdef STP_FULL_BACKTRACE_CACHE
+ return c->uwcache_user.data[depth];
+#else
+ c->uwcache_user.depth = 0;
+#endif
+ }
+
+ /* Advance uwcontext to the required depth. */
+ while (c->uwcache_user.depth <= depth) {
+ c->uwcache_user.pc = _stp_stack_unwind_one_user(c, c->uwcache_user.depth);
+#ifdef STP_FULL_BACKTRACE_CACHE
+ c->uwcache_user.data[c->uwcache_user.depth] = c->uwcache_user.pc;
+#endif
+ if (c->uwcache_user.pc == 0) {
+ /* Mark unwind completed. */
+ c->uwcache_user.depth = -c->uwcache_user.depth;
+ break;
+ }
+ c->uwcache_user.depth ++;
+ }
+
+ /* Return the program counter at the current depth. */
+ return c->uwcache_user.pc;
+}
+
+static void _stp_stack_user_print(struct context *c, int sym_flags)
+{
+ struct pt_regs *regs = NULL;
+ struct uretprobe_instance *ri = NULL;
+ unsigned n; unsigned long l;
+
+ if (c->probe_type == _STP_PROBE_HANDLER_URETPROBE)
+ ri = c->ips.ri;
+#ifdef STAPCONF_UPROBE_GET_PC
+ else if (c->probe_type == _STP_PROBE_HANDLER_UPROBE)
+ ri = GET_PC_URETPROBE_NONE;
+#endif
+
+ regs = _stp_get_uregs(c);
+
if (! current->mm || ! regs) {
if (sym_flags & _STP_SYM_SYMBOL)
_stp_printf("<no user backtrace at %s>\n",
@@ -314,25 +576,18 @@ static void _stp_stack_user_print(struct context *c, int sym_flags)
/* ... otherwise this dereference fails */
_stp_print_addr(ri->rp->u.vaddr, sym_flags, current);
_stp_print("Returning to : ");
- _stp_print_addr(ri->ret_addr, sym_flags, current);
- } else
- _stp_print_addr(ri->ret_addr, sym_flags, current);
- } else {
- _stp_print_addr(REG_IP(regs), sym_flags, current);
+ }
}
-#else
- _stp_print_addr(REG_IP(regs), sym_flags, current);
#endif
+ _stp_print_addr(_stp_stack_user_get(c, 0), sym_flags, current);
/* print rest of stack... */
#ifdef STP_USE_DWARF_UNWINDER
- if (c->uregs == &c->uwcontext.info.regs) {
- /* Unwinder needs the reg state, clear uregs ref. */
- c->uregs = NULL;
- c->probe_flags &= ~_STP_PROBE_STATE_FULL_UREGS;
+ for (n = 1; n < MAXBACKTRACE; n++) {
+ l = _stp_stack_user_get(c, n);
+ if (l == 0) break; // No user space fallback available
+ _stp_print_addr(l, sym_flags, current);
}
- __stp_dwarf_stack_user_print(regs, sym_flags, MAXBACKTRACE,
- &c->uwcontext, ri, uregs_valid);
#else
/* User stack traces only supported for arches with dwarf unwinder. */
if (sym_flags & _STP_SYM_SYMBOL)
diff --git a/runtime/unwind/unwind.h b/runtime/unwind/unwind.h
index a11dc48..594e9a8 100644
--- a/runtime/unwind/unwind.h
+++ b/runtime/unwind/unwind.h
@@ -336,4 +336,16 @@ struct unwind_context {
static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 };
+#ifndef MAXBACKTRACE
+#define MAXBACKTRACE 20
+#endif
+
+struct unwind_cache {
+ int depth; /* if < 0, unwind finished at -depth; if 0, context not set up */
+ unsigned long pc;
+#if defined(STP_FULL_BACKTRACE_CACHE)
+ unsigned long data[MAXBACKTRACE];
+#endif
+};
+
#endif /*_STP_UNWIND_H_*/
diff --git a/tapset/linux/context-symbols.stp b/tapset/linux/context-symbols.stp
index e574aed..dafad17 100644
--- a/tapset/linux/context-symbols.stp
+++ b/tapset/linux/context-symbols.stp
@@ -12,13 +12,24 @@
//processor.
// </tapsetdescription>
+function __stack_raw (n:long) %{ /* pragma:unwind */
+ /* basic sanity check for bounds: */
+ if (unlikely(STAP_ARG_n < 0 || STAP_ARG_n >= MAXBACKTRACE))
+ STAP_RETVALUE = 0;
+ else
+ STAP_RETVALUE = _stp_stack_kernel_get (CONTEXT, (unsigned)STAP_ARG_n);
+%}
/**
* sfunction stack - return PC at stack unwind level n
* @n: number of levels to descend in the stack.
*/
function stack:long (n:long) {
- /* XXX this is the naive method */
+ if (n < 0 || n >= %{ MAXBACKTRACE %}) return 0
+ r = __stack_raw(n);
+ if (r != 0) return r
+
+ /* fallback: parse backtrace() to go deeper in the stack */
b = backtrace (); orig_n = n;
sym = tokenize (b, " ");
if (sym == "") @__context_unwind_error(orig_n);
diff --git a/tapset/linux/ucontext-symbols.stp b/tapset/linux/ucontext-symbols.stp
index 33d5a36..da642e0 100644
--- a/tapset/linux/ucontext-symbols.stp
+++ b/tapset/linux/ucontext-symbols.stp
@@ -13,12 +13,23 @@
// the function symbol of an address.
// </tapsetdescription>
+function __ustack_raw (n:long) %{ /* pragma:unwind */
+ /* basic sanity check for bounds: */
+ if (unlikely(STAP_ARG_n < 0 || STAP_ARG_n >= MAXBACKTRACE))
+ STAP_RETVALUE = 0;
+ else
+ STAP_RETVALUE = _stp_stack_user_get (CONTEXT, (unsigned)STAP_ARG_n);
+%}
+
/**
* sfunction ustack - return PC at user-space stack unwind level n
* @n: number of levels to descend in the stack.
*/
function ustack:long (n:long) {
- /* XXX this is the naive method */
+ r = __ustack_raw(n);
+ if (r != 0) return r
+
+ /* fallback: parse backtrace() to go deeper in the stack */
b = ubacktrace (); orig_n = n;
sym = tokenize (b, " ");
if (sym == "") @__context_unwind_error(orig_n);
diff --git a/tapsets.cxx b/tapsets.cxx
index 5070485..2a9ff3d 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -183,6 +183,11 @@ common_probe_entryfn_prologue (translator_output* o, string statestr,
o->newline() << "c->cycles_base = 0;";
o->newline() << "#endif";
*/
+
+ o->newline() << "#if defined(STP_NEED_UNWIND_DATA)";
+ o->newline() << "c->uwcache_user.depth = 0;";
+ o->newline() << "c->uwcache_kernel.depth = 0;";
+ o->newline() << "#endif";
}