linux/arch/csky/kernel/stacktrace.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3#include <linux/sched/debug.h>
   4#include <linux/sched/task_stack.h>
   5#include <linux/stacktrace.h>
   6#include <linux/ftrace.h>
   7#include <linux/ptrace.h>
   8
   9#ifdef CONFIG_FRAME_POINTER
  10
  11struct stackframe {
  12        unsigned long fp;
  13        unsigned long ra;
  14};
  15
  16void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
  17                             bool (*fn)(unsigned long, void *), void *arg)
  18{
  19        unsigned long fp, sp, pc;
  20
  21        if (regs) {
  22                fp = frame_pointer(regs);
  23                sp = user_stack_pointer(regs);
  24                pc = instruction_pointer(regs);
  25        } else if (task == NULL || task == current) {
  26                const register unsigned long current_sp __asm__ ("sp");
  27                const register unsigned long current_fp __asm__ ("r8");
  28                fp = current_fp;
  29                sp = current_sp;
  30                pc = (unsigned long)walk_stackframe;
  31        } else {
  32                /* task blocked in __switch_to */
  33                fp = thread_saved_fp(task);
  34                sp = thread_saved_sp(task);
  35                pc = thread_saved_lr(task);
  36        }
  37
  38        for (;;) {
  39                unsigned long low, high;
  40                struct stackframe *frame;
  41
  42                if (unlikely(!__kernel_text_address(pc) || fn(pc, arg)))
  43                        break;
  44
  45                /* Validate frame pointer */
  46                low = sp;
  47                high = ALIGN(sp, THREAD_SIZE);
  48                if (unlikely(fp < low || fp > high || fp & 0x3))
  49                        break;
  50                /* Unwind stack frame */
  51                frame = (struct stackframe *)fp;
  52                sp = fp;
  53                fp = frame->fp;
  54                pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
  55                                           (unsigned long *)(fp - 8));
  56        }
  57}
  58
  59#else /* !CONFIG_FRAME_POINTER */
  60
  61static void notrace walk_stackframe(struct task_struct *task,
  62        struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
  63{
  64        unsigned long sp, pc;
  65        unsigned long *ksp;
  66
  67        if (regs) {
  68                sp = user_stack_pointer(regs);
  69                pc = instruction_pointer(regs);
  70        } else if (task == NULL || task == current) {
  71                const register unsigned long current_sp __asm__ ("sp");
  72                sp = current_sp;
  73                pc = (unsigned long)walk_stackframe;
  74        } else {
  75                /* task blocked in __switch_to */
  76                sp = thread_saved_sp(task);
  77                pc = thread_saved_lr(task);
  78        }
  79
  80        if (unlikely(sp & 0x3))
  81                return;
  82
  83        ksp = (unsigned long *)sp;
  84        while (!kstack_end(ksp)) {
  85                if (__kernel_text_address(pc) && unlikely(fn(pc, arg)))
  86                        break;
  87                pc = (*ksp++) - 0x4;
  88        }
  89}
  90#endif /* CONFIG_FRAME_POINTER */
  91
  92static bool print_trace_address(unsigned long pc, void *arg)
  93{
  94        print_ip_sym((const char *)arg, pc);
  95        return false;
  96}
  97
  98void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
  99{
 100        pr_cont("Call Trace:\n");
 101        walk_stackframe(task, NULL, print_trace_address, (void *)loglvl);
 102}
 103
 104static bool save_wchan(unsigned long pc, void *arg)
 105{
 106        if (!in_sched_functions(pc)) {
 107                unsigned long *p = arg;
 108                *p = pc;
 109                return true;
 110        }
 111        return false;
 112}
 113
 114unsigned long get_wchan(struct task_struct *task)
 115{
 116        unsigned long pc = 0;
 117
 118        if (likely(task && task != current && !task_is_running(task)))
 119                walk_stackframe(task, NULL, save_wchan, &pc);
 120        return pc;
 121}
 122
 123#ifdef CONFIG_STACKTRACE
 124static bool __save_trace(unsigned long pc, void *arg, bool nosched)
 125{
 126        struct stack_trace *trace = arg;
 127
 128        if (unlikely(nosched && in_sched_functions(pc)))
 129                return false;
 130        if (unlikely(trace->skip > 0)) {
 131                trace->skip--;
 132                return false;
 133        }
 134
 135        trace->entries[trace->nr_entries++] = pc;
 136        return (trace->nr_entries >= trace->max_entries);
 137}
 138
 139static bool save_trace(unsigned long pc, void *arg)
 140{
 141        return __save_trace(pc, arg, false);
 142}
 143
 144/*
 145 * Save stack-backtrace addresses into a stack_trace buffer.
 146 */
 147void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
 148{
 149        walk_stackframe(tsk, NULL, save_trace, trace);
 150}
 151EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
 152
 153void save_stack_trace(struct stack_trace *trace)
 154{
 155        save_stack_trace_tsk(NULL, trace);
 156}
 157EXPORT_SYMBOL_GPL(save_stack_trace);
 158
 159#endif /* CONFIG_STACKTRACE */
 160