static int dt_handle_err(dtrace_hdl_t *dtp, dtrace_probedata_t *data) { dtrace_eprobedesc_t *epd = data->dtpda_edesc, *errepd; dtrace_probedesc_t *pd = data->dtpda_pdesc, *errpd; dtrace_errdata_t err; dtrace_epid_t epid; char where[30]; char details[30]; char offinfo[30]; const int slop = 80; const char *faultstr; char *str; int len; assert(epd->dtepd_uarg == DT_ECB_ERROR); if (epd->dtepd_nrecs != 5 || strcmp(pd->dtpd_provider, "dtrace") != 0 || strcmp(pd->dtpd_name, "ERROR") != 0) return (dt_set_errno(dtp, EDT_BADERROR)); /* * This is an error. We have the following items here: EPID, * faulting action, DIF offset, fault code and faulting address. */ epid = (uint32_t)DT_REC(uint64_t, 0); if (dt_epid_lookup(dtp, epid, &errepd, &errpd) != 0) return (dt_set_errno(dtp, EDT_BADERROR)); err.dteda_edesc = errepd; err.dteda_pdesc = errpd; err.dteda_cpu = data->dtpda_cpu; err.dteda_action = (int)DT_REC(uint64_t, 1); err.dteda_offset = (int)DT_REC(uint64_t, 2); err.dteda_fault = (int)DT_REC(uint64_t, 3); err.dteda_addr = DT_REC(uint64_t, 4); faultstr = dtrace_faultstr(dtp, err.dteda_fault); len = sizeof (where) + sizeof (offinfo) + strlen(faultstr) + strlen(errpd->dtpd_provider) + strlen(errpd->dtpd_mod) + strlen(errpd->dtpd_name) + strlen(errpd->dtpd_func) + slop; str = (char *)alloca(len); if (err.dteda_action == 0) { (void) sprintf(where, "predicate"); } else { (void) sprintf(where, "action #%d", err.dteda_action); } if (err.dteda_offset != -1) { (void) sprintf(offinfo, " at DIF offset %d", err.dteda_offset); } else { offinfo[0] = 0; } switch (err.dteda_fault) { case DTRACEFLT_BADADDR: case DTRACEFLT_BADALIGN: case DTRACEFLT_BADSTACK: (void) sprintf(details, " (0x%llx)", (u_longlong_t)err.dteda_addr); break; default: details[0] = 0; } (void) snprintf(str, len, "error on enabled probe ID %u " "(ID %u: %s:%s:%s:%s): %s%s in %s%s\n", epid, errpd->dtpd_id, errpd->dtpd_provider, errpd->dtpd_mod, errpd->dtpd_func, errpd->dtpd_name, dtrace_faultstr(dtp, err.dteda_fault), details, where, offinfo); err.dteda_msg = str; if (dtp->dt_errhdlr == NULL) return (dt_set_errno(dtp, EDT_ERRABORT)); if ((*dtp->dt_errhdlr)(&err, dtp->dt_errarg) == DTRACE_HANDLE_ABORT) return (dt_set_errno(dtp, EDT_ERRABORT)); return (0); }
int dtrace_probe_iter(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, dtrace_probe_f *func, void *arg) { const char *provider = pdp ? pdp->dtpd_provider : NULL; dtrace_id_t id = DTRACE_IDNONE; dtrace_probedesc_t pd; dt_probe_iter_t pit; int cmd, rv; bzero(&pit, sizeof (pit)); pit.pit_hdl = dtp; pit.pit_func = func; pit.pit_arg = arg; pit.pit_pat = pdp ? pdp->dtpd_name : NULL; for (pit.pit_pvp = dt_list_next(&dtp->dt_provlist); pit.pit_pvp != NULL; pit.pit_pvp = dt_list_next(pit.pit_pvp)) { if (pit.pit_pvp->pv_flags & DT_PROVIDER_IMPL) continue; /* we'll get these later using dt_ioctl() */ if (!dt_gmatch(pit.pit_pvp->pv_desc.dtvd_name, provider)) continue; (void) strlcpy(pit.pit_desc.dtpd_provider, pit.pit_pvp->pv_desc.dtvd_name, DTRACE_PROVNAMELEN); if ((rv = dt_idhash_iter(pit.pit_pvp->pv_probes, (dt_idhash_f *)dt_probe_iter, &pit)) != 0) return (rv); } if (pdp != NULL) cmd = DTRACEIOC_PROBEMATCH; else cmd = DTRACEIOC_PROBES; for (;;) { if (pdp != NULL) bcopy(pdp, &pd, sizeof (pd)); pd.dtpd_id = id; if (dt_ioctl(dtp, cmd, &pd) != 0) break; else if ((rv = func(dtp, &pd, arg)) != 0) return (rv); pit.pit_matches++; id = pd.dtpd_id + 1; } switch (errno) { case ESRCH: case EBADF: return (pit.pit_matches ? 0 : dt_set_errno(dtp, EDT_NOPROBE)); case EINVAL: return (dt_set_errno(dtp, EDT_BADPGLOB)); default: return (dt_set_errno(dtp, errno)); } }
void * dtrace_dof_create(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t flags) { dt_dof_t *ddo = &dtp->dt_dof; const dtrace_ecbdesc_t *edp, *last; const dtrace_probedesc_t *pdp; const dtrace_actdesc_t *ap; const dt_stmt_t *stp; uint_t maxacts = 0; uint_t maxfmt = 0; dt_provider_t *pvp; dt_xlator_t *dxp; dof_actdesc_t *dofa; dof_sec_t *sp; size_t ssize, lsize; dof_hdr_t h; dt_buf_t dof; char *fmt; uint_t i; if (flags & ~DTRACE_D_MASK) { (void) dt_set_errno(dtp, EINVAL); return (NULL); } flags |= dtp->dt_dflags; if (dof_hdr(dtp, pgp->dp_dofversion, &h) != 0) return (NULL); if (dt_dof_reset(dtp, pgp) != 0) return (NULL); /* * Iterate through the statement list computing the maximum number of * actions and the maximum format string for allocating local buffers. */ for (last = NULL, stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = dt_list_next(stp), last = edp) { dtrace_stmtdesc_t *sdp = stp->ds_desc; dtrace_actdesc_t *ap = sdp->dtsd_action; if (sdp->dtsd_fmtdata != NULL) { i = dtrace_printf_format(dtp, sdp->dtsd_fmtdata, NULL, 0); maxfmt = MAX(maxfmt, i); } if ((edp = sdp->dtsd_ecbdesc) == last) continue; /* same ecb as previous statement */ for (i = 0, ap = edp->dted_action; ap; ap = ap->dtad_next) i++; maxacts = MAX(maxacts, i); } dofa = alloca(sizeof (dof_actdesc_t) * maxacts); fmt = alloca(maxfmt + 1); ddo->ddo_strsec = dof_add_lsect(ddo, NULL, DOF_SECT_STRTAB, 1, 0, 0, 0); (void) dof_add_string(ddo, ""); /* * If there are references to dynamic translators in the program, add * an imported translator table entry for each referenced translator. */ if (pgp->dp_xrefslen != 0) { for (dxp = dt_list_next(&dtp->dt_xlators); dxp != NULL; dxp = dt_list_next(dxp)) { if (dxp->dx_id < pgp->dp_xrefslen && pgp->dp_xrefs[dxp->dx_id] != NULL) dof_add_translator(ddo, dxp, DOF_SECT_XLIMPORT); } } /* * Now iterate through the statement list, creating the DOF section * headers and data for each one and adding them to our buffers. */ for (last = NULL, stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = dt_list_next(stp), last = edp) { dof_secidx_t probesec = DOF_SECIDX_NONE; dof_secidx_t prdsec = DOF_SECIDX_NONE; dof_secidx_t actsec = DOF_SECIDX_NONE; const dt_stmt_t *next = stp; dtrace_stmtdesc_t *sdp = stp->ds_desc; dof_stridx_t strndx = 0; dof_probedesc_t dofp; dof_ecbdesc_t dofe; uint_t i; if ((edp = stp->ds_desc->dtsd_ecbdesc) == last) continue; /* same ecb as previous statement */ pdp = &edp->dted_probe; /* * Add a DOF_SECT_PROBEDESC for the ECB's probe description, * and copy the probe description strings into the string table. */ dofp.dofp_strtab = ddo->ddo_strsec; dofp.dofp_provider = dof_add_string(ddo, pdp->dtpd_provider); dofp.dofp_mod = dof_add_string(ddo, pdp->dtpd_mod); dofp.dofp_func = dof_add_string(ddo, pdp->dtpd_func); dofp.dofp_name = dof_add_string(ddo, pdp->dtpd_name); dofp.dofp_id = pdp->dtpd_id; probesec = dof_add_lsect(ddo, &dofp, DOF_SECT_PROBEDESC, sizeof (dof_secidx_t), 0, sizeof (dof_probedesc_t), sizeof (dof_probedesc_t)); /* * If there is a predicate DIFO associated with the ecbdesc, * write out the DIFO sections and save the DIFO section index. */ if (edp->dted_pred.dtpdd_difo != NULL) prdsec = dof_add_difo(ddo, edp->dted_pred.dtpdd_difo); /* * Now iterate through the action list generating DIFOs as * referenced therein and adding action descriptions to 'dofa'. */ for (i = 0, ap = edp->dted_action; ap != NULL; ap = ap->dtad_next, i++) { if (ap->dtad_difo != NULL) { dofa[i].dofa_difo = dof_add_difo(ddo, ap->dtad_difo); } else dofa[i].dofa_difo = DOF_SECIDX_NONE; /* * If the first action in a statement has string data, * add the string to the global string table. This can * be due either to a printf() format string * (dtsd_fmtdata) or a print() type string * (dtsd_strdata). */ if (sdp != NULL && ap == sdp->dtsd_action) { if (sdp->dtsd_fmtdata != NULL) { (void) dtrace_printf_format(dtp, sdp->dtsd_fmtdata, fmt, maxfmt + 1); strndx = dof_add_string(ddo, fmt); } else if (sdp->dtsd_strdata != NULL) { strndx = dof_add_string(ddo, sdp->dtsd_strdata); } else strndx = 0; /* use dtad_arg instead */ if ((next = dt_list_next(next)) != NULL) sdp = next->ds_desc; else sdp = NULL; } if (strndx != 0) { dofa[i].dofa_arg = strndx; dofa[i].dofa_strtab = ddo->ddo_strsec; } else { dofa[i].dofa_arg = ap->dtad_arg; dofa[i].dofa_strtab = DOF_SECIDX_NONE; } dofa[i].dofa_kind = ap->dtad_kind; dofa[i].dofa_ntuple = ap->dtad_ntuple; dofa[i].dofa_uarg = ap->dtad_uarg; } if (i > 0) { actsec = dof_add_lsect(ddo, dofa, DOF_SECT_ACTDESC, sizeof (uint64_t), 0, sizeof (dof_actdesc_t), sizeof (dof_actdesc_t) * i); } /* * Now finally, add the DOF_SECT_ECBDESC referencing all the * previously created sub-sections. */ dofe.dofe_probes = probesec; dofe.dofe_pred = prdsec; dofe.dofe_actions = actsec; dofe.dofe_pad = 0; dofe.dofe_uarg = edp->dted_uarg; (void) dof_add_lsect(ddo, &dofe, DOF_SECT_ECBDESC, sizeof (uint64_t), 0, 0, sizeof (dof_ecbdesc_t)); } /* * If any providers are user-defined, output DOF sections corresponding * to the providers and the probes and arguments that they define. */ if (flags & DTRACE_D_PROBES) { for (pvp = dt_list_next(&dtp->dt_provlist); pvp != NULL; pvp = dt_list_next(pvp)) dof_add_provider(ddo, pvp); } /* * If we're not stripping unloadable sections, generate compiler * comments and any other unloadable miscellany. */ if (!(flags & DTRACE_D_STRIP)) { (void) dof_add_usect(ddo, _dtrace_version, DOF_SECT_COMMENTS, sizeof (char), 0, 0, strlen(_dtrace_version) + 1); (void) dof_add_usect(ddo, &dtp->dt_uts, DOF_SECT_UTSNAME, sizeof (char), 0, 0, sizeof (struct utsname)); } /* * Compute and fill in the appropriate values for the dof_hdr_t's * dofh_secnum, dofh_loadsz, and dofh_filez values. */ h.dofh_secnum = ddo->ddo_nsecs; ssize = sizeof (h) + dt_buf_len(&ddo->ddo_secs); assert(ssize == sizeof (h) + sizeof (dof_sec_t) * ddo->ddo_nsecs); h.dofh_loadsz = ssize + dt_buf_len(&ddo->ddo_ldata) + dt_buf_len(&ddo->ddo_strs); if (dt_buf_len(&ddo->ddo_udata) != 0) { lsize = roundup(h.dofh_loadsz, sizeof (uint64_t)); h.dofh_filesz = lsize + dt_buf_len(&ddo->ddo_udata); } else { lsize = h.dofh_loadsz; h.dofh_filesz = lsize; } /* * Set the global DOF_SECT_STRTAB's offset to be after the header, * section headers, and other loadable data. Since we're going to * iterate over the buffer data directly, we must check for errors. */ if ((i = dt_buf_error(&ddo->ddo_secs)) != 0) { (void) dt_set_errno(dtp, i); return (NULL); } sp = dt_buf_ptr(&ddo->ddo_secs); assert(sp[ddo->ddo_strsec].dofs_type == DOF_SECT_STRTAB); sp[ddo->ddo_strsec].dofs_offset = ssize + dt_buf_len(&ddo->ddo_ldata); sp[ddo->ddo_strsec].dofs_size = dt_buf_len(&ddo->ddo_strs); /* * Now relocate all the other section headers by adding the appropriate * delta to their respective dofs_offset values. */ for (i = 0; i < ddo->ddo_nsecs; i++, sp++) { if (i == ddo->ddo_strsec) continue; /* already relocated above */ if (sp->dofs_flags & DOF_SECF_LOAD) sp->dofs_offset += ssize; else sp->dofs_offset += lsize; } /* * Finally, assemble the complete in-memory DOF buffer by writing the * header and then concatenating all our buffers. dt_buf_concat() will * propagate any errors and cause dt_buf_claim() to return NULL. */ dt_buf_create(dtp, &dof, "dof", h.dofh_filesz); dt_buf_write(dtp, &dof, &h, sizeof (h), sizeof (uint64_t)); dt_buf_concat(dtp, &dof, &ddo->ddo_secs, sizeof (uint64_t)); dt_buf_concat(dtp, &dof, &ddo->ddo_ldata, sizeof (uint64_t)); dt_buf_concat(dtp, &dof, &ddo->ddo_strs, sizeof (char)); dt_buf_concat(dtp, &dof, &ddo->ddo_udata, sizeof (uint64_t)); return (dt_buf_claim(dtp, &dof)); }
int dt_probe_define(dt_provider_t *pvp, dt_probe_t *prp, const char *fname, const char *rname, uint32_t offset, int isenabled) { dtrace_hdl_t *dtp = pvp->pv_hdl; dt_probe_instance_t *pip; uint32_t **offs; uint_t *noffs, *maxoffs; assert(fname != NULL); for (pip = prp->pr_inst; pip != NULL; pip = pip->pi_next) { if (strcmp(pip->pi_fname, fname) == 0 && ((rname == NULL && pip->pi_rname[0] == '\0') || (rname != NULL && strcmp(pip->pi_rname, rname)) == 0)) break; } if (pip == NULL) { if ((pip = dt_zalloc(dtp, sizeof (*pip))) == NULL) return (-1); if ((pip->pi_offs = dt_zalloc(dtp, sizeof (uint32_t))) == NULL) { dt_free(dtp, pip); return (-1); } if ((pip->pi_enoffs = dt_zalloc(dtp, sizeof (uint32_t))) == NULL) { dt_free(dtp, pip->pi_offs); dt_free(dtp, pip); return (-1); } (void) strlcpy(pip->pi_fname, fname, sizeof (pip->pi_fname)); if (rname != NULL) { if (strlen(rname) + 1 > sizeof (pip->pi_rname)) { dt_free(dtp, pip->pi_offs); dt_free(dtp, pip); return (dt_set_errno(dtp, EDT_COMPILER)); } (void) strcpy(pip->pi_rname, rname); } pip->pi_noffs = 0; pip->pi_maxoffs = 1; pip->pi_nenoffs = 0; pip->pi_maxenoffs = 1; pip->pi_next = prp->pr_inst; prp->pr_inst = pip; } if (isenabled) { offs = &pip->pi_enoffs; noffs = &pip->pi_nenoffs; maxoffs = &pip->pi_maxenoffs; } else { offs = &pip->pi_offs; noffs = &pip->pi_noffs; maxoffs = &pip->pi_maxoffs; } if (*noffs == *maxoffs) { uint_t new_max = *maxoffs * 2; uint32_t *new_offs = dt_alloc(dtp, sizeof (uint32_t) * new_max); if (new_offs == NULL) return (-1); bcopy(*offs, new_offs, sizeof (uint32_t) * *maxoffs); dt_free(dtp, *offs); *maxoffs = new_max; *offs = new_offs; } dt_dprintf("defined probe %s %s:%s %s() +0x%x (%s)\n", isenabled ? "(is-enabled)" : "", pvp->pv_desc.dtvd_name, prp->pr_ident->di_name, fname, offset, rname != NULL ? rname : fname); assert(*noffs < *maxoffs); (*offs)[(*noffs)++] = offset; return (0); }
dt_probe_t * dt_probe_info(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, dtrace_probeinfo_t *pip) { int m_is_glob = pdp->dtpd_mod[0] == '\0' || strisglob(pdp->dtpd_mod); int f_is_glob = pdp->dtpd_func[0] == '\0' || strisglob(pdp->dtpd_func); int n_is_glob = pdp->dtpd_name[0] == '\0' || strisglob(pdp->dtpd_name); dt_probe_t *prp = NULL; const dtrace_pattr_t *pap; dt_provider_t *pvp; dt_ident_t *idp; /* * Attempt to lookup the probe in our existing cache for this provider. * If none is found and an explicit probe ID was specified, discover * that specific probe and cache its description and arguments. */ if ((pvp = dt_provider_lookup(dtp, pdp->dtpd_provider)) != NULL) { size_t keylen = dt_probe_keylen(pdp); char *key = dt_probe_key(pdp, alloca(keylen)); if ((idp = dt_idhash_lookup(pvp->pv_probes, key)) != NULL) prp = idp->di_data; else if (pdp->dtpd_id != DTRACE_IDNONE) prp = dt_probe_discover(pvp, pdp); } /* * If no probe was found in our cache, convert the caller's partial * probe description into a fully-formed matching probe description by * iterating over up to at most two probes that match 'pdp'. We then * call dt_probe_discover() on the resulting probe identifier. */ if (prp == NULL) { dtrace_probedesc_t pd; int m; bzero(&pd, sizeof (pd)); pd.dtpd_id = DTRACE_IDNONE; /* * Call dtrace_probe_iter() to find matching probes. Our * dt_probe_desc() callback will produce the following results: * * m < 0 dtrace_probe_iter() found zero matches (or failed). * m > 0 dtrace_probe_iter() found more than one match. * m = 0 dtrace_probe_iter() found exactly one match. */ if ((m = dtrace_probe_iter(dtp, pdp, dt_probe_desc, &pd)) < 0) return (NULL); /* dt_errno is set for us */ if ((pvp = dt_provider_lookup(dtp, pd.dtpd_provider)) == NULL) return (NULL); /* dt_errno is set for us */ /* * If more than one probe was matched, then do not report probe * information if either of the following conditions is true: * * (a) The Arguments Data stability of the matched provider is * less than Evolving. * * (b) Any description component that is at least Evolving is * empty or is specified using a globbing expression. * * These conditions imply that providers that provide Evolving * or better Arguments Data stability must guarantee that all * probes with identical field names in a field of Evolving or * better Name stability have identical argument signatures. */ if (m > 0) { if (pvp->pv_desc.dtvd_attr.dtpa_args.dtat_data < DTRACE_STABILITY_EVOLVING) { (void) dt_set_errno(dtp, EDT_UNSTABLE); return (NULL); } if (pvp->pv_desc.dtvd_attr.dtpa_mod.dtat_name >= DTRACE_STABILITY_EVOLVING && m_is_glob) { (void) dt_set_errno(dtp, EDT_UNSTABLE); return (NULL); } if (pvp->pv_desc.dtvd_attr.dtpa_func.dtat_name >= DTRACE_STABILITY_EVOLVING && f_is_glob) { (void) dt_set_errno(dtp, EDT_UNSTABLE); return (NULL); } if (pvp->pv_desc.dtvd_attr.dtpa_name.dtat_name >= DTRACE_STABILITY_EVOLVING && n_is_glob) { (void) dt_set_errno(dtp, EDT_UNSTABLE); return (NULL); } } /* * If we matched a probe exported by dtrace(7D), then discover * the real attributes. Otherwise grab the static declaration. */ if (pd.dtpd_id != DTRACE_IDNONE) prp = dt_probe_discover(pvp, &pd); else prp = dt_probe_lookup(pvp, pd.dtpd_name); if (prp == NULL) return (NULL); /* dt_errno is set for us */ } assert(pvp != NULL && prp != NULL); /* * Compute the probe description attributes by taking the minimum of * the attributes of the specified fields. If no provider is specified * or a glob pattern is used for the provider, use Unstable attributes. */ if (pdp->dtpd_provider[0] == '\0' || strisglob(pdp->dtpd_provider)) pap = &_dtrace_prvdesc; else pap = &pvp->pv_desc.dtvd_attr; pip->dtp_attr = pap->dtpa_provider; if (!m_is_glob) pip->dtp_attr = dt_attr_min(pip->dtp_attr, pap->dtpa_mod); if (!f_is_glob) pip->dtp_attr = dt_attr_min(pip->dtp_attr, pap->dtpa_func); if (!n_is_glob) pip->dtp_attr = dt_attr_min(pip->dtp_attr, pap->dtpa_name); pip->dtp_arga = pap->dtpa_args; pip->dtp_argv = prp->pr_argv; pip->dtp_argc = prp->pr_argc; return (prp); }
/* * If a probe was discovered from the kernel, ask dtrace(7D) for a description * of each of its arguments, including native and translated types. */ static dt_probe_t * dt_probe_discover(dt_provider_t *pvp, const dtrace_probedesc_t *pdp) { dtrace_hdl_t *dtp = pvp->pv_hdl; char *name = dt_probe_key(pdp, alloca(dt_probe_keylen(pdp))); dt_node_t *xargs, *nargs; dt_ident_t *idp; dt_probe_t *prp; dtrace_typeinfo_t dtt; int i, nc, xc; int adc = _dtrace_argmax; dtrace_argdesc_t *adv = alloca(sizeof (dtrace_argdesc_t) * adc); dtrace_argdesc_t *adp = adv; assert(strcmp(pvp->pv_desc.dtvd_name, pdp->dtpd_provider) == 0); assert(pdp->dtpd_id != DTRACE_IDNONE); dt_dprintf("discovering probe %s:%s id=%d\n", pvp->pv_desc.dtvd_name, name, pdp->dtpd_id); for (nc = -1, i = 0; i < adc; i++, adp++) { bzero(adp, sizeof (dtrace_argdesc_t)); adp->dtargd_ndx = i; adp->dtargd_id = pdp->dtpd_id; if (dt_ioctl(dtp, DTRACEIOC_PROBEARG, adp) != 0) { (void) dt_set_errno(dtp, errno); return (NULL); } if (adp->dtargd_ndx == DTRACE_ARGNONE) break; /* all argument descs have been retrieved */ nc = MAX(nc, adp->dtargd_mapping); } xc = i; nc++; /* * Now that we have discovered the number of native and translated * arguments from the argument descriptions, allocate a new probe ident * and corresponding dt_probe_t and hash it into the provider. */ xargs = dt_probe_alloc_args(pvp, xc); nargs = dt_probe_alloc_args(pvp, nc); if ((xc != 0 && xargs == NULL) || (nc != 0 && nargs == NULL)) return (NULL); /* dt_errno is set for us */ idp = dt_ident_create(name, DT_IDENT_PROBE, DT_IDFLG_ORPHAN, pdp->dtpd_id, _dtrace_defattr, 0, &dt_idops_probe, NULL, dtp->dt_gen); if (idp == NULL) { (void) dt_set_errno(dtp, EDT_NOMEM); return (NULL); } if ((prp = dt_probe_create(dtp, idp, 2, nargs, nc, xargs, xc)) == NULL) { dt_ident_destroy(idp); return (NULL); } dt_probe_declare(pvp, prp); /* * Once our new dt_probe_t is fully constructed, iterate over the * cached argument descriptions and assign types to prp->pr_nargv[] * and prp->pr_xargv[] and assign mappings to prp->pr_mapping[]. */ for (adp = adv, i = 0; i < xc; i++, adp++) { if (dtrace_type_strcompile(dtp, adp->dtargd_native, &dtt) != 0) { dt_dprintf("failed to resolve input type %s " "for %s:%s arg #%d: %s\n", adp->dtargd_native, pvp->pv_desc.dtvd_name, name, i + 1, dtrace_errmsg(dtp, dtrace_errno(dtp))); dtt.dtt_object = NULL; dtt.dtt_ctfp = NULL; dtt.dtt_type = CTF_ERR; } else { dt_node_type_assign(prp->pr_nargv[adp->dtargd_mapping], dtt.dtt_ctfp, dtt.dtt_type); } if (dtt.dtt_type != CTF_ERR && (adp->dtargd_xlate[0] == '\0' || strcmp(adp->dtargd_native, adp->dtargd_xlate) == 0)) { dt_node_type_propagate(prp->pr_nargv[ adp->dtargd_mapping], prp->pr_xargv[i]); } else if (dtrace_type_strcompile(dtp, adp->dtargd_xlate, &dtt) != 0) { dt_dprintf("failed to resolve output type %s " "for %s:%s arg #%d: %s\n", adp->dtargd_xlate, pvp->pv_desc.dtvd_name, name, i + 1, dtrace_errmsg(dtp, dtrace_errno(dtp))); dtt.dtt_object = NULL; dtt.dtt_ctfp = NULL; dtt.dtt_type = CTF_ERR; } else { dt_node_type_assign(prp->pr_xargv[i], dtt.dtt_ctfp, dtt.dtt_type); } prp->pr_mapping[i] = adp->dtargd_mapping; prp->pr_argv[i] = dtt; } return (prp); }
/*ARGSUSED*/ int dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern) { uint8_t *text; int size; ulong_t i, end = symp->st_size; #if defined(sun) pid_t pid = Pstatus(P)->pr_pid; char dmodel = Pstatus(P)->pr_dmodel; #else pid_t pid = proc_getpid(P); #if __i386__ char dmodel = PR_MODEL_ILP32; #elif __amd64__ char dmodel = PR_MODEL_LP64; #endif #endif ftp->ftps_type = DTFTP_OFFSETS; ftp->ftps_pc = (uintptr_t)symp->st_value; ftp->ftps_size = (size_t)symp->st_size; ftp->ftps_noffs = 0; if ((text = malloc(symp->st_size)) == NULL) { dt_dprintf("mr sparkle: malloc() failed\n"); return (DT_PROC_ERR); } if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { dt_dprintf("mr sparkle: Pread() failed\n"); free(text); return (DT_PROC_ERR); } /* * We can't instrument offsets in functions with jump tables as * we might interpret a jump table offset as an instruction. */ if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { free(text); return (0); } if (strcmp("*", pattern) == 0) { for (i = 0; i < end; i += size) { ftp->ftps_offs[ftp->ftps_noffs++] = i; size = dt_instr_size(&text[i], dtp, pid, symp->st_value + i, dmodel); /* bail if we hit an invalid opcode */ if (size <= 0) break; } } else { char name[sizeof (i) * 2 + 1]; for (i = 0; i < end; i += size) { (void) snprintf(name, sizeof (name), "%lx", i); if (gmatch(name, pattern)) ftp->ftps_offs[ftp->ftps_noffs++] = i; size = dt_instr_size(&text[i], dtp, pid, symp->st_value + i, dmodel); /* bail if we hit an invalid opcode */ if (size <= 0) break; } } free(text); if (ftp->ftps_noffs > 0) { if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { dt_dprintf("fasttrap probe creation ioctl failed: %s\n", strerror(errno)); return (dt_set_errno(dtp, errno)); } } return (ftp->ftps_noffs); }
/*ARGSUSED*/ int dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off) { ftp->ftps_type = DTFTP_OFFSETS; ftp->ftps_pc = (uintptr_t)symp->st_value; ftp->ftps_size = (size_t)symp->st_size; ftp->ftps_noffs = 1; if (strcmp("-", ftp->ftps_func) == 0) { ftp->ftps_offs[0] = off; } else { uint8_t *text; ulong_t i; int size; #if defined(sun) pid_t pid = Pstatus(P)->pr_pid; char dmodel = Pstatus(P)->pr_dmodel; #else pid_t pid = proc_getpid(P); #if __i386__ char dmodel = PR_MODEL_ILP32; #elif __amd64__ char dmodel = PR_MODEL_LP64; #endif #endif if ((text = malloc(symp->st_size)) == NULL) { dt_dprintf("mr sparkle: malloc() failed\n"); return (DT_PROC_ERR); } if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { dt_dprintf("mr sparkle: Pread() failed\n"); free(text); return (DT_PROC_ERR); } /* * We can't instrument offsets in functions with jump tables * as we might interpret a jump table offset as an * instruction. */ if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { free(text); return (0); } for (i = 0; i < symp->st_size; i += size) { if (i == off) { ftp->ftps_offs[0] = i; break; } /* * If we've passed the desired offset without a * match, then the given offset must not lie on a * instruction boundary. */ if (i > off) { free(text); return (DT_PROC_ALIGN); } size = dt_instr_size(&text[i], dtp, pid, symp->st_value + i, dmodel); /* * If we hit an invalid instruction, bail as if we * couldn't find the offset. */ if (size <= 0) { free(text); return (DT_PROC_ALIGN); } } free(text); } if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { dt_dprintf("fasttrap probe creation ioctl failed: %s\n", strerror(errno)); return (dt_set_errno(dtp, errno)); } return (ftp->ftps_noffs); }
/*ARGSUSED*/ int dt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) { uint8_t *text; ulong_t i, end; int size; #if defined(sun) pid_t pid = Pstatus(P)->pr_pid; char dmodel = Pstatus(P)->pr_dmodel; #else pid_t pid = proc_getpid(P); #if __i386__ char dmodel = PR_MODEL_ILP32; #elif __amd64__ char dmodel = PR_MODEL_LP64; #endif #endif /* * We allocate a few extra bytes at the end so we don't have to check * for overrunning the buffer. */ if ((text = calloc(1, symp->st_size + 4)) == NULL) { dt_dprintf("mr sparkle: malloc() failed\n"); return (DT_PROC_ERR); } if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { dt_dprintf("mr sparkle: Pread() failed\n"); free(text); return (DT_PROC_ERR); } ftp->ftps_type = DTFTP_RETURN; ftp->ftps_pc = (uintptr_t)symp->st_value; ftp->ftps_size = (size_t)symp->st_size; ftp->ftps_noffs = 0; /* * If there's a jump table in the function we're only willing to * instrument these specific (and equivalent) instruction sequences: * leave * [rep] ret * and * movl %ebp,%esp * popl %ebp * [rep] ret * * We do this to avoid accidentally interpreting jump table * offsets as actual instructions. */ if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { for (i = 0, end = ftp->ftps_size; i < end; i += size) { size = dt_instr_size(&text[i], dtp, pid, symp->st_value + i, dmodel); /* bail if we hit an invalid opcode */ if (size <= 0) break; if (text[i] == DT_LEAVE && text[i + 1] == DT_RET) { dt_dprintf("leave/ret at %lx\n", i + 1); ftp->ftps_offs[ftp->ftps_noffs++] = i + 1; size = 2; } else if (text[i] == DT_LEAVE && text[i + 1] == DT_REP && text[i + 2] == DT_RET) { dt_dprintf("leave/rep ret at %lx\n", i + 1); ftp->ftps_offs[ftp->ftps_noffs++] = i + 1; size = 3; } else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP && text[i + 2] == DT_POPL_EBP && text[i + 3] == DT_RET) { dt_dprintf("movl/popl/ret at %lx\n", i + 3); ftp->ftps_offs[ftp->ftps_noffs++] = i + 3; size = 4; } else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP && text[i + 2] == DT_POPL_EBP && text[i + 3] == DT_REP && text[i + 4] == DT_RET) { dt_dprintf("movl/popl/rep ret at %lx\n", i + 3); ftp->ftps_offs[ftp->ftps_noffs++] = i + 3; size = 5; } } } else { for (i = 0, end = ftp->ftps_size; i < end; i += size) { size = dt_instr_size(&text[i], dtp, pid, symp->st_value + i, dmodel); /* bail if we hit an invalid opcode */ if (size <= 0) break; /* ordinary ret */ if (size == 1 && text[i] == DT_RET) goto is_ret; /* two-byte ret */ if (size == 2 && text[i] == DT_REP && text[i + 1] == DT_RET) goto is_ret; /* ret <imm16> */ if (size == 3 && text[i] == DT_RET16) goto is_ret; /* two-byte ret <imm16> */ if (size == 4 && text[i] == DT_REP && text[i + 1] == DT_RET16) goto is_ret; /* 32-bit displacement jmp outside of the function */ if (size == 5 && text[i] == DT_JMP32 && symp->st_size <= (uintptr_t)(i + size + *(int32_t *)&text[i + 1])) goto is_ret; /* 8-bit displacement jmp outside of the function */ if (size == 2 && text[i] == DT_JMP8 && symp->st_size <= (uintptr_t)(i + size + *(int8_t *)&text[i + 1])) goto is_ret; /* 32-bit disp. conditional jmp outside of the func. */ if (size == 6 && DT_ISJ32(*(uint16_t *)&text[i]) && symp->st_size <= (uintptr_t)(i + size + *(int32_t *)&text[i + 2])) goto is_ret; /* 8-bit disp. conditional jmp outside of the func. */ if (size == 2 && DT_ISJ8(text[i]) && symp->st_size <= (uintptr_t)(i + size + *(int8_t *)&text[i + 1])) goto is_ret; continue; is_ret: dt_dprintf("return at offset %lx\n", i); ftp->ftps_offs[ftp->ftps_noffs++] = i; } } free(text); if (ftp->ftps_noffs > 0) { if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { dt_dprintf("fasttrap probe creation ioctl failed: %s\n", strerror(errno)); return (dt_set_errno(dtp, errno)); } } return (ftp->ftps_noffs); }
static int dt_opt_rate(dtrace_hdl_t *dtp, const char *arg, uintptr_t option) { char *end; int i; dtrace_optval_t mul = 1, val = 0; const struct { char *name; hrtime_t mul; } suffix[] = { { "ns", NANOSEC / NANOSEC }, { "nsec", NANOSEC / NANOSEC }, { "us", NANOSEC / MICROSEC }, { "usec", NANOSEC / MICROSEC }, { "ms", NANOSEC / MILLISEC }, { "msec", NANOSEC / MILLISEC }, { "s", NANOSEC / SEC }, { "sec", NANOSEC / SEC }, { "m", NANOSEC * (hrtime_t)60 }, { "min", NANOSEC * (hrtime_t)60 }, { "h", NANOSEC * (hrtime_t)60 * (hrtime_t)60 }, { "hour", NANOSEC * (hrtime_t)60 * (hrtime_t)60 }, { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) }, { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) }, { "hz", 0 }, { NULL } }; if (arg != NULL) { #ifndef VBOX errno = 0; val = strtoull(arg, &end, 0); #else int rc = RTStrToInt64Ex(arg, &end, 0, &val); if (rc != VWRN_TRAILING_CHARS) return (dt_set_errno(dtp, EDT_BADOPTVAL)); #endif for (i = 0; suffix[i].name != NULL; i++) { if (strcasecmp(suffix[i].name, end) == 0) { mul = suffix[i].mul; break; } } if (suffix[i].name == NULL && *end != '\0' || val < 0) return (dt_set_errno(dtp, EDT_BADOPTVAL)); if (mul == 0) { /* * The rate has been specified in frequency-per-second. */ if (val != 0) val = NANOSEC / val; } else { val *= mul; } } dtp->dt_options[option] = val; return (0); }