Index: head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c =================================================================== --- head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c (revision 271696) +++ head/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c (revision 271697) @@ -1,622 +1,724 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2012,2013 Justin Hibbits * * $FreeBSD$ */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "regset.h" /* Offset to the LR Save word (ppc32) */ #define RETURN_OFFSET 4 /* 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) { return (greg_t)__builtin_frame_address(0); } void dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes, 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; aframes++; 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--; if ((aframes == 0) && (caller != 0)) { pcstack[depth++] = caller; } } else { pcstack[depth++] = callpc; } - sp = *(uintptr_t*)sp; + osp = sp; + sp = dtrace_next_sp(sp); } for (; depth < pcstack_limit; depth++) { pcstack[depth] = 0; } } static int dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc, uintptr_t sp) { proc_t *p = curproc; int ret = 0; ASSERT(pcstack == NULL || pcstack_limit > 0); while (pc != 0) { ret++; if (pcstack != NULL) { *pcstack++ = (uint64_t)pc; pcstack_limit--; if (pcstack_limit <= 0) break; } if (sp == 0) break; if (SV_PROC_FLAG(p, SV_ILP32)) { pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET)); sp = dtrace_fuword32((void *)sp); } else { pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64)); sp = dtrace_fuword64((void *)sp); } } return (ret); } void dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit) { proc_t *p = curproc; struct trapframe *tf; uintptr_t pc, sp; volatile uint16_t *flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; int n; if (*flags & CPU_DTRACE_FAULT) return; if (pcstack_limit <= 0) return; /* * If there's no user context we still need to zero the stack. */ if (p == NULL || (tf = curthread->td_frame) == NULL) goto zero; *pcstack++ = (uint64_t)p->p_pid; pcstack_limit--; if (pcstack_limit <= 0) return; pc = tf->srr0; sp = tf->fixreg[1]; if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { /* * In an entry probe. The frame pointer has not yet been * pushed (that happens in the function prologue). The * best approach is to add the current pc as a missing top * of stack and back the pc up to the caller, which is stored * at the current stack pointer address since the call * instruction puts it there right before the branch. */ *pcstack++ = (uint64_t)pc; pcstack_limit--; if (pcstack_limit <= 0) return; pc = tf->lr; } n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp); ASSERT(n >= 0); ASSERT(n <= pcstack_limit); pcstack += n; pcstack_limit -= n; zero: while (pcstack_limit-- > 0) *pcstack++ = 0; } int dtrace_getustackdepth(void) { proc_t *p = curproc; struct trapframe *tf; uintptr_t pc, sp; int n = 0; if (p == NULL || (tf = curthread->td_frame) == NULL) return (0); if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT)) return (-1); pc = tf->srr0; sp = tf->fixreg[1]; if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { /* * In an entry probe. The frame pointer has not yet been * pushed (that happens in the function prologue). The * best approach is to add the current pc as a missing top * of stack and back the pc up to the caller, which is stored * at the current stack pointer address since the call * instruction puts it there right before the branch. */ if (SV_PROC_FLAG(p, SV_ILP32)) { pc = dtrace_fuword32((void *) sp); } else pc = dtrace_fuword64((void *) sp); n++; } n += dtrace_getustack_common(NULL, 0, pc, sp); return (n); } void dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit) { proc_t *p = curproc; struct trapframe *tf; uintptr_t pc, sp; volatile uint16_t *flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags; #ifdef notyet /* XXX signal stack */ uintptr_t oldcontext; size_t s1, s2; #endif if (*flags & CPU_DTRACE_FAULT) return; if (pcstack_limit <= 0) return; /* * If there's no user context we still need to zero the stack. */ if (p == NULL || (tf = curthread->td_frame) == NULL) goto zero; *pcstack++ = (uint64_t)p->p_pid; pcstack_limit--; if (pcstack_limit <= 0) return; pc = tf->srr0; sp = tf->fixreg[1]; #ifdef notyet /* XXX signal stack */ oldcontext = lwp->lwp_oldcontext; s1 = sizeof (struct xframe) + 2 * sizeof (long); s2 = s1 + sizeof (siginfo_t); #endif if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) { *pcstack++ = (uint64_t)pc; *fpstack++ = 0; pcstack_limit--; if (pcstack_limit <= 0) return; if (SV_PROC_FLAG(p, SV_ILP32)) { pc = dtrace_fuword32((void *)sp); } else { pc = dtrace_fuword64((void *)sp); } } while (pc != 0) { *pcstack++ = (uint64_t)pc; *fpstack++ = sp; pcstack_limit--; if (pcstack_limit <= 0) break; if (sp == 0) break; #ifdef notyet /* XXX signal stack */ if (oldcontext == sp + s1 || oldcontext == sp + s2) { ucontext_t *ucp = (ucontext_t *)oldcontext; greg_t *gregs = ucp->uc_mcontext.gregs; sp = dtrace_fulword(&gregs[REG_FP]); pc = dtrace_fulword(&gregs[REG_PC]); oldcontext = dtrace_fulword(&ucp->uc_link); } else #endif /* XXX */ { if (SV_PROC_FLAG(p, SV_ILP32)) { pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET)); sp = dtrace_fuword32((void *)sp); } else { pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64)); sp = dtrace_fuword64((void *)sp); } } /* * This is totally bogus: if we faulted, we're going to clear * the fault and break. This is to deal with the apparently * broken Java stacks on x86. */ if (*flags & CPU_DTRACE_FAULT) { *flags &= ~CPU_DTRACE_FAULT; break; } } zero: while (pcstack_limit-- > 0) *pcstack++ = 0; } /*ARGSUSED*/ uint64_t dtrace_getarg(int arg, int aframes) { 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))) { +#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 * 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 (arg <= inreg) { stack = &rp->fixreg[3]; } else { stack = (uintptr_t *)(rp->fixreg[1]); arg -= inreg; } goto load; } } /* * 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); } 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 dtrace_getreg(struct trapframe *rp, uint_t reg) { if (reg < 32) return (rp->fixreg[reg]); switch (reg) { case 33: return (rp->lr); case 34: return (rp->cr); case 35: return (rp->xer); case 36: return (rp->ctr); case 37: return (rp->srr0); case 38: return (rp->srr1); case 39: return (rp->exc); default: DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP); return (0); } } static int dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size) { ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr); if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = uaddr; return (0); } return (1); } void dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size, volatile uint16_t *flags) { if (dtrace_copycheck(uaddr, kaddr, size)) if (copyin((const void *)uaddr, (void *)kaddr, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; } } void dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size, volatile uint16_t *flags) { if (dtrace_copycheck(uaddr, kaddr, size)) { if (copyout((const void *)kaddr, (void *)uaddr, size)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; } } } void dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size, volatile uint16_t *flags) { size_t actual; int error; if (dtrace_copycheck(uaddr, kaddr, size)) { error = copyinstr((const void *)uaddr, (void *)kaddr, size, &actual); /* ENAMETOOLONG is not a fault condition. */ if (error && error != ENAMETOOLONG) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; } } } /* * The bulk of this function could be replaced to match dtrace_copyinstr() * if we ever implement a copyoutstr(). */ void dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size, volatile uint16_t *flags) { size_t len; if (dtrace_copycheck(uaddr, kaddr, size)) { len = strlen((const char *)kaddr); if (len > size) len = size; if (copyout((const void *)kaddr, (void *)uaddr, len)) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; } } } uint8_t dtrace_fuword8(void *uaddr) { if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; return (0); } return (fubyte(uaddr)); } uint16_t dtrace_fuword16(void *uaddr) { uint16_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; } uint32_t dtrace_fuword32(void *uaddr) { if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) { DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr; return (0); } return (fuword32(uaddr)); } uint64_t dtrace_fuword64(void *uaddr) { uint64_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; } 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; } Index: head/sys/cddl/dev/fbt/powerpc/fbt_isa.c =================================================================== --- head/sys/cddl/dev/fbt/powerpc/fbt_isa.c (revision 271696) +++ head/sys/cddl/dev/fbt/powerpc/fbt_isa.c (revision 271697) @@ -1,241 +1,241 @@ /* * 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 jb@freebsd.org * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org * * $FreeBSD$ * */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include "fbt.h" #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) #define FBT_ENTRY "entry" #define FBT_RETURN "return" 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) #ifdef __powerpc64__ tmp |= 0xfffffffffc000000ULL; #else tmp |= 0xfc000000UL; #endif frame->srr0 += tmp; } cpu->cpu_dtrace_caller = 0; } return (fbt->fbtp_rval); } } return (0); } void fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val) { *fbt->fbtp_patchpoint = val; __syncicache(fbt->fbtp_patchpoint, 4); } 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; uint32_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) { /* * 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); instr = (uint32_t *) symval->value; limit = (uint32_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); + name, FBT_ENTRY, 7, 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_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, 5, fbt); + name, FBT_RETURN, 7, 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 += 4; goto again; }