Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157// SPDX-License-Identifier: GPL-2.0
#include <linux/sched/debug.h>#include <linux/sched/task_stack.h>#include <linux/stacktrace.h>#include <linux/ftrace.h>#include <linux/ptrace.h>
#ifdef CONFIG_FRAME_POINTER
struct stackframe { unsigned long fp; unsigned long ra;};
void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg){ unsigned long fp, sp, pc;
if (regs) { fp = frame_pointer(regs); sp = user_stack_pointer(regs); pc = instruction_pointer(regs); } else if (task == NULL || task == current) { const register unsigned long current_fp __asm__ ("r8"); fp = current_fp; sp = current_stack_pointer; pc = (unsigned long)walk_stackframe; } else { /* task blocked in __switch_to */ fp = thread_saved_fp(task); sp = thread_saved_sp(task); pc = thread_saved_lr(task); }
for (;;) { unsigned long low, high; struct stackframe *frame;
if (unlikely(!__kernel_text_address(pc) || fn(pc, arg))) break;
/* Validate frame pointer */ low = sp; high = ALIGN(sp, THREAD_SIZE); if (unlikely(fp < low || fp > high || fp & 0x3)) break; /* Unwind stack frame */ frame = (struct stackframe *)fp; sp = fp; fp = frame->fp; pc = ftrace_graph_ret_addr(current, NULL, frame->ra, (unsigned long *)(fp - 8)); }}
#else /* !CONFIG_FRAME_POINTER */
static void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg){ unsigned long sp, pc; unsigned long *ksp;
if (regs) { sp = user_stack_pointer(regs); pc = instruction_pointer(regs); } else if (task == NULL || task == current) { sp = current_stack_pointer; pc = (unsigned long)walk_stackframe; } else { /* task blocked in __switch_to */ sp = thread_saved_sp(task); pc = thread_saved_lr(task); }
if (unlikely(sp & 0x3)) return;
ksp = (unsigned long *)sp; while (!kstack_end(ksp)) { if (__kernel_text_address(pc) && unlikely(fn(pc, arg))) break; pc = (*ksp++) - 0x4; }}#endif /* CONFIG_FRAME_POINTER */
static bool print_trace_address(unsigned long pc, void *arg){ print_ip_sym((const char *)arg, pc); return false;}
void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl){ pr_cont("Call Trace:\n"); walk_stackframe(task, NULL, print_trace_address, (void *)loglvl);}
static bool save_wchan(unsigned long pc, void *arg){ if (!in_sched_functions(pc)) { unsigned long *p = arg; *p = pc; return true; } return false;}
unsigned long __get_wchan(struct task_struct *task){ unsigned long pc = 0;
walk_stackframe(task, NULL, save_wchan, &pc); return pc;}
#ifdef CONFIG_STACKTRACEstatic bool __save_trace(unsigned long pc, void *arg, bool nosched){ struct stack_trace *trace = arg;
if (unlikely(nosched && in_sched_functions(pc))) return false; if (unlikely(trace->skip > 0)) { trace->skip--; return false; }
trace->entries[trace->nr_entries++] = pc; return (trace->nr_entries >= trace->max_entries);}
static bool save_trace(unsigned long pc, void *arg){ return __save_trace(pc, arg, false);}
/* * Save stack-backtrace addresses into a stack_trace buffer. */void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace){ walk_stackframe(tsk, NULL, save_trace, trace);}EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
void save_stack_trace(struct stack_trace *trace){ save_stack_trace_tsk(NULL, trace);}EXPORT_SYMBOL_GPL(save_stack_trace);
#endif /* CONFIG_STACKTRACE */