runtime/stack.c - systemtap
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#ifndef _STACK_C_
#define _STACK_C_
#ifndef MAXBACKTRACE
#define MAXBACKTRACE 20
#endif
#include "sym.c"
#include "regs.h"
#include "linux/uprobes-inc.h"
#if defined(STAPCONF_KERNEL_STACKTRACE) || defined(STAPCONF_KERNEL_STACKTRACE_NO_BP)
#include <linux/stacktrace.h>
#include <asm/stacktrace.h>
#endif
static void _stp_stack_print_fallback(unsigned long, int, int, int);
#ifdef STP_USE_DWARF_UNWINDER
#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)
{
FIXME#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__)
#include "stack-ia64.c"
#elif defined (__arm__)
#include "stack-arm.c"
#elif defined (__s390__)
#include "stack-s390.c"
#else
#ifndef STP_USE_DWARF_UNWINDER
#error "Unsupported architecture"
#endif
#endif
#if defined(STAPCONF_KERNEL_STACKTRACE) || defined(STAPCONF_KERNEL_STACKTRACE_NO_BP)
struct print_stack_data
{
int flags;
int levels;
int skip;
};
#if defined(STAPCONF_STACKTRACE_OPS_WARNING)
static void print_stack_warning(void *data, char *msg)
{
}
static void
print_stack_warning_symbol(void *data, char *msg, unsigned long symbol)
{
}
#endif
static int print_stack_stack(void *data, char *name)
{
return -1;
}
static void print_stack_address(void *data, unsigned long addr, int reliable)
{
struct print_stack_data *sdata = data;
if (sdata->skip > 0)
sdata->skip--;
else if (sdata->levels > 0) {
_stp_print_addr(addr,
sdata->flags | (reliable ? 0 :_STP_SYM_INEXACT),
NULL);
sdata->levels--;
}
}
static const struct stacktrace_ops print_stack_ops = {
#if defined(STAPCONF_STACKTRACE_OPS_WARNING)
.warning = print_stack_warning,
.warning_symbol = print_stack_warning_symbol,
#endif
.stack = print_stack_stack,
.address = print_stack_address,
#if defined(STAPCONF_WALK_STACK)
.walk_stack = print_context_stack,
#endif
};
static void _stp_stack_print_fallback(unsigned long stack,
int sym_flags, int levels, int skip)
{
struct print_stack_data print_data;
print_data.flags = sym_flags;
print_data.levels = levels;
print_data.skip = skip;
#if defined(STAPCONF_KERNEL_STACKTRACE)
dump_trace(current, NULL, (long *)stack, 0, &print_stack_ops,
&print_data);
#else
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) {
_stp_print_addr(0, v | _STP_SYM_INEXACT, NULL);
}
#endif
static struct pt_regs *_stp_get_uregs(struct context *c)
{
if (c->uregs && c->user_mode_p)
c->full_uregs_p = 1;
else if (c->uregs == NULL)
{
dbug_unwind(1, "computing uregs\n");
c->uregs = _stp_current_pt_regs();
if (c->uregs && _stp_task_pt_regs_valid(current, c->uregs))
c->full_uregs_p = 1;
#if defined(STP_USE_DWARF_UNWINDER) && !defined (__powerpc__)
else if (c->uregs != NULL && c->kregs != NULL && !c->user_mode_p)
{
struct unwind_frame_info *info = &c->uwcontext_kernel.info;
int ret = 0;
int levels;
if (UNW_PC(info) == REG_IP(c->uregs))
{
levels = 0;
dbug_unwind(1, "feeling lucky, info pc == uregs pc\n");
}
else
{
levels = MAXBACKTRACE;
arch_unw_init_frame_info(info, c->kregs, 0);
dbug_unwind(1, "Trying to recover... searching for 0x%llx\n",
(unsigned long long) REG_IP(c->uregs));
XXX dbug_unwind(1, "clearing kernel unwind cache\n");
c->uwcache_kernel.state = uwcache_uninitialized;
}
while (levels > 0 && ret == 0 && UNW_PC(info) != REG_IP(c->uregs))
{
levels--;
ret = unwind(&c->uwcontext_kernel, 0);
dbug_unwind(1, "unwind levels: %d, ret: %d, pc=0x%llx\n",
levels, ret, (unsigned long long) UNW_PC(info));
}
if (ret == 0 && UNW_PC(info) == REG_IP(c->uregs))
{
UNW_SP(info) = REG_SP(c->uregs); c->uregs = &info->regs;
c->full_uregs_p = 1;
dbug_unwind(1, "recovered with pc=0x%llx sp=0x%llx\n",
(unsigned long long) UNW_PC(info),
(unsigned long long) UNW_SP(info));
}
else
dbug_unwind(1, "failed to recover user reg state\n");
}
#endif
}
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) { dbug_unwind(1, "STARTING kernel unwind\n");
if (! c->kregs) {
#if defined (__i386__) || defined (__x86_64__)
arch_unw_init_frame_info(&c->uwcontext_kernel.info, NULL, 0);
return UNW_PC(&c->uwcontext_kernel.info);
#else
return 0;
#endif
} else if (c->probe_type == stp_probe_type_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
regs = c->kregs;
info = &c->uwcontext_kernel.info;
dbug_unwind(1, "CONTINUING kernel unwind to depth %d\n", depth);
if (depth == 1) {
if (c->uregs == &c->uwcontext_kernel.info.regs) {
dbug_unwind(1, "clearing uregs\n");
c->uregs = NULL;
c->full_uregs_p = 0;
}
arch_unw_init_frame_info(info, regs, 0);
}
ret = unwind(&c->uwcontext_kernel, 0);
dbug_unwind(1, "ret=%d PC=%llx SP=%llx\n", ret,
(unsigned long long) UNW_PC(info),
(unsigned long long) UNW_SP(info));
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)
{
unsigned long pc = 0;
if (c->uwcache_kernel.state == uwcache_uninitialized) {
c->uwcache_kernel.depth = 0;
c->uwcache_kernel.state = uwcache_partial;
}
if (unlikely(depth >= MAXBACKTRACE))
return 0;
if (depth < c->uwcache_kernel.depth)
return c->uwcache_kernel.pc[depth];
else if (c->uwcache_kernel.state == uwcache_finished)
return 0;
while (c->uwcache_kernel.depth <= depth) {
pc = c->uwcache_kernel.pc[c->uwcache_kernel.depth]
= _stp_stack_unwind_one_kernel(c, c->uwcache_kernel.depth);
c->uwcache_kernel.depth ++;
if (pc == 0 || pc == _stp_kretprobe_trampoline) {
c->uwcache_kernel.state = uwcache_finished;
break;
XXX }
}
return pc;
}
static void _stp_stack_kernel_print(struct context *c, int sym_flags)
{
unsigned n, remaining;
unsigned long l;
if (c->probe_type == stp_probe_type_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);
_stp_print("Returning to : ");
}
_stp_print_addr(_stp_stack_kernel_get(c, 0), sym_flags, NULL);
#ifdef STP_USE_DWARF_UNWINDER
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);
}
}
#else
if (! c->kregs) {
#if defined(STAPCONF_KERNEL_STACKTRACE) || defined(STAPCONF_KERNEL_STACKTRACE_NO_BP)
unsigned long sp;
int skip;
#ifdef CONFIG_FRAME_POINTER
sp = *(unsigned long *) __builtin_frame_address (0);
skip = 1; #else
sp = 0;
skip = 5; #endif
_stp_stack_print_fallback(sp, sym_flags,
MAXBACKTRACE, skip);
#else
if (sym_flags & _STP_SYM_SYMBOL)
_stp_printf("<no kernel backtrace at %s>\n",
c->probe_point);
else
_stp_print("\n");
#endif
return;
}
else
__stp_stack_print(c->kregs, sym_flags, MAXBACKTRACE);
#endif
}
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_type_uretprobe)
ri = c->ips.ri;
#ifdef STAPCONF_UPROBE_GET_PC
else if (c->probe_type == stp_probe_type_uprobe)
ri = GET_PC_URETPROBE_NONE;
#endif
XXX regs = _stp_get_uregs(c);
uregs_valid = c->full_uregs_p;
if (! current->mm || ! regs)
return 0;
if (depth == 0) { dbug_unwind(1, "STARTING user unwind\n");
#ifdef STAPCONF_UPROBE_GET_PC
if (c->probe_type == stp_probe_type_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) { if (c->uregs == &c->uwcontext_user.info.regs) {
dbug_unwind(1, "clearing uregs\n");
c->uregs = NULL;
c->full_uregs_p = 0;
}
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=%llx SP=%llx\n", ret,
(unsigned long long) UNW_PC(info), (unsigned long long) UNW_SP(info));
if (ret || ! _stp_valid_pc_addr(UNW_PC(info), current)) {
return 0;
}
return UNW_PC(info);
#else
return 0;
#endif
}
static unsigned long _stp_stack_user_get(struct context *c, unsigned depth)
{
unsigned long pc = 0;
if (c->uwcache_user.state == uwcache_uninitialized) {
c->uwcache_user.depth = 0;
c->uwcache_user.state = uwcache_partial;
}
if (unlikely(depth >= MAXBACKTRACE))
return 0;
if (depth < c->uwcache_user.depth)
return c->uwcache_user.pc[depth];
else if (c->uwcache_user.state == uwcache_finished)
return 0;
while (c->uwcache_user.depth <= depth) {
pc = c->uwcache_user.pc[c->uwcache_user.depth]
= _stp_stack_unwind_one_user(c, c->uwcache_user.depth);
c->uwcache_user.depth ++;
if (pc == 0) {
c->uwcache_user.state = uwcache_finished;
break;
}
}
return 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_type_uretprobe)
ri = c->ips.ri;
#ifdef STAPCONF_UPROBE_GET_PC
else if (c->probe_type == stp_probe_type_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",
c->probe_point);
else
_stp_print("\n");
return;
}
#ifdef STAPCONF_UPROBE_GET_PC
if (c->probe_type == stp_probe_type_uretprobe && ri) {
if ((sym_flags & _STP_SYM_FULL) == _STP_SYM_FULL) {
_stp_print("Returning from: ");
_stp_print_addr(ri->rp->u.vaddr, sym_flags, current);
_stp_print("Returning to : ");
}
}
#endif
_stp_print_addr(_stp_stack_user_get(c, 0), sym_flags, current);
#ifdef STP_USE_DWARF_UNWINDER
for (n = 1; n < MAXBACKTRACE; n++) {
l = _stp_stack_user_get(c, n);
if (l == 0) break; _stp_print_addr(l, sym_flags, current);
}
#else
if (sym_flags & _STP_SYM_SYMBOL)
_stp_printf("<no user backtrace support on arch>\n");
else
_stp_print("\n");
#endif
}
static void _stp_stack_kernel_sprint(char *str, int size, struct context* c,
int sym_flags)
{
_stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id());
_stp_print_flush();
_stp_stack_kernel_print(c, sym_flags);
strlcpy(str, pb->buf, size < (int)pb->len ? size : (int)pb->len);
pb->len = 0;
}
static void _stp_stack_user_sprint(char *str, int size, struct context* c,
int sym_flags)
{
_stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id());
_stp_print_flush();
_stp_stack_user_print(c, sym_flags);
strlcpy(str, pb->buf, size < (int)pb->len ? size : (int)pb->len);
pb->len = 0;
}
#endif