- case DTRACEIOC_AGGDESC: {
- dtrace_aggdesc_t aggdesc;
- dtrace_action_t *act;
- dtrace_aggregation_t *agg;
- int nrecs;
- uint32_t offs;
- dtrace_recdesc_t *lrec;
- void *buf;
- size_t size;
- uintptr_t dest;
-
- if (copyin((void *)arg, &aggdesc, sizeof (aggdesc)) != 0)
- return (EFAULT);
-
- lck_mtx_lock(&dtrace_lock);
-
- if ((agg = dtrace_aggid2agg(state, aggdesc.dtagd_id)) == NULL) {
- lck_mtx_unlock(&dtrace_lock);
- return (EINVAL);
- }
-
- aggdesc.dtagd_epid = agg->dtag_ecb->dte_epid;
-
- nrecs = aggdesc.dtagd_nrecs;
- aggdesc.dtagd_nrecs = 0;
-
- offs = agg->dtag_base;
- lrec = &agg->dtag_action.dta_rec;
- aggdesc.dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - offs;
-
- for (act = agg->dtag_first; ; act = act->dta_next) {
- ASSERT(act->dta_intuple ||
- DTRACEACT_ISAGG(act->dta_kind));
-
- /*
- * If this action has a record size of zero, it
- * denotes an argument to the aggregating action.
- * Because the presence of this record doesn't (or
- * shouldn't) affect the way the data is interpreted,
- * we don't copy it out to save user-level the
- * confusion of dealing with a zero-length record.
- */
- if (act->dta_rec.dtrd_size == 0) {
- ASSERT(agg->dtag_hasarg);
- continue;
- }
-
- aggdesc.dtagd_nrecs++;
-
- if (act == &agg->dtag_action)
- break;
- }
-
- /*
- * Now that we have the size, we need to allocate a temporary
- * buffer in which to store the complete description. We need
- * the temporary buffer to be able to drop dtrace_lock()
- * across the copyout(), below.
- */
- size = sizeof (dtrace_aggdesc_t) +
- (aggdesc.dtagd_nrecs * sizeof (dtrace_recdesc_t));
-
- buf = kmem_alloc(size, KM_SLEEP);
- dest = (uintptr_t)buf;
-
- bcopy(&aggdesc, (void *)dest, sizeof (aggdesc));
- dest += offsetof(dtrace_aggdesc_t, dtagd_rec[0]);
-
- for (act = agg->dtag_first; ; act = act->dta_next) {
- dtrace_recdesc_t rec = act->dta_rec;
-
- /*
- * See the comment in the above loop for why we pass
- * over zero-length records.
- */
- if (rec.dtrd_size == 0) {
- ASSERT(agg->dtag_hasarg);
- continue;
- }
-
- if (nrecs-- == 0)
- break;
-
- rec.dtrd_offset -= offs;
- bcopy(&rec, (void *)dest, sizeof (rec));
- dest += sizeof (dtrace_recdesc_t);
-
- if (act == &agg->dtag_action)
- break;
- }
-
- lck_mtx_unlock(&dtrace_lock);
-
- if (copyout(buf, (void *)arg, dest - (uintptr_t)buf) != 0) {
- kmem_free(buf, size);
- return (EFAULT);
- }
-
- kmem_free(buf, size);
- return (0);
- }
-
- case DTRACEIOC_ENABLE: {
- dof_hdr_t *dof;
- dtrace_enabling_t *enab = NULL;
- dtrace_vstate_t *vstate;
- int err = 0;
-
- *rv = 0;
-
- /*
- * If a NULL argument has been passed, we take this as our
- * cue to reevaluate our enablings.
- */
- if (arg == NULL) {
- dtrace_enabling_matchall();
-
- return (0);
- }
-
- if ((dof = dtrace_dof_copyin(arg, &rval)) == NULL)
- return (rval);
-
- lck_mtx_lock(&cpu_lock);
- lck_mtx_lock(&dtrace_lock);
- vstate = &state->dts_vstate;
-
- if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) {
- lck_mtx_unlock(&dtrace_lock);
- lck_mtx_unlock(&cpu_lock);
- dtrace_dof_destroy(dof);
- return (EBUSY);
- }
-
- if (dtrace_dof_slurp(dof, vstate, cr, &enab, 0, B_TRUE) != 0) {
- lck_mtx_unlock(&dtrace_lock);
- lck_mtx_unlock(&cpu_lock);
- dtrace_dof_destroy(dof);
- return (EINVAL);
- }
-
- if ((rval = dtrace_dof_options(dof, state)) != 0) {
- dtrace_enabling_destroy(enab);
- lck_mtx_unlock(&dtrace_lock);
- lck_mtx_unlock(&cpu_lock);
- dtrace_dof_destroy(dof);
- return (rval);
- }
-
- if ((err = dtrace_enabling_match(enab, rv)) == 0) {
- err = dtrace_enabling_retain(enab);
- } else {
- dtrace_enabling_destroy(enab);
- }
-
- lck_mtx_unlock(&cpu_lock);
- lck_mtx_unlock(&dtrace_lock);
- dtrace_dof_destroy(dof);
-
- return (err);
- }
-
- case DTRACEIOC_REPLICATE: {
- dtrace_repldesc_t desc;
- dtrace_probedesc_t *match = &desc.dtrpd_match;
- dtrace_probedesc_t *create = &desc.dtrpd_create;
- int err;
-
- if (copyin((void *)arg, &desc, sizeof (desc)) != 0)
- return (EFAULT);
-
- match->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
- match->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
- match->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
- match->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
-
- create->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
- create->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
- create->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
- create->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
-
- lck_mtx_lock(&dtrace_lock);
- err = dtrace_enabling_replicate(state, match, create);
- lck_mtx_unlock(&dtrace_lock);
-
- return (err);
- }
-
- case DTRACEIOC_PROBEMATCH:
- case DTRACEIOC_PROBES: {
- dtrace_probe_t *probe = NULL;
- dtrace_probedesc_t desc;
- dtrace_probekey_t pkey;
- dtrace_id_t i;
- int m = 0;
- uint32_t priv;
- uid_t uid;
- zoneid_t zoneid;
-
- if (copyin((void *)arg, &desc, sizeof (desc)) != 0)
- return (EFAULT);
-
- desc.dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
- desc.dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
- desc.dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
- desc.dtpd_name[DTRACE_NAMELEN - 1] = '\0';
-
- /*
- * Before we attempt to match this probe, we want to give
- * all providers the opportunity to provide it.
- */
- if (desc.dtpd_id == DTRACE_IDNONE) {
- lck_mtx_lock(&dtrace_provider_lock);
- dtrace_probe_provide(&desc, NULL);
- lck_mtx_unlock(&dtrace_provider_lock);
- desc.dtpd_id++;
- }
-
- if (cmd == DTRACEIOC_PROBEMATCH) {
- dtrace_probekey(&desc, &pkey);
- pkey.dtpk_id = DTRACE_IDNONE;
- }
-
- dtrace_cred2priv(cr, &priv, &uid, &zoneid);
-
- lck_mtx_lock(&dtrace_lock);
-
- if (cmd == DTRACEIOC_PROBEMATCH) {
- for (i = desc.dtpd_id; i <= dtrace_nprobes; i++) {
- if ((probe = dtrace_probes[i - 1]) != NULL &&
- (m = dtrace_match_probe(probe, &pkey,
- priv, uid, zoneid)) != 0)
- break;
- }
-
- if (m < 0) {
- lck_mtx_unlock(&dtrace_lock);
- return (EINVAL);
- }
-
- } else {
- for (i = desc.dtpd_id; i <= dtrace_nprobes; i++) {
- if ((probe = dtrace_probes[i - 1]) != NULL &&
- dtrace_match_priv(probe, priv, uid, zoneid))
- break;
- }
- }
-
- if (probe == NULL) {
- lck_mtx_unlock(&dtrace_lock);
- return (ESRCH);
- }
-
- dtrace_probe_description(probe, &desc);
- lck_mtx_unlock(&dtrace_lock);
-
- if (copyout(&desc, (void *)arg, sizeof (desc)) != 0)
- return (EFAULT);
-
- return (0);
- }
-
- case DTRACEIOC_PROBEARG: {
- dtrace_argdesc_t desc;
- dtrace_probe_t *probe;
- dtrace_provider_t *prov;
-
- if (copyin((void *)arg, &desc, sizeof (desc)) != 0)
- return (EFAULT);
-
- if (desc.dtargd_id == DTRACE_IDNONE)
- return (EINVAL);
-
- if (desc.dtargd_ndx == DTRACE_ARGNONE)
- return (EINVAL);
-
- lck_mtx_lock(&dtrace_provider_lock);
- lck_mtx_lock(&mod_lock);
- lck_mtx_lock(&dtrace_lock);
-
- if (desc.dtargd_id > dtrace_nprobes) {
- lck_mtx_unlock(&dtrace_lock);
- lck_mtx_unlock(&mod_lock);
- lck_mtx_unlock(&dtrace_provider_lock);
- return (EINVAL);
- }
-
- if ((probe = dtrace_probes[desc.dtargd_id - 1]) == NULL) {
- lck_mtx_unlock(&dtrace_lock);
- lck_mtx_unlock(&mod_lock);
- lck_mtx_unlock(&dtrace_provider_lock);
- return (EINVAL);
- }
-
- lck_mtx_unlock(&dtrace_lock);
-
- prov = probe->dtpr_provider;
-
- if (prov->dtpv_pops.dtps_getargdesc == NULL) {
- /*
- * There isn't any typed information for this probe.
- * Set the argument number to DTRACE_ARGNONE.
- */
- desc.dtargd_ndx = DTRACE_ARGNONE;
- } else {
- desc.dtargd_native[0] = '\0';
- desc.dtargd_xlate[0] = '\0';
- desc.dtargd_mapping = desc.dtargd_ndx;
-
- prov->dtpv_pops.dtps_getargdesc(prov->dtpv_arg,
- probe->dtpr_id, probe->dtpr_arg, &desc);
- }
-
- lck_mtx_unlock(&mod_lock);
- lck_mtx_unlock(&dtrace_provider_lock);
-
- if (copyout(&desc, (void *)arg, sizeof (desc)) != 0)
- return (EFAULT);
-
- return (0);
- }
-
- case DTRACEIOC_GO: {
- processorid_t cpuid;
- rval = dtrace_state_go(state, &cpuid);
-
- if (rval != 0)
- return (rval);
-
- if (copyout(&cpuid, (void *)arg, sizeof (cpuid)) != 0)
- return (EFAULT);
-
- return (0);
- }
-
- case DTRACEIOC_STOP: {
- processorid_t cpuid;
-
- lck_mtx_lock(&dtrace_lock);
- rval = dtrace_state_stop(state, &cpuid);
- lck_mtx_unlock(&dtrace_lock);
-
- if (rval != 0)
- return (rval);
-
- if (copyout(&cpuid, (void *)arg, sizeof (cpuid)) != 0)
- return (EFAULT);
-
- return (0);
- }
-
- case DTRACEIOC_DOFGET: {
- dof_hdr_t hdr, *dof;
- uint64_t len;
-
- if (copyin((void *)arg, &hdr, sizeof (hdr)) != 0)
- return (EFAULT);
-
- lck_mtx_lock(&dtrace_lock);
- dof = dtrace_dof_create(state);
- lck_mtx_unlock(&dtrace_lock);
-
- len = MIN(hdr.dofh_loadsz, dof->dofh_loadsz);
- rval = copyout(dof, (void *)arg, len);
- dtrace_dof_destroy(dof);
-
- return (rval == 0 ? 0 : EFAULT);
- }
-
- case DTRACEIOC_AGGSNAP:
- case DTRACEIOC_BUFSNAP: {
- dtrace_bufdesc_t desc;
- caddr_t cached;
- dtrace_buffer_t *buf;
-
- if (copyin((void *)arg, &desc, sizeof (desc)) != 0)
- return (EFAULT);
-
- if (desc.dtbd_cpu < 0 || desc.dtbd_cpu >= NCPU)
- return (EINVAL);
-
- lck_mtx_lock(&dtrace_lock);
-
- if (cmd == DTRACEIOC_BUFSNAP) {
- buf = &state->dts_buffer[desc.dtbd_cpu];
- } else {
- buf = &state->dts_aggbuffer[desc.dtbd_cpu];
- }
-
- if (buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL)) {
- size_t sz = buf->dtb_offset;
-
- if (state->dts_activity != DTRACE_ACTIVITY_STOPPED) {
- lck_mtx_unlock(&dtrace_lock);
- return (EBUSY);
- }
-
- /*
- * If this buffer has already been consumed, we're
- * going to indicate that there's nothing left here
- * to consume.
- */
- if (buf->dtb_flags & DTRACEBUF_CONSUMED) {
- lck_mtx_unlock(&dtrace_lock);
-
- desc.dtbd_size = 0;
- desc.dtbd_drops = 0;
- desc.dtbd_errors = 0;
- desc.dtbd_oldest = 0;
- sz = sizeof (desc);
-
- if (copyout(&desc, (void *)arg, sz) != 0)
- return (EFAULT);
-
- return (0);
- }
-
- /*
- * If this is a ring buffer that has wrapped, we want
- * to copy the whole thing out.
- */
- if (buf->dtb_flags & DTRACEBUF_WRAPPED) {
- dtrace_buffer_polish(buf);
- sz = buf->dtb_size;
- }
-
- if (copyout(buf->dtb_tomax, desc.dtbd_data, sz) != 0) {
- lck_mtx_unlock(&dtrace_lock);
- return (EFAULT);
- }
-
- desc.dtbd_size = sz;
- desc.dtbd_drops = buf->dtb_drops;
- desc.dtbd_errors = buf->dtb_errors;
- desc.dtbd_oldest = buf->dtb_xamot_offset;
-
- lck_mtx_unlock(&dtrace_lock);
-
- if (copyout(&desc, (void *)arg, sizeof (desc)) != 0)
- return (EFAULT);
-
- buf->dtb_flags |= DTRACEBUF_CONSUMED;
-
- return (0);
- }
-
- if (buf->dtb_tomax == NULL) {
- ASSERT(buf->dtb_xamot == NULL);
- lck_mtx_unlock(&dtrace_lock);
- return (ENOENT);
- }
-
- cached = buf->dtb_tomax;
- ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH));
-
- dtrace_xcall(desc.dtbd_cpu,
- (dtrace_xcall_t)dtrace_buffer_switch, buf);
-
- state->dts_errors += buf->dtb_xamot_errors;
-
- /*
- * If the buffers did not actually switch, then the cross call
- * did not take place -- presumably because the given CPU is
- * not in the ready set. If this is the case, we'll return
- * ENOENT.
- */
- if (buf->dtb_tomax == cached) {
- ASSERT(buf->dtb_xamot != cached);
- lck_mtx_unlock(&dtrace_lock);
- return (ENOENT);
- }
-
- ASSERT(cached == buf->dtb_xamot);
-
- /*
- * We have our snapshot; now copy it out.
- */
- if (copyout(buf->dtb_xamot, desc.dtbd_data,
- buf->dtb_xamot_offset) != 0) {
- lck_mtx_unlock(&dtrace_lock);
- return (EFAULT);
- }
-
- desc.dtbd_size = buf->dtb_xamot_offset;
- desc.dtbd_drops = buf->dtb_xamot_drops;
- desc.dtbd_errors = buf->dtb_xamot_errors;
- desc.dtbd_oldest = 0;
-
- lck_mtx_unlock(&dtrace_lock);
-
- /*
- * Finally, copy out the buffer description.
- */
- if (copyout(&desc, (void *)arg, sizeof (desc)) != 0)
- return (EFAULT);
-
- return (0);
- }
-
- case DTRACEIOC_CONF: {
- dtrace_conf_t conf;
-
- bzero(&conf, sizeof (conf));
- conf.dtc_difversion = DIF_VERSION;
- conf.dtc_difintregs = DIF_DIR_NREGS;
- conf.dtc_diftupregs = DIF_DTR_NREGS;
- conf.dtc_ctfmodel = CTF_MODEL_NATIVE;
-
- if (copyout(&conf, (void *)arg, sizeof (conf)) != 0)
- return (EFAULT);
-
- return (0);
- }
-
- case DTRACEIOC_STATUS: {
- dtrace_status_t stat;
- dtrace_dstate_t *dstate;
- int i, j;
- uint64_t nerrs;
-
- /*
- * See the comment in dtrace_state_deadman() for the reason
- * for setting dts_laststatus to INT64_MAX before setting
- * it to the correct value.
- */
- state->dts_laststatus = INT64_MAX;
- dtrace_membar_producer();
- state->dts_laststatus = dtrace_gethrtime();
-
- bzero(&stat, sizeof (stat));
-
- lck_mtx_lock(&dtrace_lock);
-
- if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) {
- lck_mtx_unlock(&dtrace_lock);
- return (ENOENT);
- }
-
- if (state->dts_activity == DTRACE_ACTIVITY_DRAINING)
- stat.dtst_exiting = 1;
-
- nerrs = state->dts_errors;
- dstate = &state->dts_vstate.dtvs_dynvars;
-
- for (i = 0; i < NCPU; i++) {
- dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[i];
-
- stat.dtst_dyndrops += dcpu->dtdsc_drops;
- stat.dtst_dyndrops_dirty += dcpu->dtdsc_dirty_drops;
- stat.dtst_dyndrops_rinsing += dcpu->dtdsc_rinsing_drops;
-
- if (state->dts_buffer[i].dtb_flags & DTRACEBUF_FULL)
- stat.dtst_filled++;
-
- nerrs += state->dts_buffer[i].dtb_errors;
-
- for (j = 0; j < state->dts_nspeculations; j++) {
- dtrace_speculation_t *spec;
- dtrace_buffer_t *buf;
-
- spec = &state->dts_speculations[j];
- buf = &spec->dtsp_buffer[i];
- stat.dtst_specdrops += buf->dtb_xamot_drops;
- }
- }
-
- stat.dtst_specdrops_busy = state->dts_speculations_busy;
- stat.dtst_specdrops_unavail = state->dts_speculations_unavail;
- stat.dtst_stkstroverflows = state->dts_stkstroverflows;
- stat.dtst_dblerrors = state->dts_dblerrors;
- stat.dtst_killed =
- (state->dts_activity == DTRACE_ACTIVITY_KILLED);
- stat.dtst_errors = nerrs;
-
- lck_mtx_unlock(&dtrace_lock);
-
- if (copyout(&stat, (void *)arg, sizeof (stat)) != 0)
- return (EFAULT);
-
- return (0);
- }
-
- case DTRACEIOC_FORMAT: {
- dtrace_fmtdesc_t fmt;
- char *str;
- int len;
-
- if (copyin((void *)arg, &fmt, sizeof (fmt)) != 0)
- return (EFAULT);
-
- lck_mtx_lock(&dtrace_lock);
-
- if (fmt.dtfd_format == 0 ||
- fmt.dtfd_format > state->dts_nformats) {
- lck_mtx_unlock(&dtrace_lock);
- return (EINVAL);
- }
-
- /*
- * Format strings are allocated contiguously and they are
- * never freed; if a format index is less than the number
- * of formats, we can assert that the format map is non-NULL
- * and that the format for the specified index is non-NULL.
- */
- ASSERT(state->dts_formats != NULL);
- str = state->dts_formats[fmt.dtfd_format - 1];
- ASSERT(str != NULL);
-
- len = strlen(str) + 1;
-
- if (len > fmt.dtfd_length) {
- fmt.dtfd_length = len;
-
- if (copyout(&fmt, (void *)arg, sizeof (fmt)) != 0) {
- lck_mtx_unlock(&dtrace_lock);
- return (EINVAL);
- }
- } else {
- if (copyout(str, fmt.dtfd_string, len) != 0) {
- lck_mtx_unlock(&dtrace_lock);
- return (EINVAL);
- }
- }
-
- lck_mtx_unlock(&dtrace_lock);
- return (0);
- }
-
- default:
- break;
- }
-
- return (ENOTTY);