]> sourceware.org Git - systemtap.git/blobdiff - runtime/stack.c
Rename CONTEXT regflags to probe_flags. Now simply indicates user mode.
[systemtap.git] / runtime / stack.c
index 68a7e4fa20fd164a5f8f0782d674cfd19c9c12cd..4dfb4b2198b62855f9380f63b2ad99055358a90d 100644 (file)
@@ -1,6 +1,6 @@
 /*  -*- linux-c -*-
  * Stack tracing functions
- * Copyright (C) 2005-2009 Red Hat Inc.
+ * Copyright (C) 2005-2009, 2011 Red Hat Inc.
  * Copyright (C) 2005 Intel Corporation.
  *
  * This file is part of systemtap, and is free software.  You can
@@ -9,9 +9,21 @@
  * later version.
  */
 
+/*
+  The translator will only include this file if the session needs any
+  of the backtrace functions.  Currently indicated by having the session
+  need_unwind flag, which is set by tapset functions marked with
+  pragme:unwind.
+*/
+
 #ifndef _STACK_C_
 #define _STACK_C_
 
+/* Maximum number of backtrace levels. */
+#ifndef MAXBACKTRACE
+#define MAXBACKTRACE 20
+#endif
+
 /** @file stack.c
  * @brief Stack Tracing Functions
  */
@@ -23,9 +35,6 @@
 
 #include "sym.c"
 #include "regs.h"
-#include "unwind.c"
-
-#define MAXBACKTRACE 20
 
 /* If uprobes isn't in the kernel, pull it in from the runtime. */
 #if defined(CONFIG_UTRACE)      /* uprobes doesn't work without utrace */
@@ -46,14 +55,12 @@ struct uretprobe_instance;
 #include <asm/stacktrace.h>
 #endif
 
-static void _stp_stack_print_fallback(unsigned long, int, int);
+static void _stp_stack_print_fallback(unsigned long, int, int, int);
 
-#if defined (__x86_64__)
-#include "stack-x86_64.c"
+#if (defined(__i386__) || defined(__x86_64__))
+#include "stack-x86.c"
 #elif defined (__ia64__)
 #include "stack-ia64.c"
-#elif  defined (__i386__)
-#include "stack-i386.c"
 #elif defined (__powerpc__)
 #include "stack-ppc.c"
 #elif defined (__arm__)
@@ -68,11 +75,12 @@ static void _stp_stack_print_fallback(unsigned long, int, int);
 
 struct print_stack_data
 {
-        int verbose;
-        int max_level;
-        int level;
+        int flags;
+        int levels;
+        int skip;
 };
 
+#if defined(STAPCONF_STACKTRACE_OPS_WARNING)
 static void print_stack_warning(void *data, char *msg)
 {
 }
@@ -81,6 +89,7 @@ static void
 print_stack_warning_symbol(void *data, char *msg, unsigned long symbol)
 {
 }
