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-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"