From owner-svn-src-all@FreeBSD.ORG Sat Aug 31 16:30:21 2013 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id CB22BABA; Sat, 31 Aug 2013 16:30:21 +0000 (UTC) (envelope-from jhibbits@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id B81B329D7; Sat, 31 Aug 2013 16:30:21 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r7VGULpU082445; Sat, 31 Aug 2013 16:30:21 GMT (envelope-from jhibbits@svn.freebsd.org) Received: (from jhibbits@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r7VGULKJ082442; Sat, 31 Aug 2013 16:30:21 GMT (envelope-from jhibbits@svn.freebsd.org) Message-Id: <201308311630.r7VGULKJ082442@svn.freebsd.org> From: Justin Hibbits Date: Sat, 31 Aug 2013 16:30:21 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r255099 - in head/sys/cddl/dev: dtrace/powerpc fbt X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 31 Aug 2013 16:30:21 -0000 Author: jhibbits Date: Sat Aug 31 16:30:20 2013 New Revision: 255099 URL: http://svnweb.freebsd.org/changeset/base/255099 Log: Fixes for DTrace on PowerPC: - Implement dtrace_getarg() - Sync fbt with x86, and fix a typo. - Pull in the time synchronization code from amd64. Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c head/sys/cddl/dev/fbt/fbt_powerpc.c Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c ============================================================================== --- head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c Sat Aug 31 16:21:13 2013 (r255098) +++ head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c Sat Aug 31 16:30:20 2013 (r255099) @@ -349,50 +349,84 @@ zero: uint64_t dtrace_getarg(int arg, int aframes) { - return (0); -} - -#ifdef notyet -{ - int depth = 0; - register_t sp; - vm_offset_t callpc; - pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller; - - if (intrpc != 0) - pcstack[depth++] = (pc_t) intrpc; - - aframes++; - - sp = dtrace_getfp(); - - while (depth < pcstack_limit) { - if (!INKERNEL((long) frame)) - break; - - callpc = *(void **)(sp + RETURN_OFFSET); - - if (!INKERNEL(callpc)) - break; + uintptr_t val; + uintptr_t *fp = (uintptr_t *)dtrace_getfp(); + uintptr_t *stack; + int i; + + /* + * A total of 8 arguments are passed via registers; any argument with + * index of 7 or lower is therefore in a register. + */ + int inreg = 7; + + for (i = 1; i <= aframes; i++) { + fp = (uintptr_t *)*fp; + + /* + * 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))) { + /* + * 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 + * structure, we must increment beyond the frame structure. If the + * argument that we're seeking is passed on the stack, we'll pull + * the true stack pointer out of the saved registers and decrement + * our argument by the number of arguments passed in registers; if + * the argument we're seeking is passed in regsiters, we can just + * load it directly. + */ +#ifdef __powerpc64__ + struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 48); +#else + struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 8); +#endif - if (aframes > 0) { - aframes--; - if ((aframes == 0) && (caller != 0)) { - pcstack[depth++] = caller; + if (arg <= inreg) { + stack = &rp->fixreg[3]; + } else { + stack = (uintptr_t *)(rp->fixreg[1]); + arg -= inreg; } - } - else { - pcstack[depth++] = callpc; + goto load; } - sp = *(void **)sp; } - for (; depth < pcstack_limit; depth++) { - pcstack[depth] = 0; + /* + * We know that we did not come through a trap to get into + * dtrace_probe() -- the provider simply called dtrace_probe() + * directly. As this is the case, we need to shift the argument + * that we're looking for: the probe ID is the first argument to + * dtrace_probe(), so the argument n will actually be found where + * one would expect to find argument (n + 1). + */ + arg++; + + if (arg <= inreg) { + /* + * This shouldn't happen. If the argument is passed in a + * register then it should have been, well, passed in a + * register... + */ + DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); + return (0); } + + arg -= (inreg + 1); + stack = fp + 2; + +load: + DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT); + val = stack[arg]; + DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT); + + return (val); + return (0); } -#endif int dtrace_getstackdepth(int aframes) Modified: head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c ============================================================================== --- head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c Sat Aug 31 16:21:13 2013 (r255098) +++ head/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c Sat Aug 31 16:30:20 2013 (r255099) @@ -51,6 +51,8 @@ extern int dtrace_in_probe; extern dtrace_id_t dtrace_probeid_error; extern int (*dtrace_invop_jump_addr)(struct trapframe *); +extern void dtrace_getnanotime(struct timespec *tsp); + int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t); void dtrace_invop_init(void); void dtrace_invop_uninit(void); @@ -63,13 +65,13 @@ typedef struct dtrace_invop_hdlr { dtrace_invop_hdlr_t *dtrace_invop_hdlr; int -dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t eax) +dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t arg0) { dtrace_invop_hdlr_t *hdlr; int rval; for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) - if ((rval = hdlr->dtih_func(addr, stack, eax)) != 0) + if ((rval = hdlr->dtih_func(addr, stack, arg0)) != 0) return (rval); return (0); @@ -134,7 +136,7 @@ dtrace_xcall(processorid_t cpu, dtrace_x CPU_SETOF(cpu, &cpus); smp_rendezvous_cpus(cpus, smp_no_rendevous_barrier, func, - smp_no_rendevous_barrier, arg); + smp_no_rendevous_barrier, arg); } static void @@ -145,9 +147,82 @@ dtrace_sync_func(void) void dtrace_sync(void) { - dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL); + dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL); +} + +static int64_t tgt_cpu_tsc; +static int64_t hst_cpu_tsc; +static int64_t timebase_skew[MAXCPU]; +static uint64_t nsec_scale; + +/* See below for the explanation of this macro. */ +/* This is taken from the amd64 dtrace_subr, to provide a synchronized timer + * between multiple processors in dtrace. Since PowerPC Timebases can be much + * lower than x86, the scale shift is 26 instead of 28, allowing for a 15.63MHz + * timebase. + */ +#define SCALE_SHIFT 26 + +static void +dtrace_gethrtime_init_cpu(void *arg) +{ + uintptr_t cpu = (uintptr_t) arg; + + if (cpu == curcpu) + tgt_cpu_tsc = mftb(); + else + hst_cpu_tsc = mftb(); +} + +static void +dtrace_gethrtime_init(void *arg) +{ + struct pcpu *pc; + uint64_t tb_f; + cpuset_t map; + int i; + + tb_f = cpu_tickrate(); + + /* + * The following line checks that nsec_scale calculated below + * doesn't overflow 32-bit unsigned integer, so that it can multiply + * another 32-bit integer without overflowing 64-bit. + * Thus minimum supported Timebase frequency is 15.63MHz. + */ + KASSERT(tb_f > (NANOSEC >> (32 - SCALE_SHIFT)), ("Timebase frequency is too low")); + + /* + * We scale up NANOSEC/tb_f ratio to preserve as much precision + * as possible. + * 2^26 factor was chosen quite arbitrarily from practical + * considerations: + * - it supports TSC frequencies as low as 15.63MHz (see above); + */ + nsec_scale = ((uint64_t)NANOSEC << SCALE_SHIFT) / tb_f; + + /* The current CPU is the reference one. */ + sched_pin(); + timebase_skew[curcpu] = 0; + CPU_FOREACH(i) { + if (i == curcpu) + continue; + + pc = pcpu_find(i); + CPU_SETOF(PCPU_GET(cpuid), &map); + CPU_SET(pc->pc_cpuid, &map); + + smp_rendezvous_cpus(map, NULL, + dtrace_gethrtime_init_cpu, + smp_no_rendevous_barrier, (void *)(uintptr_t) i); + + timebase_skew[i] = tgt_cpu_tsc - hst_cpu_tsc; + } + sched_unpin(); } +SYSINIT(dtrace_gethrtime_init, SI_SUB_SMP, SI_ORDER_ANY, dtrace_gethrtime_init, NULL); + /* * DTrace needs a high resolution time function which can * be called from a probe context and guaranteed not to have @@ -158,12 +233,21 @@ dtrace_sync(void) uint64_t dtrace_gethrtime() { - struct timespec curtime; - - nanouptime(&curtime); - - return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec); + uint64_t timebase; + uint32_t lo; + uint32_t hi; + /* + * We split timebase value into lower and higher 32-bit halves and separately + * scale them with nsec_scale, then we scale them down by 2^28 + * (see nsec_scale calculations) taking into account 32-bit shift of + * the higher half and finally add. + */ + timebase = mftb() - timebase_skew[curcpu]; + lo = timebase; + hi = timebase >> 32; + return (((lo * nsec_scale) >> SCALE_SHIFT) + + ((hi * nsec_scale) << (32 - SCALE_SHIFT))); } uint64_t @@ -171,12 +255,12 @@ dtrace_gethrestime(void) { struct timespec curtime; - getnanotime(&curtime); + dtrace_getnanotime(&curtime); return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec); } -/* Function to handle DTrace traps during probes. See amd64/amd64/trap.c */ +/* Function to handle DTrace traps during probes. See powerpc/powerpc/trap.c */ int dtrace_trap(struct trapframe *frame, u_int type) { @@ -196,34 +280,34 @@ dtrace_trap(struct trapframe *frame, u_i * All the rest will be handled in the usual way. */ switch (type) { - /* Page fault. */ - case EXC_DSI: - case EXC_DSE: - /* Flag a bad address. */ - cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR; - cpu_core[curcpu].cpuc_dtrace_illval = frame->cpu.aim.dar; - - /* - * Offset the instruction pointer to the instruction - * following the one causing the fault. - */ - frame->srr0 += sizeof(int); - return (1); - case EXC_ISI: - case EXC_ISE: - /* Flag a bad address. */ - cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR; - cpu_core[curcpu].cpuc_dtrace_illval = frame->srr0; - - /* - * Offset the instruction pointer to the instruction - * following the one causing the fault. - */ - frame->srr0 += sizeof(int); - return (1); - default: - /* Handle all other traps in the usual way. */ - break; + /* Page fault. */ + case EXC_DSI: + case EXC_DSE: + /* Flag a bad address. */ + cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR; + cpu_core[curcpu].cpuc_dtrace_illval = frame->cpu.aim.dar; + + /* + * Offset the instruction pointer to the instruction + * following the one causing the fault. + */ + frame->srr0 += sizeof(int); + return (1); + case EXC_ISI: + case EXC_ISE: + /* Flag a bad address. */ + cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR; + cpu_core[curcpu].cpuc_dtrace_illval = frame->srr0; + + /* + * Offset the instruction pointer to the instruction + * following the one causing the fault. + */ + frame->srr0 += sizeof(int); + return (1); + default: + /* Handle all other traps in the usual way. */ + break; } } @@ -237,28 +321,29 @@ dtrace_probe_error(dtrace_state_t *state { dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state, - (uintptr_t)epid, - (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs); + (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; + 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; + frame->srr0 = frame->srr0 + 4; + break; + default: + return (-1); + break; } return (0); Modified: head/sys/cddl/dev/fbt/fbt_powerpc.c ============================================================================== --- head/sys/cddl/dev/fbt/fbt_powerpc.c Sat Aug 31 16:21:13 2013 (r255098) +++ head/sys/cddl/dev/fbt/fbt_powerpc.c Sat Aug 31 16:30:20 2013 (r255099) @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -172,7 +173,11 @@ fbt_invop(uintptr_t addr, uintptr_t *sta tmp = fbt->fbtp_savedval & FBT_BR_MASK; /* Sign extend. */ if (tmp & 0x02000000) - tmp |= 0xFC000000; +#ifdef __powerpc64__ + tmp |= 0xfffffffffc000000ULL; +#else + tmp |= 0xfc000000UL; +#endif frame->srr0 += tmp; } cpu->cpu_dtrace_caller = 0; @@ -193,9 +198,12 @@ fbt_provide_module_function(linker_file_ const char *name = symval->name; fbt_probe_t *fbt, *retfbt; int j; - int size; u_int32_t *instr, *limit; + /* PowerPC64 uses '.' prefixes on symbol names, ignore it. */ + if (name[0] == '.') + name++; + if (strncmp(name, "dtrace_", 7) == 0 && strncmp(name, "dtrace_safe_", 12) != 0) { /* @@ -210,8 +218,6 @@ fbt_provide_module_function(linker_file_ if (name[0] == '_' && name[1] == '_') return (0); - size = symval->size; - instr = (u_int32_t *) symval->value; limit = (u_int32_t *) symval->value + symval->size; @@ -219,7 +225,7 @@ fbt_provide_module_function(linker_file_ if (*instr == FBT_MFLR_R0) break; - if (*instr != FBT_MFLR_R0); + if (*instr != FBT_MFLR_R0) return (0); fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO); @@ -264,9 +270,6 @@ again: } } - if (*instr == FBT_MFLR_R0) - return (0); - if (*instr != FBT_MTLR_R0) { instr++; goto again; @@ -291,7 +294,7 @@ again: if (retfbt == NULL) { fbt->fbtp_id = dtrace_probe_create(fbt_id, modname, - name, FBT_RETURN, 3, fbt); + name, FBT_RETURN, 5, fbt); } else { retfbt->fbtp_next = fbt; fbt->fbtp_id = retfbt->fbtp_id; @@ -317,7 +320,7 @@ again: lf->fbt_nentries++; - instr += size; + instr += 4; goto again; } @@ -434,6 +437,7 @@ fbt_enable(void *arg, dtrace_id_t id, vo for (; fbt != NULL; fbt = fbt->fbtp_next) { *fbt->fbtp_patchpoint = fbt->fbtp_patchval; + __syncicache(fbt->fbtp_patchpoint, 4); } } @@ -449,8 +453,10 @@ fbt_disable(void *arg, dtrace_id_t id, v if ((ctl->loadcnt != fbt->fbtp_loadcnt)) return; - for (; fbt != NULL; fbt = fbt->fbtp_next) + for (; fbt != NULL; fbt = fbt->fbtp_next) { *fbt->fbtp_patchpoint = fbt->fbtp_savedval; + __syncicache(fbt->fbtp_patchpoint, 4); + } } static void @@ -464,8 +470,10 @@ fbt_suspend(void *arg, dtrace_id_t id, v if ((ctl->loadcnt != fbt->fbtp_loadcnt)) return; - for (; fbt != NULL; fbt = fbt->fbtp_next) + for (; fbt != NULL; fbt = fbt->fbtp_next) { *fbt->fbtp_patchpoint = fbt->fbtp_savedval; + __syncicache(fbt->fbtp_patchpoint, 4); + } } static void @@ -479,15 +487,16 @@ fbt_resume(void *arg, dtrace_id_t id, vo if ((ctl->loadcnt != fbt->fbtp_loadcnt)) return; - for (; fbt != NULL; fbt = fbt->fbtp_next) + for (; fbt != NULL; fbt = fbt->fbtp_next) { *fbt->fbtp_patchpoint = fbt->fbtp_patchval; + __syncicache(fbt->fbtp_patchpoint, 4); + } } 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; @@ -519,11 +528,6 @@ fbt_ctfoff_init(modctl_t *lf, linker_ctf 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 || @@ -690,6 +694,8 @@ fbt_typoff_init(linker_ctf_t *lc) pop[kind]++; } + /* account for a sentinel value below */ + ctf_typemax++; *lc->typlenp = ctf_typemax; if ((xp = malloc(sizeof(uint32_t) * ctf_typemax, M_LINKER, M_ZERO | M_WAITOK)) == NULL) @@ -1171,6 +1177,11 @@ fbt_getargdesc(void *arg __unused, dtrac uint32_t offset; ushort_t info, kind, n; + if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) { + (void) strcpy(desc->dtargd_native, "int"); + return; + } + desc->dtargd_ndx = DTRACE_ARGNONE; /* Get a pointer to the CTF data and it's length. */ @@ -1221,12 +1232,19 @@ fbt_getargdesc(void *arg __unused, dtrac return; } - /* Check if the requested argument doesn't exist. */ - if (ndx >= n) - return; + if (fbt->fbtp_roffset != 0) { + /* Only return type is available for args[1] in return probe. */ + if (ndx > 1) + return; + ASSERT(ndx == 1); + } else { + /* Check if the requested argument doesn't exist. */ + if (ndx >= n) + return; - /* Skip the return type and arguments up to the one requested. */ - dp += ndx + 1; + /* Skip the return type and arguments up to the one requested. */ + dp += ndx + 1; + } if (fbt_type_name(&lc, *dp, desc->dtargd_native, sizeof(desc->dtargd_native)) > 0) desc->dtargd_ndx = ndx; @@ -1234,6 +1252,15 @@ fbt_getargdesc(void *arg __unused, dtrac return; } +static int +fbt_linker_file_cb(linker_file_t lf, void *arg) +{ + + fbt_provide_module(arg, lf); + + return (0); +} + static void fbt_load(void *dummy) { @@ -1257,6 +1284,9 @@ fbt_load(void *dummy) if (dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_USER, NULL, &fbt_pops, NULL, &fbt_id) != 0) return; + + /* Create probes for the kernel and already-loaded modules. */ + linker_file_foreach(fbt_linker_file_cb, NULL); }