diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2011-05-16 14:23:44 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2011-05-19 22:55:59 -0400 |
commit | 571d76acdab95876aeff869ab6449f826c23aa43 (patch) | |
tree | b52ceacfa83b1ab4c5a6950007ce8be03cec192e /arch/tile | |
parent | 8aaf1dda42576b0f8dffb004065baa806f4df9b6 (diff) |
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>
Diffstat (limited to 'arch/tile')
-rw-r--r-- | arch/tile/include/asm/processor.h | 7 | ||||
-rw-r--r-- | arch/tile/include/asm/signal.h | 4 | ||||
-rw-r--r-- | arch/tile/kernel/compat_signal.c | 4 | ||||
-rw-r--r-- | arch/tile/kernel/signal.c | 128 | ||||
-rw-r--r-- | arch/tile/kernel/single_step.c | 4 | ||||
-rw-r--r-- | arch/tile/kernel/traps.c | 1 | ||||
-rw-r--r-- | arch/tile/mm/fault.c | 24 |
7 files changed, 150 insertions, 22 deletions
diff --git a/arch/tile/include/asm/processor.h b/arch/tile/include/asm/processor.h index d6b43ddfcc04..34c1e01ffb5e 100644 --- a/arch/tile/include/asm/processor.h +++ b/arch/tile/include/asm/processor.h | |||
@@ -257,10 +257,6 @@ static inline void cpu_relax(void) | |||
257 | barrier(); | 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 | /* Info on this processor (see fs/proc/cpuinfo.c) */ | 260 | /* Info on this processor (see fs/proc/cpuinfo.c) */ |
265 | struct seq_operations; | 261 | struct seq_operations; |
266 | extern const struct seq_operations cpuinfo_op; | 262 | extern const struct seq_operations cpuinfo_op; |
@@ -271,9 +267,6 @@ extern char chip_model[64]; | |||
271 | /* Data on which physical memory controller corresponds to which NUMA node. */ | 267 | /* Data on which physical memory controller corresponds to which NUMA node. */ |
272 | extern int node_controller[]; | 268 | extern int node_controller[]; |
273 | 269 | ||
274 | /* Do we dump information to the console when a user application crashes? */ | ||
275 | extern int show_crashinfo; | ||
276 | |||
277 | #if CHIP_HAS_CBOX_HOME_MAP() | 270 | #if CHIP_HAS_CBOX_HOME_MAP() |
278 | /* Does the heap allocator return hash-for-home pages by default? */ | 271 | /* Does the heap allocator return hash-for-home pages by default? */ |
279 | extern int hash_default; | 272 | extern int hash_default; |
diff --git a/arch/tile/include/asm/signal.h b/arch/tile/include/asm/signal.h index 81d92a45cd4b..1e1e616783eb 100644 --- a/arch/tile/include/asm/signal.h +++ b/arch/tile/include/asm/signal.h | |||
@@ -28,6 +28,10 @@ struct pt_regs; | |||
28 | int restore_sigcontext(struct pt_regs *, struct sigcontext __user *); | 28 | int restore_sigcontext(struct pt_regs *, struct sigcontext __user *); |
29 | int setup_sigcontext(struct sigcontext __user *, struct pt_regs *); | 29 | int setup_sigcontext(struct sigcontext __user *, struct pt_regs *); |
30 | void do_signal(struct pt_regs *regs); | 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 | #endif | 35 | #endif |
32 | 36 | ||
33 | #endif /* _ASM_TILE_SIGNAL_H */ | 37 | #endif /* _ASM_TILE_SIGNAL_H */ |
diff --git a/arch/tile/kernel/compat_signal.c b/arch/tile/kernel/compat_signal.c index dbb0dfc7bece..a7869ad62776 100644 --- a/arch/tile/kernel/compat_signal.c +++ b/arch/tile/kernel/compat_signal.c | |||
@@ -317,7 +317,7 @@ long compat_sys_rt_sigreturn(struct pt_regs *regs) | |||
317 | return 0; | 317 | return 0; |
318 | 318 | ||
319 | badframe: | 319 | badframe: |
320 | force_sig(SIGSEGV, current); | 320 | signal_fault("bad sigreturn frame", regs, frame, 0); |
321 | return 0; | 321 | return 0; |
322 | } | 322 | } |
323 | 323 | ||
@@ -431,6 +431,6 @@ int compat_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
431 | return 0; | 431 | return 0; |
432 | 432 | ||
433 | give_sigsegv: | 433 | give_sigsegv: |
434 | force_sigsegv(sig, current); | 434 | signal_fault("bad setup frame", regs, frame, sig); |
435 | return -EFAULT; | 435 | return -EFAULT; |
436 | } | 436 | } |
diff --git a/arch/tile/kernel/signal.c b/arch/tile/kernel/signal.c index 1260321155f1..bedaf4e9f3a7 100644 --- a/arch/tile/kernel/signal.c +++ b/arch/tile/kernel/signal.c | |||
@@ -39,7 +39,6 @@ | |||
39 | 39 | ||
40 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | 40 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) |
41 | 41 | ||
42 | |||
43 | SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss, | 42 | SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss, |
44 | stack_t __user *, uoss, struct pt_regs *, regs) | 43 | stack_t __user *, uoss, struct pt_regs *, regs) |
45 | { | 44 | { |
@@ -78,6 +77,13 @@ int restore_sigcontext(struct pt_regs *regs, | |||
78 | return err; | 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 | /* The assembly shim for this function arranges to ignore the return value. */ | 87 | /* The assembly shim for this function arranges to ignore the return value. */ |
82 | SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs) | 88 | SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs) |
83 | { | 89 | { |
@@ -105,7 +111,7 @@ SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs) | |||
105 | return 0; | 111 | return 0; |
106 | 112 | ||
107 | badframe: | 113 | badframe: |
108 | force_sig(SIGSEGV, current); | 114 | signal_fault("bad sigreturn frame", regs, frame, 0); |
109 | return 0; | 115 | return 0; |
110 | } | 116 | } |
111 | 117 | ||
@@ -231,7 +237,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
231 | return 0; | 237 | return 0; |
232 | 238 | ||
233 | give_sigsegv: | 239 | give_sigsegv: |
234 | force_sigsegv(sig, current); | 240 | signal_fault("bad setup frame", regs, frame, sig); |
235 | return -EFAULT; | 241 | return -EFAULT; |
236 | } | 242 | } |
237 | 243 | ||
@@ -245,7 +251,6 @@ static int handle_signal(unsigned long sig, siginfo_t *info, | |||
245 | { | 251 | { |
246 | int ret; | 252 | int ret; |
247 | 253 | ||
248 | |||
249 | /* Are we from a system call? */ | 254 | /* Are we from a system call? */ |
250 | if (regs->faultnum == INT_SWINT_1) { | 255 | if (regs->faultnum == INT_SWINT_1) { |
251 | /* If so, check system call restarting.. */ | 256 | /* If so, check system call restarting.. */ |
@@ -363,3 +368,118 @@ done: | |||
363 | /* Avoid double syscall restart if there are nested signals. */ | 368 | /* Avoid double syscall restart if there are nested signals. */ |
364 | regs->faultnum = INT_SWINT_1_SIGRETURN; | 369 | regs->faultnum = INT_SWINT_1_SIGRETURN; |
365 | } | 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 | } | ||
485 | } | ||
diff --git a/arch/tile/kernel/single_step.c b/arch/tile/kernel/single_step.c index 86df5a239b70..4032ca8e51b6 100644 --- a/arch/tile/kernel/single_step.c +++ b/arch/tile/kernel/single_step.c | |||
@@ -186,6 +186,8 @@ static tile_bundle_bits rewrite_load_store_unaligned( | |||
186 | .si_code = SEGV_MAPERR, | 186 | .si_code = SEGV_MAPERR, |
187 | .si_addr = addr | 187 | .si_addr = addr |
188 | }; | 188 | }; |
189 | trace_unhandled_signal("segfault", regs, | ||
190 | (unsigned long)addr, SIGSEGV); | ||
189 | force_sig_info(info.si_signo, &info, current); | 191 | force_sig_info(info.si_signo, &info, current); |
190 | return (tile_bundle_bits) 0; | 192 | return (tile_bundle_bits) 0; |
191 | } | 193 | } |
@@ -196,6 +198,8 @@ static tile_bundle_bits rewrite_load_store_unaligned( | |||
196 | .si_code = BUS_ADRALN, | 198 | .si_code = BUS_ADRALN, |
197 | .si_addr = addr | 199 | .si_addr = addr |
198 | }; | 200 | }; |
201 | trace_unhandled_signal("unaligned trap", regs, | ||
202 | (unsigned long)addr, SIGBUS); | ||
199 | force_sig_info(info.si_signo, &info, current); | 203 | force_sig_info(info.si_signo, &info, current); |
200 | return (tile_bundle_bits) 0; | 204 | return (tile_bundle_bits) 0; |
201 | } | 205 | } |
diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c index 5474fc2e77e8..f9803dfa7357 100644 --- a/arch/tile/kernel/traps.c +++ b/arch/tile/kernel/traps.c | |||
@@ -308,6 +308,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
308 | info.si_addr = (void __user *)address; | 308 | info.si_addr = (void __user *)address; |
309 | if (signo == SIGILL) | 309 | if (signo == SIGILL) |
310 | info.si_trapno = fault_num; | 310 | info.si_trapno = fault_num; |
311 | trace_unhandled_signal("trap", regs, address, signo); | ||
311 | force_sig_info(signo, &info, current); | 312 | force_sig_info(signo, &info, current); |
312 | } | 313 | } |
313 | 314 | ||
diff --git a/arch/tile/mm/fault.c b/arch/tile/mm/fault.c index 24ca54a0703b..25b7b90fd620 100644 --- a/arch/tile/mm/fault.c +++ b/arch/tile/mm/fault.c | |||
@@ -43,8 +43,11 @@ | |||
43 | 43 | ||
44 | #include <arch/interrupts.h> | 44 | #include <arch/interrupts.h> |
45 | 45 | ||
46 | static noinline void force_sig_info_fault(int si_signo, int si_code, | 46 | static noinline void force_sig_info_fault(const char *type, int si_signo, |
47 | unsigned long address, int fault_num, struct task_struct *tsk) | 47 | int si_code, unsigned long address, |
48 | int fault_num, | ||
49 | struct task_struct *tsk, | ||
50 | struct pt_regs *regs) | ||
48 | { | 51 | { |
49 | siginfo_t info; | 52 | siginfo_t info; |
50 | 53 | ||
@@ -59,6 +62,7 @@ static noinline void force_sig_info_fault(int si_signo, int si_code, | |||
59 | info.si_code = si_code; | 62 | info.si_code = si_code; |
60 | info.si_addr = (void __user *)address; | 63 | info.si_addr = (void __user *)address; |
61 | info.si_trapno = fault_num; | 64 | info.si_trapno = fault_num; |
65 | trace_unhandled_signal(type, regs, address, si_signo); | ||
62 | force_sig_info(si_signo, &info, tsk); | 66 | force_sig_info(si_signo, &info, tsk); |
63 | } | 67 | } |
64 | 68 | ||
@@ -71,11 +75,12 @@ SYSCALL_DEFINE2(cmpxchg_badaddr, unsigned long, address, | |||
71 | struct pt_regs *, regs) | 75 | struct pt_regs *, regs) |
72 | { | 76 | { |
73 | if (address >= PAGE_OFFSET) | 77 | if (address >= PAGE_OFFSET) |
74 | force_sig_info_fault(SIGSEGV, SEGV_MAPERR, address, | 78 | force_sig_info_fault("atomic segfault", SIGSEGV, SEGV_MAPERR, |
75 | INT_DTLB_MISS, current); | 79 | address, INT_DTLB_MISS, current, regs); |
76 | else | 80 | else |
77 | force_sig_info_fault(SIGBUS, BUS_ADRALN, address, | 81 | force_sig_info_fault("atomic alignment fault", SIGBUS, |
78 | INT_UNALIGN_DATA, current); | 82 | BUS_ADRALN, address, |
83 | INT_UNALIGN_DATA, current, regs); | ||
79 | 84 | ||
80 | /* | 85 | /* |
81 | * Adjust pc to point at the actual instruction, which is unusual | 86 | * Adjust pc to point at the actual instruction, which is unusual |
@@ -471,8 +476,8 @@ bad_area_nosemaphore: | |||
471 | */ | 476 | */ |
472 | local_irq_enable(); | 477 | local_irq_enable(); |
473 | 478 | ||
474 | force_sig_info_fault(SIGSEGV, si_code, address, | 479 | force_sig_info_fault("segfault", SIGSEGV, si_code, address, |
475 | fault_num, tsk); | 480 | fault_num, tsk, regs); |
476 | return 0; | 481 | return 0; |
477 | } | 482 | } |
478 | 483 | ||
@@ -547,7 +552,8 @@ do_sigbus: | |||
547 | if (is_kernel_mode) | 552 | if (is_kernel_mode) |
548 | goto no_context; | 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 | return 0; | 557 | return 0; |
552 | } | 558 | } |
553 | 559 | ||