]>
Commit | Line | Data |
---|---|---|
aa6f4d2b | 1 | #include <linux/sched.h> |
b17b0153 | 2 | #include <linux/sched/debug.h> |
aa6f4d2b RV |
3 | #include <linux/stacktrace.h> |
4 | #include <asm/stacktrace.h> | |
5 | ||
6 | void walk_stackframe(unsigned long sp, | |
7 | int (*fn)(unsigned long addr, void *data), | |
8 | void *data) | |
9 | { | |
10 | unsigned long high = ALIGN(sp, THREAD_SIZE); | |
11 | ||
12 | for (; sp <= high - 4; sp += 4) { | |
13 | unsigned long addr = *(unsigned long *) sp; | |
14 | ||
15 | if (!kernel_text_address(addr)) | |
16 | continue; | |
17 | ||
18 | if (fn(addr, data)) | |
19 | break; | |
20 | } | |
21 | } | |
22 | ||
23 | struct stack_trace_data { | |
24 | struct stack_trace *trace; | |
25 | unsigned int no_sched_functions; | |
26 | unsigned int skip; | |
27 | }; | |
28 | ||
29 | #ifdef CONFIG_STACKTRACE | |
30 | ||
31 | static int save_trace(unsigned long addr, void *d) | |
32 | { | |
33 | struct stack_trace_data *data = d; | |
34 | struct stack_trace *trace = data->trace; | |
35 | ||
36 | if (data->no_sched_functions && in_sched_functions(addr)) | |
37 | return 0; | |
38 | ||
39 | if (data->skip) { | |
40 | data->skip--; | |
41 | return 0; | |
42 | } | |
43 | ||
44 | trace->entries[trace->nr_entries++] = addr; | |
45 | ||
46 | return trace->nr_entries >= trace->max_entries; | |
47 | } | |
48 | ||
49 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |
50 | { | |
51 | struct stack_trace_data data; | |
52 | unsigned long sp; | |
53 | ||
54 | data.trace = trace; | |
55 | data.skip = trace->skip; | |
56 | ||
57 | if (tsk != current) { | |
58 | data.no_sched_functions = 1; | |
59 | sp = tsk->thread.ksp; | |
60 | } else { | |
61 | data.no_sched_functions = 0; | |
62 | sp = rdsp(); | |
63 | } | |
64 | ||
65 | walk_stackframe(sp, save_trace, &data); | |
66 | if (trace->nr_entries < trace->max_entries) | |
67 | trace->entries[trace->nr_entries++] = ULONG_MAX; | |
68 | } | |
69 | ||
70 | void save_stack_trace(struct stack_trace *trace) | |
71 | { | |
72 | save_stack_trace_tsk(current, trace); | |
73 | } | |
74 | EXPORT_SYMBOL_GPL(save_stack_trace); | |
75 | ||
76 | #endif /* CONFIG_STACKTRACE */ |