Branch data Line data Source code
1 : : #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)
8 : : /*
9 : : * Unwind the current stack frame and store the new register values in the
10 : : * structure passed as argument. Unwinding is equivalent to a function return,
11 : : * hence the new PC value rather than LR should be used for backtrace.
12 : : *
13 : : * With framepointer enabled, a simple function prologue looks like this:
14 : : * mov ip, sp
15 : : * stmdb sp!, {fp, ip, lr, pc}
16 : : * sub fp, ip, #4
17 : : *
18 : : * A simple function epilogue looks like this:
19 : : * ldm sp, {fp, sp, pc}
20 : : *
21 : : * Note that with framepointer enabled, even the leaf functions have the same
22 : : * prologue and epilogue, therefore we can ignore the LR value in this case.
23 : : */
24 : : int notrace unwind_frame(struct stackframe *frame)
25 : : {
26 : : unsigned long high, low;
27 : : unsigned long fp = frame->fp;
28 : :
29 : : /* only go to a higher address on the stack */
30 : : low = frame->sp;
31 : : high = ALIGN(low, THREAD_SIZE);
32 : :
33 : : /* check current frame pointer is within bounds */
34 : : if (fp < low + 12 || fp > high - 4)
35 : : return -EINVAL;
36 : :
37 : : /* restore the registers from the stack frame */
38 : : frame->fp = *(unsigned long *)(fp - 12);
39 : : frame->sp = *(unsigned long *)(fp - 8);
40 : : frame->pc = *(unsigned long *)(fp - 4);
41 : :
42 : : return 0;
43 : : }
44 : : #endif
45 : :
46 : 2 : void notrace walk_stackframe(struct stackframe *frame,
47 : : int (*fn)(struct stackframe *, void *), void *data)
48 : : {
49 : : while (1) {
50 : : int ret;
51 : :
52 [ + - ]: 14 : if (fn(frame, data))
53 : : break;
54 : 14 : ret = unwind_frame(frame);
55 [ + + ]: 14 : if (ret < 0)
56 : : break;
57 : : }
58 : 0 : }
59 : : EXPORT_SYMBOL(walk_stackframe);
60 : :
61 : : #ifdef CONFIG_STACKTRACE
62 : : struct stack_trace_data {
63 : : struct stack_trace *trace;
64 : : unsigned int no_sched_functions;
65 : : unsigned int skip;
66 : : };
67 : :
68 : 0 : static int save_trace(struct stackframe *frame, void *d)
69 : : {
70 : : struct stack_trace_data *data = d;
71 : 14 : struct stack_trace *trace = data->trace;
72 : 14 : unsigned long addr = frame->pc;
73 : :
74 [ - + ][ # # ]: 14 : if (data->no_sched_functions && in_sched_functions(addr))
75 : : return 0;
76 [ - + ]: 28 : if (data->skip) {
77 : 0 : data->skip--;
78 : 0 : return 0;
79 : : }
80 : :
81 : 14 : trace->entries[trace->nr_entries++] = addr;
82 : :
83 : 14 : return trace->nr_entries >= trace->max_entries;
84 : : }
85 : :
86 : 0 : void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
87 : : {
88 : : struct stack_trace_data data;
89 : : struct stackframe frame;
90 : :
91 : 2 : data.trace = trace;
92 : 2 : data.skip = trace->skip;
93 : :
94 [ - + ]: 2 : if (tsk != current) {
95 : : #ifdef CONFIG_SMP
96 : : /*
97 : : * What guarantees do we have here that 'tsk' is not
98 : : * running on another CPU? For now, ignore it as we
99 : : * can't guarantee we won't explode.
100 : : */
101 [ # # ]: 0 : if (trace->nr_entries < trace->max_entries)
102 : 0 : trace->entries[trace->nr_entries++] = ULONG_MAX;
103 : 0 : return;
104 : : #else
105 : : data.no_sched_functions = 1;
106 : : frame.fp = thread_saved_fp(tsk);
107 : : frame.sp = thread_saved_sp(tsk);
108 : : frame.lr = 0; /* recovered from the stack */
109 : : frame.pc = thread_saved_pc(tsk);
110 : : #endif
111 : : } else {
112 : 2 : data.no_sched_functions = 0;
113 : 2 : frame.fp = (unsigned long)__builtin_frame_address(0);
114 : 2 : frame.sp = __builtin_stack_pointer();
115 : 2 : frame.lr = (unsigned long)__builtin_return_address(0);
116 : 2 : frame.pc = (unsigned long)save_stack_trace_tsk;
117 : : }
118 : :
119 : 2 : walk_stackframe(&frame, save_trace, &data);
120 [ + - ]: 4 : if (trace->nr_entries < trace->max_entries)
121 : 2 : trace->entries[trace->nr_entries++] = ULONG_MAX;
122 : : }
123 : :
124 : 0 : void save_stack_trace(struct stack_trace *trace)
125 : : {
126 : 0 : save_stack_trace_tsk(current, trace);
127 : 0 : }
128 : : EXPORT_SYMBOL_GPL(save_stack_trace);
129 : : #endif
|