diff options
-rw-r--r-- | include/linux/stacktrace.h | 27 | ||||
-rw-r--r-- | kernel/stacktrace.c | 170 |
2 files changed, 182 insertions, 15 deletions
diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h index ba29a0613e66..a24340b3e9e1 100644 --- a/include/linux/stacktrace.h +++ b/include/linux/stacktrace.h | |||
@@ -3,11 +3,26 @@ | |||
3 | #define __LINUX_STACKTRACE_H | 3 | #define __LINUX_STACKTRACE_H |
4 | 4 | ||
5 | #include <linux/types.h> | 5 | #include <linux/types.h> |
6 | #include <asm/errno.h> | ||
6 | 7 | ||
7 | struct task_struct; | 8 | struct task_struct; |
8 | struct pt_regs; | 9 | struct pt_regs; |
9 | 10 | ||
10 | #ifdef CONFIG_STACKTRACE | 11 | #ifdef CONFIG_STACKTRACE |
12 | void stack_trace_print(unsigned long *trace, unsigned int nr_entries, | ||
13 | int spaces); | ||
14 | int stack_trace_snprint(char *buf, size_t size, unsigned long *entries, | ||
15 | unsigned int nr_entries, int spaces); | ||
16 | unsigned int stack_trace_save(unsigned long *store, unsigned int size, | ||
17 | unsigned int skipnr); | ||
18 | unsigned int stack_trace_save_tsk(struct task_struct *task, | ||
19 | unsigned long *store, unsigned int size, | ||
20 | unsigned int skipnr); | ||
21 | unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store, | ||
22 | unsigned int size, unsigned int skipnr); | ||
23 | unsigned int stack_trace_save_user(unsigned long *store, unsigned int size); | ||
24 | |||
25 | /* Internal interfaces. Do not use in generic code */ | ||
11 | struct stack_trace { | 26 | struct stack_trace { |
12 | unsigned int nr_entries, max_entries; | 27 | unsigned int nr_entries, max_entries; |
13 | unsigned long *entries; | 28 | unsigned long *entries; |
@@ -41,4 +56,16 @@ extern void save_stack_trace_user(struct stack_trace *trace); | |||
41 | # define save_stack_trace_tsk_reliable(tsk, trace) ({ -ENOSYS; }) | 56 | # define save_stack_trace_tsk_reliable(tsk, trace) ({ -ENOSYS; }) |
42 | #endif /* CONFIG_STACKTRACE */ | 57 | #endif /* CONFIG_STACKTRACE */ |
43 | 58 | ||
59 | #if defined(CONFIG_STACKTRACE) && defined(CONFIG_HAVE_RELIABLE_STACKTRACE) | ||
60 | int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store, | ||
61 | unsigned int size); | ||
62 | #else | ||
63 | static inline int stack_trace_save_tsk_reliable(struct task_struct *tsk, | ||
64 | unsigned long *store, | ||
65 | unsigned int size) | ||
66 | { | ||
67 | return -ENOSYS; | ||
68 | } | ||
69 | #endif | ||
70 | |||
44 | #endif /* __LINUX_STACKTRACE_H */ | 71 | #endif /* __LINUX_STACKTRACE_H */ |
diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c index f8edee9c792d..b38333b3bc18 100644 --- a/kernel/stacktrace.c +++ b/kernel/stacktrace.c | |||
@@ -11,35 +11,54 @@ | |||
11 | #include <linux/kallsyms.h> | 11 | #include <linux/kallsyms.h> |
12 | #include <linux/stacktrace.h> | 12 | #include <linux/stacktrace.h> |
13 | 13 | ||
14 | void print_stack_trace(struct stack_trace *trace, int spaces) | 14 | /** |
15 | * stack_trace_print - Print the entries in the stack trace | ||
16 | * @entries: Pointer to storage array | ||
17 | * @nr_entries: Number of entries in the storage array | ||
18 | * @spaces: Number of leading spaces to print | ||
19 | */ | ||
20 | void stack_trace_print(unsigned long *entries, unsigned int nr_entries, | ||
21 | int spaces) | ||
15 | { | 22 | { |
16 | int i; | 23 | unsigned int i; |
17 | 24 | ||
18 | if (WARN_ON(!trace->entries)) | 25 | if (WARN_ON(!entries)) |
19 | return; | 26 | return; |
20 | 27 | ||
21 | for (i = 0; i < trace->nr_entries; i++) | 28 | for (i = 0; i < nr_entries; i++) |
22 | printk("%*c%pS\n", 1 + spaces, ' ', (void *)trace->entries[i]); | 29 | printk("%*c%pS\n", 1 + spaces, ' ', (void *)entries[i]); |
30 | } | ||
31 | EXPORT_SYMBOL_GPL(stack_trace_print); | ||
32 | |||
33 | void print_stack_trace(struct stack_trace *trace, int spaces) | ||
34 | { | ||
35 | stack_trace_print(trace->entries, trace->nr_entries, spaces); | ||
23 | } | 36 | } |
24 | EXPORT_SYMBOL_GPL(print_stack_trace); | 37 | EXPORT_SYMBOL_GPL(print_stack_trace); |
25 | 38 | ||
26 | int snprint_stack_trace(char *buf, size_t size, | 39 | /** |
27 | struct stack_trace *trace, int spaces) | 40 | * stack_trace_snprint - Print the entries in the stack trace into a buffer |
41 | * @buf: Pointer to the print buffer | ||
42 | * @size: Size of the print buffer | ||
43 | * @entries: Pointer to storage array | ||
44 | * @nr_entries: Number of entries in the storage array | ||
45 | * @spaces: Number of leading spaces to print | ||
46 | * | ||
47 | * Return: Number of bytes printed. | ||
48 | */ | ||
49 | int stack_trace_snprint(char *buf, size_t size, unsigned long *entries, | ||
50 | unsigned int nr_entries, int spaces) | ||
28 | { | 51 | { |
29 | int i; | 52 | unsigned int generated, i, total = 0; |
30 | int generated; | ||
31 | int total = 0; | ||
32 | 53 | ||
33 | if (WARN_ON(!trace->entries)) | 54 | if (WARN_ON(!entries)) |
34 | return 0; | 55 | return 0; |
35 | 56 | ||
36 | for (i = 0; i < trace->nr_entries; i++) { | 57 | for (i = 0; i < nr_entries && size; i++) { |
37 | generated = snprintf(buf, size, "%*c%pS\n", 1 + spaces, ' ', | 58 | generated = snprintf(buf, size, "%*c%pS\n", 1 + spaces, ' ', |
38 | (void *)trace->entries[i]); | 59 | (void *)entries[i]); |
39 | 60 | ||
40 | total += generated; | 61 | total += generated; |
41 | |||
42 | /* Assume that generated isn't a negative number */ | ||
43 | if (generated >= size) { | 62 | if (generated >= size) { |
44 | buf += size; | 63 | buf += size; |
45 | size = 0; | 64 | size = 0; |
@@ -51,6 +70,14 @@ int snprint_stack_trace(char *buf, size_t size, | |||
51 | 70 | ||
52 | return total; | 71 | return total; |
53 | } | 72 | } |
73 | EXPORT_SYMBOL_GPL(stack_trace_snprint); | ||
74 | |||
75 | int snprint_stack_trace(char *buf, size_t size, | ||
76 | struct stack_trace *trace, int spaces) | ||
77 | { | ||
78 | return stack_trace_snprint(buf, size, trace->entries, | ||
79 | trace->nr_entries, spaces); | ||
80 | } | ||
54 | EXPORT_SYMBOL_GPL(snprint_stack_trace); | 81 | EXPORT_SYMBOL_GPL(snprint_stack_trace); |
55 | 82 | ||
56 | /* | 83 | /* |
@@ -77,3 +104,116 @@ save_stack_trace_tsk_reliable(struct task_struct *tsk, | |||
77 | WARN_ONCE(1, KERN_INFO "save_stack_tsk_reliable() not implemented yet.\n"); | 104 | WARN_ONCE(1, KERN_INFO "save_stack_tsk_reliable() not implemented yet.\n"); |
78 | return -ENOSYS; | 105 | return -ENOSYS; |
79 | } | 106 | } |
107 | |||
108 | /** | ||
109 | * stack_trace_save - Save a stack trace into a storage array | ||
110 | * @store: Pointer to storage array | ||
111 | * @size: Size of the storage array | ||
112 | * @skipnr: Number of entries to skip at the start of the stack trace | ||
113 | * | ||
114 | * Return: Number of trace entries stored | ||
115 | */ | ||
116 | unsigned int stack_trace_save(unsigned long *store, unsigned int size, | ||
117 | unsigned int skipnr) | ||
118 | { | ||
119 | struct stack_trace trace = { | ||
120 | .entries = store, | ||
121 | .max_entries = size, | ||
122 | .skip = skipnr + 1, | ||
123 | }; | ||
124 | |||
125 | save_stack_trace(&trace); | ||
126 | return trace.nr_entries; | ||
127 | } | ||
128 | EXPORT_SYMBOL_GPL(stack_trace_save); | ||
129 | |||
130 | /** | ||
131 | * stack_trace_save_tsk - Save a task stack trace into a storage array | ||
132 | * @task: The task to examine | ||
133 | * @store: Pointer to storage array | ||
134 | * @size: Size of the storage array | ||
135 | * @skipnr: Number of entries to skip at the start of the stack trace | ||
136 | * | ||
137 | * Return: Number of trace entries stored | ||
138 | */ | ||
139 | unsigned int stack_trace_save_tsk(struct task_struct *task, | ||
140 | unsigned long *store, unsigned int size, | ||
141 | unsigned int skipnr) | ||
142 | { | ||
143 | struct stack_trace trace = { | ||
144 | .entries = store, | ||
145 | .max_entries = size, | ||
146 | .skip = skipnr + 1, | ||
147 | }; | ||
148 | |||
149 | save_stack_trace_tsk(task, &trace); | ||
150 | return trace.nr_entries; | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array | ||
155 | * @regs: Pointer to pt_regs to examine | ||
156 | * @store: Pointer to storage array | ||
157 | * @size: Size of the storage array | ||
158 | * @skipnr: Number of entries to skip at the start of the stack trace | ||
159 | * | ||
160 | * Return: Number of trace entries stored | ||
161 | */ | ||
162 | unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store, | ||
163 | unsigned int size, unsigned int skipnr) | ||
164 | { | ||
165 | struct stack_trace trace = { | ||
166 | .entries = store, | ||
167 | .max_entries = size, | ||
168 | .skip = skipnr, | ||
169 | }; | ||
170 | |||
171 | save_stack_trace_regs(regs, &trace); | ||
172 | return trace.nr_entries; | ||
173 | } | ||
174 | |||
175 | #ifdef CONFIG_HAVE_RELIABLE_STACKTRACE | ||
176 | /** | ||
177 | * stack_trace_save_tsk_reliable - Save task stack with verification | ||
178 | * @tsk: Pointer to the task to examine | ||
179 | * @store: Pointer to storage array | ||
180 | * @size: Size of the storage array | ||
181 | * | ||
182 | * Return: An error if it detects any unreliable features of the | ||
183 | * stack. Otherwise it guarantees that the stack trace is | ||
184 | * reliable and returns the number of entries stored. | ||
185 | * | ||
186 | * If the task is not 'current', the caller *must* ensure the task is inactive. | ||
187 | */ | ||
188 | int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store, | ||
189 | unsigned int size) | ||
190 | { | ||
191 | struct stack_trace trace = { | ||
192 | .entries = store, | ||
193 | .max_entries = size, | ||
194 | }; | ||
195 | int ret = save_stack_trace_tsk_reliable(tsk, &trace); | ||
196 | |||
197 | return ret ? ret : trace.nr_entries; | ||
198 | } | ||
199 | #endif | ||
200 | |||
201 | #ifdef CONFIG_USER_STACKTRACE_SUPPORT | ||
202 | /** | ||
203 | * stack_trace_save_user - Save a user space stack trace into a storage array | ||
204 | * @store: Pointer to storage array | ||
205 | * @size: Size of the storage array | ||
206 | * | ||
207 | * Return: Number of trace entries stored | ||
208 | */ | ||
209 | unsigned int stack_trace_save_user(unsigned long *store, unsigned int size) | ||
210 | { | ||
211 | struct stack_trace trace = { | ||
212 | .entries = store, | ||
213 | .max_entries = size, | ||
214 | }; | ||
215 | |||
216 | save_stack_trace_user(&trace); | ||
217 | return trace.nr_entries; | ||
218 | } | ||
219 | #endif /* CONFIG_USER_STACKTRACE_SUPPORT */ | ||