#include <unistd.h>
#include <strings.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <alloca.h>
#include <dt_impl.h>
#include <dt_program.h>
#include <dt_printf.h>
#include <dt_provider.h>
#include <dt_ld.h>
dtrace_prog_t *
dt_program_create(dtrace_hdl_t *dtp)
{
dtrace_prog_t *pgp = dt_zalloc(dtp, sizeof (dtrace_prog_t));
if (pgp != NULL) {
dt_list_append(&dtp->dt_programs, pgp);
} else {
(void) dt_set_errno(dtp, EDT_NOMEM);
return (NULL);
}
pgp->dp_dofversion = DOF_VERSION_3;
return (pgp);
}
void
dt_program_destroy(dtrace_hdl_t *dtp, dtrace_prog_t *pgp)
{
dt_stmt_t *stp, *next;
uint_t i;
for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
next = dt_list_next(stp);
dtrace_stmt_destroy(dtp, stp->ds_desc);
dt_free(dtp, stp);
}
for (i = 0; i < pgp->dp_xrefslen; i++)
dt_free(dtp, pgp->dp_xrefs[i]);
dt_free(dtp, pgp->dp_xrefs);
dt_list_delete(&dtp->dt_programs, pgp);
dt_free(dtp, pgp);
}
void
dtrace_program_info(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
dtrace_proginfo_t *pip)
{
dt_stmt_t *stp;
dtrace_actdesc_t *ap;
dtrace_ecbdesc_t *last = NULL;
if (pip == NULL)
return;
bzero(pip, sizeof (dtrace_proginfo_t));
if (dt_list_next(&pgp->dp_stmts) != NULL) {
pip->dpi_descattr = _dtrace_maxattr;
pip->dpi_stmtattr = _dtrace_maxattr;
} else {
pip->dpi_descattr = _dtrace_defattr;
pip->dpi_stmtattr = _dtrace_defattr;
}
for (stp = dt_list_next(&pgp->dp_stmts); stp; stp = dt_list_next(stp)) {
dtrace_ecbdesc_t *edp = stp->ds_desc->dtsd_ecbdesc;
if (edp == last)
continue;
last = edp;
pip->dpi_descattr =
dt_attr_min(stp->ds_desc->dtsd_descattr, pip->dpi_descattr);
pip->dpi_stmtattr =
dt_attr_min(stp->ds_desc->dtsd_stmtattr, pip->dpi_stmtattr);
if (edp->dted_action == NULL)
pip->dpi_recgens++;
for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
if (ap->dtad_kind == DTRACEACT_SPECULATE) {
pip->dpi_speculations++;
continue;
}
if (DTRACEACT_ISAGG(ap->dtad_kind)) {
pip->dpi_recgens -= ap->dtad_arg;
pip->dpi_aggregates++;
continue;
}
if (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind))
continue;
if (ap->dtad_kind == DTRACEACT_DIFEXPR &&
ap->dtad_difo->dtdo_rtype.dtdt_kind ==
DIF_TYPE_CTF &&
ap->dtad_difo->dtdo_rtype.dtdt_size == 0)
continue;
pip->dpi_recgens++;
}
}
}
int
dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
dtrace_proginfo_t *pip)
{
void *dof;
int n, err;
dtrace_program_info(dtp, pgp, pip);
if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
return (-1);
n = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof);
if (n == -1 && (errno & 0xfffff000))
n = (((unsigned int)errno) >> 12);
dtrace_dof_destroy(dtp, dof);
if (n == -1) {
switch (errno) {
case EINVAL:
err = EDT_DIFINVAL;
break;
case EFAULT:
err = EDT_DIFFAULT;
break;
case E2BIG:
err = EDT_DIFSIZE;
break;
case EBUSY:
err = EDT_ENABLING_ERR;
break;
default:
err = errno;
}
return (dt_set_errno(dtp, err));
}
if (pip != NULL)
pip->dpi_matches += n;
return (0);
}
static void
dt_ecbdesc_hold(dtrace_ecbdesc_t *edp)
{
edp->dted_refcnt++;
}
void
dt_ecbdesc_release(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
{
if (--edp->dted_refcnt > 0)
return;
dt_difo_free(dtp, edp->dted_pred.dtpdd_difo);
assert(edp->dted_action == NULL);
dt_free(dtp, edp);
}
dtrace_ecbdesc_t *
dt_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
{
dtrace_ecbdesc_t *edp;
if ((edp = dt_zalloc(dtp, sizeof (dtrace_ecbdesc_t))) == NULL) {
(void) dt_set_errno(dtp, EDT_NOMEM);
return (NULL);
}
edp->dted_probe = *pdp;
dt_ecbdesc_hold(edp);
return (edp);
}
dtrace_stmtdesc_t *
dtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
{
dtrace_stmtdesc_t *sdp;
if ((sdp = dt_zalloc(dtp, sizeof (dtrace_stmtdesc_t))) == NULL)
return (NULL);
dt_ecbdesc_hold(edp);
sdp->dtsd_ecbdesc = edp;
sdp->dtsd_descattr = _dtrace_defattr;
sdp->dtsd_stmtattr = _dtrace_defattr;
return (sdp);
}
dtrace_actdesc_t *
dtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
{
dtrace_actdesc_t *new;
dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
if ((new = dt_alloc(dtp, sizeof (dtrace_actdesc_t))) == NULL)
return (NULL);
if (sdp->dtsd_action_last != NULL) {
assert(sdp->dtsd_action != NULL);
assert(sdp->dtsd_action_last->dtad_next == NULL);
sdp->dtsd_action_last->dtad_next = new;
} else {
dtrace_actdesc_t *ap = edp->dted_action;
assert(sdp->dtsd_action == NULL);
sdp->dtsd_action = new;
while (ap != NULL && ap->dtad_next != NULL)
ap = ap->dtad_next;
if (ap == NULL)
edp->dted_action = new;
else
ap->dtad_next = new;
}
sdp->dtsd_action_last = new;
bzero(new, sizeof (dtrace_actdesc_t));
new->dtad_uarg = (uintptr_t)sdp;
return (new);
}
int
dtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp)
{
dt_stmt_t *stp = dt_alloc(dtp, sizeof (dt_stmt_t));
if (stp == NULL)
return (-1);
dt_list_append(&pgp->dp_stmts, stp);
stp->ds_desc = sdp;
return (0);
}
int
dtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
dtrace_stmt_f *func, void *data)
{
dt_stmt_t *stp, *next;
int status = 0;
for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
next = dt_list_next(stp);
if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0)
break;
}
return (status);
}
void
dtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
{
dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
if (sdp->dtsd_action != NULL) {
dtrace_actdesc_t *last = sdp->dtsd_action_last;
dtrace_actdesc_t *ap, *next;
assert(last != NULL);
for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
if (ap == sdp->dtsd_action)
break;
if (ap->dtad_next == sdp->dtsd_action)
break;
}
assert(ap != NULL);
if (ap == edp->dted_action)
edp->dted_action = last->dtad_next;
else
ap->dtad_next = last->dtad_next;
last->dtad_next = NULL;
for (ap = sdp->dtsd_action; ap != NULL; ap = next) {
assert(ap->dtad_uarg == (uintptr_t)sdp);
dt_difo_free(dtp, ap->dtad_difo);
next = ap->dtad_next;
dt_free(dtp, ap);
}
}
if (sdp->dtsd_fmtdata != NULL)
dt_printf_destroy(sdp->dtsd_fmtdata);
dt_free(dtp, sdp->dtsd_strdata);
dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
dt_free(dtp, sdp);
}
typedef struct dt_header_info {
dtrace_hdl_t *dthi_dtp;
FILE *dthi_out;
char *dthi_pmname;
char *dthi_pfname;
int dthi_empty;
} dt_header_info_t;
static void
dt_header_fmt_macro(char *buf, const char *str)
{
for (;;) {
if (islower(*str)) {
*buf++ = *str++ + 'A' - 'a';
} else if (*str == '-') {
*buf++ = '_';
str++;
} else if (*str == '.') {
*buf++ = '_';
str++;
} else if ((*buf++ = *str++) == '\0') {
break;
}
}
}
static void
dt_header_fmt_func(char *buf, const char *str)
{
for (;;) {
if (*str == '-') {
*buf++ = '_';
*buf++ = '_';
str++;
} else if ((*buf++ = *str++) == '\0') {
break;
}
}
}
static bool dt_is_single_rank_pointer_type(ctf_file_t *file, ctf_id_t type) {
if (ctf_type_kind(file, type) == CTF_K_POINTER) {
ctf_id_t referenced_type = ctf_type_reference(file, type);
if (ctf_type_kind(file, referenced_type) != CTF_K_POINTER &&
ctf_type_kind(file, referenced_type) != CTF_K_FUNCTION) {
return TRUE;
}
}
return FALSE;
}
static int
dt_header_decl(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
{
dt_header_info_t *infop = data;
dtrace_hdl_t *dtp = infop->dthi_dtp;
dt_probe_t *prp = idp->di_data;
dt_node_t *dnp;
char buf[DT_TYPE_NAMELEN];
char *fname;
const char *p;
int i;
p = prp->pr_name;
for (i = 0; (p = strchr(p, '-')) != NULL; i++)
p++;
fname = alloca(strlen(prp->pr_name) + 1 + i);
dt_header_fmt_func(fname, prp->pr_name);
char* probe;
if ((probe = dt_ld_encode_probe(infop->dthi_pfname, fname, prp)) == NULL)
return (dt_set_errno(dtp, errno));
if (fprintf(infop->dthi_out, "extern void %s(", probe) < 0)
return (dt_set_errno(dtp, errno));
for (dnp = prp->pr_nargs, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
if (fprintf(infop->dthi_out, "%s%s",
dt_is_single_rank_pointer_type(dnp->dn_ctfp, dnp->dn_type) ? "const " : "",
ctf_type_name(dnp->dn_ctfp, dnp->dn_type,
buf, sizeof (buf))) < 0)
return (dt_set_errno(dtp, errno));
if (i + 1 != prp->pr_nargc &&
fprintf(infop->dthi_out, ", ") < 0)
return (dt_set_errno(dtp, errno));
}
if (i == 0 && fprintf(infop->dthi_out, "void") < 0)
return (dt_set_errno(dtp, errno));
if (fprintf(infop->dthi_out, ");\n") < 0)
return (dt_set_errno(dtp, errno));
char* isenabled;
if ((isenabled = dt_ld_encode_isenabled(infop->dthi_pfname, fname)) == NULL)
return (dt_set_errno(dtp, errno));
if (fprintf(infop->dthi_out, "extern int %s(void);\n", isenabled) < 0)
return (dt_set_errno(dtp, errno));
free(isenabled);
isenabled = NULL;
return (0);
}
static int
dt_header_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
{
dt_header_info_t *infop = data;
dtrace_hdl_t *dtp = infop->dthi_dtp;
dt_probe_t *prp = idp->di_data;
char *mname, *fname;
const char *p;
int i;
p = prp->pr_name;
for (i = 0; (p = strchr(p, '-')) != NULL; i++)
p++;
mname = alloca(strlen(prp->pr_name) + 1);
dt_header_fmt_macro(mname, prp->pr_name);
fname = alloca(strlen(prp->pr_name) + 1 + i);
dt_header_fmt_func(fname, prp->pr_name);
if (fprintf(infop->dthi_out, "#define\t%s_%s(",
infop->dthi_pmname, mname) < 0)
return (dt_set_errno(dtp, errno));
for (i = 0; i < prp->pr_nargc; i++) {
if (fprintf(infop->dthi_out, "arg%d", i) < 0)
return (dt_set_errno(dtp, errno));
if (i + 1 != prp->pr_nargc &&
fprintf(infop->dthi_out, ", ") < 0)
return (dt_set_errno(dtp, errno));
}
if (fprintf(infop->dthi_out, ") \\\n") < 0)
return (dt_set_errno(dtp, errno));
if (fprintf(infop->dthi_out, "do { \\\n\t") < 0)
return (dt_set_errno(dtp, errno));
if (!infop->dthi_empty) {
if (fprintf(infop->dthi_out, "__asm__ volatile(\".reference \" %s_TYPEDEFS); \\\n\t", infop->dthi_pmname) < 0)
return (dt_set_errno(dtp, errno));
char* probe;
if ((probe = dt_ld_encode_probe(infop->dthi_pfname, fname, prp)) == NULL)
return (dt_set_errno(dtp, errno));
if (fprintf(infop->dthi_out, "%s(", probe) < 0)
return (dt_set_errno(dtp, errno));
free(probe);
probe = NULL;
for (i = 0; i < prp->pr_nargc; i++) {
if (fprintf(infop->dthi_out, "arg%d", i) < 0)
return (dt_set_errno(dtp, errno));
if (i + 1 != prp->pr_nargc &&
fprintf(infop->dthi_out, ", ") < 0)
return (dt_set_errno(dtp, errno));
}
if (fprintf(infop->dthi_out, "); \\\n\t") < 0)
return (dt_set_errno(dtp, errno));
if (!infop->dthi_empty) {
if (fprintf(infop->dthi_out, "__asm__ volatile(\".reference \" %s_STABILITY); \\\n", infop->dthi_pmname) < 0)
return (dt_set_errno(dtp, errno));
}
}
if (fprintf(infop->dthi_out, "} while (0)\n") < 0)
return (dt_set_errno(dtp, errno));
if (!infop->dthi_empty) {
char* isenabled;
if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() \\\n",
infop->dthi_pmname, mname) < 0)
return (dt_set_errno(dtp, errno));
if ((isenabled = dt_ld_encode_isenabled(infop->dthi_pfname, fname)) == NULL)
return (dt_set_errno(dtp, errno));
if (fprintf(infop->dthi_out, "\t({ int _r = %s(); \\\n", isenabled) < 0)
return (dt_set_errno(dtp, errno));
if (fprintf(infop->dthi_out, "\t\t__asm__ volatile(\"\"); \\\n") < 0)
return (dt_set_errno(dtp, errno));
if (fprintf(infop->dthi_out, "\t\t_r; })\n") < 0)
return (dt_set_errno(dtp, errno));
free(isenabled);
isenabled = NULL;
} else {
if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() (0)\n",
infop->dthi_pmname, mname) < 0)
return (dt_set_errno(dtp, errno));
}
return (0);
}
static int
dt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out)
{
dt_header_info_t info;
const char *p;
int i;
if (pvp->pv_flags & DT_PROVIDER_IMPL)
return (0);
p = pvp->pv_desc.dtvd_name;
for (i = 0; (p = strchr(p, '-')) != NULL; i++)
p++;
info.dthi_dtp = dtp;
info.dthi_out = out;
info.dthi_empty = 0;
info.dthi_pmname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1);
dt_header_fmt_macro(info.dthi_pmname, pvp->pv_desc.dtvd_name);
info.dthi_pfname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1 + i);
dt_header_fmt_func(info.dthi_pfname, pvp->pv_desc.dtvd_name);
char* stability;
if ((stability = dt_ld_encode_stability(info.dthi_pfname, pvp)) == NULL)
return (dt_set_errno(dtp, errno));
if (fprintf(out, "#define %s_STABILITY \"%s\"\n\n", info.dthi_pmname, stability) < 0)
return (dt_set_errno(dtp, errno));
free(stability);
stability = NULL;
char* typedefs;
if ((typedefs = dt_ld_encode_typedefs(info.dthi_pfname, pvp)) == NULL)
return (dt_set_errno(dtp, errno));
if (fprintf(out, "#define %s_TYPEDEFS \"%s\"\n\n", info.dthi_pmname, typedefs) < 0)
return (dt_set_errno(dtp, errno));
free(typedefs);
typedefs = NULL;
if (fprintf(out, "#if !defined(DTRACE_PROBES_DISABLED) || !DTRACE_PROBES_DISABLED\n\n") < 0)
return (dt_set_errno(dtp, errno));
if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
return (-1);
if (fprintf(out, "\n\n") < 0)
return (dt_set_errno(dtp, errno));
if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0)
return (-1);
if (fprintf(out, "\n#else\n\n") < 0)
return (dt_set_errno(dtp, errno));
info.dthi_empty = 1;
if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
return (-1);
if (fprintf(out, "\n#endif /* !defined(DTRACE_PROBES_DISABLED) || !DTRACE_PROBES_DISABLED */\n\n") < 0)
return (dt_set_errno(dtp, errno));
return (0);
}
int
dtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname)
{
dt_provider_t *pvp;
char *mfname, *p;
if (fname != NULL) {
if ((p = strrchr(fname, '/')) != NULL)
fname = p + 1;
mfname = alloca(strlen(fname) + 1);
dt_header_fmt_macro(mfname, fname);
if (fprintf(out, "#ifndef\t_%s\n#define\t_%s\n\n",
mfname, mfname) < 0)
return (dt_set_errno(dtp, errno));
}
if (fprintf(out, "#include <unistd.h>\n\n") < 0)
return (-1);
if (fprintf(out, "#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\n") < 0)
return (-1);
for (pvp = dt_list_next(&dtp->dt_provlist);
pvp != NULL; pvp = dt_list_next(pvp)) {
if (dt_header_provider(dtp, pvp, out) != 0)
return (-1);
}
if (fprintf(out, "\n#ifdef\t__cplusplus\n}\n#endif\n") < 0)
return (dt_set_errno(dtp, errno));
if (fname != NULL && fprintf(out, "\n#endif\t/* _%s */\n", mfname) < 0)
return (dt_set_errno(dtp, errno));
return (0);
}