1*457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21df0f0ffSAtsushi Nemoto /*
31df0f0ffSAtsushi Nemoto * Stack trace management functions
41df0f0ffSAtsushi Nemoto *
51df0f0ffSAtsushi Nemoto * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
61df0f0ffSAtsushi Nemoto */
71df0f0ffSAtsushi Nemoto #include <linux/sched.h>
8b17b0153SIngo Molnar #include <linux/sched/debug.h>
968db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
101df0f0ffSAtsushi Nemoto #include <linux/stacktrace.h>
1173bc256dSPaul Gortmaker #include <linux/export.h>
121df0f0ffSAtsushi Nemoto #include <asm/stacktrace.h>
131df0f0ffSAtsushi Nemoto
141df0f0ffSAtsushi Nemoto /*
151df0f0ffSAtsushi Nemoto * Save stack-backtrace addresses into a stack_trace buffer:
161df0f0ffSAtsushi Nemoto */
save_raw_context_stack(struct stack_trace * trace,unsigned long reg29,int savesched)171df0f0ffSAtsushi Nemoto static void save_raw_context_stack(struct stack_trace *trace,
18e1e16115SAaro Koskinen unsigned long reg29, int savesched)
191df0f0ffSAtsushi Nemoto {
201df0f0ffSAtsushi Nemoto unsigned long *sp = (unsigned long *)reg29;
211df0f0ffSAtsushi Nemoto unsigned long addr;
221df0f0ffSAtsushi Nemoto
231df0f0ffSAtsushi Nemoto while (!kstack_end(sp)) {
241df0f0ffSAtsushi Nemoto addr = *sp++;
25e1e16115SAaro Koskinen if (__kernel_text_address(addr) &&
26e1e16115SAaro Koskinen (savesched || !in_sched_functions(addr))) {
2723126692SAtsushi Nemoto if (trace->skip > 0)
2823126692SAtsushi Nemoto trace->skip--;
291df0f0ffSAtsushi Nemoto else
3023126692SAtsushi Nemoto trace->entries[trace->nr_entries++] = addr;
311df0f0ffSAtsushi Nemoto if (trace->nr_entries >= trace->max_entries)
321df0f0ffSAtsushi Nemoto break;
331df0f0ffSAtsushi Nemoto }
341df0f0ffSAtsushi Nemoto }
351df0f0ffSAtsushi Nemoto }
361df0f0ffSAtsushi Nemoto
save_context_stack(struct stack_trace * trace,struct task_struct * tsk,struct pt_regs * regs,int savesched)372ec220e2SKen Chen static void save_context_stack(struct stack_trace *trace,
38e1e16115SAaro Koskinen struct task_struct *tsk, struct pt_regs *regs, int savesched)
391df0f0ffSAtsushi Nemoto {
401df0f0ffSAtsushi Nemoto unsigned long sp = regs->regs[29];
411df0f0ffSAtsushi Nemoto #ifdef CONFIG_KALLSYMS
421df0f0ffSAtsushi Nemoto unsigned long ra = regs->regs[31];
431df0f0ffSAtsushi Nemoto unsigned long pc = regs->cp0_epc;
441df0f0ffSAtsushi Nemoto
451df0f0ffSAtsushi Nemoto if (raw_show_trace || !__kernel_text_address(pc)) {
461924600cSAtsushi Nemoto unsigned long stack_page =
472ec220e2SKen Chen (unsigned long)task_stack_page(tsk);
4823126692SAtsushi Nemoto if (stack_page && sp >= stack_page &&
4923126692SAtsushi Nemoto sp <= stack_page + THREAD_SIZE - 32)
50e1e16115SAaro Koskinen save_raw_context_stack(trace, sp, savesched);
511924600cSAtsushi Nemoto return;
521df0f0ffSAtsushi Nemoto }
531df0f0ffSAtsushi Nemoto do {
54e1e16115SAaro Koskinen if (savesched || !in_sched_functions(pc)) {
5523126692SAtsushi Nemoto if (trace->skip > 0)
5623126692SAtsushi Nemoto trace->skip--;
571df0f0ffSAtsushi Nemoto else
5823126692SAtsushi Nemoto trace->entries[trace->nr_entries++] = pc;
591df0f0ffSAtsushi Nemoto if (trace->nr_entries >= trace->max_entries)
601df0f0ffSAtsushi Nemoto break;
61e1e16115SAaro Koskinen }
622ec220e2SKen Chen pc = unwind_stack(tsk, &sp, pc, &ra);
631df0f0ffSAtsushi Nemoto } while (pc);
641df0f0ffSAtsushi Nemoto #else
65e1e16115SAaro Koskinen save_raw_context_stack(trace, sp, savesched);
661df0f0ffSAtsushi Nemoto #endif
671df0f0ffSAtsushi Nemoto }
681df0f0ffSAtsushi Nemoto
691df0f0ffSAtsushi Nemoto /*
701df0f0ffSAtsushi Nemoto * Save stack-backtrace addresses into a stack_trace buffer.
711df0f0ffSAtsushi Nemoto */
save_stack_trace(struct stack_trace * trace)72ab1b6f03SChristoph Hellwig void save_stack_trace(struct stack_trace *trace)
731df0f0ffSAtsushi Nemoto {
742ec220e2SKen Chen save_stack_trace_tsk(current, trace);
752ec220e2SKen Chen }
762ec220e2SKen Chen EXPORT_SYMBOL_GPL(save_stack_trace);
772ec220e2SKen Chen
save_stack_trace_tsk(struct task_struct * tsk,struct stack_trace * trace)782ec220e2SKen Chen void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
792ec220e2SKen Chen {
801df0f0ffSAtsushi Nemoto struct pt_regs dummyregs;
811df0f0ffSAtsushi Nemoto struct pt_regs *regs = &dummyregs;
821df0f0ffSAtsushi Nemoto
831df0f0ffSAtsushi Nemoto WARN_ON(trace->nr_entries || !trace->max_entries);
841df0f0ffSAtsushi Nemoto
852ec220e2SKen Chen if (tsk != current) {
862ec220e2SKen Chen regs->regs[29] = tsk->thread.reg29;
872ec220e2SKen Chen regs->regs[31] = 0;
882ec220e2SKen Chen regs->cp0_epc = tsk->thread.reg31;
892ec220e2SKen Chen } else
901df0f0ffSAtsushi Nemoto prepare_frametrace(regs);
91e1e16115SAaro Koskinen save_context_stack(trace, tsk, regs, tsk == current);
921df0f0ffSAtsushi Nemoto }
932ec220e2SKen Chen EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
94