linux/arch/riscv/kernel/stacktrace.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2008 ARM Limited
   4 * Copyright (C) 2014 Regents of the University of California
   5 */
   6
   7#include <linux/export.h>
   8#include <linux/kallsyms.h>
   9#include <linux/sched.h>
  10#include <linux/sched/debug.h>
  11#include <linux/sched/task_stack.h>
  12#include <linux/stacktrace.h>
  13#include <linux/ftrace.h>
  14
  15#include <asm/stacktrace.h>
  16
  17#ifdef CONFIG_FRAME_POINTER
  18
  19void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
  20                             bool (*fn)(void *, unsigned long), void *arg)
  21{
  22        unsigned long fp, sp, pc;
  23        int level = 0;
  24
  25        if (regs) {
  26                fp = frame_pointer(regs);
  27                sp = user_stack_pointer(regs);
  28                pc = instruction_pointer(regs);
  29        } else if (task == NULL || task == current) {
  30                fp = (unsigned long)__builtin_frame_address(0);
  31                sp = current_stack_pointer;
  32                pc = (unsigned long)walk_stackframe;
  33        } else {
  34                /* task blocked in __switch_to */
  35                fp = task->thread.s[0];
  36                sp = task->thread.sp;
  37                pc = task->thread.ra;
  38        }
  39
  40        for (;;) {
  41                unsigned long low, high;
  42                struct stackframe *frame;
  43
  44                if (unlikely(!__kernel_text_address(pc) || (level++ >= 1 && !fn(arg, pc))))
  45                        break;
  46
  47                /* Validate frame pointer */
  48                low = sp + sizeof(struct stackframe);
  49                high = ALIGN(sp, THREAD_SIZE);
  50                if (unlikely(fp < low || fp > high || fp & 0x7))
  51                        break;
  52                /* Unwind stack frame */
  53                frame = (struct stackframe *)fp - 1;
  54                sp = fp;
  55                if (regs && (regs->epc == pc) && (frame->fp & 0x7)) {
  56                        fp = frame->ra;
  57                        pc = regs->ra;
  58                } else {
  59                        fp = frame->fp;
  60                        pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
  61                                                   (unsigned long *)(fp - 8));
  62                }
  63
  64        }
  65}
  66
  67#else /* !CONFIG_FRAME_POINTER */
  68
  69void notrace walk_stackframe(struct task_struct *task,
  70        struct pt_regs *regs, bool (*fn)(void *, unsigned long), void *arg)
  71{
  72        unsigned long sp, pc;
  73        unsigned long *ksp;
  74
  75        if (regs) {
  76                sp = user_stack_pointer(regs);
  77                pc = instruction_pointer(regs);
  78        } else if (task == NULL || task == current) {
  79                sp = current_stack_pointer;
  80                pc = (unsigned long)walk_stackframe;
  81        } else {
  82                /* task blocked in __switch_to */
  83                sp = task->thread.sp;
  84                pc = task->thread.ra;
  85        }
  86
  87        if (unlikely(sp & 0x7))
  88                return;
  89
  90        ksp = (unsigned long *)sp;
  91        while (!kstack_end(ksp)) {
  92                if (__kernel_text_address(pc) && unlikely(!fn(arg, pc)))
  93                        break;
  94                pc = (*ksp++) - 0x4;
  95        }
  96}
  97
  98#endif /* CONFIG_FRAME_POINTER */
  99
 100static bool print_trace_address(void *arg, unsigned long pc)
 101{
 102        const char *loglvl = arg;
 103
 104        print_ip_sym(loglvl, pc);
 105        return true;
 106}
 107
 108noinline void dump_backtrace(struct pt_regs *regs, struct task_struct *task,
 109                    const char *loglvl)
 110{
 111        walk_stackframe(task, regs, print_trace_address, (void *)loglvl);
 112}
 113
 114void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
 115{
 116        pr_cont("%sCall Trace:\n", loglvl);
 117        dump_backtrace(NULL, task, loglvl);
 118}
 119
 120static bool save_wchan(void *arg, unsigned long pc)
 121{
 122        if (!in_sched_functions(pc)) {
 123                unsigned long *p = arg;
 124                *p = pc;
 125                return false;
 126        }
 127        return true;
 128}
 129
 130unsigned long __get_wchan(struct task_struct *task)
 131{
 132        unsigned long pc = 0;
 133
 134        if (!try_get_task_stack(task))
 135                return 0;
 136        walk_stackframe(task, NULL, save_wchan, &pc);
 137        put_task_stack(task);
 138        return pc;
 139}
 140
 141noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
 142                     struct task_struct *task, struct pt_regs *regs)
 143{
 144        walk_stackframe(task, regs, consume_entry, cookie);
 145}
 146