blob: 05d5a32a9ece567cd0cd1bf4773d1ccc77d36e22 [file] [log] [blame]
Nicholas Flintham1e3d3112013-04-10 10:48:38 +01001#include <linux/export.h>
2#include <linux/sched.h>
3#include <linux/stacktrace.h>
4
5#include <asm/stacktrace.h>
6
7#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
8int notrace unwind_frame(struct stackframe *frame)
9{
10 unsigned long high, low;
11 unsigned long fp = frame->fp;
12
13
14 low = frame->sp;
15 high = ALIGN(low, THREAD_SIZE);
16
17
18 if (fp < (low + 12) || fp + 4 >= high)
19 return -EINVAL;
20
21
22 frame->fp = *(unsigned long *)(fp - 12);
23 frame->sp = *(unsigned long *)(fp - 8);
24 frame->pc = *(unsigned long *)(fp - 4);
25
26 return 0;
27}
28#endif
29
30void notrace walk_stackframe(struct stackframe *frame,
31 int (*fn)(struct stackframe *, void *), void *data)
32{
33 while (1) {
34 int ret;
35
36 if (fn(frame, data))
37 break;
38 ret = unwind_frame(frame);
39 if (ret < 0)
40 break;
41 }
42}
43EXPORT_SYMBOL(walk_stackframe);
44
45#ifdef CONFIG_STACKTRACE
46struct stack_trace_data {
47 struct stack_trace *trace;
48 unsigned int no_sched_functions;
49 unsigned int skip;
50};
51
52static int save_trace(struct stackframe *frame, void *d)
53{
54 struct stack_trace_data *data = d;
55 struct stack_trace *trace = data->trace;
56 unsigned long addr = frame->pc;
57
58 if (data->no_sched_functions && in_sched_functions(addr))
59 return 0;
60 if (data->skip) {
61 data->skip--;
62 return 0;
63 }
64
65 trace->entries[trace->nr_entries++] = addr;
66
67 return trace->nr_entries >= trace->max_entries;
68}
69
70void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
71{
72 struct stack_trace_data data;
73 struct stackframe frame;
74
75 data.trace = trace;
76 data.skip = trace->skip;
77
78 if (tsk != current) {
79#ifdef CONFIG_SMP
80 if (trace->nr_entries < trace->max_entries)
81 trace->entries[trace->nr_entries++] = ULONG_MAX;
82 return;
83#else
84 data.no_sched_functions = 1;
85 frame.fp = thread_saved_fp(tsk);
86 frame.sp = thread_saved_sp(tsk);
87 frame.lr = 0;
88 frame.pc = thread_saved_pc(tsk);
89#endif
90 } else {
91 register unsigned long current_sp asm ("sp");
92
93 data.no_sched_functions = 0;
94 frame.fp = (unsigned long)__builtin_frame_address(0);
95 frame.sp = current_sp;
96 frame.lr = (unsigned long)__builtin_return_address(0);
97 frame.pc = (unsigned long)save_stack_trace_tsk;
98 }
99
100 walk_stackframe(&frame, save_trace, &data);
101 if (trace->nr_entries < trace->max_entries)
102 trace->entries[trace->nr_entries++] = ULONG_MAX;
103}
104
105void save_stack_trace(struct stack_trace *trace)
106{
107 save_stack_trace_tsk(current, trace);
108}
109EXPORT_SYMBOL_GPL(save_stack_trace);
110#endif