Fixes for DTrace on PowerPC:

- Implement dtrace_getarg()
- Sync fbt with x86, and fix a typo.
- Pull in the time synchronization code from amd64.
This commit is contained in:
Justin Hibbits 2013-08-31 16:30:20 +00:00
parent f00154a765
commit f0bd82a11b
3 changed files with 268 additions and 119 deletions

View File

@ -349,51 +349,85 @@ zero:
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))) {
/*
* 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);
}
#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;
if (aframes > 0) {
aframes--;
if ((aframes == 0) && (caller != 0)) {
pcstack[depth++] = caller;
}
}
else {
pcstack[depth++] = callpc;
}
sp = *(void **)sp;
}
for (; depth < pcstack_limit; depth++) {
pcstack[depth] = 0;
}
}
#endif
int
dtrace_getstackdepth(int aframes)
{

View File

@ -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_xcall_t func, void *arg)
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_int type)
* 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;
/* 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);
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;
/*
* 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_epid_t epid, int which,
{
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);

View File

@ -57,6 +57,7 @@
#include <sys/sysproto.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <machine/md_var.h>
#include <machine/stdarg.h>
#include <sys/dtrace.h>
@ -172,7 +173,11 @@ fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
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_t lf, int symindx,
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_t lf, int symindx,
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_t lf, int symindx,
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, void *parg)
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, void *parg)
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, void *parg)
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, void *parg)
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_t *lc)
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, dtrace_id_t id __unused, void *parg, dtrace_a
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, dtrace_id_t id __unused, void *parg, dtrace_a
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, dtrace_id_t id __unused, void *parg, dtrace_a
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);
}