linux/arch/x86/kernel/stacktrace.c
<<
>>
Prefs
   1/*
   2 * Stack trace management functions
   3 *
   4 *  Copyright (C) 2006-2009 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
   5 */
   6#include <linux/sched.h>
   7#include <linux/stacktrace.h>
   8#include <linux/module.h>
   9#include <linux/uaccess.h>
  10#include <asm/stacktrace.h>
  11
  12static void save_stack_warning(void *data, char *msg)
  13{
  14}
  15
  16static void
  17save_stack_warning_symbol(void *data, char *msg, unsigned long symbol)
  18{
  19}
  20
  21static int save_stack_stack(void *data, char *name)
  22{
  23        return 0;
  24}
  25
  26static void save_stack_address(void *data, unsigned long addr, int reliable)
  27{
  28        struct stack_trace *trace = data;
  29        if (!reliable)
  30                return;
  31        if (trace->skip > 0) {
  32                trace->skip--;
  33                return;
  34        }
  35        if (trace->nr_entries < trace->max_entries)
  36                trace->entries[trace->nr_entries++] = addr;
  37}
  38
  39static void
  40save_stack_address_nosched(void *data, unsigned long addr, int reliable)
  41{
  42        struct stack_trace *trace = (struct stack_trace *)data;
  43        if (!reliable)
  44                return;
  45        if (in_sched_functions(addr))
  46                return;
  47        if (trace->skip > 0) {
  48                trace->skip--;
  49                return;
  50        }
  51        if (trace->nr_entries < trace->max_entries)
  52                trace->entries[trace->nr_entries++] = addr;
  53}
  54
  55static const struct stacktrace_ops save_stack_ops = {
  56        .warning = save_stack_warning,
  57        .warning_symbol = save_stack_warning_symbol,
  58        .stack = save_stack_stack,
  59        .address = save_stack_address,
  60};
  61
  62static const struct stacktrace_ops save_stack_ops_nosched = {
  63        .warning = save_stack_warning,
  64        .warning_symbol = save_stack_warning_symbol,
  65        .stack = save_stack_stack,
  66        .address = save_stack_address_nosched,
  67};
  68
  69/*
  70 * Save stack-backtrace addresses into a stack_trace buffer.
  71 */
  72void save_stack_trace(struct stack_trace *trace)
  73{
  74        dump_trace(current, NULL, NULL, 0, &save_stack_ops, trace);
  75        if (trace->nr_entries < trace->max_entries)
  76                trace->entries[trace->nr_entries++] = ULONG_MAX;
  77}
  78EXPORT_SYMBOL_GPL(save_stack_trace);
  79
  80void save_stack_trace_bp(struct stack_trace *trace, unsigned long bp)
  81{
  82        dump_trace(current, NULL, NULL, bp, &save_stack_ops, trace);
  83        if (trace->nr_entries < trace->max_entries)
  84                trace->entries[trace->nr_entries++] = ULONG_MAX;
  85}
  86
  87void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
  88{
  89        dump_trace(tsk, NULL, NULL, 0, &save_stack_ops_nosched, trace);
  90        if (trace->nr_entries < trace->max_entries)
  91                trace->entries[trace->nr_entries++] = ULONG_MAX;
  92}
  93EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
  94
  95/* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */
  96
  97struct stack_frame {
  98        const void __user       *next_fp;
  99        unsigned long           ret_addr;
 100};
 101
 102static int copy_stack_frame(const void __user *fp, struct stack_frame *frame)
 103{
 104        int ret;
 105
 106        if (!access_ok(VERIFY_READ, fp, sizeof(*frame)))
 107                return 0;
 108
 109        ret = 1;
 110        pagefault_disable();
 111        if (__copy_from_user_inatomic(frame, fp, sizeof(*frame)))
 112                ret = 0;
 113        pagefault_enable();
 114
 115        return ret;
 116}
 117
 118static inline void __save_stack_trace_user(struct stack_trace *trace)
 119{
 120        const struct pt_regs *regs = task_pt_regs(current);
 121        const void __user *fp = (const void __user *)regs->bp;
 122
 123        if (trace->nr_entries < trace->max_entries)
 124                trace->entries[trace->nr_entries++] = regs->ip;
 125
 126        while (trace->nr_entries < trace->max_entries) {
 127                struct stack_frame frame;
 128
 129                frame.next_fp = NULL;
 130                frame.ret_addr = 0;
 131                if (!copy_stack_frame(fp, &frame))
 132                        break;
 133                if ((unsigned long)fp < regs->sp)
 134                        break;
 135                if (frame.ret_addr) {
 136                        trace->entries[trace->nr_entries++] =
 137                                frame.ret_addr;
 138                }
 139                if (fp == frame.next_fp)
 140                        break;
 141                fp = frame.next_fp;
 142        }
 143}
 144
 145void save_stack_trace_user(struct stack_trace *trace)
 146{
 147        /*
 148         * Trace user stack if we are not a kernel thread
 149         */
 150        if (current->mm) {
 151                __save_stack_trace_user(trace);
 152        }
 153        if (trace->nr_entries < trace->max_entries)
 154                trace->entries[trace->nr_entries++] = ULONG_MAX;
 155}
 156
 157