Fix the stack tracing for dtrace/powerpc.

Summary:
Fix the stack tracing for dtrace/powerpc by using the trapexit/asttrapexit
return address sentinels instead of checking within the kernel address space.

As part of this, I had to add new inline functions.  FBT traces the kernel, so
we have to have special case handling for this, since a trap will create a full
new trap frame, and there's no way to pass around the 'real' stack.  I handle
this by special-casing 'aframes == 0' with the trap frame.  If aframes counts
out to the trap frame, then assume we're looking for the full kernel trap frame,
so switch to the real stack pointer.

Test Plan: Tested on powerpc64

Reviewers: rpaulo, markj, nwhitehorn

Reviewed By: markj, nwhitehorn

Differential Revision: https://reviews.freebsd.org/D788

MFC after:	3 week
Relnotes:	Yes
This commit is contained in:
Justin Hibbits 2014-09-17 02:43:47 +00:00
parent 76cd7220b5
commit e40a5cd3ec
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=271697
2 changed files with 123 additions and 21 deletions

View File

@ -52,9 +52,103 @@
/* Offset to LR Save word (ppc64). CR Save area sits between back chain and LR */
#define RETURN_OFFSET64 16
#ifdef __powerpc64__
#define OFFSET 4 /* Account for the TOC reload slot */
#else
#define OFFSET 0
#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)
{
vm_offset_t callpc;
#ifdef __powerpc64__
callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
#else
callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
#endif
if ((callpc & 3) || (callpc < 0x100))
return (0);
/*
* 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);
return (1);
}
static __inline uintptr_t
dtrace_next_sp(uintptr_t sp)
{
vm_offset_t callpc;
#ifdef __powerpc64__
callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
#else
callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
#endif
/*
* 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
return (*(uintptr_t*)sp);
}
static __inline uintptr_t
dtrace_get_pc(uintptr_t sp)
{
vm_offset_t callpc;
#ifdef __powerpc64__
callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
#else
callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
#endif
/*
* 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
return (callpc);
}
greg_t
dtrace_getfp(void)
{
@ -66,10 +160,11 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
uint32_t *intrpc)
{
int depth = 0;
register_t sp;
uintptr_t osp, sp;
vm_offset_t callpc;
pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
osp = PAGE_SIZE;
if (intrpc != 0)
pcstack[depth++] = (pc_t) intrpc;
@ -78,17 +173,12 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
sp = dtrace_getfp();
while (depth < pcstack_limit) {
if (!INKERNEL((long) sp))
if (sp <= osp)
break;
#ifdef __powerpc64__
callpc = *(uintptr_t *)(sp + RETURN_OFFSET64);
#else
callpc = *(uintptr_t *)(sp + RETURN_OFFSET);
#endif
if (!INKERNEL(callpc))
if (!dtrace_sp_inkernel(sp, aframes))
break;
callpc = dtrace_get_pc(sp);
if (aframes > 0) {
aframes--;
@ -100,7 +190,8 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
pcstack[depth++] = callpc;
}
sp = *(uintptr_t*)sp;
osp = sp;
sp = dtrace_next_sp(sp);
}
for (; depth < pcstack_limit; depth++) {
@ -368,8 +459,11 @@ dtrace_getarg(int arg, int aframes)
* On ppc32 AIM, and booke, trapexit() is the immediately following
* label. On ppc64 AIM trapexit() follows a nop.
*/
if (((long)(fp[1]) == (long)trapexit) ||
(((long)(fp[1]) + 4 == (long)trapexit))) {
#ifdef __powerpc64__
if ((long)(fp[2]) + 4 == (long)trapexit) {
#else
if ((long)(fp[1]) == (long)trapexit) {
#endif
/*
* In the case of powerpc, we will use the pointer to the regs
* structure that was pushed when we took the trap. To get this
@ -433,23 +527,31 @@ int
dtrace_getstackdepth(int aframes)
{
int depth = 0;
register_t sp;
uintptr_t osp, sp;
vm_offset_t callpc;
osp = PAGE_SIZE;
aframes++;
sp = dtrace_getfp();
depth++;
for(;;) {
if (!INKERNEL((long) sp))
if (sp <= osp)
break;
if (!INKERNEL((long) *(void **)sp))
if (!dtrace_sp_inkernel(sp, aframes))
break;
depth++;
if (aframes == 0)
depth++;
else
aframes--;
osp = sp;
sp = *(uintptr_t *)sp;
}
if (depth < aframes)
return 0;
else
return depth - aframes;
return (0);
return (depth);
}
ulong_t

View File

@ -147,7 +147,7 @@ fbt_provide_module_function(linker_file_t lf, int symindx,
fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
fbt->fbtp_name = name;
fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
name, FBT_ENTRY, 3, fbt);
name, FBT_ENTRY, 7, fbt);
fbt->fbtp_patchpoint = instr;
fbt->fbtp_ctl = lf;
fbt->fbtp_loadcnt = lf->loadcnt;
@ -210,7 +210,7 @@ fbt_provide_module_function(linker_file_t lf, int symindx,
if (retfbt == NULL) {
fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
name, FBT_RETURN, 5, fbt);
name, FBT_RETURN, 7, fbt);
} else {
retfbt->fbtp_next = fbt;
fbt->fbtp_id = retfbt->fbtp_id;