mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-13 00:29:50 +00:00
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>
This commit is contained in:
parent
8aaf1dda42
commit
571d76acda
@ -257,10 +257,6 @@ static inline void cpu_relax(void)
|
|||||||
barrier();
|
barrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct siginfo;
|
|
||||||
extern void arch_coredump_signal(struct siginfo *, struct pt_regs *);
|
|
||||||
#define arch_coredump_signal arch_coredump_signal
|
|
||||||
|
|
||||||
/* Info on this processor (see fs/proc/cpuinfo.c) */
|
/* Info on this processor (see fs/proc/cpuinfo.c) */
|
||||||
struct seq_operations;
|
struct seq_operations;
|
||||||
extern const struct seq_operations cpuinfo_op;
|
extern const struct seq_operations cpuinfo_op;
|
||||||
@ -271,9 +267,6 @@ extern char chip_model[64];
|
|||||||
/* Data on which physical memory controller corresponds to which NUMA node. */
|
/* Data on which physical memory controller corresponds to which NUMA node. */
|
||||||
extern int node_controller[];
|
extern int node_controller[];
|
||||||
|
|
||||||
/* Do we dump information to the console when a user application crashes? */
|
|
||||||
extern int show_crashinfo;
|
|
||||||
|
|
||||||
#if CHIP_HAS_CBOX_HOME_MAP()
|
#if CHIP_HAS_CBOX_HOME_MAP()
|
||||||
/* Does the heap allocator return hash-for-home pages by default? */
|
/* Does the heap allocator return hash-for-home pages by default? */
|
||||||
extern int hash_default;
|
extern int hash_default;
|
||||||
|
@ -28,6 +28,10 @@ struct pt_regs;
|
|||||||
int restore_sigcontext(struct pt_regs *, struct sigcontext __user *);
|
int restore_sigcontext(struct pt_regs *, struct sigcontext __user *);
|
||||||
int setup_sigcontext(struct sigcontext __user *, struct pt_regs *);
|
int setup_sigcontext(struct sigcontext __user *, struct pt_regs *);
|
||||||
void do_signal(struct pt_regs *regs);
|
void do_signal(struct pt_regs *regs);
|
||||||
|
void signal_fault(const char *type, struct pt_regs *,
|
||||||
|
void __user *frame, int sig);
|
||||||
|
void trace_unhandled_signal(const char *type, struct pt_regs *regs,
|
||||||
|
unsigned long address, int signo);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _ASM_TILE_SIGNAL_H */
|
#endif /* _ASM_TILE_SIGNAL_H */
|
||||||
|
@ -317,7 +317,7 @@ long compat_sys_rt_sigreturn(struct pt_regs *regs)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
badframe:
|
badframe:
|
||||||
force_sig(SIGSEGV, current);
|
signal_fault("bad sigreturn frame", regs, frame, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,6 +431,6 @@ int compat_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
give_sigsegv:
|
give_sigsegv:
|
||||||
force_sigsegv(sig, current);
|
signal_fault("bad setup frame", regs, frame, sig);
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
|
|
||||||
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
||||||
|
|
||||||
|
|
||||||
SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss,
|
SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss,
|
||||||
stack_t __user *, uoss, struct pt_regs *, regs)
|
stack_t __user *, uoss, struct pt_regs *, regs)
|
||||||
{
|
{
|
||||||
@ -78,6 +77,13 @@ int restore_sigcontext(struct pt_regs *regs,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void signal_fault(const char *type, struct pt_regs *regs,
|
||||||
|
void __user *frame, int sig)
|
||||||
|
{
|
||||||
|
trace_unhandled_signal(type, regs, (unsigned long)frame, SIGSEGV);
|
||||||
|
force_sigsegv(sig, current);
|
||||||
|
}
|
||||||
|
|
||||||
/* The assembly shim for this function arranges to ignore the return value. */
|
/* The assembly shim for this function arranges to ignore the return value. */
|
||||||
SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs)
|
SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs)
|
||||||
{
|
{
|
||||||
@ -105,7 +111,7 @@ SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
badframe:
|
badframe:
|
||||||
force_sig(SIGSEGV, current);
|
signal_fault("bad sigreturn frame", regs, frame, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +237,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
give_sigsegv:
|
give_sigsegv:
|
||||||
force_sigsegv(sig, current);
|
signal_fault("bad setup frame", regs, frame, sig);
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +251,6 @@ static int handle_signal(unsigned long sig, siginfo_t *info,
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|
||||||
/* Are we from a system call? */
|
/* Are we from a system call? */
|
||||||
if (regs->faultnum == INT_SWINT_1) {
|
if (regs->faultnum == INT_SWINT_1) {
|
||||||
/* If so, check system call restarting.. */
|
/* If so, check system call restarting.. */
|
||||||
@ -363,3 +368,118 @@ done:
|
|||||||
/* Avoid double syscall restart if there are nested signals. */
|
/* Avoid double syscall restart if there are nested signals. */
|
||||||
regs->faultnum = INT_SWINT_1_SIGRETURN;
|
regs->faultnum = INT_SWINT_1_SIGRETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int show_unhandled_signals = 1;
|
||||||
|
|
||||||
|
static int __init crashinfo(char *str)
|
||||||
|
{
|
||||||
|
unsigned long val;
|
||||||
|
const char *word;
|
||||||
|
|
||||||
|
if (*str == '\0')
|
||||||
|
val = 2;
|
||||||
|
else if (*str != '=' || strict_strtoul(++str, 0, &val) != 0)
|
||||||
|
return 0;
|
||||||
|
show_unhandled_signals = val;
|
||||||
|
switch (show_unhandled_signals) {
|
||||||
|
case 0:
|
||||||
|
word = "No";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
word = "One-line";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
word = "Detailed";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pr_info("%s crash reports will be generated on the console\n", word);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
__setup("crashinfo", crashinfo);
|
||||||
|
|
||||||
|
static void dump_mem(void __user *address)
|
||||||
|
{
|
||||||
|
void __user *addr;
|
||||||
|
enum { region_size = 256, bytes_per_line = 16 };
|
||||||
|
int i, j, k;
|
||||||
|
int found_readable_mem = 0;
|
||||||
|
|
||||||
|
pr_err("\n");
|
||||||
|
if (!access_ok(VERIFY_READ, address, 1)) {
|
||||||
|
pr_err("Not dumping at address 0x%lx (kernel address)\n",
|
||||||
|
(unsigned long)address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = (void __user *)
|
||||||
|
(((unsigned long)address & -bytes_per_line) - region_size/2);
|
||||||
|
if (addr > address)
|
||||||
|
addr = NULL;
|
||||||
|
for (i = 0; i < region_size;
|
||||||
|
addr += bytes_per_line, i += bytes_per_line) {
|
||||||
|
unsigned char buf[bytes_per_line];
|
||||||
|
char line[100];
|
||||||
|
if (copy_from_user(buf, addr, bytes_per_line))
|
||||||
|
continue;
|
||||||
|
if (!found_readable_mem) {
|
||||||
|
pr_err("Dumping memory around address 0x%lx:\n",
|
||||||
|
(unsigned long)address);
|
||||||
|
found_readable_mem = 1;
|
||||||
|
}
|
||||||
|
j = sprintf(line, REGFMT":", (unsigned long)addr);
|
||||||
|
for (k = 0; k < bytes_per_line; ++k)
|
||||||
|
j += sprintf(&line[j], " %02x", buf[k]);
|
||||||
|
pr_err("%s\n", line);
|
||||||
|
}
|
||||||
|
if (!found_readable_mem)
|
||||||
|
pr_err("No readable memory around address 0x%lx\n",
|
||||||
|
(unsigned long)address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void trace_unhandled_signal(const char *type, struct pt_regs *regs,
|
||||||
|
unsigned long address, int sig)
|
||||||
|
{
|
||||||
|
struct task_struct *tsk = current;
|
||||||
|
|
||||||
|
if (show_unhandled_signals == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If the signal is handled, don't show it here. */
|
||||||
|
if (!is_global_init(tsk)) {
|
||||||
|
void __user *handler =
|
||||||
|
tsk->sighand->action[sig-1].sa.sa_handler;
|
||||||
|
if (handler != SIG_IGN && handler != SIG_DFL)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rate-limit the one-line output, not the detailed output. */
|
||||||
|
if (show_unhandled_signals <= 1 && !printk_ratelimit())
|
||||||
|
return;
|
||||||
|
|
||||||
|
printk("%s%s[%d]: %s at %lx pc "REGFMT" signal %d",
|
||||||
|
task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
|
||||||
|
tsk->comm, task_pid_nr(tsk), type, address, regs->pc, sig);
|
||||||
|
|
||||||
|
print_vma_addr(KERN_CONT " in ", regs->pc);
|
||||||
|
|
||||||
|
printk(KERN_CONT "\n");
|
||||||
|
|
||||||
|
if (show_unhandled_signals > 1) {
|
||||||
|
switch (sig) {
|
||||||
|
case SIGILL:
|
||||||
|
case SIGFPE:
|
||||||
|
case SIGSEGV:
|
||||||
|
case SIGBUS:
|
||||||
|
pr_err("User crash: signal %d,"
|
||||||
|
" trap %ld, address 0x%lx\n",
|
||||||
|
sig, regs->faultnum, address);
|
||||||
|
show_regs(regs);
|
||||||
|
dump_mem((void __user *)address);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pr_err("User crash: signal %d, trap %ld\n",
|
||||||
|
sig, regs->faultnum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -186,6 +186,8 @@ static tile_bundle_bits rewrite_load_store_unaligned(
|
|||||||
.si_code = SEGV_MAPERR,
|
.si_code = SEGV_MAPERR,
|
||||||
.si_addr = addr
|
.si_addr = addr
|
||||||
};
|
};
|
||||||
|
trace_unhandled_signal("segfault", regs,
|
||||||
|
(unsigned long)addr, SIGSEGV);
|
||||||
force_sig_info(info.si_signo, &info, current);
|
force_sig_info(info.si_signo, &info, current);
|
||||||
return (tile_bundle_bits) 0;
|
return (tile_bundle_bits) 0;
|
||||||
}
|
}
|
||||||
@ -196,6 +198,8 @@ static tile_bundle_bits rewrite_load_store_unaligned(
|
|||||||
.si_code = BUS_ADRALN,
|
.si_code = BUS_ADRALN,
|
||||||
.si_addr = addr
|
.si_addr = addr
|
||||||
};
|
};
|
||||||
|
trace_unhandled_signal("unaligned trap", regs,
|
||||||
|
(unsigned long)addr, SIGBUS);
|
||||||
force_sig_info(info.si_signo, &info, current);
|
force_sig_info(info.si_signo, &info, current);
|
||||||
return (tile_bundle_bits) 0;
|
return (tile_bundle_bits) 0;
|
||||||
}
|
}
|
||||||
|
@ -308,6 +308,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num,
|
|||||||
info.si_addr = (void __user *)address;
|
info.si_addr = (void __user *)address;
|
||||||
if (signo == SIGILL)
|
if (signo == SIGILL)
|
||||||
info.si_trapno = fault_num;
|
info.si_trapno = fault_num;
|
||||||
|
trace_unhandled_signal("trap", regs, address, signo);
|
||||||
force_sig_info(signo, &info, current);
|
force_sig_info(signo, &info, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +43,11 @@
|
|||||||
|
|
||||||
#include <arch/interrupts.h>
|
#include <arch/interrupts.h>
|
||||||
|
|
||||||
static noinline void force_sig_info_fault(int si_signo, int si_code,
|
static noinline void force_sig_info_fault(const char *type, int si_signo,
|
||||||
unsigned long address, int fault_num, struct task_struct *tsk)
|
int si_code, unsigned long address,
|
||||||
|
int fault_num,
|
||||||
|
struct task_struct *tsk,
|
||||||
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
siginfo_t info;
|
siginfo_t info;
|
||||||
|
|
||||||
@ -59,6 +62,7 @@ static noinline void force_sig_info_fault(int si_signo, int si_code,
|
|||||||
info.si_code = si_code;
|
info.si_code = si_code;
|
||||||
info.si_addr = (void __user *)address;
|
info.si_addr = (void __user *)address;
|
||||||
info.si_trapno = fault_num;
|
info.si_trapno = fault_num;
|
||||||
|
trace_unhandled_signal(type, regs, address, si_signo);
|
||||||
force_sig_info(si_signo, &info, tsk);
|
force_sig_info(si_signo, &info, tsk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,11 +75,12 @@ SYSCALL_DEFINE2(cmpxchg_badaddr, unsigned long, address,
|
|||||||
struct pt_regs *, regs)
|
struct pt_regs *, regs)
|
||||||
{
|
{
|
||||||
if (address >= PAGE_OFFSET)
|
if (address >= PAGE_OFFSET)
|
||||||
force_sig_info_fault(SIGSEGV, SEGV_MAPERR, address,
|
force_sig_info_fault("atomic segfault", SIGSEGV, SEGV_MAPERR,
|
||||||
INT_DTLB_MISS, current);
|
address, INT_DTLB_MISS, current, regs);
|
||||||
else
|
else
|
||||||
force_sig_info_fault(SIGBUS, BUS_ADRALN, address,
|
force_sig_info_fault("atomic alignment fault", SIGBUS,
|
||||||
INT_UNALIGN_DATA, current);
|
BUS_ADRALN, address,
|
||||||
|
INT_UNALIGN_DATA, current, regs);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adjust pc to point at the actual instruction, which is unusual
|
* Adjust pc to point at the actual instruction, which is unusual
|
||||||
@ -471,8 +476,8 @@ bad_area_nosemaphore:
|
|||||||
*/
|
*/
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
|
|
||||||
force_sig_info_fault(SIGSEGV, si_code, address,
|
force_sig_info_fault("segfault", SIGSEGV, si_code, address,
|
||||||
fault_num, tsk);
|
fault_num, tsk, regs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,7 +552,8 @@ do_sigbus:
|
|||||||
if (is_kernel_mode)
|
if (is_kernel_mode)
|
||||||
goto no_context;
|
goto no_context;
|
||||||
|
|
||||||
force_sig_info_fault(SIGBUS, BUS_ADRERR, address, fault_num, tsk);
|
force_sig_info_fault("bus error", SIGBUS, BUS_ADRERR, address,
|
||||||
|
fault_num, tsk, regs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1496,7 +1496,7 @@ static struct ctl_table fs_table[] = {
|
|||||||
|
|
||||||
static struct ctl_table debug_table[] = {
|
static struct ctl_table debug_table[] = {
|
||||||
#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || \
|
#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || \
|
||||||
defined(CONFIG_S390)
|
defined(CONFIG_S390) || defined(CONFIG_TILE)
|
||||||
{
|
{
|
||||||
.procname = "exception-trace",
|
.procname = "exception-trace",
|
||||||
.data = &show_unhandled_signals,
|
.data = &show_unhandled_signals,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user