Author: jhibbits
Date: Mon Mar 18 05:30:18 2013
New Revision: 248457
URL: http://svnweb.freebsd.org/changeset/base/248457

Log:
  Add FBT for PowerPC DTrace.  Also, clean up the DTrace assembly code,
  much of which is not necessary for PowerPC.
  
  The FBT module can likely be factored into 3 separate files: common,
  intel, and powerpc, rather than duplicating most of the code between
  the x86 and PowerPC flavors.
  
  All DTrace modules for PowerPC will be MFC'd together once Fasttrap is
  completed.

Added:
  head/sys/cddl/dev/fbt/fbt_powerpc.c   (contents, props changed)
Modified:
  head/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h
  head/sys/cddl/dev/dtrace/powerpc/dtrace_asm.S
  head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
  head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c
  head/sys/modules/dtrace/Makefile
  head/sys/modules/dtrace/dtraceall/dtraceall.c
  head/sys/modules/dtrace/fbt/Makefile
  head/sys/powerpc/aim/trap.c
  head/sys/powerpc/aim/trap_subr32.S
  head/sys/powerpc/aim/trap_subr64.S

Modified: head/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h   Mon Mar 18 
04:46:17 2013        (r248456)
+++ head/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h   Mon Mar 18 
05:30:18 2013        (r248457)
@@ -2313,10 +2313,10 @@ extern int dtrace_mach_aframes(void);
 #if defined(__i386) || defined(__amd64)
 extern int dtrace_instr_size(uchar_t *instr);
 extern int dtrace_instr_size_isa(uchar_t *, model_t, int *);
-extern void dtrace_invop_add(int (*)(uintptr_t, uintptr_t *, uintptr_t));
-extern void dtrace_invop_remove(int (*)(uintptr_t, uintptr_t *, uintptr_t));
 extern void dtrace_invop_callsite(void);
 #endif
+extern void dtrace_invop_add(int (*)(uintptr_t, uintptr_t *, uintptr_t));
+extern void dtrace_invop_remove(int (*)(uintptr_t, uintptr_t *, uintptr_t));
 
 #ifdef __sparc
 extern int dtrace_blksuword32(uintptr_t, uint32_t *, int);