+#endif
 
 static int print_stack_stack(void *data, char *name)
 {
@@ -90,13 +99,21 @@ static int print_stack_stack(void *data, char *name)
 static void print_stack_address(void *data, unsigned long addr, int reliable)
 {
        struct print_stack_data *sdata = data;
-        if (sdata->level++ < sdata->max_level)
-                _stp_print_addr(addr, sdata->verbose | _STP_SYM_INEXACT, NULL);
+       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)
@@ -104,16 +121,24 @@ static const struct stacktrace_ops print_stack_ops = {
 #endif
 };
 
-static void _stp_stack_print_fallback(unsigned long stack, int verbose, int levels)
+/* Used for kernel backtrace printing when other mechanisms fail. */
+static void _stp_stack_print_fallback(unsigned long stack,
+                                     int sym_flags, int levels, int skip)
 {
         struct print_stack_data print_data;
-        print_data.verbose = verbose;
-        print_data.max_level = levels;
-        print_data.level = 0;
+        print_data.flags = sym_flags;
+        print_data.levels = levels;
+        print_data.skip = skip;
         dump_trace(current, NULL, (long *)stack, 0, &print_stack_ops,
                    &print_data);
 }
-#endif
+#else
+static void _stp_stack_print_fallback(unsigned long s, int v, int l, int k) {
+       /* Don't guess, just give up. */
+       _stp_print_addr(0, v | _STP_SYM_INEXACT, NULL);
+}
+
+#endif /* defined(STAPCONF_KERNEL_STACKTRACE) */
 
 // Without KPROBES very little works atm.
 // But this file is unconditionally imported, while these two functions are only
@@ -125,39 +150,110 @@ static void _stp_stack_print_fallback(unsigned long stack, int verbose, int leve
  * @param verbose _STP_SYM_FULL or _STP_SYM_BRIEF
  */
 
-static void _stp_stack_print(struct pt_regs *regs, int verbose,
-                            struct kretprobe_instance *pi, int levels,
-                            struct task_struct *tsk,
-                            struct unwind_context *context,
-                            struct uretprobe_instance *ri, int uregs_valid)
+static void _stp_stack_print(struct context *c, int sym_flags, int stack_flags)
 {
+       struct pt_regs *regs = NULL;
+       struct task_struct *tsk = NULL;
+       int uregs_valid = 0;
+       struct uretprobe_instance *ri;
+
+       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
+       else
+               ri = NULL;
+
+       if (stack_flags == _STP_STACK_KERNEL) {
+               if (! c->regs
+                   || (c->probe_flags & _STP_PROBE_STATE_USER_MODE)) {
+                       /* 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). */
+#if defined(STAPCONF_KERNEL_STACKTRACE)
+                       unsigned long sp;
+                       int skip;
+#ifdef CONFIG_FRAME_POINTER
+                       sp  = *(unsigned long *) __builtin_frame_address (0);
+                       skip = 1; /* Skip just this frame. */
+#else
+                       sp = 0;
+                       skip = 5; /* yes, that many framework frames. */
+#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 {
+                       regs = c->regs;
+                       ri = NULL; /* This is a hint for GCC so that it can
+                                     eliminate the call to uprobe_get_pc()
+                                     in __stp_stack_print() below. */
+               }
+       } else if (stack_flags == _STP_STACK_USER) {
+               /* use task_pt_regs, regs might be kernel regs, or not set. */
+               if (c->regs && (c->probe_flags & _STP_PROBE_STATE_USER_MODE)) {
+                       regs = c->regs;
+                       uregs_valid = 1;
+               } else {
+                       regs = task_pt_regs(current);
+                       uregs_valid = _stp_task_pt_regs_valid(current, regs);
+               }
+
+               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;
+               } else {
+                       tsk = current;
+               }
+       }
+
        /* print the current address */
-       if (pi) {
-               if ((verbose & _STP_SYM_FULL) == _STP_SYM_FULL) {
+       if (stack_flags == _STP_STACK_KERNEL
+           && 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(pi),
-                                       verbose, tsk);
+                       _stp_print_addr((unsigned long)_stp_probe_addr_r(c->ips.krp.pi),
+                                       sym_flags, tsk);
                        _stp_print("Returning to  : ");
                }
-               _stp_print_addr((unsigned long)_stp_ret_addr_r(pi), verbose, tsk);
+               _stp_print_addr((unsigned long)_stp_ret_addr_r(c->ips.krp.pi),
+                               sym_flags, tsk);
 #ifdef STAPCONF_UPROBE_GET_PC
-       } else if (ri && ri != GET_PC_URETPROBE_NONE) {
-               if ((verbose & _STP_SYM_FULL) == _STP_SYM_FULL) {
+       } else if (stack_flags == _STP_STACK_USER
+                  && c->probe_type == _STP_PROBE_HANDLER_URETPROBE
+                  && ri) {
+               if ((sym_flags & _STP_SYM_FULL) == _STP_SYM_FULL) {
                        _stp_print("Returning from: ");
                        /* ... otherwise this dereference fails */
-                       _stp_print_addr(ri->rp->u.vaddr, verbose, tsk);
+                       _stp_print_addr(ri->rp->u.vaddr, sym_flags, tsk);
                        _stp_print("Returning to  : ");
-                       _stp_print_addr(ri->ret_addr, verbose, tsk);
+                       _stp_print_addr(ri->ret_addr, sym_flags, tsk);
                } else
-                       _stp_print_addr(ri->ret_addr, verbose, tsk);
+                       _stp_print_addr(ri->ret_addr, sym_flags, tsk);
 #endif
        } else {
-               _stp_print_addr(REG_IP(regs), verbose, tsk);
+               _stp_print_addr(REG_IP(regs), sym_flags, tsk);
        }
 
        /* print rest of stack... */
-       __stp_stack_print(regs, verbose, levels, tsk,
-                         context, ri, uregs_valid);
+       __stp_stack_print(regs, sym_flags, MAXBACKTRACE, tsk,
+                         &c->uwcontext, ri, uregs_valid);
 }
 
 /** Writes stack backtrace to a string
@@ -166,12 +262,8 @@ static void _stp_stack_print(struct pt_regs *regs, int verbose,
  * @param regs A pointer to the struct pt_regs.
  * @returns void
  */
-static void _stp_stack_sprint(char *str, int size, int flags,
-                             struct pt_regs *regs,
-                             struct kretprobe_instance *pi,
-                             int levels, struct task_struct *tsk,
-                             struct unwind_context *context,
-                             struct uretprobe_instance *ri, int uregs_valid)
+static void _stp_stack_sprint(char *str, int size, struct context* c,
+                             int sym_flags, int stack_flags)
 {
        /* To get an hex string, we use a simple trick.
         * First flush the print buffer,
@@ -181,13 +273,7 @@ static void _stp_stack_sprint(char *str, int size, int flags,
        _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id());
        _stp_print_flush();
 
-       if (pi)
-               _stp_print_addr((int64_t) (long) _stp_ret_addr_r(pi),
-                               flags, tsk);
-
-       _stp_print_addr((int64_t) REG_IP(regs), flags, tsk);
-       __stp_stack_print(regs, flags, levels, tsk,
-                         context, ri, uregs_valid);
+       _stp_stack_print(c, sym_flags, stack_flags);
 
        strlcpy(str, pb->buf, size < (int)pb->len ? size : (int)pb->len);
        pb->len = 0;
@@ -195,38 +281,4 @@ static void _stp_stack_sprint(char *str, int size, int flags,
 
 #endif /* CONFIG_KPROBES */
 
-static void _stp_stack_print_tsk(struct task_struct *tsk, int verbose, int levels)
-{
-#if defined(STAPCONF_KERNEL_STACKTRACE)
-        int i;
-        unsigned long backtrace[MAXBACKTRACE];
-        struct stack_trace trace;
-        int maxLevels = min(levels, MAXBACKTRACE);
-        memset(&trace, 0, sizeof(trace));
-        trace.entries = &backtrace[0];
-        trace.max_entries = maxLevels;
-        trace.skip = 0;
-        save_stack_trace_tsk(tsk, &trace);
-        for (i = 0; i < maxLevels; ++i) {
-                if (backtrace[i] == 0 || backtrace[i] == ULONG_MAX)
-                        break;
-               _stp_print_addr(backtrace[i], verbose, tsk);
-        }
-#endif
-}
-
-/** Writes a task stack backtrace to a string
- *
- * @param str string
- * @param tsk A pointer to the task_struct
- * @returns void
- */
-static void _stp_stack_snprint_tsk(char *str, int size, struct task_struct *tsk, int verbose, int levels)
-{
-       _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id());
-       _stp_print_flush();
-       _stp_stack_print_tsk(tsk, verbose, levels);
-       strlcpy(str, pb->buf, size < (int)pb->len ? size : (int)pb->len);
-       pb->len = 0;
-}
 #endif /* _STACK_C_ */
This page took 0.032634 seconds and 5 git commands to generate.