Commit 571d76acdab95876aeff869ab6449f826c23aa43

Authored by Chris Metcalf
1 parent 8aaf1dda42

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
... ... @@ -308,6 +308,7 @@
308 308 info.si_addr = (void __user *)address;
309 309 if (signo == SIGILL)
310 310 info.si_trapno = fault_num;
  311 + trace_unhandled_signal("trap", regs, address, signo);
311 312 force_sig_info(signo, &info, current);
312 313 }
313 314  
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  
... ... @@ -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,