Fix stack tracing in dtrace for powerpc

The current method only sort of works, and usually doesn't work reliably.
Also, on Book-E the return address from DEBUG exceptions is not the sentinel
addresses, so it won't exit the loop correctly.

Fix this by better handling trap frames during unwinding, and using the
common trap handler for debug traps, as the code in that segment is
identical between the two.

MFC after:	1 week
This commit is contained in:
jhibbits 2017-05-11 00:23:51 +00:00
parent 433716df1d
commit fe778c6778
2 changed files with 29 additions and 42 deletions

View File

@ -38,6 +38,7 @@
#include <machine/frame.h>
#include <machine/md_var.h>
#include <machine/psl.h>
#include <machine/reg.h>
#include <machine/stack.h>
@ -54,16 +55,19 @@
#ifdef __powerpc64__
#define OFFSET 4 /* Account for the TOC reload slot */
#define FRAME_OFFSET 48
#else
#define OFFSET 0
#define FRAME_OFFSET 8
#endif
#define INKERNEL(x) ((x) <= VM_MAX_KERNEL_ADDRESS && \
(x) >= VM_MIN_KERNEL_ADDRESS)
static __inline int
dtrace_sp_inkernel(uintptr_t sp, int aframes)
dtrace_sp_inkernel(uintptr_t sp)
{
struct trapframe *frame;
vm_offset_t callpc;
#ifdef __powerpc64__
@ -77,14 +81,15 @@ dtrace_sp_inkernel(uintptr_t sp, int aframes)
/*
* trapexit() and asttrapexit() are sentinels
* for kernel stack tracing.
*
* Special-case this for 'aframes == 0', because fbt sets aframes to the
* trap callchain depth, so we want to break out of it.
*/
if ((callpc + OFFSET == (vm_offset_t) &trapexit ||
callpc + OFFSET == (vm_offset_t) &asttrapexit) &&
aframes != 0)
return (0);
if (callpc + OFFSET == (vm_offset_t) &trapexit ||
callpc + OFFSET == (vm_offset_t) &asttrapexit) {
if (sp == 0)
return (0);
frame = (struct trapframe *)(sp + FRAME_OFFSET);
return ((frame->srr1 & PSL_PR) == 0);
}
return (1);
}
@ -93,6 +98,7 @@ static __inline uintptr_t
dtrace_next_sp(uintptr_t sp)
{
vm_offset_t callpc;
struct trapframe *frame;
#ifdef __powerpc64__
callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
@ -103,18 +109,13 @@ dtrace_next_sp(uintptr_t sp)
/*
* trapexit() and asttrapexit() are sentinels
* for kernel stack tracing.
*
* Special-case this for 'aframes == 0', because fbt sets aframes to the
* trap callchain depth, so we want to break out of it.
*/
if ((callpc + OFFSET == (vm_offset_t) &trapexit ||
callpc + OFFSET == (vm_offset_t) &asttrapexit))
/* Access the trap frame */
#ifdef __powerpc64__
return (*(uintptr_t *)sp + 48 + sizeof(register_t));
#else
return (*(uintptr_t *)sp + 8 + sizeof(register_t));
#endif
callpc + OFFSET == (vm_offset_t) &asttrapexit)) {
/* Access the trap frame */
frame = (struct trapframe *)(sp + FRAME_OFFSET);
return (*(uintptr_t *)(frame->fixreg[1]));
}
return (*(uintptr_t*)sp);
}
@ -122,6 +123,7 @@ dtrace_next_sp(uintptr_t sp)
static __inline uintptr_t
dtrace_get_pc(uintptr_t sp)
{
struct trapframe *frame;
vm_offset_t callpc;
#ifdef __powerpc64__
@ -133,18 +135,13 @@ dtrace_get_pc(uintptr_t sp)
/*
* trapexit() and asttrapexit() are sentinels
* for kernel stack tracing.
*
* Special-case this for 'aframes == 0', because fbt sets aframes to the
* trap callchain depth, so we want to break out of it.
*/
if ((callpc + OFFSET == (vm_offset_t) &trapexit ||
callpc + OFFSET == (vm_offset_t) &asttrapexit))
/* Access the trap frame */
#ifdef __powerpc64__
return (*(uintptr_t *)sp + 48 + offsetof(struct trapframe, lr));
#else
return (*(uintptr_t *)sp + 8 + offsetof(struct trapframe, lr));
#endif
callpc + OFFSET == (vm_offset_t) &asttrapexit)) {
/* Access the trap frame */
frame = (struct trapframe *)(sp + FRAME_OFFSET);
return (frame->srr0);
}
return (callpc);
}
@ -176,7 +173,7 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
if (sp <= osp)
break;
if (!dtrace_sp_inkernel(sp, aframes))
if (!dtrace_sp_inkernel(sp))
break;
callpc = dtrace_get_pc(sp);
@ -537,7 +534,7 @@ dtrace_getstackdepth(int aframes)
if (sp <= osp)
break;
if (!dtrace_sp_inkernel(sp, aframes))
if (!dtrace_sp_inkernel(sp))
break;
if (aframes == 0)

View File

@ -981,12 +981,12 @@ int_debug_int:
LOAD %r4,0(%r5) /* interrupt_vector_base in r4 */
add %r4,%r4,%r5
CMPL cr0, %r3, %r4
blt 1f
blt trap_common
LOAD %r4,4(%r5) /* interrupt_vector_top in r4 */
add %r4,%r4,%r5
addi %r4,%r4,4
CMPL cr0, %r3, %r4
bge 1f
bge trap_common
/* Disable single-stepping for the interrupt handlers. */
LOAD %r3, FRAME_SRR1+CALLSIZE(%r1);
rlwinm %r3, %r3, 0, 23, 21
@ -999,16 +999,6 @@ int_debug_int:
mtspr SPR_SRR1, %r4
mtlr %r14
blr
1:
GET_TOCBASE(%r2)
addi %r3, %r1, CALLSIZE
bl CNAME(trap)
TOC_RESTORE
/*
* Handle ASTs, needed for proper support of single-stepping.
* We actually need to return to the process with an rfi.
*/
b trapexit
/*****************************************************************************
* Common trap code