Linux Audio

Check our new training course

Loading...
v4.6
  1/*
  2 * Stack tracing support
  3 *
  4 * Copyright (C) 2012 ARM Ltd.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License version 2 as
  8 * published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope that it will be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 * GNU General Public License for more details.
 14 *
 15 * You should have received a copy of the GNU General Public License
 16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 17 */
 18#include <linux/kernel.h>
 19#include <linux/export.h>
 20#include <linux/ftrace.h>
 21#include <linux/sched.h>
 22#include <linux/stacktrace.h>
 23
 24#include <asm/irq.h>
 
 25#include <asm/stacktrace.h>
 26
 27/*
 28 * AArch64 PCS assigns the frame pointer to x29.
 29 *
 30 * A simple function prologue looks like this:
 31 * 	sub	sp, sp, #0x10
 32 *   	stp	x29, x30, [sp]
 33 *	mov	x29, sp
 34 *
 35 * A simple function epilogue looks like this:
 36 *	mov	sp, x29
 37 *	ldp	x29, x30, [sp]
 38 *	add	sp, sp, #0x10
 39 */
 40int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
 41{
 42	unsigned long high, low;
 43	unsigned long fp = frame->fp;
 44	unsigned long irq_stack_ptr;
 45
 
 
 
 46	/*
 47	 * Switching between stacks is valid when tracing current and in
 48	 * non-preemptible context.
 49	 */
 50	if (tsk == current && !preemptible())
 51		irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id());
 52	else
 53		irq_stack_ptr = 0;
 54
 55	low  = frame->sp;
 56	/* irq stacks are not THREAD_SIZE aligned */
 57	if (on_irq_stack(frame->sp, raw_smp_processor_id()))
 58		high = irq_stack_ptr;
 59	else
 60		high = ALIGN(low, THREAD_SIZE) - 0x20;
 61
 62	if (fp < low || fp > high || fp & 0xf)
 63		return -EINVAL;
 64
 65	frame->sp = fp + 0x10;
 66	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
 67	frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8));
 68
 69#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 70	if (tsk && tsk->ret_stack &&
 71			(frame->pc == (unsigned long)return_to_handler)) {
 72		/*
 73		 * This is a case where function graph tracer has
 74		 * modified a return address (LR) in a stack frame
 75		 * to hook a function return.
 76		 * So replace it to an original value.
 77		 */
 78		frame->pc = tsk->ret_stack[frame->graph--].ret;
 79	}
 80#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 81
 82	/*
 83	 * Check whether we are going to walk through from interrupt stack
 84	 * to task stack.
 85	 * If we reach the end of the stack - and its an interrupt stack,
 86	 * unpack the dummy frame to find the original elr.
 87	 *
 88	 * Check the frame->fp we read from the bottom of the irq_stack,
 89	 * and the original task stack pointer are both in current->stack.
 90	 */
 91	if (frame->sp == irq_stack_ptr) {
 92		struct pt_regs *irq_args;
 93		unsigned long orig_sp = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr);
 94
 95		if (object_is_on_stack((void *)orig_sp) &&
 96		   object_is_on_stack((void *)frame->fp)) {
 97			frame->sp = orig_sp;
 98
 99			/* orig_sp is the saved pt_regs, find the elr */
100			irq_args = (struct pt_regs *)orig_sp;
101			frame->pc = irq_args->pc;
102		} else {
103			/*
104			 * This frame has a non-standard format, and we
105			 * didn't fix it, because the data looked wrong.
106			 * Refuse to output this frame.
107			 */
108			return -EINVAL;
109		}
110	}
111
112	return 0;
113}
114
115void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
116		     int (*fn)(struct stackframe *, void *), void *data)
117{
118	while (1) {
119		int ret;
120
121		if (fn(frame, data))
122			break;
123		ret = unwind_frame(tsk, frame);
124		if (ret < 0)
125			break;
126	}
127}
128EXPORT_SYMBOL(walk_stackframe);
129
130#ifdef CONFIG_STACKTRACE
131struct stack_trace_data {
132	struct stack_trace *trace;
133	unsigned int no_sched_functions;
134	unsigned int skip;
135};
136
137static int save_trace(struct stackframe *frame, void *d)
138{
139	struct stack_trace_data *data = d;
140	struct stack_trace *trace = data->trace;
141	unsigned long addr = frame->pc;
142
143	if (data->no_sched_functions && in_sched_functions(addr))
144		return 0;
145	if (data->skip) {
146		data->skip--;
147		return 0;
148	}
149
150	trace->entries[trace->nr_entries++] = addr;
151
152	return trace->nr_entries >= trace->max_entries;
153}
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
156{
157	struct stack_trace_data data;
158	struct stackframe frame;
159
 
 
 
160	data.trace = trace;
161	data.skip = trace->skip;
162
163	if (tsk != current) {
164		data.no_sched_functions = 1;
165		frame.fp = thread_saved_fp(tsk);
166		frame.sp = thread_saved_sp(tsk);
167		frame.pc = thread_saved_pc(tsk);
168	} else {
169		data.no_sched_functions = 0;
170		frame.fp = (unsigned long)__builtin_frame_address(0);
171		frame.sp = current_stack_pointer;
172		frame.pc = (unsigned long)save_stack_trace_tsk;
173	}
174#ifdef CONFIG_FUNCTION_GRAPH_TRACER
175	frame.graph = tsk->curr_ret_stack;
176#endif
177
178	walk_stackframe(tsk, &frame, save_trace, &data);
179	if (trace->nr_entries < trace->max_entries)
180		trace->entries[trace->nr_entries++] = ULONG_MAX;
 
 
181}
182
183void save_stack_trace(struct stack_trace *trace)
184{
185	save_stack_trace_tsk(current, trace);
186}
187EXPORT_SYMBOL_GPL(save_stack_trace);
188#endif
v4.10.11
  1/*
  2 * Stack tracing support
  3 *
  4 * Copyright (C) 2012 ARM Ltd.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License version 2 as
  8 * published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope that it will be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 * GNU General Public License for more details.
 14 *
 15 * You should have received a copy of the GNU General Public License
 16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 17 */
 18#include <linux/kernel.h>
 19#include <linux/export.h>
 20#include <linux/ftrace.h>
 21#include <linux/sched.h>
 22#include <linux/stacktrace.h>
 23
 24#include <asm/irq.h>
 25#include <asm/stack_pointer.h>
 26#include <asm/stacktrace.h>
 27
 28/*
 29 * AArch64 PCS assigns the frame pointer to x29.
 30 *
 31 * A simple function prologue looks like this:
 32 * 	sub	sp, sp, #0x10
 33 *   	stp	x29, x30, [sp]
 34 *	mov	x29, sp
 35 *
 36 * A simple function epilogue looks like this:
 37 *	mov	sp, x29
 38 *	ldp	x29, x30, [sp]
 39 *	add	sp, sp, #0x10
 40 */
 41int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
 42{
 43	unsigned long high, low;
 44	unsigned long fp = frame->fp;
 45	unsigned long irq_stack_ptr;
 46
 47	if (!tsk)
 48		tsk = current;
 49
 50	/*
 51	 * Switching between stacks is valid when tracing current and in
 52	 * non-preemptible context.
 53	 */
 54	if (tsk == current && !preemptible())
 55		irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id());
 56	else
 57		irq_stack_ptr = 0;
 58
 59	low  = frame->sp;
 60	/* irq stacks are not THREAD_SIZE aligned */
 61	if (on_irq_stack(frame->sp, raw_smp_processor_id()))
 62		high = irq_stack_ptr;
 63	else
 64		high = ALIGN(low, THREAD_SIZE) - 0x20;
 65
 66	if (fp < low || fp > high || fp & 0xf)
 67		return -EINVAL;
 68
 69	frame->sp = fp + 0x10;
 70	frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
 71	frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8));
 72
 73#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 74	if (tsk->ret_stack &&
 75			(frame->pc == (unsigned long)return_to_handler)) {
 76		/*
 77		 * This is a case where function graph tracer has
 78		 * modified a return address (LR) in a stack frame
 79		 * to hook a function return.
 80		 * So replace it to an original value.
 81		 */
 82		frame->pc = tsk->ret_stack[frame->graph--].ret;
 83	}
 84#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 85
 86	/*
 87	 * Check whether we are going to walk through from interrupt stack
 88	 * to task stack.
 89	 * If we reach the end of the stack - and its an interrupt stack,
 90	 * unpack the dummy frame to find the original elr.
 91	 *
 92	 * Check the frame->fp we read from the bottom of the irq_stack,
 93	 * and the original task stack pointer are both in current->stack.
 94	 */
 95	if (frame->sp == irq_stack_ptr) {
 96		struct pt_regs *irq_args;
 97		unsigned long orig_sp = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr);
 98
 99		if (object_is_on_stack((void *)orig_sp) &&
100		   object_is_on_stack((void *)frame->fp)) {
101			frame->sp = orig_sp;
102
103			/* orig_sp is the saved pt_regs, find the elr */
104			irq_args = (struct pt_regs *)orig_sp;
105			frame->pc = irq_args->pc;
106		} else {
107			/*
108			 * This frame has a non-standard format, and we
109			 * didn't fix it, because the data looked wrong.
110			 * Refuse to output this frame.
111			 */
112			return -EINVAL;
113		}
114	}
115
116	return 0;
117}
118
119void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
120		     int (*fn)(struct stackframe *, void *), void *data)
121{
122	while (1) {
123		int ret;
124
125		if (fn(frame, data))
126			break;
127		ret = unwind_frame(tsk, frame);
128		if (ret < 0)
129			break;
130	}
131}
 
132
133#ifdef CONFIG_STACKTRACE
134struct stack_trace_data {
135	struct stack_trace *trace;
136	unsigned int no_sched_functions;
137	unsigned int skip;
138};
139
140static int save_trace(struct stackframe *frame, void *d)
141{
142	struct stack_trace_data *data = d;
143	struct stack_trace *trace = data->trace;
144	unsigned long addr = frame->pc;
145
146	if (data->no_sched_functions && in_sched_functions(addr))
147		return 0;
148	if (data->skip) {
149		data->skip--;
150		return 0;
151	}
152
153	trace->entries[trace->nr_entries++] = addr;
154
155	return trace->nr_entries >= trace->max_entries;
156}
157
158void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
159{
160	struct stack_trace_data data;
161	struct stackframe frame;
162
163	data.trace = trace;
164	data.skip = trace->skip;
165	data.no_sched_functions = 0;
166
167	frame.fp = regs->regs[29];
168	frame.sp = regs->sp;
169	frame.pc = regs->pc;
170#ifdef CONFIG_FUNCTION_GRAPH_TRACER
171	frame.graph = current->curr_ret_stack;
172#endif
173
174	walk_stackframe(current, &frame, save_trace, &data);
175	if (trace->nr_entries < trace->max_entries)
176		trace->entries[trace->nr_entries++] = ULONG_MAX;
177}
178
179void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
180{
181	struct stack_trace_data data;
182	struct stackframe frame;
183
184	if (!try_get_task_stack(tsk))
185		return;
186
187	data.trace = trace;
188	data.skip = trace->skip;
189
190	if (tsk != current) {
191		data.no_sched_functions = 1;
192		frame.fp = thread_saved_fp(tsk);
193		frame.sp = thread_saved_sp(tsk);
194		frame.pc = thread_saved_pc(tsk);
195	} else {
196		data.no_sched_functions = 0;
197		frame.fp = (unsigned long)__builtin_frame_address(0);
198		frame.sp = current_stack_pointer;
199		frame.pc = (unsigned long)save_stack_trace_tsk;
200	}
201#ifdef CONFIG_FUNCTION_GRAPH_TRACER
202	frame.graph = tsk->curr_ret_stack;
203#endif
204
205	walk_stackframe(tsk, &frame, save_trace, &data);
206	if (trace->nr_entries < trace->max_entries)
207		trace->entries[trace->nr_entries++] = ULONG_MAX;
208
209	put_task_stack(tsk);
210}
211
212void save_stack_trace(struct stack_trace *trace)
213{
214	save_stack_trace_tsk(current, trace);
215}
216EXPORT_SYMBOL_GPL(save_stack_trace);
217#endif