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;
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to