Commit 571d76acdab95876aeff869ab6449f826c23aa43
1 parent
8aaf1dda42
Exists in
master
and in
4 other branches
arch/tile: support signal "exception-trace" hook
This change adds support for /proc/sys/debug/exception-trace to tile. Like x86 and sparc, by default it is set to "1", generating a one-line printk whenever a user process crashes. By setting it to "2", we get a much more complete userspace diagnostic at crash time, including a user-space backtrace, register dump, and memory dump around the address of the crash. Some vestiges of the Tilera-internal version of this support are removed with this patch (the show_crashinfo variable and the arch_coredump_signal function). We retain a "crashinfo" boot parameter which allows you to set the boot-time value of exception-trace. Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Showing 8 changed files with 151 additions and 23 deletions Side-by-side Diff
arch/tile/include/asm/processor.h
... | ... | @@ -257,10 +257,6 @@ |
257 | 257 | barrier(); |
258 | 258 | } |
259 | 259 | |
260 | -struct siginfo; | |
261 | -extern void arch_coredump_signal(struct siginfo *, struct pt_regs *); | |
262 | -#define arch_coredump_signal arch_coredump_signal | |
263 | - | |
264 | 260 | /* Info on this processor (see fs/proc/cpuinfo.c) */ |
265 | 261 | struct seq_operations; |
266 | 262 | extern const struct seq_operations cpuinfo_op; |
... | ... | @@ -270,9 +266,6 @@ |
270 | 266 | |
271 | 267 | /* Data on which physical memory controller corresponds to which NUMA node. */ |
272 | 268 | extern int node_controller[]; |
273 | - | |
274 | -/* Do we dump information to the console when a user application crashes? */ | |
275 | -extern int show_crashinfo; | |
276 | 269 | |
277 | 270 | #if CHIP_HAS_CBOX_HOME_MAP() |
278 | 271 | /* Does the heap allocator return hash-for-home pages by default? */ |
arch/tile/include/asm/signal.h
... | ... | @@ -28,6 +28,10 @@ |
28 | 28 | int restore_sigcontext(struct pt_regs *, struct sigcontext __user *); |
29 | 29 | int setup_sigcontext(struct sigcontext __user *, struct pt_regs *); |
30 | 30 | void do_signal(struct pt_regs *regs); |
31 | +void signal_fault(const char *type, struct pt_regs *, | |
32 | + void __user *frame, int sig); | |
33 | +void trace_unhandled_signal(const char *type, struct pt_regs *regs, | |
34 | + unsigned long address, int signo); | |
31 | 35 | #endif |
32 | 36 | |
33 | 37 | #endif /* _ASM_TILE_SIGNAL_H */ |
arch/tile/kernel/compat_signal.c
... | ... | @@ -317,7 +317,7 @@ |
317 | 317 | return 0; |
318 | 318 | |
319 | 319 | badframe: |
320 | - force_sig(SIGSEGV, current); | |
320 | + signal_fault("bad sigreturn frame", regs, frame, 0); | |
321 | 321 | return 0; |
322 | 322 | } |
323 | 323 | |
... | ... | @@ -431,7 +431,7 @@ |
431 | 431 | return 0; |
432 | 432 | |
433 | 433 | give_sigsegv: |
434 | - force_sigsegv(sig, current); | |
434 | + signal_fault("bad setup frame", regs, frame, sig); | |
435 | 435 | return -EFAULT; |
436 | 436 | } |
arch/tile/kernel/signal.c
... | ... | @@ -39,7 +39,6 @@ |
39 | 39 | |
40 | 40 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) |
41 | 41 | |
42 | - | |
43 | 42 | SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss, |
44 | 43 | stack_t __user *, uoss, struct pt_regs *, regs) |
45 | 44 | { |
... | ... | @@ -78,6 +77,13 @@ |
78 | 77 | return err; |
79 | 78 | } |
80 | 79 | |
80 | +void signal_fault(const char *type, struct pt_regs *regs, | |
81 | + void __user *frame, int sig) | |
82 | +{ | |
83 | + trace_unhandled_signal(type, regs, (unsigned long)frame, SIGSEGV); | |
84 | + force_sigsegv(sig, current); | |
85 | +} | |
86 | + | |
81 | 87 | /* The assembly shim for this function arranges to ignore the return value. */ |
82 | 88 | SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs) |
83 | 89 | { |
... | ... | @@ -105,7 +111,7 @@ |
105 | 111 | return 0; |
106 | 112 | |
107 | 113 | badframe: |
108 | - force_sig(SIGSEGV, current); | |
114 | + signal_fault("bad sigreturn frame", regs, frame, 0); | |
109 | 115 | return 0; |
110 | 116 | } |
111 | 117 | |
... | ... | @@ -231,7 +237,7 @@ |
231 | 237 | return 0; |
232 | 238 | |
233 | 239 | give_sigsegv: |
234 | - force_sigsegv(sig, current); | |
240 | + signal_fault("bad setup frame", regs, frame, sig); | |
235 | 241 | return -EFAULT; |
236 | 242 | } |
237 | 243 | |
... | ... | @@ -245,7 +251,6 @@ |
245 | 251 | { |
246 | 252 | int ret; |
247 | 253 | |
248 | - | |
249 | 254 | /* Are we from a system call? */ |
250 | 255 | if (regs->faultnum == INT_SWINT_1) { |
251 | 256 | /* If so, check system call restarting.. */ |
... | ... | @@ -362,5 +367,120 @@ |
362 | 367 | done: |
363 | 368 | /* Avoid double syscall restart if there are nested signals. */ |
364 | 369 | regs->faultnum = INT_SWINT_1_SIGRETURN; |
370 | +} | |
371 | + | |
372 | +int show_unhandled_signals = 1; | |
373 | + | |
374 | +static int __init crashinfo(char *str) | |
375 | +{ | |
376 | + unsigned long val; | |
377 | + const char *word; | |
378 | + | |
379 | + if (*str == '\0') | |
380 | + val = 2; | |
381 | + else if (*str != '=' || strict_strtoul(++str, 0, &val) != 0) | |
382 | + return 0; | |
383 | + show_unhandled_signals = val; | |
384 | + switch (show_unhandled_signals) { | |
385 | + case 0: | |
386 | + word = "No"; | |
387 | + break; | |
388 | + case 1: | |
389 | + word = "One-line"; | |
390 | + break; | |
391 | + default: | |
392 | + word = "Detailed"; | |
393 | + break; | |
394 | + } | |
395 | + pr_info("%s crash reports will be generated on the console\n", word); | |
396 | + return 1; | |
397 | +} | |
398 | +__setup("crashinfo", crashinfo); | |
399 | + | |
400 | +static void dump_mem(void __user *address) | |
401 | +{ | |
402 | + void __user *addr; | |
403 | + enum { region_size = 256, bytes_per_line = 16 }; | |
404 | + int i, j, k; | |
405 | + int found_readable_mem = 0; | |
406 | + | |
407 | + pr_err("\n"); | |
408 | + if (!access_ok(VERIFY_READ, address, 1)) { | |
409 | + pr_err("Not dumping at address 0x%lx (kernel address)\n", | |
410 | + (unsigned long)address); | |
411 | + return; | |
412 | + } | |
413 | + | |
414 | + addr = (void __user *) | |
415 | + (((unsigned long)address & -bytes_per_line) - region_size/2); | |
416 | + if (addr > address) | |
417 | + addr = NULL; | |
418 | + for (i = 0; i < region_size; | |
419 | + addr += bytes_per_line, i += bytes_per_line) { | |
420 | + unsigned char buf[bytes_per_line]; | |
421 | + char line[100]; | |
422 | + if (copy_from_user(buf, addr, bytes_per_line)) | |
423 | + continue; | |
424 | + if (!found_readable_mem) { | |
425 | + pr_err("Dumping memory around address 0x%lx:\n", | |
426 | + (unsigned long)address); | |
427 | + found_readable_mem = 1; | |
428 | + } | |
429 | + j = sprintf(line, REGFMT":", (unsigned long)addr); | |
430 | + for (k = 0; k < bytes_per_line; ++k) | |
431 | + j += sprintf(&line[j], " %02x", buf[k]); | |
432 | + pr_err("%s\n", line); | |
433 | + } | |
434 | + if (!found_readable_mem) | |
435 | + pr_err("No readable memory around address 0x%lx\n", | |
436 | + (unsigned long)address); | |
437 | +} | |
438 | + | |
439 | +void trace_unhandled_signal(const char *type, struct pt_regs *regs, | |
440 | + unsigned long address, int sig) | |
441 | +{ | |
442 | + struct task_struct *tsk = current; | |
443 | + | |
444 | + if (show_unhandled_signals == 0) | |
445 | + return; | |
446 | + | |
447 | + /* If the signal is handled, don't show it here. */ | |
448 | + if (!is_global_init(tsk)) { | |
449 | + void __user *handler = | |
450 | + tsk->sighand->action[sig-1].sa.sa_handler; | |
451 | + if (handler != SIG_IGN && handler != SIG_DFL) | |
452 | + return; | |
453 | + } | |
454 | + | |
455 | + /* Rate-limit the one-line output, not the detailed output. */ | |
456 | + if (show_unhandled_signals <= 1 && !printk_ratelimit()) | |
457 | + return; | |
458 | + | |
459 | + printk("%s%s[%d]: %s at %lx pc "REGFMT" signal %d", | |
460 | + task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG, | |
461 | + tsk->comm, task_pid_nr(tsk), type, address, regs->pc, sig); | |
462 | + | |
463 | + print_vma_addr(KERN_CONT " in ", regs->pc); | |
464 | + | |
465 | + printk(KERN_CONT "\n"); | |
466 | + | |
467 | + if (show_unhandled_signals > 1) { | |
468 | + switch (sig) { | |
469 | + case SIGILL: | |
470 | + case SIGFPE: | |
471 | + case SIGSEGV: | |
472 | + case SIGBUS: | |
473 | + pr_err("User crash: signal %d," | |
474 | + " trap %ld, address 0x%lx\n", | |
475 | + sig, regs->faultnum, address); | |
476 | + show_regs(regs); | |
477 | + dump_mem((void __user *)address); | |
478 | + break; | |
479 | + default: | |
480 | + pr_err("User crash: signal %d, trap %ld\n", | |
481 | + sig, regs->faultnum); | |
482 | + break; | |
483 | + } | |
484 | + } | |
365 | 485 | } |
arch/tile/kernel/single_step.c
... | ... | @@ -186,6 +186,8 @@ |
186 | 186 | .si_code = SEGV_MAPERR, |
187 | 187 | .si_addr = addr |
188 | 188 | }; |
189 | + trace_unhandled_signal("segfault", regs, | |
190 | + (unsigned long)addr, SIGSEGV); | |
189 | 191 | force_sig_info(info.si_signo, &info, current); |
190 | 192 | return (tile_bundle_bits) 0; |
191 | 193 | } |
... | ... | @@ -196,6 +198,8 @@ |
196 | 198 | .si_code = BUS_ADRALN, |
197 | 199 | .si_addr = addr |
198 | 200 | }; |
201 | + trace_unhandled_signal("unaligned trap", regs, | |
202 | + (unsigned long)addr, SIGBUS); | |
199 | 203 | force_sig_info(info.si_signo, &info, current); |
200 | 204 | return (tile_bundle_bits) 0; |
201 | 205 | } |
arch/tile/kernel/traps.c
arch/tile/mm/fault.c
... | ... | @@ -43,8 +43,11 @@ |
43 | 43 | |
44 | 44 | #include <arch/interrupts.h> |
45 | 45 | |
46 | -static noinline void force_sig_info_fault(int si_signo, int si_code, | |
47 | - unsigned long address, int fault_num, struct task_struct *tsk) | |
46 | +static noinline void force_sig_info_fault(const char *type, int si_signo, | |
47 | + int si_code, unsigned long address, | |
48 | + int fault_num, | |
49 | + struct task_struct *tsk, | |
50 | + struct pt_regs *regs) | |
48 | 51 | { |
49 | 52 | siginfo_t info; |
50 | 53 | |
... | ... | @@ -59,6 +62,7 @@ |
59 | 62 | info.si_code = si_code; |
60 | 63 | info.si_addr = (void __user *)address; |
61 | 64 | info.si_trapno = fault_num; |
65 | + trace_unhandled_signal(type, regs, address, si_signo); | |
62 | 66 | force_sig_info(si_signo, &info, tsk); |
63 | 67 | } |
64 | 68 | |
65 | 69 | |
... | ... | @@ -71,11 +75,12 @@ |
71 | 75 | struct pt_regs *, regs) |
72 | 76 | { |
73 | 77 | if (address >= PAGE_OFFSET) |
74 | - force_sig_info_fault(SIGSEGV, SEGV_MAPERR, address, | |
75 | - INT_DTLB_MISS, current); | |
78 | + force_sig_info_fault("atomic segfault", SIGSEGV, SEGV_MAPERR, | |
79 | + address, INT_DTLB_MISS, current, regs); | |
76 | 80 | else |
77 | - force_sig_info_fault(SIGBUS, BUS_ADRALN, address, | |
78 | - INT_UNALIGN_DATA, current); | |
81 | + force_sig_info_fault("atomic alignment fault", SIGBUS, | |
82 | + BUS_ADRALN, address, | |
83 | + INT_UNALIGN_DATA, current, regs); | |
79 | 84 | |
80 | 85 | /* |
81 | 86 | * Adjust pc to point at the actual instruction, which is unusual |
... | ... | @@ -471,8 +476,8 @@ |
471 | 476 | */ |
472 | 477 | local_irq_enable(); |
473 | 478 | |
474 | - force_sig_info_fault(SIGSEGV, si_code, address, | |
475 | - fault_num, tsk); | |
479 | + force_sig_info_fault("segfault", SIGSEGV, si_code, address, | |
480 | + fault_num, tsk, regs); | |
476 | 481 | return 0; |
477 | 482 | } |
478 | 483 | |
... | ... | @@ -547,7 +552,8 @@ |
547 | 552 | if (is_kernel_mode) |
548 | 553 | goto no_context; |
549 | 554 | |
550 | - force_sig_info_fault(SIGBUS, BUS_ADRERR, address, fault_num, tsk); | |
555 | + force_sig_info_fault("bus error", SIGBUS, BUS_ADRERR, address, | |
556 | + fault_num, tsk, regs); | |
551 | 557 | return 0; |
552 | 558 | } |
553 | 559 |
kernel/sysctl.c
... | ... | @@ -1496,7 +1496,7 @@ |
1496 | 1496 | |
1497 | 1497 | static struct ctl_table debug_table[] = { |
1498 | 1498 | #if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || \ |
1499 | - defined(CONFIG_S390) | |
1499 | + defined(CONFIG_S390) || defined(CONFIG_TILE) | |
1500 | 1500 | { |
1501 | 1501 | .procname = "exception-trace", |
1502 | 1502 | .data = &show_unhandled_signals, |