Commit | Line | Data |
---|---|---|
7b104bcb | 1 | #include <linux/module.h> |
f16fb1ec RK |
2 | #include <linux/sched.h> |
3 | #include <linux/stacktrace.h> | |
4 | ||
5 | #include "stacktrace.h" | |
6 | ||
7 | int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, | |
8 | int (*fn)(struct stackframe *, void *), void *data) | |
9 | { | |
10 | struct stackframe *frame; | |
11 | ||
12 | do { | |
13 | /* | |
14 | * Check current frame pointer is within bounds | |
15 | */ | |
5b10c8e4 | 16 | if (fp < (low + 12) || fp + 4 >= high) |
f16fb1ec RK |
17 | break; |
18 | ||
19 | frame = (struct stackframe *)(fp - 12); | |
20 | ||
21 | if (fn(frame, data)) | |
22 | break; | |
23 | ||
24 | /* | |
25 | * Update the low bound - the next frame must always | |
26 | * be at a higher address than the current frame. | |
27 | */ | |
28 | low = fp + 4; | |
29 | fp = frame->fp; | |
30 | } while (fp); | |
31 | ||
32 | return 0; | |
33 | } | |
7b104bcb | 34 | EXPORT_SYMBOL(walk_stackframe); |
f16fb1ec RK |
35 | |
36 | #ifdef CONFIG_STACKTRACE | |
37 | struct stack_trace_data { | |
38 | struct stack_trace *trace; | |
f76e9154 | 39 | unsigned int no_sched_functions; |
f16fb1ec RK |
40 | unsigned int skip; |
41 | }; | |
42 | ||
43 | static int save_trace(struct stackframe *frame, void *d) | |
44 | { | |
45 | struct stack_trace_data *data = d; | |
46 | struct stack_trace *trace = data->trace; | |
f76e9154 | 47 | unsigned long addr = frame->lr; |
f16fb1ec | 48 | |
f76e9154 NP |
49 | if (data->no_sched_functions && in_sched_functions(addr)) |
50 | return 0; | |
f16fb1ec RK |
51 | if (data->skip) { |
52 | data->skip--; | |
53 | return 0; | |
54 | } | |
55 | ||
f76e9154 | 56 | trace->entries[trace->nr_entries++] = addr; |
f16fb1ec RK |
57 | |
58 | return trace->nr_entries >= trace->max_entries; | |
59 | } | |
60 | ||
f76e9154 | 61 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) |
f16fb1ec RK |
62 | { |
63 | struct stack_trace_data data; | |
64 | unsigned long fp, base; | |
65 | ||
66 | data.trace = trace; | |
67 | data.skip = trace->skip; | |
f76e9154 NP |
68 | base = (unsigned long)task_stack_page(tsk); |
69 | ||
70 | if (tsk != current) { | |
71 | #ifdef CONFIG_SMP | |
72 | /* | |
73 | * What guarantees do we have here that 'tsk' | |
74 | * is not running on another CPU? | |
75 | */ | |
76 | BUG(); | |
77 | #else | |
78 | data.no_sched_functions = 1; | |
79 | fp = thread_saved_fp(tsk); | |
80 | #endif | |
81 | } else { | |
82 | data.no_sched_functions = 0; | |
83 | asm("mov %0, fp" : "=r" (fp)); | |
84 | } | |
f16fb1ec RK |
85 | |
86 | walk_stackframe(fp, base, base + THREAD_SIZE, save_trace, &data); | |
f76e9154 NP |
87 | if (trace->nr_entries < trace->max_entries) |
88 | trace->entries[trace->nr_entries++] = ULONG_MAX; | |
89 | } | |
90 | ||
91 | void save_stack_trace(struct stack_trace *trace) | |
92 | { | |
93 | save_stack_trace_tsk(current, trace); | |
f16fb1ec | 94 | } |
7b4c9505 | 95 | EXPORT_SYMBOL_GPL(save_stack_trace); |
f16fb1ec | 96 | #endif |