Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 #include <linux/sched/debug.h>
0004 #include <linux/sched/task_stack.h>
0005 #include <linux/stacktrace.h>
0006 #include <linux/ftrace.h>
0007 #include <linux/ptrace.h>
0008 
0009 #ifdef CONFIG_FRAME_POINTER
0010 
0011 struct stackframe {
0012     unsigned long fp;
0013     unsigned long ra;
0014 };
0015 
0016 void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
0017                  bool (*fn)(unsigned long, void *), void *arg)
0018 {
0019     unsigned long fp, sp, pc;
0020 
0021     if (regs) {
0022         fp = frame_pointer(regs);
0023         sp = user_stack_pointer(regs);
0024         pc = instruction_pointer(regs);
0025     } else if (task == NULL || task == current) {
0026         const register unsigned long current_sp __asm__ ("sp");
0027         const register unsigned long current_fp __asm__ ("r8");
0028         fp = current_fp;
0029         sp = current_sp;
0030         pc = (unsigned long)walk_stackframe;
0031     } else {
0032         /* task blocked in __switch_to */
0033         fp = thread_saved_fp(task);
0034         sp = thread_saved_sp(task);
0035         pc = thread_saved_lr(task);
0036     }
0037 
0038     for (;;) {
0039         unsigned long low, high;
0040         struct stackframe *frame;
0041 
0042         if (unlikely(!__kernel_text_address(pc) || fn(pc, arg)))
0043             break;
0044 
0045         /* Validate frame pointer */
0046         low = sp;
0047         high = ALIGN(sp, THREAD_SIZE);
0048         if (unlikely(fp < low || fp > high || fp & 0x3))
0049             break;
0050         /* Unwind stack frame */
0051         frame = (struct stackframe *)fp;
0052         sp = fp;
0053         fp = frame->fp;
0054         pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
0055                        (unsigned long *)(fp - 8));
0056     }
0057 }
0058 
0059 #else /* !CONFIG_FRAME_POINTER */
0060 
0061 static void notrace walk_stackframe(struct task_struct *task,
0062     struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
0063 {
0064     unsigned long sp, pc;
0065     unsigned long *ksp;
0066 
0067     if (regs) {
0068         sp = user_stack_pointer(regs);
0069         pc = instruction_pointer(regs);
0070     } else if (task == NULL || task == current) {
0071         const register unsigned long current_sp __asm__ ("sp");
0072         sp = current_sp;
0073         pc = (unsigned long)walk_stackframe;
0074     } else {
0075         /* task blocked in __switch_to */
0076         sp = thread_saved_sp(task);
0077         pc = thread_saved_lr(task);
0078     }
0079 
0080     if (unlikely(sp & 0x3))
0081         return;
0082 
0083     ksp = (unsigned long *)sp;
0084     while (!kstack_end(ksp)) {
0085         if (__kernel_text_address(pc) && unlikely(fn(pc, arg)))
0086             break;
0087         pc = (*ksp++) - 0x4;
0088     }
0089 }
0090 #endif /* CONFIG_FRAME_POINTER */
0091 
0092 static bool print_trace_address(unsigned long pc, void *arg)
0093 {
0094     print_ip_sym((const char *)arg, pc);
0095     return false;
0096 }
0097 
0098 void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
0099 {
0100     pr_cont("Call Trace:\n");
0101     walk_stackframe(task, NULL, print_trace_address, (void *)loglvl);
0102 }
0103 
0104 static bool save_wchan(unsigned long pc, void *arg)
0105 {
0106     if (!in_sched_functions(pc)) {
0107         unsigned long *p = arg;
0108         *p = pc;
0109         return true;
0110     }
0111     return false;
0112 }
0113 
0114 unsigned long __get_wchan(struct task_struct *task)
0115 {
0116     unsigned long pc = 0;
0117 
0118     walk_stackframe(task, NULL, save_wchan, &pc);
0119     return pc;
0120 }
0121 
0122 #ifdef CONFIG_STACKTRACE
0123 static bool __save_trace(unsigned long pc, void *arg, bool nosched)
0124 {
0125     struct stack_trace *trace = arg;
0126 
0127     if (unlikely(nosched && in_sched_functions(pc)))
0128         return false;
0129     if (unlikely(trace->skip > 0)) {
0130         trace->skip--;
0131         return false;
0132     }
0133 
0134     trace->entries[trace->nr_entries++] = pc;
0135     return (trace->nr_entries >= trace->max_entries);
0136 }
0137 
0138 static bool save_trace(unsigned long pc, void *arg)
0139 {
0140     return __save_trace(pc, arg, false);
0141 }
0142 
0143 /*
0144  * Save stack-backtrace addresses into a stack_trace buffer.
0145  */
0146 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
0147 {
0148     walk_stackframe(tsk, NULL, save_trace, trace);
0149 }
0150 EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
0151 
0152 void save_stack_trace(struct stack_trace *trace)
0153 {
0154     save_stack_trace_tsk(NULL, trace);
0155 }
0156 EXPORT_SYMBOL_GPL(save_stack_trace);
0157 
0158 #endif /* CONFIG_STACKTRACE */