Commit 14bc28ad authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] ppc64: better stack traces

This improves the stack traces we get on PPC64 by putting a marker in
those stack frames that are created as a result of an interrupt or
exception.  The marker is "regshere" (0x7265677368657265).

With this, stack traces show where exceptions have occurred, which can
be very useful.  This also improves the accuracy of the trace because
the relevant return address can be in the link register at the time of
the exception rather than on the stack.  We now print the PC and
exception type for each exception frame, and then the link register if
appropriate as the next item in the trace.
parent e1ff5fe0
...@@ -1281,6 +1281,10 @@ _GLOBAL(save_remaining_regs) ...@@ -1281,6 +1281,10 @@ _GLOBAL(save_remaining_regs)
SAVE_4GPRS(16, r1) SAVE_4GPRS(16, r1)
SAVE_8GPRS(24, r1) SAVE_8GPRS(24, r1)
/* Set the marker value "regshere" just before the reg values */
SET_REG_TO_CONST(r22, 0x7265677368657265)
std r22,STACK_FRAME_OVERHEAD-16(r1)
/* /*
* Clear the RESULT field * Clear the RESULT field
*/ */
......
...@@ -457,16 +457,16 @@ int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, ...@@ -457,16 +457,16 @@ int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
static int kstack_depth_to_print = 64; static int kstack_depth_to_print = 64;
static inline int validate_sp(unsigned long sp, struct task_struct *p) static int validate_sp(unsigned long sp, struct task_struct *p,
unsigned long nbytes)
{ {
unsigned long stack_page = (unsigned long)p->thread_info; unsigned long stack_page = (unsigned long)p->thread_info;
if (sp < stack_page + sizeof(struct thread_struct)) if (sp >= stack_page + sizeof(struct thread_struct)
return 0; && sp <= stack_page + THREAD_SIZE - nbytes)
if (sp >= stack_page + THREAD_SIZE) return 1;
return 0;
return 1; return 0;
} }
unsigned long get_wchan(struct task_struct *p) unsigned long get_wchan(struct task_struct *p)
...@@ -478,12 +478,12 @@ unsigned long get_wchan(struct task_struct *p) ...@@ -478,12 +478,12 @@ unsigned long get_wchan(struct task_struct *p)
return 0; return 0;
sp = p->thread.ksp; sp = p->thread.ksp;
if (!validate_sp(sp, p)) if (!validate_sp(sp, p, 112))
return 0; return 0;
do { do {
sp = *(unsigned long *)sp; sp = *(unsigned long *)sp;
if (!validate_sp(sp, p)) if (!validate_sp(sp, p, 112))
return 0; return 0;
if (count > 0) { if (count > 0) {
ip = *(unsigned long *)(sp + 16); ip = *(unsigned long *)(sp + 16);
...@@ -496,9 +496,10 @@ unsigned long get_wchan(struct task_struct *p) ...@@ -496,9 +496,10 @@ unsigned long get_wchan(struct task_struct *p)
void show_stack(struct task_struct *p, unsigned long *_sp) void show_stack(struct task_struct *p, unsigned long *_sp)
{ {
unsigned long ip; unsigned long ip, newsp, lr;
int count = 0; int count = 0;
unsigned long sp = (unsigned long)_sp; unsigned long sp = (unsigned long)_sp;
int firstframe = 1;
if (sp == 0) { if (sp == 0) {
if (p) { if (p) {
...@@ -509,17 +510,40 @@ void show_stack(struct task_struct *p, unsigned long *_sp) ...@@ -509,17 +510,40 @@ void show_stack(struct task_struct *p, unsigned long *_sp)
} }
} }
if (!validate_sp(sp, p)) lr = 0;
return;
printk("Call Trace:\n"); printk("Call Trace:\n");
do { do {
sp = *(unsigned long *)sp; if (!validate_sp(sp, p, 112))
if (!validate_sp(sp, p))
return; return;
ip = *(unsigned long *)(sp + 16);
printk("[%016lx] ", ip); _sp = (unsigned long *) sp;
print_symbol("%s\n", ip); newsp = _sp[0];
ip = _sp[2];
if (!firstframe || ip != lr) {
printk("[%016lx] [%016lx] ", sp, ip);
print_symbol("%s", ip);
if (firstframe)
printk(" (unreliable)");
printk("\n");
}
firstframe = 0;
/*
* See if this is an exception frame.
* We look for the "regshere" marker in the current frame.
*/
if (validate_sp(sp, p, sizeof(struct pt_regs) + 400)
&& _sp[12] == 0x7265677368657265) {
struct pt_regs *regs = (struct pt_regs *)
(sp + STACK_FRAME_OVERHEAD);
printk("--- Exception: %lx", regs->trap);
print_symbol(" at %s\n", regs->nip);
lr = regs->link;
print_symbol(" LR = %s\n", lr);
firstframe = 1;
}
sp = newsp;
} while (count++ < kstack_depth_to_print); } while (count++ < kstack_depth_to_print);
} }
......
...@@ -1399,8 +1399,7 @@ static void xmon_show_stack(unsigned long sp, unsigned long lr, ...@@ -1399,8 +1399,7 @@ static void xmon_show_stack(unsigned long sp, unsigned long lr,
/* Look for "regshere" marker to see if this is /* Look for "regshere" marker to see if this is
an exception frame. */ an exception frame. */
if (newsp - sp == sizeof(struct pt_regs) + 400 if (mread(sp + 0x60, &marker, sizeof(unsigned long))
&& mread(sp + 0x60, &marker, sizeof(unsigned long))
&& marker == 0x7265677368657265) { && marker == 0x7265677368657265) {
if (mread(sp + 0x70, &regs, sizeof(regs)) if (mread(sp + 0x70, &regs, sizeof(regs))
!= sizeof(regs)) { != sizeof(regs)) {
...@@ -1417,12 +1416,6 @@ static void xmon_show_stack(unsigned long sp, unsigned long lr, ...@@ -1417,12 +1416,6 @@ static void xmon_show_stack(unsigned long sp, unsigned long lr,
if (newsp == 0) if (newsp == 0)
break; break;
if (newsp < sp) {
printf("Stack chain goes %s: %.16lx\n",
(newsp < KERNELBASE? "into userspace":
"backwards"), newsp);
break;
}
sp = newsp; sp = newsp;
} while (count++ < xmon_depth_to_print); } while (count++ < xmon_depth_to_print);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment