.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * kernel/stacktrace.c |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * |
---|
6 | 7 | * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com> |
---|
7 | 8 | */ |
---|
| 9 | +#include <linux/sched/task_stack.h> |
---|
| 10 | +#include <linux/sched/debug.h> |
---|
8 | 11 | #include <linux/sched.h> |
---|
9 | 12 | #include <linux/kernel.h> |
---|
10 | 13 | #include <linux/export.h> |
---|
11 | 14 | #include <linux/kallsyms.h> |
---|
12 | 15 | #include <linux/stacktrace.h> |
---|
13 | 16 | |
---|
14 | | -void print_stack_trace(struct stack_trace *trace, int spaces) |
---|
| 17 | +/** |
---|
| 18 | + * stack_trace_print - Print the entries in the stack trace |
---|
| 19 | + * @entries: Pointer to storage array |
---|
| 20 | + * @nr_entries: Number of entries in the storage array |
---|
| 21 | + * @spaces: Number of leading spaces to print |
---|
| 22 | + */ |
---|
| 23 | +void stack_trace_print(const unsigned long *entries, unsigned int nr_entries, |
---|
| 24 | + int spaces) |
---|
15 | 25 | { |
---|
16 | | - int i; |
---|
| 26 | + unsigned int i; |
---|
17 | 27 | |
---|
18 | | - if (WARN_ON(!trace->entries)) |
---|
| 28 | + if (WARN_ON(!entries)) |
---|
19 | 29 | return; |
---|
20 | 30 | |
---|
21 | | - for (i = 0; i < trace->nr_entries; i++) |
---|
22 | | - printk("%*c%pS\n", 1 + spaces, ' ', (void *)trace->entries[i]); |
---|
| 31 | + for (i = 0; i < nr_entries; i++) |
---|
| 32 | + printk("%*c%pS\n", 1 + spaces, ' ', (void *)entries[i]); |
---|
23 | 33 | } |
---|
24 | | -EXPORT_SYMBOL_GPL(print_stack_trace); |
---|
| 34 | +EXPORT_SYMBOL_GPL(stack_trace_print); |
---|
25 | 35 | |
---|
26 | | -int snprint_stack_trace(char *buf, size_t size, |
---|
27 | | - struct stack_trace *trace, int spaces) |
---|
| 36 | +/** |
---|
| 37 | + * stack_trace_snprint - Print the entries in the stack trace into a buffer |
---|
| 38 | + * @buf: Pointer to the print buffer |
---|
| 39 | + * @size: Size of the print buffer |
---|
| 40 | + * @entries: Pointer to storage array |
---|
| 41 | + * @nr_entries: Number of entries in the storage array |
---|
| 42 | + * @spaces: Number of leading spaces to print |
---|
| 43 | + * |
---|
| 44 | + * Return: Number of bytes printed. |
---|
| 45 | + */ |
---|
| 46 | +int stack_trace_snprint(char *buf, size_t size, const unsigned long *entries, |
---|
| 47 | + unsigned int nr_entries, int spaces) |
---|
28 | 48 | { |
---|
29 | | - int i; |
---|
30 | | - int generated; |
---|
31 | | - int total = 0; |
---|
| 49 | + unsigned int generated, i, total = 0; |
---|
32 | 50 | |
---|
33 | | - if (WARN_ON(!trace->entries)) |
---|
| 51 | + if (WARN_ON(!entries)) |
---|
34 | 52 | return 0; |
---|
35 | 53 | |
---|
36 | | - for (i = 0; i < trace->nr_entries; i++) { |
---|
| 54 | + for (i = 0; i < nr_entries && size; i++) { |
---|
37 | 55 | generated = snprintf(buf, size, "%*c%pS\n", 1 + spaces, ' ', |
---|
38 | | - (void *)trace->entries[i]); |
---|
| 56 | + (void *)entries[i]); |
---|
39 | 57 | |
---|
40 | 58 | total += generated; |
---|
41 | | - |
---|
42 | | - /* Assume that generated isn't a negative number */ |
---|
43 | 59 | if (generated >= size) { |
---|
44 | 60 | buf += size; |
---|
45 | 61 | size = 0; |
---|
.. | .. |
---|
51 | 67 | |
---|
52 | 68 | return total; |
---|
53 | 69 | } |
---|
54 | | -EXPORT_SYMBOL_GPL(snprint_stack_trace); |
---|
| 70 | +EXPORT_SYMBOL_GPL(stack_trace_snprint); |
---|
| 71 | + |
---|
| 72 | +#ifdef CONFIG_ARCH_STACKWALK |
---|
| 73 | + |
---|
| 74 | +struct stacktrace_cookie { |
---|
| 75 | + unsigned long *store; |
---|
| 76 | + unsigned int size; |
---|
| 77 | + unsigned int skip; |
---|
| 78 | + unsigned int len; |
---|
| 79 | +}; |
---|
| 80 | + |
---|
| 81 | +static bool stack_trace_consume_entry(void *cookie, unsigned long addr) |
---|
| 82 | +{ |
---|
| 83 | + struct stacktrace_cookie *c = cookie; |
---|
| 84 | + |
---|
| 85 | + if (c->len >= c->size) |
---|
| 86 | + return false; |
---|
| 87 | + |
---|
| 88 | + if (c->skip > 0) { |
---|
| 89 | + c->skip--; |
---|
| 90 | + return true; |
---|
| 91 | + } |
---|
| 92 | + c->store[c->len++] = addr; |
---|
| 93 | + return c->len < c->size; |
---|
| 94 | +} |
---|
| 95 | + |
---|
| 96 | +static bool stack_trace_consume_entry_nosched(void *cookie, unsigned long addr) |
---|
| 97 | +{ |
---|
| 98 | + if (in_sched_functions(addr)) |
---|
| 99 | + return true; |
---|
| 100 | + return stack_trace_consume_entry(cookie, addr); |
---|
| 101 | +} |
---|
| 102 | + |
---|
| 103 | +/** |
---|
| 104 | + * stack_trace_save - Save a stack trace into a storage array |
---|
| 105 | + * @store: Pointer to storage array |
---|
| 106 | + * @size: Size of the storage array |
---|
| 107 | + * @skipnr: Number of entries to skip at the start of the stack trace |
---|
| 108 | + * |
---|
| 109 | + * Return: Number of trace entries stored. |
---|
| 110 | + */ |
---|
| 111 | +unsigned int stack_trace_save(unsigned long *store, unsigned int size, |
---|
| 112 | + unsigned int skipnr) |
---|
| 113 | +{ |
---|
| 114 | + stack_trace_consume_fn consume_entry = stack_trace_consume_entry; |
---|
| 115 | + struct stacktrace_cookie c = { |
---|
| 116 | + .store = store, |
---|
| 117 | + .size = size, |
---|
| 118 | + .skip = skipnr + 1, |
---|
| 119 | + }; |
---|
| 120 | + |
---|
| 121 | + arch_stack_walk(consume_entry, &c, current, NULL); |
---|
| 122 | + return c.len; |
---|
| 123 | +} |
---|
| 124 | +EXPORT_SYMBOL_GPL(stack_trace_save); |
---|
| 125 | + |
---|
| 126 | +/** |
---|
| 127 | + * stack_trace_save_tsk - Save a task stack trace into a storage array |
---|
| 128 | + * @task: The task to examine |
---|
| 129 | + * @store: Pointer to storage array |
---|
| 130 | + * @size: Size of the storage array |
---|
| 131 | + * @skipnr: Number of entries to skip at the start of the stack trace |
---|
| 132 | + * |
---|
| 133 | + * Return: Number of trace entries stored. |
---|
| 134 | + */ |
---|
| 135 | +unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store, |
---|
| 136 | + unsigned int size, unsigned int skipnr) |
---|
| 137 | +{ |
---|
| 138 | + stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched; |
---|
| 139 | + struct stacktrace_cookie c = { |
---|
| 140 | + .store = store, |
---|
| 141 | + .size = size, |
---|
| 142 | + /* skip this function if they are tracing us */ |
---|
| 143 | + .skip = skipnr + (current == tsk), |
---|
| 144 | + }; |
---|
| 145 | + |
---|
| 146 | + if (!try_get_task_stack(tsk)) |
---|
| 147 | + return 0; |
---|
| 148 | + |
---|
| 149 | + arch_stack_walk(consume_entry, &c, tsk, NULL); |
---|
| 150 | + put_task_stack(tsk); |
---|
| 151 | + return c.len; |
---|
| 152 | +} |
---|
| 153 | +EXPORT_SYMBOL_GPL(stack_trace_save_tsk); |
---|
| 154 | + |
---|
| 155 | +/** |
---|
| 156 | + * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array |
---|
| 157 | + * @regs: Pointer to pt_regs to examine |
---|
| 158 | + * @store: Pointer to storage array |
---|
| 159 | + * @size: Size of the storage array |
---|
| 160 | + * @skipnr: Number of entries to skip at the start of the stack trace |
---|
| 161 | + * |
---|
| 162 | + * Return: Number of trace entries stored. |
---|
| 163 | + */ |
---|
| 164 | +unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store, |
---|
| 165 | + unsigned int size, unsigned int skipnr) |
---|
| 166 | +{ |
---|
| 167 | + stack_trace_consume_fn consume_entry = stack_trace_consume_entry; |
---|
| 168 | + struct stacktrace_cookie c = { |
---|
| 169 | + .store = store, |
---|
| 170 | + .size = size, |
---|
| 171 | + .skip = skipnr, |
---|
| 172 | + }; |
---|
| 173 | + |
---|
| 174 | + arch_stack_walk(consume_entry, &c, current, regs); |
---|
| 175 | + return c.len; |
---|
| 176 | +} |
---|
| 177 | +EXPORT_SYMBOL_GPL(stack_trace_save_regs); |
---|
| 178 | + |
---|
| 179 | +#ifdef CONFIG_HAVE_RELIABLE_STACKTRACE |
---|
| 180 | +/** |
---|
| 181 | + * stack_trace_save_tsk_reliable - Save task stack with verification |
---|
| 182 | + * @tsk: Pointer to the task to examine |
---|
| 183 | + * @store: Pointer to storage array |
---|
| 184 | + * @size: Size of the storage array |
---|
| 185 | + * |
---|
| 186 | + * Return: An error if it detects any unreliable features of the |
---|
| 187 | + * stack. Otherwise it guarantees that the stack trace is |
---|
| 188 | + * reliable and returns the number of entries stored. |
---|
| 189 | + * |
---|
| 190 | + * If the task is not 'current', the caller *must* ensure the task is inactive. |
---|
| 191 | + */ |
---|
| 192 | +int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store, |
---|
| 193 | + unsigned int size) |
---|
| 194 | +{ |
---|
| 195 | + stack_trace_consume_fn consume_entry = stack_trace_consume_entry; |
---|
| 196 | + struct stacktrace_cookie c = { |
---|
| 197 | + .store = store, |
---|
| 198 | + .size = size, |
---|
| 199 | + }; |
---|
| 200 | + int ret; |
---|
| 201 | + |
---|
| 202 | + /* |
---|
| 203 | + * If the task doesn't have a stack (e.g., a zombie), the stack is |
---|
| 204 | + * "reliably" empty. |
---|
| 205 | + */ |
---|
| 206 | + if (!try_get_task_stack(tsk)) |
---|
| 207 | + return 0; |
---|
| 208 | + |
---|
| 209 | + ret = arch_stack_walk_reliable(consume_entry, &c, tsk); |
---|
| 210 | + put_task_stack(tsk); |
---|
| 211 | + return ret ? ret : c.len; |
---|
| 212 | +} |
---|
| 213 | +#endif |
---|
| 214 | + |
---|
| 215 | +#ifdef CONFIG_USER_STACKTRACE_SUPPORT |
---|
| 216 | +/** |
---|
| 217 | + * stack_trace_save_user - Save a user space stack trace into a storage array |
---|
| 218 | + * @store: Pointer to storage array |
---|
| 219 | + * @size: Size of the storage array |
---|
| 220 | + * |
---|
| 221 | + * Return: Number of trace entries stored. |
---|
| 222 | + */ |
---|
| 223 | +unsigned int stack_trace_save_user(unsigned long *store, unsigned int size) |
---|
| 224 | +{ |
---|
| 225 | + stack_trace_consume_fn consume_entry = stack_trace_consume_entry; |
---|
| 226 | + struct stacktrace_cookie c = { |
---|
| 227 | + .store = store, |
---|
| 228 | + .size = size, |
---|
| 229 | + }; |
---|
| 230 | + mm_segment_t fs; |
---|
| 231 | + |
---|
| 232 | + /* Trace user stack if not a kernel thread */ |
---|
| 233 | + if (current->flags & PF_KTHREAD) |
---|
| 234 | + return 0; |
---|
| 235 | + |
---|
| 236 | + fs = force_uaccess_begin(); |
---|
| 237 | + arch_stack_walk_user(consume_entry, &c, task_pt_regs(current)); |
---|
| 238 | + force_uaccess_end(fs); |
---|
| 239 | + |
---|
| 240 | + return c.len; |
---|
| 241 | +} |
---|
| 242 | +#endif |
---|
| 243 | + |
---|
| 244 | +#else /* CONFIG_ARCH_STACKWALK */ |
---|
55 | 245 | |
---|
56 | 246 | /* |
---|
57 | 247 | * Architectures that do not implement save_stack_trace_*() |
---|
.. | .. |
---|
70 | 260 | WARN_ONCE(1, KERN_INFO "save_stack_trace_regs() not implemented yet.\n"); |
---|
71 | 261 | } |
---|
72 | 262 | |
---|
73 | | -__weak int |
---|
74 | | -save_stack_trace_tsk_reliable(struct task_struct *tsk, |
---|
75 | | - struct stack_trace *trace) |
---|
| 263 | +/** |
---|
| 264 | + * stack_trace_save - Save a stack trace into a storage array |
---|
| 265 | + * @store: Pointer to storage array |
---|
| 266 | + * @size: Size of the storage array |
---|
| 267 | + * @skipnr: Number of entries to skip at the start of the stack trace |
---|
| 268 | + * |
---|
| 269 | + * Return: Number of trace entries stored |
---|
| 270 | + */ |
---|
| 271 | +unsigned int stack_trace_save(unsigned long *store, unsigned int size, |
---|
| 272 | + unsigned int skipnr) |
---|
76 | 273 | { |
---|
77 | | - WARN_ONCE(1, KERN_INFO "save_stack_tsk_reliable() not implemented yet.\n"); |
---|
78 | | - return -ENOSYS; |
---|
| 274 | + struct stack_trace trace = { |
---|
| 275 | + .entries = store, |
---|
| 276 | + .max_entries = size, |
---|
| 277 | + .skip = skipnr + 1, |
---|
| 278 | + }; |
---|
| 279 | + |
---|
| 280 | + save_stack_trace(&trace); |
---|
| 281 | + return trace.nr_entries; |
---|
79 | 282 | } |
---|
| 283 | +EXPORT_SYMBOL_GPL(stack_trace_save); |
---|
| 284 | + |
---|
| 285 | +/** |
---|
| 286 | + * stack_trace_save_tsk - Save a task stack trace into a storage array |
---|
| 287 | + * @task: The task to examine |
---|
| 288 | + * @store: Pointer to storage array |
---|
| 289 | + * @size: Size of the storage array |
---|
| 290 | + * @skipnr: Number of entries to skip at the start of the stack trace |
---|
| 291 | + * |
---|
| 292 | + * Return: Number of trace entries stored |
---|
| 293 | + */ |
---|
| 294 | +unsigned int stack_trace_save_tsk(struct task_struct *task, |
---|
| 295 | + unsigned long *store, unsigned int size, |
---|
| 296 | + unsigned int skipnr) |
---|
| 297 | +{ |
---|
| 298 | + struct stack_trace trace = { |
---|
| 299 | + .entries = store, |
---|
| 300 | + .max_entries = size, |
---|
| 301 | + /* skip this function if they are tracing us */ |
---|
| 302 | + .skip = skipnr + (current == task), |
---|
| 303 | + }; |
---|
| 304 | + |
---|
| 305 | + save_stack_trace_tsk(task, &trace); |
---|
| 306 | + return trace.nr_entries; |
---|
| 307 | +} |
---|
| 308 | + |
---|
| 309 | +/** |
---|
| 310 | + * stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array |
---|
| 311 | + * @regs: Pointer to pt_regs to examine |
---|
| 312 | + * @store: Pointer to storage array |
---|
| 313 | + * @size: Size of the storage array |
---|
| 314 | + * @skipnr: Number of entries to skip at the start of the stack trace |
---|
| 315 | + * |
---|
| 316 | + * Return: Number of trace entries stored |
---|
| 317 | + */ |
---|
| 318 | +unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store, |
---|
| 319 | + unsigned int size, unsigned int skipnr) |
---|
| 320 | +{ |
---|
| 321 | + struct stack_trace trace = { |
---|
| 322 | + .entries = store, |
---|
| 323 | + .max_entries = size, |
---|
| 324 | + .skip = skipnr, |
---|
| 325 | + }; |
---|
| 326 | + |
---|
| 327 | + save_stack_trace_regs(regs, &trace); |
---|
| 328 | + return trace.nr_entries; |
---|
| 329 | +} |
---|
| 330 | + |
---|
| 331 | +#ifdef CONFIG_HAVE_RELIABLE_STACKTRACE |
---|
| 332 | +/** |
---|
| 333 | + * stack_trace_save_tsk_reliable - Save task stack with verification |
---|
| 334 | + * @tsk: Pointer to the task to examine |
---|
| 335 | + * @store: Pointer to storage array |
---|
| 336 | + * @size: Size of the storage array |
---|
| 337 | + * |
---|
| 338 | + * Return: An error if it detects any unreliable features of the |
---|
| 339 | + * stack. Otherwise it guarantees that the stack trace is |
---|
| 340 | + * reliable and returns the number of entries stored. |
---|
| 341 | + * |
---|
| 342 | + * If the task is not 'current', the caller *must* ensure the task is inactive. |
---|
| 343 | + */ |
---|
| 344 | +int stack_trace_save_tsk_reliable(struct task_struct *tsk, unsigned long *store, |
---|
| 345 | + unsigned int size) |
---|
| 346 | +{ |
---|
| 347 | + struct stack_trace trace = { |
---|
| 348 | + .entries = store, |
---|
| 349 | + .max_entries = size, |
---|
| 350 | + }; |
---|
| 351 | + int ret = save_stack_trace_tsk_reliable(tsk, &trace); |
---|
| 352 | + |
---|
| 353 | + return ret ? ret : trace.nr_entries; |
---|
| 354 | +} |
---|
| 355 | +#endif |
---|
| 356 | + |
---|
| 357 | +#ifdef CONFIG_USER_STACKTRACE_SUPPORT |
---|
| 358 | +/** |
---|
| 359 | + * stack_trace_save_user - Save a user space stack trace into a storage array |
---|
| 360 | + * @store: Pointer to storage array |
---|
| 361 | + * @size: Size of the storage array |
---|
| 362 | + * |
---|
| 363 | + * Return: Number of trace entries stored |
---|
| 364 | + */ |
---|
| 365 | +unsigned int stack_trace_save_user(unsigned long *store, unsigned int size) |
---|
| 366 | +{ |
---|
| 367 | + struct stack_trace trace = { |
---|
| 368 | + .entries = store, |
---|
| 369 | + .max_entries = size, |
---|
| 370 | + }; |
---|
| 371 | + |
---|
| 372 | + save_stack_trace_user(&trace); |
---|
| 373 | + return trace.nr_entries; |
---|
| 374 | +} |
---|
| 375 | +#endif /* CONFIG_USER_STACKTRACE_SUPPORT */ |
---|
| 376 | + |
---|
| 377 | +#endif /* !CONFIG_ARCH_STACKWALK */ |
---|