X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/2d21ac55c334faf3a56e5634905ed6987fc787d4..060df5ea7c632b1ac8cc8aac1fb59758165c2084:/bsd/dev/dtrace/fasttrap.c diff --git a/bsd/dev/dtrace/fasttrap.c b/bsd/dev/dtrace/fasttrap.c index b0828e6dc..814778290 100644 --- a/bsd/dev/dtrace/fasttrap.c +++ b/bsd/dev/dtrace/fasttrap.c @@ -20,12 +20,12 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* - * #pragma ident "@(#)fasttrap.c 1.21 06/06/12 SMI" + * #pragma ident "@(#)fasttrap.c 1.26 08/04/21 SMI" */ #include @@ -51,7 +51,12 @@ #include -#define proc_t struct proc +/* Solaris proc_t is the struct. Darwin's proc_t is a pointer to it. */ +#define proc_t struct proc /* Steer clear of the Darwin typedef for proc_t */ + +__private_extern__ +void +qsort(void *a, size_t n, size_t es, int (*cmp)(const void *, const void *)); /* * User-Land Trap-Based Tracing @@ -496,8 +501,17 @@ fasttrap_fork(proc_t *p, proc_t *cp) lck_mtx_lock(&bucket->ftb_mtx); for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { if (tp->ftt_pid == ppid && - !tp->ftt_proc->ftpc_defunct) { + tp->ftt_proc->ftpc_acount != 0) { fasttrap_tracepoint_remove(cp, tp); + + /* + * The count of active providers can only be + * decremented (i.e. to zero) during exec, + * exit, and removal of a meta provider so it + * should be impossible to drop the count + * mid-fork. + */ + ASSERT(tp->ftt_proc->ftpc_acount != 0); } } lck_mtx_unlock(&bucket->ftb_mtx); @@ -613,8 +627,14 @@ fasttrap_tracepoint_enable(proc_t *p, fasttrap_probe_t *probe, uint_t index) again: lck_mtx_lock(&bucket->ftb_mtx); for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) { + /* + * Note that it's safe to access the active count on the + * associated proc structure because we know that at least one + * provider (this one) will still be around throughout this + * operation. + */ if (tp->ftt_pid != pid || tp->ftt_pc != pc || - tp->ftt_proc->ftpc_defunct) + tp->ftt_proc->ftpc_acount == 0) continue; /* @@ -825,10 +845,12 @@ fasttrap_tracepoint_disable(proc_t *p, fasttrap_probe_t *probe, uint_t index) if (tp->ftt_ids != NULL) { tmp_probe = tp->ftt_ids->fti_probe; + /* LINTED - alignment */ tmp_index = FASTTRAP_ID_INDEX(tp->ftt_ids); tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp; } else { tmp_probe = tp->ftt_retids->fti_probe; + /* LINTED - alignment */ tmp_index = FASTTRAP_ID_INDEX(tp->ftt_retids); tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp; } @@ -1176,7 +1198,7 @@ fasttrap_pid_getargdesc(void *arg, dtrace_id_t id, void *parg, #pragma unused(arg, id) fasttrap_probe_t *probe = parg; char *str; - int i; + int i, ndx; desc->dtargd_native[0] = '\0'; desc->dtargd_xlate[0] = '\0'; @@ -1187,14 +1209,11 @@ fasttrap_pid_getargdesc(void *arg, dtrace_id_t id, void *parg, return; } - /* - * We only need to set this member if the argument is remapped. - */ - if (probe->ftp_argmap != NULL) - desc->dtargd_mapping = probe->ftp_argmap[desc->dtargd_ndx]; + ndx = (probe->ftp_argmap != NULL) ? + probe->ftp_argmap[desc->dtargd_ndx] : desc->dtargd_ndx; str = probe->ftp_ntypes; - for (i = 0; i < desc->dtargd_mapping; i++) { + for (i = 0; i < ndx; i++) { str += strlen(str) + 1; } @@ -1296,10 +1315,12 @@ fasttrap_proc_lookup(pid_t pid) lck_mtx_lock(&bucket->ftb_mtx); for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) { - if (fprc->ftpc_pid == pid && !fprc->ftpc_defunct) { + if (fprc->ftpc_pid == pid && fprc->ftpc_acount != 0) { lck_mtx_lock(&fprc->ftpc_mtx); lck_mtx_unlock(&bucket->ftb_mtx); - fprc->ftpc_count++; + fprc->ftpc_rcount++; + atomic_add_64(&fprc->ftpc_acount, 1); + ASSERT(fprc->ftpc_acount <= fprc->ftpc_rcount); lck_mtx_unlock(&fprc->ftpc_mtx); return (fprc); @@ -1315,7 +1336,8 @@ fasttrap_proc_lookup(pid_t pid) new_fprc = kmem_zalloc(sizeof (fasttrap_proc_t), KM_SLEEP); ASSERT(new_fprc != NULL); new_fprc->ftpc_pid = pid; - new_fprc->ftpc_count = 1; + new_fprc->ftpc_rcount = 1; + new_fprc->ftpc_acount = 1; lck_mtx_lock(&bucket->ftb_mtx); @@ -1324,10 +1346,12 @@ fasttrap_proc_lookup(pid_t pid) * been created for this pid while we weren't under the bucket lock. */ for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) { - if (fprc->ftpc_pid == pid && !fprc->ftpc_defunct) { + if (fprc->ftpc_pid == pid && fprc->ftpc_acount != 0) { lck_mtx_lock(&fprc->ftpc_mtx); lck_mtx_unlock(&bucket->ftb_mtx); - fprc->ftpc_count++; + fprc->ftpc_rcount++; + atomic_add_64(&fprc->ftpc_acount, 1); + ASSERT(fprc->ftpc_acount <= fprc->ftpc_rcount); lck_mtx_unlock(&fprc->ftpc_mtx); kmem_free(new_fprc, sizeof (fasttrap_proc_t)); @@ -1360,15 +1384,22 @@ fasttrap_proc_release(fasttrap_proc_t *proc) lck_mtx_lock(&proc->ftpc_mtx); - ASSERT(proc->ftpc_count != 0); + ASSERT(proc->ftpc_rcount != 0); + ASSERT(proc->ftpc_acount <= proc->ftpc_rcount); - if (--proc->ftpc_count != 0) { + if (--proc->ftpc_rcount != 0) { lck_mtx_unlock(&proc->ftpc_mtx); return; } lck_mtx_unlock(&proc->ftpc_mtx); + /* + * There should definitely be no live providers associated with this + * process at this point. + */ + ASSERT(proc->ftpc_acount == 0); + bucket = &fasttrap_procs.fth_table[FASTTRAP_PROCS_INDEX(pid)]; lck_mtx_lock(&bucket->ftb_mtx); @@ -1565,6 +1596,16 @@ fasttrap_provider_free(fasttrap_provider_t *provider) ASSERT(provider->ftp_ccount == 0); ASSERT(provider->ftp_mcount == 0); + /* + * If this provider hasn't been retired, we need to explicitly drop the + * count of active providers on the associated process structure. + */ + if (!provider->ftp_retired) { + atomic_add_64(&provider->ftp_proc->ftpc_acount, -1); + ASSERT(provider->ftp_proc->ftpc_acount < + provider->ftp_proc->ftpc_rcount); + } + fasttrap_proc_release(provider->ftp_proc); #if defined(__APPLE__) @@ -1628,13 +1669,12 @@ fasttrap_provider_retire(pid_t pid, const char *name, int mprov) } /* - * Mark the provider to be removed in our post-processing step, - * mark it retired, and mark its proc as defunct (though it may - * already be marked defunct by another provider that shares the - * same proc). Marking it indicates that we should try to remove it; - * setting the retired flag indicates that we're done with this - * provider; setting the proc to be defunct indicates that all - * tracepoints associated with the traced process should be ignored. + * Mark the provider to be removed in our post-processing step, mark it + * retired, and drop the active count on its proc. Marking it indicates + * that we should try to remove it; setting the retired flag indicates + * that we're done with this provider; dropping the active the proc + * releases our hold, and when this reaches zero (as it will during + * exit or exec) the proc and associated providers become defunct. * * We obviously need to take the bucket lock before the provider lock * to perform the lookup, but we need to drop the provider lock @@ -1643,7 +1683,9 @@ fasttrap_provider_retire(pid_t pid, const char *name, int mprov) * bucket lock therefore protects the integrity of the provider hash * table. */ - fp->ftp_proc->ftpc_defunct = 1; + atomic_add_64(&fp->ftp_proc->ftpc_acount, -1); + ASSERT(fp->ftp_proc->ftpc_acount < fp->ftp_proc->ftpc_rcount); + fp->ftp_retired = 1; fp->ftp_marked = 1; provid = fp->ftp_provid; @@ -1661,6 +1703,18 @@ fasttrap_provider_retire(pid_t pid, const char *name, int mprov) fasttrap_pid_cleanup(); } +static int +fasttrap_uint32_cmp(const void *ap, const void *bp) +{ + return (*(const uint32_t *)ap - *(const uint32_t *)bp); +} + +static int +fasttrap_uint64_cmp(const void *ap, const void *bp) +{ + return (*(const uint64_t *)ap - *(const uint64_t *)bp); +} + static int fasttrap_add_probe(fasttrap_probe_spec_t *pdata) { @@ -1670,6 +1724,12 @@ fasttrap_add_probe(fasttrap_probe_spec_t *pdata) const char *name; unsigned int i, aframes, whack; + /* + * There needs to be at least one desired trace point. + */ + if (pdata->ftps_noffs == 0) + return (EINVAL); + #if defined(__APPLE__) switch (pdata->ftps_probe_type) { #endif @@ -1733,7 +1793,7 @@ fasttrap_add_probe(fasttrap_probe_spec_t *pdata) char name_str[17]; (void) snprintf(name_str, sizeof(name_str), "%llx", - (unsigned long long)pdata->ftps_offs[i]); + (uint64_t)pdata->ftps_offs[i]); if (dtrace_probe_lookup(provider->ftp_provid, pdata->ftps_mod, pdata->ftps_func, name_str) != 0) @@ -1771,6 +1831,7 @@ fasttrap_add_probe(fasttrap_probe_spec_t *pdata) tp->ftt_pc = pdata->ftps_offs[i] + pdata->ftps_pc; tp->ftt_pid = pdata->ftps_pid; + pp->ftp_tps[0].fit_tp = tp; pp->ftp_tps[0].fit_id.fti_probe = pp; #if defined(__APPLE__) @@ -1790,6 +1851,21 @@ fasttrap_add_probe(fasttrap_probe_spec_t *pdata) goto no_mem; } + /* + * Make sure all tracepoint program counter values are unique. + * We later assume that each probe has exactly one tracepoint + * for a given pc. + */ + qsort(pdata->ftps_offs, pdata->ftps_noffs, + sizeof (uint64_t), fasttrap_uint64_cmp); + for (i = 1; i < pdata->ftps_noffs; i++) { + if (pdata->ftps_offs[i] > pdata->ftps_offs[i - 1]) + continue; + + atomic_add_32(&fasttrap_total, -pdata->ftps_noffs); + goto no_mem; + } + ASSERT(pdata->ftps_noffs > 0); #if !defined(__APPLE__) pp = kmem_zalloc(offsetof(fasttrap_probe_t, @@ -1919,15 +1995,15 @@ fasttrap_meta_provide(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid) * The highest stability class that fasttrap supports is ISA; cap * the stability of the new provider accordingly. */ - if (dhpv->dthpv_pattr.dtpa_provider.dtat_class >= DTRACE_CLASS_COMMON) + if (dhpv->dthpv_pattr.dtpa_provider.dtat_class > DTRACE_CLASS_ISA) dhpv->dthpv_pattr.dtpa_provider.dtat_class = DTRACE_CLASS_ISA; - if (dhpv->dthpv_pattr.dtpa_mod.dtat_class >= DTRACE_CLASS_COMMON) + if (dhpv->dthpv_pattr.dtpa_mod.dtat_class > DTRACE_CLASS_ISA) dhpv->dthpv_pattr.dtpa_mod.dtat_class = DTRACE_CLASS_ISA; - if (dhpv->dthpv_pattr.dtpa_func.dtat_class >= DTRACE_CLASS_COMMON) + if (dhpv->dthpv_pattr.dtpa_func.dtat_class > DTRACE_CLASS_ISA) dhpv->dthpv_pattr.dtpa_func.dtat_class = DTRACE_CLASS_ISA; - if (dhpv->dthpv_pattr.dtpa_name.dtat_class >= DTRACE_CLASS_COMMON) + if (dhpv->dthpv_pattr.dtpa_name.dtat_class > DTRACE_CLASS_ISA) dhpv->dthpv_pattr.dtpa_name.dtat_class = DTRACE_CLASS_ISA; - if (dhpv->dthpv_pattr.dtpa_args.dtat_class >= DTRACE_CLASS_COMMON) + if (dhpv->dthpv_pattr.dtpa_args.dtat_class > DTRACE_CLASS_ISA) dhpv->dthpv_pattr.dtpa_args.dtat_class = DTRACE_CLASS_ISA; #if defined(__APPLE__) @@ -1952,13 +2028,10 @@ fasttrap_meta_provide(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid) * having duplicate probes. However, duplicate probes are not fatal, * and there is no way to get that by accident, so we will not check * for that case. + * + * UPDATE: It turns out there are several use cases that require adding + * probes to existing providers. Disabling this optimization for now... */ - - if (provider->ftp_mcount != 0) { - /* This is the duplicate provider case. */ - lck_mtx_unlock(&provider->ftp_mtx); - return NULL; - } #endif /* __APPLE__ */ /* @@ -1990,6 +2063,25 @@ fasttrap_meta_create_probe(void *arg, void *parg, */ ASSERT(provider->ftp_mcount > 0); + /* + * The offsets must be unique. + */ + qsort(dhpb->dthpb_offs, dhpb->dthpb_noffs, sizeof (uint32_t), + fasttrap_uint32_cmp); + for (i = 1; i < dhpb->dthpb_noffs; i++) { + if (dhpb->dthpb_base + dhpb->dthpb_offs[i] <= + dhpb->dthpb_base + dhpb->dthpb_offs[i - 1]) + return; + } + + qsort(dhpb->dthpb_enoffs, dhpb->dthpb_nenoffs, sizeof (uint32_t), + fasttrap_uint32_cmp); + for (i = 1; i < dhpb->dthpb_nenoffs; i++) { + if (dhpb->dthpb_base + dhpb->dthpb_enoffs[i] <= + dhpb->dthpb_base + dhpb->dthpb_enoffs[i - 1]) + return; + } + /* * Grab the creation lock to ensure consistency between calls to * dtrace_probe_lookup() and dtrace_probe_create() in the face of @@ -2058,7 +2150,7 @@ fasttrap_meta_create_probe(void *arg, void *parg, * Unfortunately, a side effect of this is that the relocations do not point at exactly * the location we want. We need to fix up the addresses here. The fixups vary by arch and type. */ -#if defined(__i386__) +#if defined(__i386__) || defined(__x86_64__) /* * Both 32 & 64 bit want to go back one byte, to point at the first NOP */ @@ -2102,7 +2194,7 @@ fasttrap_meta_create_probe(void *arg, void *parg, * Unfortunately, a side effect of this is that the relocations do not point at exactly * the location we want. We need to fix up the addresses here. The fixups vary by arch and type. */ -#if defined(__i386__) +#if defined(__i386__) || defined(__x86_64__) /* * Both 32 & 64 bit want to go forward two bytes, to point at a single byte nop. */ @@ -2167,28 +2259,21 @@ static dtrace_mops_t fasttrap_mops = { /*ARGSUSED*/ static int -fasttrap_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv) +fasttrap_ioctl(dev_t dev, u_long cmd, user_addr_t arg, int md, cred_t *cr, int *rv) { #pragma unused(dev, md, rv) if (!dtrace_attached()) return (EAGAIN); if (cmd == FASTTRAPIOC_MAKEPROBE) { - // FIXME! What size is arg? If it is not 64 bit, how do we pass in a 64 bit value? - fasttrap_probe_spec_t *uprobe = (void *)arg; fasttrap_probe_spec_t *probe; uint64_t noffs; size_t size, i; int ret; char *c; - /* - * FIXME! How does this work? The kern is running in 32 bit mode. It has a 32 bit pointer, - * uprobe. We do address manipulations on it, and still have a 64 bit value? This seems - * broken. What is the right way to do this? - */ - if (copyin((user_addr_t)(unsigned long)&uprobe->ftps_noffs, &noffs, - sizeof (uprobe->ftps_noffs))) + if (copyin(arg + __offsetof(fasttrap_probe_spec_t, ftps_noffs), &noffs, + sizeof (probe->ftps_noffs))) return (EFAULT); /* @@ -2209,7 +2294,7 @@ fasttrap_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv) probe = kmem_alloc(size, KM_SLEEP); - if (copyin((user_addr_t)(unsigned long)uprobe, probe, size) != 0) { + if (copyin(arg, probe, size) != 0) { kmem_free(probe, size); return (EFAULT); } @@ -2277,7 +2362,7 @@ err: uint_t index; // int ret; - if (copyin((user_addr_t)(unsigned long)arg, &instr, sizeof (instr)) != 0) + if (copyin(arg, &instr, sizeof (instr)) != 0) return (EFAULT); if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) { @@ -2311,7 +2396,7 @@ err: while (tp != NULL) { if (instr.ftiq_pid == tp->ftt_pid && instr.ftiq_pc == tp->ftt_pc && - !tp->ftt_proc->ftpc_defunct) + tp->ftt_proc->ftpc_acount != 0) break; tp = tp->ftt_next; @@ -2326,7 +2411,7 @@ err: sizeof (instr.ftiq_instr)); lck_mtx_unlock(&fasttrap_tpoints.fth_table[index].ftb_mtx); - if (copyout(&instr, (user_addr_t)(unsigned long)arg, sizeof (instr)) != 0) + if (copyout(&instr, arg, sizeof (instr)) != 0) return (EFAULT); return (0); @@ -2368,6 +2453,8 @@ fasttrap_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) * Yes, this is a WAG. */ fasttrap_max = (sane_size >> 28) * 100000; + if (fasttrap_max == 0) + fasttrap_max = 50000; #endif fasttrap_total = 0; @@ -2459,13 +2546,15 @@ _fasttrap_open(dev_t dev, int flags, int devtype, struct proc *p) static int _fasttrap_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) { -#pragma unused(p) int err, rv = 0; + user_addr_t uaddrp; - /* - * FIXME! 64 bit problem with the data var. - */ - err = fasttrap_ioctl(dev, (int)cmd, *(intptr_t *)data, fflag, CRED(), &rv); + if (proc_is64bit(p)) + uaddrp = *(user_addr_t *)data; + else + uaddrp = (user_addr_t) *(uint32_t *)data; + + err = fasttrap_ioctl(dev, cmd, uaddrp, fflag, CRED(), &rv); /* XXX Darwin's BSD ioctls only return -1 or zero. Overload errno to mimic Solaris. 20 bits suffice. */ if (err != 0) { @@ -2565,7 +2654,7 @@ fasttrap_init( void ) lck_mtx_init(&fasttrap_cleanup_mtx, fasttrap_lck_grp, fasttrap_lck_attr); lck_mtx_init(&fasttrap_count_mtx, fasttrap_lck_grp, fasttrap_lck_attr); - if (DDI_FAILURE == fasttrap_attach((dev_info_t *)device, 0 )) { + if (DDI_FAILURE == fasttrap_attach((dev_info_t *)(uintptr_t)device, 0 )) { // FIX ME! Do we remove the devfs node here? // What kind of error reporting? printf("fasttrap_init: Call to fasttrap_attach failed.\n");