• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   */
save_raw_context_stack(struct stack_trace * trace,unsigned long reg29,int savesched)17  static 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  
save_context_stack(struct stack_trace * trace,struct task_struct * tsk,struct pt_regs * regs,int savesched)37  static 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   */
save_stack_trace(struct stack_trace * trace)72  void save_stack_trace(struct stack_trace *trace)
73  {
74  	save_stack_trace_tsk(current, trace);
75  }
76  EXPORT_SYMBOL_GPL(save_stack_trace);
77  
save_stack_trace_tsk(struct task_struct * tsk,struct stack_trace * trace)78  void 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  }
93  EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
94