svn commit: r271697 - in head/sys/cddl/dev: dtrace/powerpc fbt/powerpc

Justin Hibbits jhibbits at FreeBSD.org
Wed Sep 17 02:43:48 UTC 2014


Author: jhibbits
Date: Wed Sep 17 02:43:47 2014
New Revision: 271697
URL: http://svnweb.freebsd.org/changeset/base/271697

Log:
  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

Modified:
  head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
  head/sys/cddl/dev/fbt/powerpc/fbt_isa.c

Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
==============================================================================
--- head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c	Wed Sep 17 02:32:22 2014	(r271696)
+++ head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c	Wed Sep 17 02:43:47 2014	(r271697)
@@ -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 pcs
     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 pcs
 	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 pcs
 			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

Modified: head/sys/cddl/dev/fbt/powerpc/fbt_isa.c
==============================================================================
--- head/sys/cddl/dev/fbt/powerpc/fbt_isa.c	Wed Sep 17 02:32:22 2014	(r271696)
+++ head/sys/cddl/dev/fbt/powerpc/fbt_isa.c	Wed Sep 17 02:43:47 2014	(r271697)
@@ -147,7 +147,7 @@ fbt_provide_module_function(linker_file_
 	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 @@ again:
 
 	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;


More information about the svn-src-head mailing list