@@ -2349,6 +2349,15 @@ extern void dtrace_helpers_destroy(proc_
 #define        DTRACE_INVOP_NOP                4
 #define        DTRACE_INVOP_RET                5
 
+#elif defined(__powerpc__)
+
+#define DTRACE_INVOP_RET       1
+#define DTRACE_INVOP_BCTR      2
+#define DTRACE_INVOP_BLR       3
+#define DTRACE_INVOP_JUMP      4
+#define DTRACE_INVOP_MFLR_R0   5
+#define DTRACE_INVOP_NOP       6
+
 #endif
 
 #ifdef __cplusplus

Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_asm.S
==============================================================================
--- head/sys/cddl/dev/dtrace/powerpc/dtrace_asm.S       Mon Mar 18 04:46:17 
2013        (r248456)
+++ head/sys/cddl/dev/dtrace/powerpc/dtrace_asm.S       Mon Mar 18 05:30:18 
2013        (r248457)
@@ -85,10 +85,10 @@ ASENTRY_NOPROF(dtrace_cas32)
 1:
        lwarx   %r0,0,%r3
        cmpw    %r4,%r0
-       bne             2f
+       bne     2f
        stwcx.  %r5,0,%r3
-       bne             1b
-2:     mr              %r3,%r0
+       bne     1b
+2:     mr      %r3,%r0
        blr
 END(dtrace_cas32)
 
@@ -100,22 +100,15 @@ ASENTRY_NOPROF(dtrace_casptr)
 1:
        lwarx   %r0,0,%r3
        cmpw    %r4,%r0
-       bne             2f
+       bne     2f
        stwcx.  %r5,0,%r3
-       bne             1b
-2:     mr              %r3,%r0
+       bne     1b
+2:     mr      %r3,%r0
        blr
 END(dtrace_casptr)
 
 
 /*
-uintptr_t
-dtrace_fulword(void *addr)
-*/
-ASENTRY_NOPROF(dtrace_fulword)
-END(dtrace_fulword)
-
-/*
 XXX: unoptimized
 void
 dtrace_copy(uintptr_t src, uintptr_t dest, size_t size)
@@ -127,7 +120,7 @@ ASENTRY_NOPROF(dtrace_copy)
        lbzu    %r3,1(%r7)
        stbu    %r3,1(%r8)
        addme   %r5,%r5
-       beq             2f
+       beq     2f
 2:
        blr
 END(dtrace_copy)
@@ -144,42 +137,19 @@ ASENTRY_NOPROF(dtrace_copystr)
        lbzu    %r3,1(%r7)
        stbu    %r3,1(%r8)
        addme   %r5,%r5
-       beq             2f
-       or              %r3,%r3,%r3
-       beq             2f
+       beq     2f
+       or      %r3,%r3,%r3
+       beq     2f
        andi.   %r0,%r5,0x0fff
-       beq             2f
-       lwz             %r0,0(%r6)
+       beq     2f
+       lwz     %r0,0(%r6)
        andi.   %r0,%r0,CPU_DTRACE_BADADDR
-       beq             1b
+       beq     1b
 2:
        blr
 END(dtrace_copystr)
 
 /*
-void dtrace_invop_init(void)
-*/
-ASENTRY_NOPROF(dtrace_invop_init)
-       /* XXX: impement it properly -- implement dtrace_invop_start */
-       li              %r0,0
-       li              %r3,dtrace_invop_jump_addr@l
-       addis   %r3,%r3,dtrace_invop_jump_addr@ha
-       stw             %r0,0(%r3)
-       blr
-END(dtrace_invop_init)
-
-/*
-void dtrace_invop_uninit(void)
-*/
-ASENTRY_NOPROF(dtrace_invop_uninit)
-       li              %r0,0
-       li              %r3,dtrace_invop_jump_addr@l
-       addis   %r3,%r3,dtrace_invop_jump_addr@ha
-       stw             %r0,0(%r3)
-       blr
-END(dtrace_invop_uninit)
-
-/*
  * The panic() and cmn_err() functions invoke vpanic() as a common entry point
  * into the panic code implemented in panicsys().  vpanic() is responsible
  * for passing through the format string and arguments, and constructing a

Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
==============================================================================
--- head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c       Mon Mar 18 04:46:17 
2013        (r248456)
+++ head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c       Mon Mar 18 05:30:18 
2013        (r248457)
@@ -567,3 +567,17 @@ dtrace_fuword64(void *uaddr)
        }
        return ret;
 }
+
+uintptr_t
+dtrace_fulword(void *uaddr)
+{
+       uintptr_t ret = 0;
+
+       if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
+               if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
+                       DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+                       cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+               }
+       }
+       return ret;
+}

Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c
==============================================================================
--- head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c      Mon Mar 18 04:46:17 
2013        (r248456)
+++ head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c      Mon Mar 18 05:30:18 
2013        (r248457)
@@ -49,8 +49,11 @@ __FBSDID("$FreeBSD$");
 extern uintptr_t       dtrace_in_probe_addr;
 extern int             dtrace_in_probe;
 extern dtrace_id_t     dtrace_probeid_error;
+extern int (*dtrace_invop_jump_addr)(struct trapframe *);
 
 int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
+void dtrace_invop_init(void);
+void dtrace_invop_uninit(void);
 
 typedef struct dtrace_invop_hdlr {
        int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t);
@@ -72,6 +75,44 @@ dtrace_invop(uintptr_t addr, uintptr_t *
        return (0);
 }
 
+void
+dtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
+{
+       dtrace_invop_hdlr_t *hdlr;
+
+       hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
+       hdlr->dtih_func = func;
+       hdlr->dtih_next = dtrace_invop_hdlr;
+       dtrace_invop_hdlr = hdlr;
+}
+
+void
+dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
+{
+       dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
+
+       for (;;) {
+               if (hdlr == NULL)
+                       panic("attempt to remove non-existent invop handler");
+
+               if (hdlr->dtih_func == func)
+                       break;
+
+               prev = hdlr;
+               hdlr = hdlr->dtih_next;
+       }
+
+       if (prev == NULL) {
+               ASSERT(dtrace_invop_hdlr == hdlr);
+               dtrace_invop_hdlr = hdlr->dtih_next;
+       } else {
+               ASSERT(dtrace_invop_hdlr != hdlr);
+               prev->dtih_next = hdlr->dtih_next;
+       }
+
+       kmem_free(hdlr, 0);
+}
+
 
 /*ARGSUSED*/
 void
@@ -199,3 +240,36 @@ dtrace_probe_error(dtrace_state_t *state
            (uintptr_t)epid,
            (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
 }
+
+static int
+dtrace_invop_start(struct trapframe *frame)
+{
+       switch (dtrace_invop(frame->srr0, (uintptr_t *)frame, 
frame->fixreg[3])) {
+       case DTRACE_INVOP_JUMP:
+               break;
+       case DTRACE_INVOP_BCTR:
+               frame->srr0 = frame->ctr;
+               break;
+       case DTRACE_INVOP_BLR:
+               frame->srr0 = frame->lr;
+               break;
+       case DTRACE_INVOP_MFLR_R0:
+               frame->fixreg[0] = frame->lr ;
+               break;
+       default:
+               return (-1);
+               break;
+       }
+
+       return (0);
+}
+
+void dtrace_invop_init(void)
+{
+       dtrace_invop_jump_addr = dtrace_invop_start;
+}
+
+void dtrace_invop_uninit(void)
+{
+       dtrace_invop_jump_addr = 0;
+}

Added: head/sys/cddl/dev/fbt/fbt_powerpc.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/cddl/dev/fbt/fbt_powerpc.c Mon Mar 18 05:30:18 2013        
(r248457)
@@ -0,0 +1,1321 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Portions Copyright 2006-2008 John Birrell j...@freebsd.org
+ * Portions Copyright 2013 Justin Hibbits jhibb...@freebsd.org
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/cpuvar.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/kthread.h>
+#include <sys/limits.h>
+#include <sys/linker.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/pcpu.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/selinfo.h>
+#include <sys/smp.h>
+#include <sys/syscall.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/uio.h>
+#include <sys/unistd.h>
+#include <machine/stdarg.h>
+
+#include <sys/dtrace.h>
+#include <sys/dtrace_bsd.h>
+
+static MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing");
+
+#define FBT_PATCHVAL           0x7c810808
+#define FBT_MFLR_R0            0x7c0802a6
+#define FBT_MTLR_R0            0x7c0803a6
+#define FBT_BLR                        0x4e800020
+#define FBT_BCTR               0x4e800030
+#define FBT_BRANCH             0x48000000
+#define FBT_BR_MASK            0x03fffffc
+#define FBT_IS_JUMP(instr)     ((instr & ~FBT_BR_MASK) == FBT_BRANCH)
+
+static d_open_t        fbt_open;
+static int     fbt_unload(void);
+static void    fbt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
+static void    fbt_provide_module(void *, modctl_t *);
+static void    fbt_destroy(void *, dtrace_id_t, void *);
+static void    fbt_enable(void *, dtrace_id_t, void *);
+static void    fbt_disable(void *, dtrace_id_t, void *);
+static void    fbt_load(void *);
+static void    fbt_suspend(void *, dtrace_id_t, void *);
+static void    fbt_resume(void *, dtrace_id_t, void *);
+
+#define        FBT_ENTRY       "entry"
+#define        FBT_RETURN      "return"
+#define        FBT_ADDR2NDX(addr)      ((((uintptr_t)(addr)) >> 4) & 
fbt_probetab_mask)
+#define        FBT_PROBETAB_SIZE       0x8000          /* 32k entries -- 128K 
total */
+
+static struct cdevsw fbt_cdevsw = {
+       .d_version      = D_VERSION,
+       .d_open         = fbt_open,
+       .d_name         = "fbt",
+};
+
+static dtrace_pattr_t fbt_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pops_t fbt_pops = {
+       NULL,
+       fbt_provide_module,
+       fbt_enable,
+       fbt_disable,
+       fbt_suspend,
+       fbt_resume,
+       fbt_getargdesc,
+       NULL,
+       NULL,
+       fbt_destroy
+};
+
+typedef struct fbt_probe {
+       struct fbt_probe *fbtp_hashnext;
+       uint32_t        *fbtp_patchpoint;
+       int8_t          fbtp_rval;
+       uint32_t        fbtp_patchval;
+       uint32_t        fbtp_savedval;
+       uintptr_t       fbtp_roffset;
+       dtrace_id_t     fbtp_id;
+       const char      *fbtp_name;
+       modctl_t        *fbtp_ctl;
+       int             fbtp_loadcnt;
+       int             fbtp_primary;
+       int             fbtp_invop_cnt;
+       int             fbtp_symindx;
+       struct fbt_probe *fbtp_next;
+} fbt_probe_t;
+
+static struct cdev             *fbt_cdev;
+static dtrace_provider_id_t    fbt_id;
+static fbt_probe_t             **fbt_probetab;
+static int                     fbt_probetab_size;
+static int                     fbt_probetab_mask;
+static int                     fbt_verbose = 0;
+
+static int
+fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
+{
+       struct trapframe *frame = (struct trapframe *)stack;
+       solaris_cpu_t *cpu = &solaris_cpu[curcpu];
+       fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
+       uintptr_t tmp;
+
+       for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
+               if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
+                       fbt->fbtp_invop_cnt++;
+                       if (fbt->fbtp_roffset == 0) {
+                               cpu->cpu_dtrace_caller = addr;
+
+                               dtrace_probe(fbt->fbtp_id, frame->fixreg[3],
+                                   frame->fixreg[4], frame->fixreg[5],
+                                   frame->fixreg[6], frame->fixreg[7]);
+
+                               cpu->cpu_dtrace_caller = 0;
+                       } else {
+
+                               dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
+                                   rval, 0, 0, 0);
+                               /*
+                                * The caller doesn't have the fbt item, so
+                                * fixup tail calls here.
+                                */
+                               if (fbt->fbtp_rval == DTRACE_INVOP_JUMP) {
+                                       frame->srr0 = 
(uintptr_t)fbt->fbtp_patchpoint;
+                                       tmp = fbt->fbtp_savedval & FBT_BR_MASK;
+                                       /* Sign extend. */
+                                       if (tmp & 0x02000000)
+                                               tmp |= 0xFC000000;
+                                       frame->srr0 += tmp;
+                               }
+                               cpu->cpu_dtrace_caller = 0;
+                       }
+
+                       return (fbt->fbtp_rval);
+               }
+       }
+
+       return (0);
+}
+
+static int
+fbt_provide_module_function(linker_file_t lf, int symindx,
+    linker_symval_t *symval, void *opaque)
+{
+       char *modname = opaque;
+       const char *name = symval->name;
+       fbt_probe_t *fbt, *retfbt;
+       int j;
+       int size;
+       u_int32_t *instr, *limit;
+
+       if (strncmp(name, "dtrace_", 7) == 0 &&
+           strncmp(name, "dtrace_safe_", 12) != 0) {
+               /*
+                * Anything beginning with "dtrace_" may be called
+                * from probe context unless it explicitly indicates
+                * that it won't be called from probe context by
+                * using the prefix "dtrace_safe_".
+                */
+               return (0);
+       }
+
+       if (name[0] == '_' && name[1] == '_')
+               return (0);
+
+       size = symval->size;
+
+       instr = (u_int32_t *) symval->value;
+       limit = (u_int32_t *) symval->value + symval->size;
+
+       for (; instr < limit; instr++)
+               if (*instr == FBT_MFLR_R0)
+                       break;
+
+       if (*instr != FBT_MFLR_R0);
+               return (0);
+
+       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);
+       fbt->fbtp_patchpoint = instr;
+       fbt->fbtp_ctl = lf;
+       fbt->fbtp_loadcnt = lf->loadcnt;
+       fbt->fbtp_savedval = *instr;
+       fbt->fbtp_patchval = FBT_PATCHVAL;
+       fbt->fbtp_rval = DTRACE_INVOP_MFLR_R0;
+       fbt->fbtp_symindx = symindx;
+
+       fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
+       fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
+
+       lf->fbt_nentries++;
+
+       retfbt = NULL;
+again:
+       if (instr >= limit)
+               return (0);
+
+       /*
+        * We (desperately) want to avoid erroneously instrumenting a
+        * jump table To determine if we're looking at a true instruction
+        * sequence or an inline jump table that happens to contain the same
+        * byte sequences, we resort to some heuristic sleeze:  we treat this
+        * instruction as being contained within a pointer, and see if that
+        * pointer points to within the body of the function.  If it does, we
+        * refuse to instrument it.
+        */
+       {
+               uint32_t *ptr;
+
+               ptr = *(uint32_t **)instr;
+
+               if (ptr >= (uint32_t *) symval->value && ptr < limit) {
+                       instr++;
+                       goto again;
+               }
+       }
+
+       if (*instr == FBT_MFLR_R0)
+               return (0);
+
+       if (*instr != FBT_MTLR_R0) {
+               instr++;
+               goto again;
+       }
+
+       instr++;
+
+       for (j = 0; j < 12 && instr < limit; j++, instr++) {
+               if ((*instr == FBT_BCTR) || (*instr == FBT_BLR) |
+                   FBT_IS_JUMP(*instr))
+                       break;
+       }
+
+       if (!(*instr == FBT_BCTR || *instr == FBT_BLR || FBT_IS_JUMP(*instr)))
+               goto again;
+
+       /*
+        * We have a winner!
+        */
+       fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
+       fbt->fbtp_name = name;
+
+       if (retfbt == NULL) {
+               fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
+                   name, FBT_RETURN, 3, fbt);
+       } else {
+               retfbt->fbtp_next = fbt;
+               fbt->fbtp_id = retfbt->fbtp_id;
+       }
+
+       retfbt = fbt;
+       fbt->fbtp_patchpoint = instr;
+       fbt->fbtp_ctl = lf;
+       fbt->fbtp_loadcnt = lf->loadcnt;
+       fbt->fbtp_symindx = symindx;
+
+       if (*instr == FBT_BCTR)
+               fbt->fbtp_rval = DTRACE_INVOP_BCTR;
+       else if (*instr == FBT_BLR)
+               fbt->fbtp_rval = DTRACE_INVOP_RET;
+       else
+               fbt->fbtp_rval = DTRACE_INVOP_JUMP;
+
+       fbt->fbtp_savedval = *instr;
+       fbt->fbtp_patchval = FBT_PATCHVAL;
+       fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
+       fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
+
+       lf->fbt_nentries++;
+
+       instr += size;
+       goto again;
+}
+
+static void
+fbt_provide_module(void *arg, modctl_t *lf)
+{
+       char modname[MAXPATHLEN];
+       int i;
+       size_t len;
+
+       strlcpy(modname, lf->filename, sizeof(modname));
+       len = strlen(modname);
+       if (len > 3 && strcmp(modname + len - 3, ".ko") == 0)
+               modname[len - 3] = '\0';
+
+       /*
+        * Employees of dtrace and their families are ineligible.  Void
+        * where prohibited.
+        */
+       if (strcmp(modname, "dtrace") == 0)
+               return;
+
+       /*
+        * The cyclic timer subsystem can be built as a module and DTrace
+        * depends on that, so it is ineligible too.
+        */
+       if (strcmp(modname, "cyclic") == 0)
+               return;
+
+       /*
+        * To register with DTrace, a module must list 'dtrace' as a
+        * dependency in order for the kernel linker to resolve
+        * symbols like dtrace_register(). All modules with such a
+        * dependency are ineligible for FBT tracing.
+        */
+       for (i = 0; i < lf->ndeps; i++)
+               if (strncmp(lf->deps[i]->filename, "dtrace", 6) == 0)
+                       return;
+
+       if (lf->fbt_nentries) {
+               /*
+                * This module has some FBT entries allocated; we're afraid
+                * to screw with it.
+                */
+               return;
+       }
+
+       /*
+        * List the functions in the module and the symbol values.
+        */
+       (void) linker_file_function_listall(lf, fbt_provide_module_function, 
modname);
+}
+
+static void
+fbt_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+       fbt_probe_t *fbt = parg, *next, *hash, *last;
+       modctl_t *ctl;
+       int ndx;
+
+       do {
+               ctl = fbt->fbtp_ctl;
+
+               ctl->fbt_nentries--;
+
+               /*
+                * Now we need to remove this probe from the fbt_probetab.
+                */
+               ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
+               last = NULL;
+               hash = fbt_probetab[ndx];
+
+               while (hash != fbt) {
+                       ASSERT(hash != NULL);
+                       last = hash;
+                       hash = hash->fbtp_hashnext;
+               }
+
+               if (last != NULL) {
+                       last->fbtp_hashnext = fbt->fbtp_hashnext;
+               } else {
+                       fbt_probetab[ndx] = fbt->fbtp_hashnext;
+               }
+
+               next = fbt->fbtp_next;
+               free(fbt, M_FBT);
+
+               fbt = next;
+       } while (fbt != NULL);
+}
+
+static void
+fbt_enable(void *arg, dtrace_id_t id, void *parg)
+{
+       fbt_probe_t *fbt = parg;
+       modctl_t *ctl = fbt->fbtp_ctl;
+
+       ctl->nenabled++;
+
+       /*
+        * Now check that our modctl has the expected load count.  If it
+        * doesn't, this module must have been unloaded and reloaded -- and
+        * we're not going to touch it.
+        */
+       if (ctl->loadcnt != fbt->fbtp_loadcnt) {
+               if (fbt_verbose) {
+                       printf("fbt is failing for probe %s "
+                           "(module %s reloaded)",
+                           fbt->fbtp_name, ctl->filename);
+               }
+
+               return;
+       }
+
+       for (; fbt != NULL; fbt = fbt->fbtp_next) {
+               *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
+       }
+}
+
+static void
+fbt_disable(void *arg, dtrace_id_t id, void *parg)
+{
+       fbt_probe_t *fbt = parg;
+       modctl_t *ctl = fbt->fbtp_ctl;
+
+       ASSERT(ctl->nenabled > 0);
+       ctl->nenabled--;
+
+       if ((ctl->loadcnt != fbt->fbtp_loadcnt))
+               return;
+
+       for (; fbt != NULL; fbt = fbt->fbtp_next)
+               *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
+}
+
+static void
+fbt_suspend(void *arg, dtrace_id_t id, void *parg)
+{
+       fbt_probe_t *fbt = parg;
+       modctl_t *ctl = fbt->fbtp_ctl;
+
+       ASSERT(ctl->nenabled > 0);
+
+       if ((ctl->loadcnt != fbt->fbtp_loadcnt))
+               return;
+
+       for (; fbt != NULL; fbt = fbt->fbtp_next)
+               *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
+}
+
+static void
+fbt_resume(void *arg, dtrace_id_t id, void *parg)
+{
+       fbt_probe_t *fbt = parg;
+       modctl_t *ctl = fbt->fbtp_ctl;
+
+       ASSERT(ctl->nenabled > 0);
+
+       if ((ctl->loadcnt != fbt->fbtp_loadcnt))
+               return;
+
+       for (; fbt != NULL; fbt = fbt->fbtp_next)
+               *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
+}
+
+static int
+fbt_ctfoff_init(modctl_t *lf, linker_ctf_t *lc)
+{
+       const Elf_Sym *symp = lc->symtab;;
+       const char *name;
+       const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
+       const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
+       int i;
+       uint32_t *ctfoff;
+       uint32_t objtoff = hp->cth_objtoff;
+       uint32_t funcoff = hp->cth_funcoff;
+       ushort_t info;
+       ushort_t vlen;
+
+       /* Sanity check. */
+       if (hp->cth_magic != CTF_MAGIC) {
+               printf("Bad magic value in CTF data of '%s'\n",lf->pathname);
+               return (EINVAL);
+       }
+
+       if (lc->symtab == NULL) {
+               printf("No symbol table in '%s'\n",lf->pathname);
+               return (EINVAL);
+       }
+
+       if ((ctfoff = malloc(sizeof(uint32_t) * lc->nsym, M_LINKER, M_WAITOK)) 
== NULL)
+               return (ENOMEM);
+
+       *lc->ctfoffp = ctfoff;
+
+       for (i = 0; i < lc->nsym; i++, ctfoff++, symp++) {
+               if (symp->st_name == 0 || symp->st_shndx == SHN_UNDEF) {
+                       *ctfoff = 0xffffffff;
+                       continue;
+               }
+
+               if (symp->st_name < lc->strcnt)
+                       name = lc->strtab + symp->st_name;
+               else
+                       name = "(?)";
+
+               switch (ELF_ST_TYPE(symp->st_info)) {
+               case STT_OBJECT:
+                       if (objtoff >= hp->cth_funcoff ||
+                            (symp->st_shndx == SHN_ABS && symp->st_value == 
0)) {
+                               *ctfoff = 0xffffffff;
+                                break;
+                        }
+
+                        *ctfoff = objtoff;
+                        objtoff += sizeof (ushort_t);
+                       break;
+
+               case STT_FUNC:
+                       if (funcoff >= hp->cth_typeoff) {
+                               *ctfoff = 0xffffffff;
+                               break;
+                       }
+
+                       *ctfoff = funcoff;
+
+                       info = *((const ushort_t *)(ctfdata + funcoff));
+                       vlen = CTF_INFO_VLEN(info);
+
+                       /*
+                        * If we encounter a zero pad at the end, just skip it.
+                        * Otherwise skip over the function and its return type
+                        * (+2) and the argument list (vlen).
+                        */
+                       if (CTF_INFO_KIND(info) == CTF_K_UNKNOWN && vlen == 0)
+                               funcoff += sizeof (ushort_t); /* skip pad */
+                       else
+                               funcoff += sizeof (ushort_t) * (vlen + 2);
+                       break;
+
+               default:
+                       *ctfoff = 0xffffffff;
+                       break;
+               }
+       }
+
+       return (0);
+}
+
+static ssize_t
+fbt_get_ctt_size(uint8_t version, const ctf_type_t *tp, ssize_t *sizep,
+    ssize_t *incrementp)
+{
+       ssize_t size, increment;
+
+       if (version > CTF_VERSION_1 &&
+           tp->ctt_size == CTF_LSIZE_SENT) {
+               size = CTF_TYPE_LSIZE(tp);
+               increment = sizeof (ctf_type_t);
+       } else {
+               size = tp->ctt_size;
+               increment = sizeof (ctf_stype_t);
+       }
+
+       if (sizep)
+               *sizep = size;
+       if (incrementp)
+               *incrementp = increment;
+
+       return (size);
+}
+
+static int
+fbt_typoff_init(linker_ctf_t *lc)
+{
+       const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
+       const ctf_type_t *tbuf;
+       const ctf_type_t *tend;
+       const ctf_type_t *tp;
+       const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
+       int ctf_typemax = 0;
+       uint32_t *xp;
+       ulong_t pop[CTF_K_MAX + 1] = { 0 };
+
+
+       /* Sanity check. */
+       if (hp->cth_magic != CTF_MAGIC)
+               return (EINVAL);
+
+       tbuf = (const ctf_type_t *) (ctfdata + hp->cth_typeoff);
+       tend = (const ctf_type_t *) (ctfdata + hp->cth_stroff);
+
+       int child = hp->cth_parname != 0;
+
+       /*
+        * We make two passes through the entire type section.  In this first
+        * pass, we count the number of each type and the total number of types.
+        */
+       for (tp = tbuf; tp < tend; ctf_typemax++) {
+               ushort_t kind = CTF_INFO_KIND(tp->ctt_info);
+               ulong_t vlen = CTF_INFO_VLEN(tp->ctt_info);
+               ssize_t size, increment;
+
+               size_t vbytes;
+               uint_t n;
+
+               (void) fbt_get_ctt_size(hp->cth_version, tp, &size, &increment);
+
+               switch (kind) {
+               case CTF_K_INTEGER:
+               case CTF_K_FLOAT:
+                       vbytes = sizeof (uint_t);
+                       break;
+               case CTF_K_ARRAY:
+                       vbytes = sizeof (ctf_array_t);
+                       break;
+               case CTF_K_FUNCTION:
+                       vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
+                       break;
+               case CTF_K_STRUCT:
+               case CTF_K_UNION:
+                       if (size < CTF_LSTRUCT_THRESH) {
+                               ctf_member_t *mp = (ctf_member_t *)
+                                   ((uintptr_t)tp + increment);
+
+                               vbytes = sizeof (ctf_member_t) * vlen;
+                               for (n = vlen; n != 0; n--, mp++)
+                                       child |= CTF_TYPE_ISCHILD(mp->ctm_type);
+                       } else {
+                               ctf_lmember_t *lmp = (ctf_lmember_t *)
+                                   ((uintptr_t)tp + increment);
+
+                               vbytes = sizeof (ctf_lmember_t) * vlen;
+                               for (n = vlen; n != 0; n--, lmp++)
+                                       child |=
+                                           CTF_TYPE_ISCHILD(lmp->ctlm_type);
+                       }
+                       break;
+               case CTF_K_ENUM:
+                       vbytes = sizeof (ctf_enum_t) * vlen;
+                       break;
+               case CTF_K_FORWARD:
+                       /*
+                        * For forward declarations, ctt_type is the CTF_K_*
+                        * kind for the tag, so bump that population count too.
+                        * If ctt_type is unknown, treat the tag as a struct.
+                        */
+                       if (tp->ctt_type == CTF_K_UNKNOWN ||
+                           tp->ctt_type >= CTF_K_MAX)
+                               pop[CTF_K_STRUCT]++;
+                       else
+                               pop[tp->ctt_type]++;
+                       /*FALLTHRU*/
+               case CTF_K_UNKNOWN:
+                       vbytes = 0;
+                       break;
+               case CTF_K_POINTER:
+               case CTF_K_TYPEDEF:
+               case CTF_K_VOLATILE:
+               case CTF_K_CONST:
+               case CTF_K_RESTRICT:
+                       child |= CTF_TYPE_ISCHILD(tp->ctt_type);
+                       vbytes = 0;
+                       break;
+               default:
+                       printf("%s(%d): detected invalid CTF kind -- %u\n", 
__func__, __LINE__, kind);
+                       return (EIO);
+               }
+               tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
+               pop[kind]++;
+       }
+
+       *lc->typlenp = ctf_typemax;
+
+       if ((xp = malloc(sizeof(uint32_t) * ctf_typemax, M_LINKER, M_ZERO | 
M_WAITOK)) == NULL)
+               return (ENOMEM);
+
+       *lc->typoffp = xp;
+
+       /* type id 0 is used as a sentinel value */
+       *xp++ = 0;
+
+       /*
+        * In the second pass, fill in the type offset.
+        */
+       for (tp = tbuf; tp < tend; xp++) {
+               ushort_t kind = CTF_INFO_KIND(tp->ctt_info);
+               ulong_t vlen = CTF_INFO_VLEN(tp->ctt_info);
+               ssize_t size, increment;
+
+               size_t vbytes;
+               uint_t n;
+
+               (void) fbt_get_ctt_size(hp->cth_version, tp, &size, &increment);
+
+               switch (kind) {
+               case CTF_K_INTEGER:
+               case CTF_K_FLOAT:
+                       vbytes = sizeof (uint_t);
+                       break;
+               case CTF_K_ARRAY:
+                       vbytes = sizeof (ctf_array_t);
+                       break;
+               case CTF_K_FUNCTION:
+                       vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
+                       break;
+               case CTF_K_STRUCT:
+               case CTF_K_UNION:
+                       if (size < CTF_LSTRUCT_THRESH) {
+                               ctf_member_t *mp = (ctf_member_t *)
+                                   ((uintptr_t)tp + increment);
+
+                               vbytes = sizeof (ctf_member_t) * vlen;
+                               for (n = vlen; n != 0; n--, mp++)
+                                       child |= CTF_TYPE_ISCHILD(mp->ctm_type);
+                       } else {
+                               ctf_lmember_t *lmp = (ctf_lmember_t *)
+                                   ((uintptr_t)tp + increment);
+
+                               vbytes = sizeof (ctf_lmember_t) * vlen;
+                               for (n = vlen; n != 0; n--, lmp++)
+                                       child |=
+                                           CTF_TYPE_ISCHILD(lmp->ctlm_type);
+                       }
+                       break;
+               case CTF_K_ENUM:
+                       vbytes = sizeof (ctf_enum_t) * vlen;
+                       break;
+               case CTF_K_FORWARD:
+               case CTF_K_UNKNOWN:

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to