stacktrace.c (2358B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Stack trace management functions 4 * 5 * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp> 6 */ 7#include <linux/sched.h> 8#include <linux/sched/debug.h> 9#include <linux/sched/task_stack.h> 10#include <linux/stacktrace.h> 11#include <linux/export.h> 12#include <asm/stacktrace.h> 13 14/* 15 * Save stack-backtrace addresses into a stack_trace buffer: 16 */ 17static void save_raw_context_stack(struct stack_trace *trace, 18 unsigned long reg29, int savesched) 19{ 20 unsigned long *sp = (unsigned long *)reg29; 21 unsigned long addr; 22 23 while (!kstack_end(sp)) { 24 addr = *sp++; 25 if (__kernel_text_address(addr) && 26 (savesched || !in_sched_functions(addr))) { 27 if (trace->skip > 0) 28 trace->skip--; 29 else 30 trace->entries[trace->nr_entries++] = addr; 31 if (trace->nr_entries >= trace->max_entries) 32 break; 33 } 34 } 35} 36 37static void save_context_stack(struct stack_trace *trace, 38 struct task_struct *tsk, struct pt_regs *regs, int savesched) 39{ 40 unsigned long sp = regs->regs[29]; 41#ifdef CONFIG_KALLSYMS 42 unsigned long ra = regs->regs[31]; 43 unsigned long pc = regs->cp0_epc; 44 45 if (raw_show_trace || !__kernel_text_address(pc)) { 46 unsigned long stack_page = 47 (unsigned long)task_stack_page(tsk); 48 if (stack_page && sp >= stack_page && 49 sp <= stack_page + THREAD_SIZE - 32) 50 save_raw_context_stack(trace, sp, savesched); 51 return; 52 } 53 do { 54 if (savesched || !in_sched_functions(pc)) { 55 if (trace->skip > 0) 56 trace->skip--; 57 else 58 trace->entries[trace->nr_entries++] = pc; 59 if (trace->nr_entries >= trace->max_entries) 60 break; 61 } 62 pc = unwind_stack(tsk, &sp, pc, &ra); 63 } while (pc); 64#else 65 save_raw_context_stack(trace, sp, savesched); 66#endif 67} 68 69/* 70 * Save stack-backtrace addresses into a stack_trace buffer. 71 */ 72void save_stack_trace(struct stack_trace *trace) 73{ 74 save_stack_trace_tsk(current, trace); 75} 76EXPORT_SYMBOL_GPL(save_stack_trace); 77 78void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 79{ 80 struct pt_regs dummyregs; 81 struct pt_regs *regs = &dummyregs; 82 83 WARN_ON(trace->nr_entries || !trace->max_entries); 84 85 if (tsk != current) { 86 regs->regs[29] = tsk->thread.reg29; 87 regs->regs[31] = 0; 88 regs->cp0_epc = tsk->thread.reg31; 89 } else 90 prepare_frametrace(regs); 91 save_context_stack(trace, tsk, regs, tsk == current); 92} 93EXPORT_SYMBOL_GPL(save_stack_trace_tsk);