diff options
Diffstat (limited to 'lib/libdtrace/common')
60 files changed, 39372 insertions, 0 deletions
diff --git a/lib/libdtrace/common/drti.c b/lib/libdtrace/common/drti.c new file mode 100644 index 000000000000..c983c5b595a8 --- /dev/null +++ b/lib/libdtrace/common/drti.c @@ -0,0 +1,206 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <link.h> +#include <sys/dtrace.h> + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/* + * In Solaris 10 GA, the only mechanism for communicating helper information + * is through the DTrace helper pseudo-device node in /devices; there is + * no /dev link. Because of this, USDT providers and helper actions don't + * work inside of non-global zones. This issue was addressed by adding + * the /dev and having this initialization code use that /dev link. If the + * /dev link doesn't exist it falls back to looking for the /devices node + * as this code may be embedded in a binary which runs on Solaris 10 GA. + * + * Users may set the following environment variable to affect the way + * helper initialization takes place: + * + * DTRACE_DOF_INIT_DEBUG enable debugging output + * DTRACE_DOF_INIT_DISABLE disable helper loading + * DTRACE_DOF_INIT_DEVNAME set the path to the helper node + */ + +static const char *devnamep = "/dev/dtrace/helper"; +static const char *olddevname = "/devices/pseudo/dtrace@0:helper"; + +static const char *modname; /* Name of this load object */ +static int gen; /* DOF helper generation */ +extern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */ + +static void +dprintf(int debug, const char *fmt, ...) +{ + va_list ap; + + if (debug && getenv("DTRACE_DOF_INIT_DEBUG") == NULL) + return; + + va_start(ap, fmt); + + if (modname == NULL) + (void) fprintf(stderr, "dtrace DOF: "); + else + (void) fprintf(stderr, "dtrace DOF %s: ", modname); + + (void) vfprintf(stderr, fmt, ap); + + if (fmt[strlen(fmt) - 1] != '\n') + (void) fprintf(stderr, ": %s\n", strerror(errno)); + + va_end(ap); +} + +#if defined(sun) +#pragma init(dtrace_dof_init) +#else +static void dtrace_dof_init(void) __attribute__ ((constructor)); +#endif + +static void +dtrace_dof_init(void) +{ + dof_hdr_t *dof = &__SUNW_dof; +#ifdef _LP64 + Elf64_Ehdr *elf; +#else + Elf32_Ehdr *elf; +#endif + dof_helper_t dh; +#if defined(sun) + Link_map *lmp; + Lmid_t lmid; +#else + struct link_map *lmp; + u_long lmid = 0; +#endif + int fd; + const char *p; + + if (getenv("DTRACE_DOF_INIT_DISABLE") != NULL) + return; + + if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1 || lmp == NULL) { + dprintf(1, "couldn't discover module name or address\n"); + return; + } + +#if defined(sun) + if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) { + dprintf(1, "couldn't discover link map ID\n"); + return; + } +#endif + + if ((modname = strrchr(lmp->l_name, '/')) == NULL) + modname = lmp->l_name; + else + modname++; + + if (dof->dofh_ident[DOF_ID_MAG0] != DOF_MAG_MAG0 || + dof->dofh_ident[DOF_ID_MAG1] != DOF_MAG_MAG1 || + dof->dofh_ident[DOF_ID_MAG2] != DOF_MAG_MAG2 || + dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) { + dprintf(0, ".SUNW_dof section corrupt\n"); + return; + } + + elf = (void *)lmp->l_addr; + + dh.dofhp_dof = (uintptr_t)dof; + dh.dofhp_addr = elf->e_type == ET_DYN ? (uintptr_t) lmp->l_addr : 0; + + if (lmid == 0) { + (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), + "%s", modname); + } else { + (void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod), + "LM%lu`%s", lmid, modname); + } + + if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL) + devnamep = p; + + if ((fd = open64(devnamep, O_RDWR)) < 0) { + dprintf(1, "failed to open helper device %s", devnamep); + + /* + * If the device path wasn't explicitly set, try again with + * the old device path. + */ + if (p != NULL) + return; + + devnamep = olddevname; + + if ((fd = open64(devnamep, O_RDWR)) < 0) { + dprintf(1, "failed to open helper device %s", devnamep); + return; + } + } + + if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1) + dprintf(1, "DTrace ioctl failed for DOF at %p", dof); + else + dprintf(1, "DTrace ioctl succeeded for DOF at %p\n", dof); + + (void) close(fd); +} + +#if defined(sun) +#pragma fini(dtrace_dof_fini) +#else +static void dtrace_dof_fini(void) __attribute__ ((destructor)); +#endif + +static void +dtrace_dof_fini(void) +{ + int fd; + + if ((fd = open64(devnamep, O_RDWR)) < 0) { + dprintf(1, "failed to open helper device %s", devnamep); + return; + } + + if ((gen = ioctl(fd, DTRACEHIOC_REMOVE, gen)) == -1) + dprintf(1, "DTrace ioctl failed to remove DOF (%d)\n", gen); + else + dprintf(1, "DTrace ioctl removed DOF (%d)\n", gen); + + (void) close(fd); +} diff --git a/lib/libdtrace/common/dt_aggregate.c b/lib/libdtrace/common/dt_aggregate.c new file mode 100644 index 000000000000..ac32f769a934 --- /dev/null +++ b/lib/libdtrace/common/dt_aggregate.c @@ -0,0 +1,1886 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <unistd.h> +#include <dt_impl.h> +#include <assert.h> +#if defined(sun) +#include <alloca.h> +#else +#include <sys/sysctl.h> +#endif +#include <limits.h> + +#define DTRACE_AHASHSIZE 32779 /* big 'ol prime */ + +/* + * Because qsort(3C) does not allow an argument to be passed to a comparison + * function, the variables that affect comparison must regrettably be global; + * they are protected by a global static lock, dt_qsort_lock. + */ +static pthread_mutex_t dt_qsort_lock = PTHREAD_MUTEX_INITIALIZER; + +static int dt_revsort; +static int dt_keysort; +static int dt_keypos; + +#define DT_LESSTHAN (dt_revsort == 0 ? -1 : 1) +#define DT_GREATERTHAN (dt_revsort == 0 ? 1 : -1) + +static void +dt_aggregate_count(int64_t *existing, int64_t *new, size_t size) +{ + uint_t i; + + for (i = 0; i < size / sizeof (int64_t); i++) + existing[i] = existing[i] + new[i]; +} + +static int +dt_aggregate_countcmp(int64_t *lhs, int64_t *rhs) +{ + int64_t lvar = *lhs; + int64_t rvar = *rhs; + + if (lvar < rvar) + return (DT_LESSTHAN); + + if (lvar > rvar) + return (DT_GREATERTHAN); + + return (0); +} + +/*ARGSUSED*/ +static void +dt_aggregate_min(int64_t *existing, int64_t *new, size_t size) +{ + if (*new < *existing) + *existing = *new; +} + +/*ARGSUSED*/ +static void +dt_aggregate_max(int64_t *existing, int64_t *new, size_t size) +{ + if (*new > *existing) + *existing = *new; +} + +static int +dt_aggregate_averagecmp(int64_t *lhs, int64_t *rhs) +{ + int64_t lavg = lhs[0] ? (lhs[1] / lhs[0]) : 0; + int64_t ravg = rhs[0] ? (rhs[1] / rhs[0]) : 0; + + if (lavg < ravg) + return (DT_LESSTHAN); + + if (lavg > ravg) + return (DT_GREATERTHAN); + + return (0); +} + +static int +dt_aggregate_stddevcmp(int64_t *lhs, int64_t *rhs) +{ + uint64_t lsd = dt_stddev((uint64_t *)lhs, 1); + uint64_t rsd = dt_stddev((uint64_t *)rhs, 1); + + if (lsd < rsd) + return (DT_LESSTHAN); + + if (lsd > rsd) + return (DT_GREATERTHAN); + + return (0); +} + +/*ARGSUSED*/ +static void +dt_aggregate_lquantize(int64_t *existing, int64_t *new, size_t size) +{ + int64_t arg = *existing++; + uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg); + int i; + + for (i = 0; i <= levels + 1; i++) + existing[i] = existing[i] + new[i + 1]; +} + +static long double +dt_aggregate_lquantizedsum(int64_t *lquanta) +{ + int64_t arg = *lquanta++; + int32_t base = DTRACE_LQUANTIZE_BASE(arg); + uint16_t step = DTRACE_LQUANTIZE_STEP(arg); + uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg), i; + long double total = (long double)lquanta[0] * (long double)(base - 1); + + for (i = 0; i < levels; base += step, i++) + total += (long double)lquanta[i + 1] * (long double)base; + + return (total + (long double)lquanta[levels + 1] * + (long double)(base + 1)); +} + +static int64_t +dt_aggregate_lquantizedzero(int64_t *lquanta) +{ + int64_t arg = *lquanta++; + int32_t base = DTRACE_LQUANTIZE_BASE(arg); + uint16_t step = DTRACE_LQUANTIZE_STEP(arg); + uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg), i; + + if (base - 1 == 0) + return (lquanta[0]); + + for (i = 0; i < levels; base += step, i++) { + if (base != 0) + continue; + + return (lquanta[i + 1]); + } + + if (base + 1 == 0) + return (lquanta[levels + 1]); + + return (0); +} + +static int +dt_aggregate_lquantizedcmp(int64_t *lhs, int64_t *rhs) +{ + long double lsum = dt_aggregate_lquantizedsum(lhs); + long double rsum = dt_aggregate_lquantizedsum(rhs); + int64_t lzero, rzero; + + if (lsum < rsum) + return (DT_LESSTHAN); + + if (lsum > rsum) + return (DT_GREATERTHAN); + + /* + * If they're both equal, then we will compare based on the weights at + * zero. If the weights at zero are equal (or if zero is not within + * the range of the linear quantization), then this will be judged a + * tie and will be resolved based on the key comparison. + */ + lzero = dt_aggregate_lquantizedzero(lhs); + rzero = dt_aggregate_lquantizedzero(rhs); + + if (lzero < rzero) + return (DT_LESSTHAN); + + if (lzero > rzero) + return (DT_GREATERTHAN); + + return (0); +} + +static int +dt_aggregate_quantizedcmp(int64_t *lhs, int64_t *rhs) +{ + int nbuckets = DTRACE_QUANTIZE_NBUCKETS; + long double ltotal = 0, rtotal = 0; + int64_t lzero, rzero; + uint_t i; + + for (i = 0; i < nbuckets; i++) { + int64_t bucketval = DTRACE_QUANTIZE_BUCKETVAL(i); + + if (bucketval == 0) { + lzero = lhs[i]; + rzero = rhs[i]; + } + + ltotal += (long double)bucketval * (long double)lhs[i]; + rtotal += (long double)bucketval * (long double)rhs[i]; + } + + if (ltotal < rtotal) + return (DT_LESSTHAN); + + if (ltotal > rtotal) + return (DT_GREATERTHAN); + + /* + * If they're both equal, then we will compare based on the weights at + * zero. If the weights at zero are equal, then this will be judged a + * tie and will be resolved based on the key comparison. + */ + if (lzero < rzero) + return (DT_LESSTHAN); + + if (lzero > rzero) + return (DT_GREATERTHAN); + + return (0); +} + +static void +dt_aggregate_usym(dtrace_hdl_t *dtp, uint64_t *data) +{ + uint64_t pid = data[0]; + uint64_t *pc = &data[1]; + struct ps_prochandle *P; + GElf_Sym sym; + + if (dtp->dt_vector != NULL) + return; + + if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) == NULL) + return; + + dt_proc_lock(dtp, P); + +#if defined(sun) + if (Plookup_by_addr(P, *pc, NULL, 0, &sym) == 0) +#else + if (proc_addr2sym(P, *pc, NULL, 0, &sym) == 0) +#endif + *pc = sym.st_value; + + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); +} + +static void +dt_aggregate_umod(dtrace_hdl_t *dtp, uint64_t *data) +{ + uint64_t pid = data[0]; + uint64_t *pc = &data[1]; + struct ps_prochandle *P; + const prmap_t *map; + + if (dtp->dt_vector != NULL) + return; + + if ((P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0)) == NULL) + return; + + dt_proc_lock(dtp, P); + +#if defined(sun) + if ((map = Paddr_to_map(P, *pc)) != NULL) +#else + if ((map = proc_addr2map(P, *pc)) != NULL) +#endif + *pc = map->pr_vaddr; + + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); +} + +static void +dt_aggregate_sym(dtrace_hdl_t *dtp, uint64_t *data) +{ + GElf_Sym sym; + uint64_t *pc = data; + + if (dtrace_lookup_by_addr(dtp, *pc, &sym, NULL) == 0) + *pc = sym.st_value; +} + +static void +dt_aggregate_mod(dtrace_hdl_t *dtp, uint64_t *data) +{ + uint64_t *pc = data; + dt_module_t *dmp; + + if (dtp->dt_vector != NULL) { + /* + * We don't have a way of just getting the module for a + * vectored open, and it doesn't seem to be worth defining + * one. This means that use of mod() won't get true + * aggregation in the postmortem case (some modules may + * appear more than once in aggregation output). It seems + * unlikely that anyone will ever notice or care... + */ + return; + } + + for (dmp = dt_list_next(&dtp->dt_modlist); dmp != NULL; + dmp = dt_list_next(dmp)) { + if (*pc - dmp->dm_text_va < dmp->dm_text_size) { + *pc = dmp->dm_text_va; + return; + } + } +} + +static dtrace_aggvarid_t +dt_aggregate_aggvarid(dt_ahashent_t *ent) +{ + dtrace_aggdesc_t *agg = ent->dtahe_data.dtada_desc; + caddr_t data = ent->dtahe_data.dtada_data; + dtrace_recdesc_t *rec = agg->dtagd_rec; + + /* + * First, we'll check the variable ID in the aggdesc. If it's valid, + * we'll return it. If not, we'll use the compiler-generated ID + * present as the first record. + */ + if (agg->dtagd_varid != DTRACE_AGGVARIDNONE) + return (agg->dtagd_varid); + + agg->dtagd_varid = *((dtrace_aggvarid_t *)(uintptr_t)(data + + rec->dtrd_offset)); + + return (agg->dtagd_varid); +} + + +static int +dt_aggregate_snap_cpu(dtrace_hdl_t *dtp, processorid_t cpu) +{ + dtrace_epid_t id; + uint64_t hashval; + size_t offs, roffs, size, ndx; + int i, j, rval; + caddr_t addr, data; + dtrace_recdesc_t *rec; + dt_aggregate_t *agp = &dtp->dt_aggregate; + dtrace_aggdesc_t *agg; + dt_ahash_t *hash = &agp->dtat_hash; + dt_ahashent_t *h; + dtrace_bufdesc_t b = agp->dtat_buf, *buf = &b; + dtrace_aggdata_t *aggdata; + int flags = agp->dtat_flags; + + buf->dtbd_cpu = cpu; + +#if defined(sun) + if (dt_ioctl(dtp, DTRACEIOC_AGGSNAP, buf) == -1) { +#else + if (dt_ioctl(dtp, DTRACEIOC_AGGSNAP, &buf) == -1) { +#endif + if (errno == ENOENT) { + /* + * If that failed with ENOENT, it may be because the + * CPU was unconfigured. This is okay; we'll just + * do nothing but return success. + */ + return (0); + } + + return (dt_set_errno(dtp, errno)); + } + + if (buf->dtbd_drops != 0) { + if (dt_handle_cpudrop(dtp, cpu, + DTRACEDROP_AGGREGATION, buf->dtbd_drops) == -1) + return (-1); + } + + if (buf->dtbd_size == 0) + return (0); + + if (hash->dtah_hash == NULL) { + size_t size; + + hash->dtah_size = DTRACE_AHASHSIZE; + size = hash->dtah_size * sizeof (dt_ahashent_t *); + + if ((hash->dtah_hash = malloc(size)) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + bzero(hash->dtah_hash, size); + } + + for (offs = 0; offs < buf->dtbd_size; ) { + /* + * We're guaranteed to have an ID. + */ + id = *((dtrace_epid_t *)((uintptr_t)buf->dtbd_data + + (uintptr_t)offs)); + + if (id == DTRACE_AGGIDNONE) { + /* + * This is filler to assure proper alignment of the + * next record; we simply ignore it. + */ + offs += sizeof (id); + continue; + } + + if ((rval = dt_aggid_lookup(dtp, id, &agg)) != 0) + return (rval); + + addr = buf->dtbd_data + offs; + size = agg->dtagd_size; + hashval = 0; + + for (j = 0; j < agg->dtagd_nrecs - 1; j++) { + rec = &agg->dtagd_rec[j]; + roffs = rec->dtrd_offset; + + switch (rec->dtrd_action) { + case DTRACEACT_USYM: + dt_aggregate_usym(dtp, + /* LINTED - alignment */ + (uint64_t *)&addr[roffs]); + break; + + case DTRACEACT_UMOD: + dt_aggregate_umod(dtp, + /* LINTED - alignment */ + (uint64_t *)&addr[roffs]); + break; + + case DTRACEACT_SYM: + /* LINTED - alignment */ + dt_aggregate_sym(dtp, (uint64_t *)&addr[roffs]); + break; + + case DTRACEACT_MOD: + /* LINTED - alignment */ + dt_aggregate_mod(dtp, (uint64_t *)&addr[roffs]); + break; + + default: + break; + } + + for (i = 0; i < rec->dtrd_size; i++) + hashval += addr[roffs + i]; + } + + ndx = hashval % hash->dtah_size; + + for (h = hash->dtah_hash[ndx]; h != NULL; h = h->dtahe_next) { + if (h->dtahe_hashval != hashval) + continue; + + if (h->dtahe_size != size) + continue; + + aggdata = &h->dtahe_data; + data = aggdata->dtada_data; + + for (j = 0; j < agg->dtagd_nrecs - 1; j++) { + rec = &agg->dtagd_rec[j]; + roffs = rec->dtrd_offset; + + for (i = 0; i < rec->dtrd_size; i++) + if (addr[roffs + i] != data[roffs + i]) + goto hashnext; + } + + /* + * We found it. Now we need to apply the aggregating + * action on the data here. + */ + rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; + roffs = rec->dtrd_offset; + /* LINTED - alignment */ + h->dtahe_aggregate((int64_t *)&data[roffs], + /* LINTED - alignment */ + (int64_t *)&addr[roffs], rec->dtrd_size); + + /* + * If we're keeping per CPU data, apply the aggregating + * action there as well. + */ + if (aggdata->dtada_percpu != NULL) { + data = aggdata->dtada_percpu[cpu]; + + /* LINTED - alignment */ + h->dtahe_aggregate((int64_t *)data, + /* LINTED - alignment */ + (int64_t *)&addr[roffs], rec->dtrd_size); + } + + goto bufnext; +hashnext: + continue; + } + + /* + * If we're here, we couldn't find an entry for this record. + */ + if ((h = malloc(sizeof (dt_ahashent_t))) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + bzero(h, sizeof (dt_ahashent_t)); + aggdata = &h->dtahe_data; + + if ((aggdata->dtada_data = malloc(size)) == NULL) { + free(h); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + bcopy(addr, aggdata->dtada_data, size); + aggdata->dtada_size = size; + aggdata->dtada_desc = agg; + aggdata->dtada_handle = dtp; + (void) dt_epid_lookup(dtp, agg->dtagd_epid, + &aggdata->dtada_edesc, &aggdata->dtada_pdesc); + aggdata->dtada_normal = 1; + + h->dtahe_hashval = hashval; + h->dtahe_size = size; + (void) dt_aggregate_aggvarid(h); + + rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; + + if (flags & DTRACE_A_PERCPU) { + int max_cpus = agp->dtat_maxcpu; + caddr_t *percpu = malloc(max_cpus * sizeof (caddr_t)); + + if (percpu == NULL) { + free(aggdata->dtada_data); + free(h); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + for (j = 0; j < max_cpus; j++) { + percpu[j] = malloc(rec->dtrd_size); + + if (percpu[j] == NULL) { + while (--j >= 0) + free(percpu[j]); + + free(aggdata->dtada_data); + free(h); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if (j == cpu) { + bcopy(&addr[rec->dtrd_offset], + percpu[j], rec->dtrd_size); + } else { + bzero(percpu[j], rec->dtrd_size); + } + } + + aggdata->dtada_percpu = percpu; + } + + switch (rec->dtrd_action) { + case DTRACEAGG_MIN: + h->dtahe_aggregate = dt_aggregate_min; + break; + + case DTRACEAGG_MAX: + h->dtahe_aggregate = dt_aggregate_max; + break; + + case DTRACEAGG_LQUANTIZE: + h->dtahe_aggregate = dt_aggregate_lquantize; + break; + + case DTRACEAGG_COUNT: + case DTRACEAGG_SUM: + case DTRACEAGG_AVG: + case DTRACEAGG_STDDEV: + case DTRACEAGG_QUANTIZE: + h->dtahe_aggregate = dt_aggregate_count; + break; + + default: + return (dt_set_errno(dtp, EDT_BADAGG)); + } + + if (hash->dtah_hash[ndx] != NULL) + hash->dtah_hash[ndx]->dtahe_prev = h; + + h->dtahe_next = hash->dtah_hash[ndx]; + hash->dtah_hash[ndx] = h; + + if (hash->dtah_all != NULL) + hash->dtah_all->dtahe_prevall = h; + + h->dtahe_nextall = hash->dtah_all; + hash->dtah_all = h; +bufnext: + offs += agg->dtagd_size; + } + + return (0); +} + +int +dtrace_aggregate_snap(dtrace_hdl_t *dtp) +{ + int i, rval; + dt_aggregate_t *agp = &dtp->dt_aggregate; + hrtime_t now = gethrtime(); + dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_AGGRATE]; + + if (dtp->dt_lastagg != 0) { + if (now - dtp->dt_lastagg < interval) + return (0); + + dtp->dt_lastagg += interval; + } else { + dtp->dt_lastagg = now; + } + + if (!dtp->dt_active) + return (dt_set_errno(dtp, EINVAL)); + + if (agp->dtat_buf.dtbd_size == 0) + return (0); + + for (i = 0; i < agp->dtat_ncpus; i++) { + if ((rval = dt_aggregate_snap_cpu(dtp, agp->dtat_cpus[i]))) + return (rval); + } + + return (0); +} + +static int +dt_aggregate_hashcmp(const void *lhs, const void *rhs) +{ + dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); + dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); + dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; + dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; + + if (lagg->dtagd_nrecs < ragg->dtagd_nrecs) + return (DT_LESSTHAN); + + if (lagg->dtagd_nrecs > ragg->dtagd_nrecs) + return (DT_GREATERTHAN); + + return (0); +} + +static int +dt_aggregate_varcmp(const void *lhs, const void *rhs) +{ + dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); + dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); + dtrace_aggvarid_t lid, rid; + + lid = dt_aggregate_aggvarid(lh); + rid = dt_aggregate_aggvarid(rh); + + if (lid < rid) + return (DT_LESSTHAN); + + if (lid > rid) + return (DT_GREATERTHAN); + + return (0); +} + +static int +dt_aggregate_keycmp(const void *lhs, const void *rhs) +{ + dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); + dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); + dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; + dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; + dtrace_recdesc_t *lrec, *rrec; + char *ldata, *rdata; + int rval, i, j, keypos, nrecs; + + if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0) + return (rval); + + nrecs = lagg->dtagd_nrecs - 1; + assert(nrecs == ragg->dtagd_nrecs - 1); + + keypos = dt_keypos + 1 >= nrecs ? 0 : dt_keypos; + + for (i = 1; i < nrecs; i++) { + uint64_t lval, rval; + int ndx = i + keypos; + + if (ndx >= nrecs) + ndx = ndx - nrecs + 1; + + lrec = &lagg->dtagd_rec[ndx]; + rrec = &ragg->dtagd_rec[ndx]; + + ldata = lh->dtahe_data.dtada_data + lrec->dtrd_offset; + rdata = rh->dtahe_data.dtada_data + rrec->dtrd_offset; + + if (lrec->dtrd_size < rrec->dtrd_size) + return (DT_LESSTHAN); + + if (lrec->dtrd_size > rrec->dtrd_size) + return (DT_GREATERTHAN); + + switch (lrec->dtrd_size) { + case sizeof (uint64_t): + /* LINTED - alignment */ + lval = *((uint64_t *)ldata); + /* LINTED - alignment */ + rval = *((uint64_t *)rdata); + break; + + case sizeof (uint32_t): + /* LINTED - alignment */ + lval = *((uint32_t *)ldata); + /* LINTED - alignment */ + rval = *((uint32_t *)rdata); + break; + + case sizeof (uint16_t): + /* LINTED - alignment */ + lval = *((uint16_t *)ldata); + /* LINTED - alignment */ + rval = *((uint16_t *)rdata); + break; + + case sizeof (uint8_t): + lval = *((uint8_t *)ldata); + rval = *((uint8_t *)rdata); + break; + + default: + switch (lrec->dtrd_action) { + case DTRACEACT_UMOD: + case DTRACEACT_UADDR: + case DTRACEACT_USYM: + for (j = 0; j < 2; j++) { + /* LINTED - alignment */ + lval = ((uint64_t *)ldata)[j]; + /* LINTED - alignment */ + rval = ((uint64_t *)rdata)[j]; + + if (lval < rval) + return (DT_LESSTHAN); + + if (lval > rval) + return (DT_GREATERTHAN); + } + + break; + + default: + for (j = 0; j < lrec->dtrd_size; j++) { + lval = ((uint8_t *)ldata)[j]; + rval = ((uint8_t *)rdata)[j]; + + if (lval < rval) + return (DT_LESSTHAN); + + if (lval > rval) + return (DT_GREATERTHAN); + } + } + + continue; + } + + if (lval < rval) + return (DT_LESSTHAN); + + if (lval > rval) + return (DT_GREATERTHAN); + } + + return (0); +} + +static int +dt_aggregate_valcmp(const void *lhs, const void *rhs) +{ + dt_ahashent_t *lh = *((dt_ahashent_t **)lhs); + dt_ahashent_t *rh = *((dt_ahashent_t **)rhs); + dtrace_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; + dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; + caddr_t ldata = lh->dtahe_data.dtada_data; + caddr_t rdata = rh->dtahe_data.dtada_data; + dtrace_recdesc_t *lrec, *rrec; + int64_t *laddr, *raddr; + int rval, i; + + if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0) + return (rval); + + if (lagg->dtagd_nrecs > ragg->dtagd_nrecs) + return (DT_GREATERTHAN); + + if (lagg->dtagd_nrecs < ragg->dtagd_nrecs) + return (DT_LESSTHAN); + + for (i = 0; i < lagg->dtagd_nrecs; i++) { + lrec = &lagg->dtagd_rec[i]; + rrec = &ragg->dtagd_rec[i]; + + if (lrec->dtrd_offset < rrec->dtrd_offset) + return (DT_LESSTHAN); + + if (lrec->dtrd_offset > rrec->dtrd_offset) + return (DT_GREATERTHAN); + + if (lrec->dtrd_action < rrec->dtrd_action) + return (DT_LESSTHAN); + + if (lrec->dtrd_action > rrec->dtrd_action) + return (DT_GREATERTHAN); + } + + laddr = (int64_t *)(uintptr_t)(ldata + lrec->dtrd_offset); + raddr = (int64_t *)(uintptr_t)(rdata + rrec->dtrd_offset); + + switch (lrec->dtrd_action) { + case DTRACEAGG_AVG: + rval = dt_aggregate_averagecmp(laddr, raddr); + break; + + case DTRACEAGG_STDDEV: + rval = dt_aggregate_stddevcmp(laddr, raddr); + break; + + case DTRACEAGG_QUANTIZE: + rval = dt_aggregate_quantizedcmp(laddr, raddr); + break; + + case DTRACEAGG_LQUANTIZE: + rval = dt_aggregate_lquantizedcmp(laddr, raddr); + break; + + case DTRACEAGG_COUNT: + case DTRACEAGG_SUM: + case DTRACEAGG_MIN: + case DTRACEAGG_MAX: + rval = dt_aggregate_countcmp(laddr, raddr); + break; + + default: + assert(0); + } + + return (rval); +} + +static int +dt_aggregate_valkeycmp(const void *lhs, const void *rhs) +{ + int rval; + + if ((rval = dt_aggregate_valcmp(lhs, rhs)) != 0) + return (rval); + + /* + * If we're here, the values for the two aggregation elements are + * equal. We already know that the key layout is the same for the two + * elements; we must now compare the keys themselves as a tie-breaker. + */ + return (dt_aggregate_keycmp(lhs, rhs)); +} + +static int +dt_aggregate_keyvarcmp(const void *lhs, const void *rhs) +{ + int rval; + + if ((rval = dt_aggregate_keycmp(lhs, rhs)) != 0) + return (rval); + + return (dt_aggregate_varcmp(lhs, rhs)); +} + +static int +dt_aggregate_varkeycmp(const void *lhs, const void *rhs) +{ + int rval; + + if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0) + return (rval); + + return (dt_aggregate_keycmp(lhs, rhs)); +} + +static int +dt_aggregate_valvarcmp(const void *lhs, const void *rhs) +{ + int rval; + + if ((rval = dt_aggregate_valkeycmp(lhs, rhs)) != 0) + return (rval); + + return (dt_aggregate_varcmp(lhs, rhs)); +} + +static int +dt_aggregate_varvalcmp(const void *lhs, const void *rhs) +{ + int rval; + + if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0) + return (rval); + + return (dt_aggregate_valkeycmp(lhs, rhs)); +} + +static int +dt_aggregate_keyvarrevcmp(const void *lhs, const void *rhs) +{ + return (dt_aggregate_keyvarcmp(rhs, lhs)); +} + +static int +dt_aggregate_varkeyrevcmp(const void *lhs, const void *rhs) +{ + return (dt_aggregate_varkeycmp(rhs, lhs)); +} + +static int +dt_aggregate_valvarrevcmp(const void *lhs, const void *rhs) +{ + return (dt_aggregate_valvarcmp(rhs, lhs)); +} + +static int +dt_aggregate_varvalrevcmp(const void *lhs, const void *rhs) +{ + return (dt_aggregate_varvalcmp(rhs, lhs)); +} + +static int +dt_aggregate_bundlecmp(const void *lhs, const void *rhs) +{ + dt_ahashent_t **lh = *((dt_ahashent_t ***)lhs); + dt_ahashent_t **rh = *((dt_ahashent_t ***)rhs); + int i, rval; + + if (dt_keysort) { + /* + * If we're sorting on keys, we need to scan until we find the + * last entry -- that's the representative key. (The order of + * the bundle is values followed by key to accommodate the + * default behavior of sorting by value.) If the keys are + * equal, we'll fall into the value comparison loop, below. + */ + for (i = 0; lh[i + 1] != NULL; i++) + continue; + + assert(i != 0); + assert(rh[i + 1] == NULL); + + if ((rval = dt_aggregate_keycmp(&lh[i], &rh[i])) != 0) + return (rval); + } + + for (i = 0; ; i++) { + if (lh[i + 1] == NULL) { + /* + * All of the values are equal; if we're sorting on + * keys, then we're only here because the keys were + * found to be equal and these records are therefore + * equal. If we're not sorting on keys, we'll use the + * key comparison from the representative key as the + * tie-breaker. + */ + if (dt_keysort) + return (0); + + assert(i != 0); + assert(rh[i + 1] == NULL); + return (dt_aggregate_keycmp(&lh[i], &rh[i])); + } else { + if ((rval = dt_aggregate_valcmp(&lh[i], &rh[i])) != 0) + return (rval); + } + } +} + +int +dt_aggregate_go(dtrace_hdl_t *dtp) +{ + dt_aggregate_t *agp = &dtp->dt_aggregate; + dtrace_optval_t size, cpu; + dtrace_bufdesc_t *buf = &agp->dtat_buf; + int rval, i; + + assert(agp->dtat_maxcpu == 0); + assert(agp->dtat_ncpu == 0); + assert(agp->dtat_cpus == NULL); + + agp->dtat_maxcpu = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; + agp->dtat_ncpu = dt_sysconf(dtp, _SC_NPROCESSORS_MAX); + agp->dtat_cpus = malloc(agp->dtat_ncpu * sizeof (processorid_t)); + + if (agp->dtat_cpus == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + /* + * Use the aggregation buffer size as reloaded from the kernel. + */ + size = dtp->dt_options[DTRACEOPT_AGGSIZE]; + + rval = dtrace_getopt(dtp, "aggsize", &size); + assert(rval == 0); + + if (size == 0 || size == DTRACEOPT_UNSET) + return (0); + + buf = &agp->dtat_buf; + buf->dtbd_size = size; + + if ((buf->dtbd_data = malloc(buf->dtbd_size)) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + /* + * Now query for the CPUs enabled. + */ + rval = dtrace_getopt(dtp, "cpu", &cpu); + assert(rval == 0 && cpu != DTRACEOPT_UNSET); + + if (cpu != DTRACE_CPUALL) { + assert(cpu < agp->dtat_ncpu); + agp->dtat_cpus[agp->dtat_ncpus++] = (processorid_t)cpu; + + return (0); + } + + agp->dtat_ncpus = 0; + for (i = 0; i < agp->dtat_maxcpu; i++) { + if (dt_status(dtp, i) == -1) + continue; + + agp->dtat_cpus[agp->dtat_ncpus++] = i; + } + + return (0); +} + +static int +dt_aggwalk_rval(dtrace_hdl_t *dtp, dt_ahashent_t *h, int rval) +{ + dt_aggregate_t *agp = &dtp->dt_aggregate; + dtrace_aggdata_t *data; + dtrace_aggdesc_t *aggdesc; + dtrace_recdesc_t *rec; + int i; + + switch (rval) { + case DTRACE_AGGWALK_NEXT: + break; + + case DTRACE_AGGWALK_CLEAR: { + uint32_t size, offs = 0; + + aggdesc = h->dtahe_data.dtada_desc; + rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; + size = rec->dtrd_size; + data = &h->dtahe_data; + + if (rec->dtrd_action == DTRACEAGG_LQUANTIZE) { + offs = sizeof (uint64_t); + size -= sizeof (uint64_t); + } + + bzero(&data->dtada_data[rec->dtrd_offset] + offs, size); + + if (data->dtada_percpu == NULL) + break; + + for (i = 0; i < dtp->dt_aggregate.dtat_maxcpu; i++) + bzero(data->dtada_percpu[i] + offs, size); + break; + } + + case DTRACE_AGGWALK_ERROR: + /* + * We assume that errno is already set in this case. + */ + return (dt_set_errno(dtp, errno)); + + case DTRACE_AGGWALK_ABORT: + return (dt_set_errno(dtp, EDT_DIRABORT)); + + case DTRACE_AGGWALK_DENORMALIZE: + h->dtahe_data.dtada_normal = 1; + return (0); + + case DTRACE_AGGWALK_NORMALIZE: + if (h->dtahe_data.dtada_normal == 0) { + h->dtahe_data.dtada_normal = 1; + return (dt_set_errno(dtp, EDT_BADRVAL)); + } + + return (0); + + case DTRACE_AGGWALK_REMOVE: { + dtrace_aggdata_t *aggdata = &h->dtahe_data; + int max_cpus = agp->dtat_maxcpu; + + /* + * First, remove this hash entry from its hash chain. + */ + if (h->dtahe_prev != NULL) { + h->dtahe_prev->dtahe_next = h->dtahe_next; + } else { + dt_ahash_t *hash = &agp->dtat_hash; + size_t ndx = h->dtahe_hashval % hash->dtah_size; + + assert(hash->dtah_hash[ndx] == h); + hash->dtah_hash[ndx] = h->dtahe_next; + } + + if (h->dtahe_next != NULL) + h->dtahe_next->dtahe_prev = h->dtahe_prev; + + /* + * Now remove it from the list of all hash entries. + */ + if (h->dtahe_prevall != NULL) { + h->dtahe_prevall->dtahe_nextall = h->dtahe_nextall; + } else { + dt_ahash_t *hash = &agp->dtat_hash; + + assert(hash->dtah_all == h); + hash->dtah_all = h->dtahe_nextall; + } + + if (h->dtahe_nextall != NULL) + h->dtahe_nextall->dtahe_prevall = h->dtahe_prevall; + + /* + * We're unlinked. We can safely destroy the data. + */ + if (aggdata->dtada_percpu != NULL) { + for (i = 0; i < max_cpus; i++) + free(aggdata->dtada_percpu[i]); + free(aggdata->dtada_percpu); + } + + free(aggdata->dtada_data); + free(h); + + return (0); + } + + default: + return (dt_set_errno(dtp, EDT_BADRVAL)); + } + + return (0); +} + +void +dt_aggregate_qsort(dtrace_hdl_t *dtp, void *base, size_t nel, size_t width, + int (*compar)(const void *, const void *)) +{ + int rev = dt_revsort, key = dt_keysort, keypos = dt_keypos; + dtrace_optval_t keyposopt = dtp->dt_options[DTRACEOPT_AGGSORTKEYPOS]; + + dt_revsort = (dtp->dt_options[DTRACEOPT_AGGSORTREV] != DTRACEOPT_UNSET); + dt_keysort = (dtp->dt_options[DTRACEOPT_AGGSORTKEY] != DTRACEOPT_UNSET); + + if (keyposopt != DTRACEOPT_UNSET && keyposopt <= INT_MAX) { + dt_keypos = (int)keyposopt; + } else { + dt_keypos = 0; + } + + if (compar == NULL) { + if (!dt_keysort) { + compar = dt_aggregate_varvalcmp; + } else { + compar = dt_aggregate_varkeycmp; + } + } + + qsort(base, nel, width, compar); + + dt_revsort = rev; + dt_keysort = key; + dt_keypos = keypos; +} + +int +dtrace_aggregate_walk(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) +{ + dt_ahashent_t *h, *next; + dt_ahash_t *hash = &dtp->dt_aggregate.dtat_hash; + + for (h = hash->dtah_all; h != NULL; h = next) { + /* + * dt_aggwalk_rval() can potentially remove the current hash + * entry; we need to load the next hash entry before calling + * into it. + */ + next = h->dtahe_nextall; + + if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) + return (-1); + } + + return (0); +} + +static int +dt_aggregate_walk_sorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg, + int (*sfunc)(const void *, const void *)) +{ + dt_aggregate_t *agp = &dtp->dt_aggregate; + dt_ahashent_t *h, **sorted; + dt_ahash_t *hash = &agp->dtat_hash; + size_t i, nentries = 0; + + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) + nentries++; + + sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *)); + + if (sorted == NULL) + return (-1); + + for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) + sorted[i++] = h; + + (void) pthread_mutex_lock(&dt_qsort_lock); + + if (sfunc == NULL) { + dt_aggregate_qsort(dtp, sorted, nentries, + sizeof (dt_ahashent_t *), NULL); + } else { + /* + * If we've been explicitly passed a sorting function, + * we'll use that -- ignoring the values of the "aggsortrev", + * "aggsortkey" and "aggsortkeypos" options. + */ + qsort(sorted, nentries, sizeof (dt_ahashent_t *), sfunc); + } + + (void) pthread_mutex_unlock(&dt_qsort_lock); + + for (i = 0; i < nentries; i++) { + h = sorted[i]; + + if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) { + dt_free(dtp, sorted); + return (-1); + } + } + + dt_free(dtp, sorted); + return (0); +} + +int +dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, arg, NULL)); +} + +int +dtrace_aggregate_walk_keysorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, + arg, dt_aggregate_varkeycmp)); +} + +int +dtrace_aggregate_walk_valsorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, + arg, dt_aggregate_varvalcmp)); +} + +int +dtrace_aggregate_walk_keyvarsorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, + arg, dt_aggregate_keyvarcmp)); +} + +int +dtrace_aggregate_walk_valvarsorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, + arg, dt_aggregate_valvarcmp)); +} + +int +dtrace_aggregate_walk_keyrevsorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, + arg, dt_aggregate_varkeyrevcmp)); +} + +int +dtrace_aggregate_walk_valrevsorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, + arg, dt_aggregate_varvalrevcmp)); +} + +int +dtrace_aggregate_walk_keyvarrevsorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, + arg, dt_aggregate_keyvarrevcmp)); +} + +int +dtrace_aggregate_walk_valvarrevsorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, + arg, dt_aggregate_valvarrevcmp)); +} + +int +dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars, + int naggvars, dtrace_aggregate_walk_joined_f *func, void *arg) +{ + dt_aggregate_t *agp = &dtp->dt_aggregate; + dt_ahashent_t *h, **sorted = NULL, ***bundle, **nbundle; + const dtrace_aggdata_t **data; + dt_ahashent_t *zaggdata = NULL; + dt_ahash_t *hash = &agp->dtat_hash; + size_t nentries = 0, nbundles = 0, start, zsize = 0, bundlesize; + dtrace_aggvarid_t max = 0, aggvar; + int rval = -1, *map, *remap = NULL; + int i, j; + dtrace_optval_t sortpos = dtp->dt_options[DTRACEOPT_AGGSORTPOS]; + + /* + * If the sorting position is greater than the number of aggregation + * variable IDs, we silently set it to 0. + */ + if (sortpos == DTRACEOPT_UNSET || sortpos >= naggvars) + sortpos = 0; + + /* + * First we need to translate the specified aggregation variable IDs + * into a linear map that will allow us to translate an aggregation + * variable ID into its position in the specified aggvars. + */ + for (i = 0; i < naggvars; i++) { + if (aggvars[i] == DTRACE_AGGVARIDNONE || aggvars[i] < 0) + return (dt_set_errno(dtp, EDT_BADAGGVAR)); + + if (aggvars[i] > max) + max = aggvars[i]; + } + + if ((map = dt_zalloc(dtp, (max + 1) * sizeof (int))) == NULL) + return (-1); + + zaggdata = dt_zalloc(dtp, naggvars * sizeof (dt_ahashent_t)); + + if (zaggdata == NULL) + goto out; + + for (i = 0; i < naggvars; i++) { + int ndx = i + sortpos; + + if (ndx >= naggvars) + ndx -= naggvars; + + aggvar = aggvars[ndx]; + assert(aggvar <= max); + + if (map[aggvar]) { + /* + * We have an aggregation variable that is present + * more than once in the array of aggregation + * variables. While it's unclear why one might want + * to do this, it's legal. To support this construct, + * we will allocate a remap that will indicate the + * position from which this aggregation variable + * should be pulled. (That is, where the remap will + * map from one position to another.) + */ + if (remap == NULL) { + remap = dt_zalloc(dtp, naggvars * sizeof (int)); + + if (remap == NULL) + goto out; + } + + /* + * Given that the variable is already present, assert + * that following through the mapping and adjusting + * for the sort position yields the same aggregation + * variable ID. + */ + assert(aggvars[(map[aggvar] - 1 + sortpos) % + naggvars] == aggvars[ndx]); + + remap[i] = map[aggvar]; + continue; + } + + map[aggvar] = i + 1; + } + + /* + * We need to take two passes over the data to size our allocation, so + * we'll use the first pass to also fill in the zero-filled data to be + * used to properly format a zero-valued aggregation. + */ + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + dtrace_aggvarid_t id; + int ndx; + + if ((id = dt_aggregate_aggvarid(h)) > max || !(ndx = map[id])) + continue; + + if (zaggdata[ndx - 1].dtahe_size == 0) { + zaggdata[ndx - 1].dtahe_size = h->dtahe_size; + zaggdata[ndx - 1].dtahe_data = h->dtahe_data; + } + + nentries++; + } + + if (nentries == 0) { + /* + * We couldn't find any entries; there is nothing else to do. + */ + rval = 0; + goto out; + } + + /* + * Before we sort the data, we're going to look for any holes in our + * zero-filled data. This will occur if an aggregation variable that + * we are being asked to print has not yet been assigned the result of + * any aggregating action for _any_ tuple. The issue becomes that we + * would like a zero value to be printed for all columns for this + * aggregation, but without any record description, we don't know the + * aggregating action that corresponds to the aggregation variable. To + * try to find a match, we're simply going to lookup aggregation IDs + * (which are guaranteed to be contiguous and to start from 1), looking + * for the specified aggregation variable ID. If we find a match, + * we'll use that. If we iterate over all aggregation IDs and don't + * find a match, then we must be an anonymous enabling. (Anonymous + * enablings can't currently derive either aggregation variable IDs or + * aggregation variable names given only an aggregation ID.) In this + * obscure case (anonymous enabling, multiple aggregation printa() with + * some aggregations not represented for any tuple), our defined + * behavior is that the zero will be printed in the format of the first + * aggregation variable that contains any non-zero value. + */ + for (i = 0; i < naggvars; i++) { + if (zaggdata[i].dtahe_size == 0) { + dtrace_aggvarid_t aggvar; + + aggvar = aggvars[(i - sortpos + naggvars) % naggvars]; + assert(zaggdata[i].dtahe_data.dtada_data == NULL); + + for (j = DTRACE_AGGIDNONE + 1; ; j++) { + dtrace_aggdesc_t *agg; + dtrace_aggdata_t *aggdata; + + if (dt_aggid_lookup(dtp, j, &agg) != 0) + break; + + if (agg->dtagd_varid != aggvar) + continue; + + /* + * We have our description -- now we need to + * cons up the zaggdata entry for it. + */ + aggdata = &zaggdata[i].dtahe_data; + aggdata->dtada_size = agg->dtagd_size; + aggdata->dtada_desc = agg; + aggdata->dtada_handle = dtp; + (void) dt_epid_lookup(dtp, agg->dtagd_epid, + &aggdata->dtada_edesc, + &aggdata->dtada_pdesc); + aggdata->dtada_normal = 1; + zaggdata[i].dtahe_hashval = 0; + zaggdata[i].dtahe_size = agg->dtagd_size; + break; + } + + if (zaggdata[i].dtahe_size == 0) { + caddr_t data; + + /* + * We couldn't find this aggregation, meaning + * that we have never seen it before for any + * tuple _and_ this is an anonymous enabling. + * That is, we're in the obscure case outlined + * above. In this case, our defined behavior + * is to format the data in the format of the + * first non-zero aggregation -- of which, of + * course, we know there to be at least one + * (or nentries would have been zero). + */ + for (j = 0; j < naggvars; j++) { + if (zaggdata[j].dtahe_size != 0) + break; + } + + assert(j < naggvars); + zaggdata[i] = zaggdata[j]; + + data = zaggdata[i].dtahe_data.dtada_data; + assert(data != NULL); + } + } + } + + /* + * Now we need to allocate our zero-filled data for use for + * aggregations that don't have a value corresponding to a given key. + */ + for (i = 0; i < naggvars; i++) { + dtrace_aggdata_t *aggdata = &zaggdata[i].dtahe_data; + dtrace_aggdesc_t *aggdesc = aggdata->dtada_desc; + dtrace_recdesc_t *rec; + uint64_t larg; + caddr_t zdata; + + zsize = zaggdata[i].dtahe_size; + assert(zsize != 0); + + if ((zdata = dt_zalloc(dtp, zsize)) == NULL) { + /* + * If we failed to allocated some zero-filled data, we + * need to zero out the remaining dtada_data pointers + * to prevent the wrong data from being freed below. + */ + for (j = i; j < naggvars; j++) + zaggdata[j].dtahe_data.dtada_data = NULL; + goto out; + } + + aggvar = aggvars[(i - sortpos + naggvars) % naggvars]; + + /* + * First, the easy bit. To maintain compatibility with + * consumers that pull the compiler-generated ID out of the + * data, we put that ID at the top of the zero-filled data. + */ + rec = &aggdesc->dtagd_rec[0]; + /* LINTED - alignment */ + *((dtrace_aggvarid_t *)(zdata + rec->dtrd_offset)) = aggvar; + + rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; + + /* + * Now for the more complicated part. If (and only if) this + * is an lquantize() aggregating action, zero-filled data is + * not equivalent to an empty record: we must also get the + * parameters for the lquantize(). + */ + if (rec->dtrd_action == DTRACEAGG_LQUANTIZE) { + if (aggdata->dtada_data != NULL) { + /* + * The easier case here is if we actually have + * some prototype data -- in which case we + * manually dig it out of the aggregation + * record. + */ + /* LINTED - alignment */ + larg = *((uint64_t *)(aggdata->dtada_data + + rec->dtrd_offset)); + } else { + /* + * We don't have any prototype data. As a + * result, we know that we _do_ have the + * compiler-generated information. (If this + * were an anonymous enabling, all of our + * zero-filled data would have prototype data + * -- either directly or indirectly.) So as + * gross as it is, we'll grovel around in the + * compiler-generated information to find the + * lquantize() parameters. + */ + dtrace_stmtdesc_t *sdp; + dt_ident_t *aid; + dt_idsig_t *isp; + + sdp = (dtrace_stmtdesc_t *)(uintptr_t) + aggdesc->dtagd_rec[0].dtrd_uarg; + aid = sdp->dtsd_aggdata; + isp = (dt_idsig_t *)aid->di_data; + assert(isp->dis_auxinfo != 0); + larg = isp->dis_auxinfo; + } + + /* LINTED - alignment */ + *((uint64_t *)(zdata + rec->dtrd_offset)) = larg; + } + + aggdata->dtada_data = zdata; + } + + /* + * Now that we've dealt with setting up our zero-filled data, we can + * allocate our sorted array, and take another pass over the data to + * fill it. + */ + sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *)); + + if (sorted == NULL) + goto out; + + for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) { + dtrace_aggvarid_t id; + + if ((id = dt_aggregate_aggvarid(h)) > max || !map[id]) + continue; + + sorted[i++] = h; + } + + assert(i == nentries); + + /* + * We've loaded our array; now we need to sort by value to allow us + * to create bundles of like value. We're going to acquire the + * dt_qsort_lock here, and hold it across all of our subsequent + * comparison and sorting. + */ + (void) pthread_mutex_lock(&dt_qsort_lock); + + qsort(sorted, nentries, sizeof (dt_ahashent_t *), + dt_aggregate_keyvarcmp); + + /* + * Now we need to go through and create bundles. Because the number + * of bundles is bounded by the size of the sorted array, we're going + * to reuse the underlying storage. And note that "bundle" is an + * array of pointers to arrays of pointers to dt_ahashent_t -- making + * its type (regrettably) "dt_ahashent_t ***". (Regrettable because + * '*' -- like '_' and 'X' -- should never appear in triplicate in + * an ideal world.) + */ + bundle = (dt_ahashent_t ***)sorted; + + for (i = 1, start = 0; i <= nentries; i++) { + if (i < nentries && + dt_aggregate_keycmp(&sorted[i], &sorted[i - 1]) == 0) + continue; + + /* + * We have a bundle boundary. Everything from start to + * (i - 1) belongs in one bundle. + */ + assert(i - start <= naggvars); + bundlesize = (naggvars + 2) * sizeof (dt_ahashent_t *); + + if ((nbundle = dt_zalloc(dtp, bundlesize)) == NULL) { + (void) pthread_mutex_unlock(&dt_qsort_lock); + goto out; + } + + for (j = start; j < i; j++) { + dtrace_aggvarid_t id = dt_aggregate_aggvarid(sorted[j]); + + assert(id <= max); + assert(map[id] != 0); + assert(map[id] - 1 < naggvars); + assert(nbundle[map[id] - 1] == NULL); + nbundle[map[id] - 1] = sorted[j]; + + if (nbundle[naggvars] == NULL) + nbundle[naggvars] = sorted[j]; + } + + for (j = 0; j < naggvars; j++) { + if (nbundle[j] != NULL) + continue; + + /* + * Before we assume that this aggregation variable + * isn't present (and fall back to using the + * zero-filled data allocated earlier), check the + * remap. If we have a remapping, we'll drop it in + * here. Note that we might be remapping an + * aggregation variable that isn't present for this + * key; in this case, the aggregation data that we + * copy will point to the zeroed data. + */ + if (remap != NULL && remap[j]) { + assert(remap[j] - 1 < j); + assert(nbundle[remap[j] - 1] != NULL); + nbundle[j] = nbundle[remap[j] - 1]; + } else { + nbundle[j] = &zaggdata[j]; + } + } + + bundle[nbundles++] = nbundle; + start = i; + } + + /* + * Now we need to re-sort based on the first value. + */ + dt_aggregate_qsort(dtp, bundle, nbundles, sizeof (dt_ahashent_t **), + dt_aggregate_bundlecmp); + + (void) pthread_mutex_unlock(&dt_qsort_lock); + + /* + * We're done! Now we just need to go back over the sorted bundles, + * calling the function. + */ + data = alloca((naggvars + 1) * sizeof (dtrace_aggdata_t *)); + + for (i = 0; i < nbundles; i++) { + for (j = 0; j < naggvars; j++) + data[j + 1] = NULL; + + for (j = 0; j < naggvars; j++) { + int ndx = j - sortpos; + + if (ndx < 0) + ndx += naggvars; + + assert(bundle[i][ndx] != NULL); + data[j + 1] = &bundle[i][ndx]->dtahe_data; + } + + for (j = 0; j < naggvars; j++) + assert(data[j + 1] != NULL); + + /* + * The representative key is the last element in the bundle. + * Assert that we have one, and then set it to be the first + * element of data. + */ + assert(bundle[i][j] != NULL); + data[0] = &bundle[i][j]->dtahe_data; + + if ((rval = func(data, naggvars + 1, arg)) == -1) + goto out; + } + + rval = 0; +out: + for (i = 0; i < nbundles; i++) + dt_free(dtp, bundle[i]); + + if (zaggdata != NULL) { + for (i = 0; i < naggvars; i++) + dt_free(dtp, zaggdata[i].dtahe_data.dtada_data); + } + + dt_free(dtp, zaggdata); + dt_free(dtp, sorted); + dt_free(dtp, remap); + dt_free(dtp, map); + + return (rval); +} + +int +dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp, + dtrace_aggregate_walk_f *func) +{ + dt_print_aggdata_t pd; + + pd.dtpa_dtp = dtp; + pd.dtpa_fp = fp; + pd.dtpa_allunprint = 1; + + if (func == NULL) + func = dtrace_aggregate_walk_sorted; + + if ((*func)(dtp, dt_print_agg, &pd) == -1) + return (dt_set_errno(dtp, dtp->dt_errno)); + + return (0); +} + +void +dtrace_aggregate_clear(dtrace_hdl_t *dtp) +{ + dt_aggregate_t *agp = &dtp->dt_aggregate; + dt_ahash_t *hash = &agp->dtat_hash; + dt_ahashent_t *h; + dtrace_aggdata_t *data; + dtrace_aggdesc_t *aggdesc; + dtrace_recdesc_t *rec; + int i, max_cpus = agp->dtat_maxcpu; + + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + aggdesc = h->dtahe_data.dtada_desc; + rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; + data = &h->dtahe_data; + + bzero(&data->dtada_data[rec->dtrd_offset], rec->dtrd_size); + + if (data->dtada_percpu == NULL) + continue; + + for (i = 0; i < max_cpus; i++) + bzero(data->dtada_percpu[i], rec->dtrd_size); + } +} + +void +dt_aggregate_destroy(dtrace_hdl_t *dtp) +{ + dt_aggregate_t *agp = &dtp->dt_aggregate; + dt_ahash_t *hash = &agp->dtat_hash; + dt_ahashent_t *h, *next; + dtrace_aggdata_t *aggdata; + int i, max_cpus = agp->dtat_maxcpu; + + if (hash->dtah_hash == NULL) { + assert(hash->dtah_all == NULL); + } else { + free(hash->dtah_hash); + + for (h = hash->dtah_all; h != NULL; h = next) { + next = h->dtahe_nextall; + + aggdata = &h->dtahe_data; + + if (aggdata->dtada_percpu != NULL) { + for (i = 0; i < max_cpus; i++) + free(aggdata->dtada_percpu[i]); + free(aggdata->dtada_percpu); + } + + free(aggdata->dtada_data); + free(h); + } + + hash->dtah_hash = NULL; + hash->dtah_all = NULL; + hash->dtah_size = 0; + } + + free(agp->dtat_buf.dtbd_data); + free(agp->dtat_cpus); +} diff --git a/lib/libdtrace/common/dt_as.c b/lib/libdtrace/common/dt_as.c new file mode 100644 index 000000000000..457b8fd7219c --- /dev/null +++ b/lib/libdtrace/common/dt_as.c @@ -0,0 +1,501 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <strings.h> +#include <stdlib.h> +#include <assert.h> + +#include <dt_impl.h> +#include <dt_parser.h> +#include <dt_as.h> + +void +dt_irlist_create(dt_irlist_t *dlp) +{ + bzero(dlp, sizeof (dt_irlist_t)); + dlp->dl_label = 1; +} + +void +dt_irlist_destroy(dt_irlist_t *dlp) +{ + dt_irnode_t *dip, *nip; + + for (dip = dlp->dl_list; dip != NULL; dip = nip) { + nip = dip->di_next; + free(dip); + } +} + +void +dt_irlist_append(dt_irlist_t *dlp, dt_irnode_t *dip) +{ + if (dlp->dl_last != NULL) + dlp->dl_last->di_next = dip; + else + dlp->dl_list = dip; + + dlp->dl_last = dip; + + if (dip->di_label == DT_LBL_NONE || dip->di_instr != DIF_INSTR_NOP) + dlp->dl_len++; /* don't count forward refs in instr count */ +} + +uint_t +dt_irlist_label(dt_irlist_t *dlp) +{ + return (dlp->dl_label++); +} + +/*ARGSUSED*/ +static int +dt_countvar(dt_idhash_t *dhp, dt_ident_t *idp, void *data) +{ + size_t *np = data; + + if (idp->di_flags & (DT_IDFLG_DIFR | DT_IDFLG_DIFW)) + (*np)++; /* include variable in vartab */ + + return (0); +} + +/*ARGSUSED*/ +static int +dt_copyvar(dt_idhash_t *dhp, dt_ident_t *idp, void *data) +{ + dt_pcb_t *pcb = data; + dtrace_difv_t *dvp; + ssize_t stroff; + dt_node_t dn; + + if (!(idp->di_flags & (DT_IDFLG_DIFR | DT_IDFLG_DIFW))) + return (0); /* omit variable from vartab */ + + dvp = &pcb->pcb_difo->dtdo_vartab[pcb->pcb_asvidx++]; + stroff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name); + + if (stroff == -1L) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + if (stroff > DIF_STROFF_MAX) + longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG); + + dvp->dtdv_name = (uint_t)stroff; + dvp->dtdv_id = idp->di_id; + dvp->dtdv_flags = 0; + + dvp->dtdv_kind = (idp->di_kind == DT_IDENT_ARRAY) ? + DIFV_KIND_ARRAY : DIFV_KIND_SCALAR; + + if (idp->di_flags & DT_IDFLG_LOCAL) + dvp->dtdv_scope = DIFV_SCOPE_LOCAL; + else if (idp->di_flags & DT_IDFLG_TLS) + dvp->dtdv_scope = DIFV_SCOPE_THREAD; + else + dvp->dtdv_scope = DIFV_SCOPE_GLOBAL; + + if (idp->di_flags & DT_IDFLG_DIFR) + dvp->dtdv_flags |= DIFV_F_REF; + if (idp->di_flags & DT_IDFLG_DIFW) + dvp->dtdv_flags |= DIFV_F_MOD; + + bzero(&dn, sizeof (dn)); + dt_node_type_assign(&dn, idp->di_ctfp, idp->di_type); + dt_node_diftype(pcb->pcb_hdl, &dn, &dvp->dtdv_type); + + idp->di_flags &= ~(DT_IDFLG_DIFR | DT_IDFLG_DIFW); + return (0); +} + +static ssize_t +dt_copystr(const char *s, size_t n, size_t off, dt_pcb_t *pcb) +{ + bcopy(s, pcb->pcb_difo->dtdo_strtab + off, n); + return (n); +} + +/* + * Rewrite the xlate/xlarg instruction at dtdo_buf[i] so that the instruction's + * xltab index reflects the offset 'xi' of the assigned dtdo_xlmtab[] location. + * We track the cumulative references to translators and members in the pcb's + * pcb_asxrefs[] array, a two-dimensional array of bitmaps indexed by the + * global translator id and then by the corresponding translator member id. + */ +static void +dt_as_xlate(dt_pcb_t *pcb, dtrace_difo_t *dp, + uint_t i, uint_t xi, dt_node_t *dnp) +{ + dtrace_hdl_t *dtp = pcb->pcb_hdl; + dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator; + + assert(i < dp->dtdo_len); + assert(xi < dp->dtdo_xlmlen); + + assert(dnp->dn_kind == DT_NODE_MEMBER); + assert(dnp->dn_membexpr->dn_kind == DT_NODE_XLATOR); + + assert(dxp->dx_id < dtp->dt_xlatorid); + assert(dnp->dn_membid < dxp->dx_nmembers); + + if (pcb->pcb_asxrefs == NULL) { + pcb->pcb_asxreflen = dtp->dt_xlatorid; + pcb->pcb_asxrefs = + dt_zalloc(dtp, sizeof (ulong_t *) * pcb->pcb_asxreflen); + if (pcb->pcb_asxrefs == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + } + + if (pcb->pcb_asxrefs[dxp->dx_id] == NULL) { + pcb->pcb_asxrefs[dxp->dx_id] = + dt_zalloc(dtp, BT_SIZEOFMAP(dxp->dx_nmembers)); + if (pcb->pcb_asxrefs[dxp->dx_id] == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + } + + dp->dtdo_buf[i] = DIF_INSTR_XLATE( + DIF_INSTR_OP(dp->dtdo_buf[i]), xi, DIF_INSTR_RD(dp->dtdo_buf[i])); + + BT_SET(pcb->pcb_asxrefs[dxp->dx_id], dnp->dn_membid); + dp->dtdo_xlmtab[xi] = dnp; +} + +static void +dt_as_undef(const dt_ident_t *idp, uint_t offset) +{ + const char *kind, *mark = (idp->di_flags & DT_IDFLG_USER) ? "``" : "`"; + const dtrace_syminfo_t *dts = idp->di_data; + + if (idp->di_flags & DT_IDFLG_USER) + kind = "user"; + else if (idp->di_flags & DT_IDFLG_PRIM) + kind = "primary kernel"; + else + kind = "loadable kernel"; + + yylineno = idp->di_lineno; + + xyerror(D_ASRELO, "relocation remains against %s symbol %s%s%s (offset " + "0x%x)\n", kind, dts->dts_object, mark, dts->dts_name, offset); +} + +dtrace_difo_t * +dt_as(dt_pcb_t *pcb) +{ + dtrace_hdl_t *dtp = pcb->pcb_hdl; + dt_irlist_t *dlp = &pcb->pcb_ir; + uint_t *labels = NULL; + dt_irnode_t *dip; + dtrace_difo_t *dp; + dt_ident_t *idp; + + size_t n = 0; + uint_t i; + + uint_t kmask, kbits, umask, ubits; + uint_t krel = 0, urel = 0, xlrefs = 0; + + /* + * Select bitmasks based upon the desired symbol linking policy. We + * test (di_extern->di_flags & xmask) == xbits to determine if the + * symbol should have a relocation entry generated in the loop below. + * + * DT_LINK_KERNEL = kernel symbols static, user symbols dynamic + * DT_LINK_PRIMARY = primary kernel symbols static, others dynamic + * DT_LINK_DYNAMIC = all symbols dynamic + * DT_LINK_STATIC = all symbols static + * + * By 'static' we mean that we use the symbol's value at compile-time + * in the final DIF. By 'dynamic' we mean that we create a relocation + * table entry for the symbol's value so it can be relocated later. + */ + switch (dtp->dt_linkmode) { + case DT_LINK_KERNEL: + kmask = 0; + kbits = -1u; + umask = DT_IDFLG_USER; + ubits = DT_IDFLG_USER; + break; + case DT_LINK_PRIMARY: + kmask = DT_IDFLG_USER | DT_IDFLG_PRIM; + kbits = 0; + umask = DT_IDFLG_USER; + ubits = DT_IDFLG_USER; + break; + case DT_LINK_DYNAMIC: + kmask = DT_IDFLG_USER; + kbits = 0; + umask = DT_IDFLG_USER; + ubits = DT_IDFLG_USER; + break; + case DT_LINK_STATIC: + kmask = umask = 0; + kbits = ubits = -1u; + break; + default: + xyerror(D_UNKNOWN, "internal error -- invalid link mode %u\n", + dtp->dt_linkmode); + } + + assert(pcb->pcb_difo == NULL); + pcb->pcb_difo = dt_zalloc(dtp, sizeof (dtrace_difo_t)); + + if ((dp = pcb->pcb_difo) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + dp->dtdo_buf = dt_alloc(dtp, sizeof (dif_instr_t) * dlp->dl_len); + + if (dp->dtdo_buf == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + if ((labels = dt_alloc(dtp, sizeof (uint_t) * dlp->dl_label)) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + /* + * Make an initial pass through the instruction list, filling in the + * instruction buffer with valid instructions and skipping labeled nops. + * While doing this, we also fill in our labels[] translation table + * and we count up the number of relocation table entries we will need. + */ + for (i = 0, dip = dlp->dl_list; dip != NULL; dip = dip->di_next) { + if (dip->di_label != DT_LBL_NONE) + labels[dip->di_label] = i; + + if (dip->di_label == DT_LBL_NONE || + dip->di_instr != DIF_INSTR_NOP) + dp->dtdo_buf[i++] = dip->di_instr; + + if (dip->di_extern == NULL) + continue; /* no external references needed */ + + switch (DIF_INSTR_OP(dip->di_instr)) { + case DIF_OP_SETX: + idp = dip->di_extern; + if ((idp->di_flags & kmask) == kbits) + krel++; + else if ((idp->di_flags & umask) == ubits) + urel++; + break; + case DIF_OP_XLATE: + case DIF_OP_XLARG: + xlrefs++; + break; + default: + xyerror(D_UNKNOWN, "unexpected assembler relocation " + "for opcode 0x%x\n", DIF_INSTR_OP(dip->di_instr)); + } + } + + assert(i == dlp->dl_len); + dp->dtdo_len = dlp->dl_len; + + /* + * Make a second pass through the instructions, relocating each branch + * label to the index of the final instruction in the buffer and noting + * any other instruction-specific DIFO flags such as dtdo_destructive. + */ + for (i = 0; i < dp->dtdo_len; i++) { + dif_instr_t instr = dp->dtdo_buf[i]; + uint_t op = DIF_INSTR_OP(instr); + + if (op == DIF_OP_CALL) { + if (DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUT || + DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUTSTR) + dp->dtdo_destructive = 1; + continue; + } + + if (op >= DIF_OP_BA && op <= DIF_OP_BLEU) { + assert(DIF_INSTR_LABEL(instr) < dlp->dl_label); + dp->dtdo_buf[i] = DIF_INSTR_BRANCH(op, + labels[DIF_INSTR_LABEL(instr)]); + } + } + + dt_free(dtp, labels); + pcb->pcb_asvidx = 0; + + /* + * Allocate memory for the appropriate number of variable records and + * then fill in each variable record. As we populate the variable + * table we insert the corresponding variable names into the strtab. + */ + (void) dt_idhash_iter(dtp->dt_tls, dt_countvar, &n); + (void) dt_idhash_iter(dtp->dt_globals, dt_countvar, &n); + (void) dt_idhash_iter(pcb->pcb_locals, dt_countvar, &n); + + if (n != 0) { + dp->dtdo_vartab = dt_alloc(dtp, n * sizeof (dtrace_difv_t)); + dp->dtdo_varlen = (uint32_t)n; + + if (dp->dtdo_vartab == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + (void) dt_idhash_iter(dtp->dt_tls, dt_copyvar, pcb); + (void) dt_idhash_iter(dtp->dt_globals, dt_copyvar, pcb); + (void) dt_idhash_iter(pcb->pcb_locals, dt_copyvar, pcb); + } + + /* + * Allocate memory for the appropriate number of relocation table + * entries based upon our kernel and user counts from the first pass. + */ + if (krel != 0) { + dp->dtdo_kreltab = dt_alloc(dtp, + krel * sizeof (dof_relodesc_t)); + dp->dtdo_krelen = krel; + + if (dp->dtdo_kreltab == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + } + + if (urel != 0) { + dp->dtdo_ureltab = dt_alloc(dtp, + urel * sizeof (dof_relodesc_t)); + dp->dtdo_urelen = urel; + + if (dp->dtdo_ureltab == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + } + + if (xlrefs != 0) { + dp->dtdo_xlmtab = dt_zalloc(dtp, sizeof (dt_node_t *) * xlrefs); + dp->dtdo_xlmlen = xlrefs; + + if (dp->dtdo_xlmtab == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + } + + /* + * If any relocations are needed, make another pass through the + * instruction list and fill in the relocation table entries. + */ + if (krel + urel + xlrefs != 0) { + uint_t knodef = pcb->pcb_cflags & DTRACE_C_KNODEF; + uint_t unodef = pcb->pcb_cflags & DTRACE_C_UNODEF; + + dof_relodesc_t *krp = dp->dtdo_kreltab; + dof_relodesc_t *urp = dp->dtdo_ureltab; + dt_node_t **xlp = dp->dtdo_xlmtab; + + i = 0; /* dtdo_buf[] index */ + + for (dip = dlp->dl_list; dip != NULL; dip = dip->di_next) { + dof_relodesc_t *rp; + ssize_t soff; + uint_t nodef; + + if (dip->di_label != DT_LBL_NONE && + dip->di_instr == DIF_INSTR_NOP) + continue; /* skip label declarations */ + + i++; /* advance dtdo_buf[] index */ + + if (DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLATE || + DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLARG) { + assert(dp->dtdo_buf[i - 1] == dip->di_instr); + dt_as_xlate(pcb, dp, i - 1, (uint_t) + (xlp++ - dp->dtdo_xlmtab), dip->di_extern); + continue; + } + + if ((idp = dip->di_extern) == NULL) + continue; /* no relocation entry needed */ + + if ((idp->di_flags & kmask) == kbits) { + nodef = knodef; + rp = krp++; + } else if ((idp->di_flags & umask) == ubits) { + nodef = unodef; + rp = urp++; + } else + continue; + + if (!nodef) + dt_as_undef(idp, i); + + assert(DIF_INSTR_OP(dip->di_instr) == DIF_OP_SETX); + soff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name); + + if (soff == -1L) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + if (soff > DIF_STROFF_MAX) + longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG); + + rp->dofr_name = (dof_stridx_t)soff; + rp->dofr_type = DOF_RELO_SETX; + rp->dofr_offset = DIF_INSTR_INTEGER(dip->di_instr) * + sizeof (uint64_t); + rp->dofr_data = 0; + } + + assert(krp == dp->dtdo_kreltab + dp->dtdo_krelen); + assert(urp == dp->dtdo_ureltab + dp->dtdo_urelen); + assert(xlp == dp->dtdo_xlmtab + dp->dtdo_xlmlen); + assert(i == dp->dtdo_len); + } + + /* + * Allocate memory for the compiled string table and then copy the + * chunks from the string table into the final string buffer. + */ + if ((n = dt_strtab_size(pcb->pcb_strtab)) != 0) { + if ((dp->dtdo_strtab = dt_alloc(dtp, n)) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + (void) dt_strtab_write(pcb->pcb_strtab, + (dt_strtab_write_f *)dt_copystr, pcb); + dp->dtdo_strlen = (uint32_t)n; + } + + /* + * Allocate memory for the compiled integer table and then copy the + * integer constants from the table into the final integer buffer. + */ + if ((n = dt_inttab_size(pcb->pcb_inttab)) != 0) { + if ((dp->dtdo_inttab = dt_alloc(dtp, + n * sizeof (uint64_t))) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + dt_inttab_write(pcb->pcb_inttab, dp->dtdo_inttab); + dp->dtdo_intlen = (uint32_t)n; + } + + /* + * Fill in the DIFO return type from the type associated with the + * node saved in pcb_dret, and then clear pcb_difo and pcb_dret + * now that the assembler has completed successfully. + */ + dt_node_diftype(dtp, pcb->pcb_dret, &dp->dtdo_rtype); + pcb->pcb_difo = NULL; + pcb->pcb_dret = NULL; + + if (pcb->pcb_cflags & DTRACE_C_DIFV) + dt_dis(dp, stderr); + + return (dp); +} diff --git a/lib/libdtrace/common/dt_as.h b/lib/libdtrace/common/dt_as.h new file mode 100644 index 000000000000..2acd94091206 --- /dev/null +++ b/lib/libdtrace/common/dt_as.h @@ -0,0 +1,64 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_AS_H +#define _DT_AS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/dtrace.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dt_irnode { + uint_t di_label; /* label number or DT_LBL_NONE */ + dif_instr_t di_instr; /* instruction opcode */ + void *di_extern; /* opcode-specific external reference */ + struct dt_irnode *di_next; /* next instruction */ +} dt_irnode_t; + +#define DT_LBL_NONE 0 /* no label on this instruction */ + +typedef struct dt_irlist { + dt_irnode_t *dl_list; /* pointer to first node in list */ + dt_irnode_t *dl_last; /* pointer to last node in list */ + uint_t dl_len; /* number of valid instructions */ + uint_t dl_label; /* next label number to assign */ +} dt_irlist_t; + +extern void dt_irlist_create(dt_irlist_t *); +extern void dt_irlist_destroy(dt_irlist_t *); +extern void dt_irlist_append(dt_irlist_t *, dt_irnode_t *); +extern uint_t dt_irlist_label(dt_irlist_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_AS_H */ diff --git a/lib/libdtrace/common/dt_buf.c b/lib/libdtrace/common/dt_buf.c new file mode 100644 index 000000000000..324e778213ca --- /dev/null +++ b/lib/libdtrace/common/dt_buf.c @@ -0,0 +1,177 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * DTrace Memory Buffer Routines + * + * The routines in this file are used to create an automatically resizing + * memory buffer that can be written to like a file. Memory buffers are + * used to construct DOF to ioctl() to dtrace(7D), and provide semantics that + * simplify caller code. Specifically, any allocation errors result in an + * error code being set inside the buffer which is maintained persistently and + * propagates to another buffer if the buffer in error is concatenated. These + * semantics permit callers to execute a large series of writes without needing + * to check for errors and then perform a single check before using the buffer. + */ + +#include <sys/sysmacros.h> +#include <strings.h> + +#include <dt_impl.h> +#include <dt_buf.h> + +void +dt_buf_create(dtrace_hdl_t *dtp, dt_buf_t *bp, const char *name, size_t len) +{ + if (len == 0) + len = _dtrace_bufsize; + + bp->dbu_buf = bp->dbu_ptr = dt_zalloc(dtp, len); + bp->dbu_len = len; + + if (bp->dbu_buf == NULL) + bp->dbu_err = dtrace_errno(dtp); + else + bp->dbu_err = 0; + + bp->dbu_resizes = 0; + bp->dbu_name = name; +} + +void +dt_buf_destroy(dtrace_hdl_t *dtp, dt_buf_t *bp) +{ + dt_dprintf("dt_buf_destroy(%s): size=%lu resizes=%u\n", + bp->dbu_name, (ulong_t)bp->dbu_len, bp->dbu_resizes); + + dt_free(dtp, bp->dbu_buf); +} + +void +dt_buf_reset(dtrace_hdl_t *dtp, dt_buf_t *bp) +{ + if ((bp->dbu_ptr = bp->dbu_buf) != NULL) + bp->dbu_err = 0; + else + dt_buf_create(dtp, bp, bp->dbu_name, bp->dbu_len); +} + +void +dt_buf_write(dtrace_hdl_t *dtp, dt_buf_t *bp, + const void *buf, size_t len, size_t align) +{ + size_t off = (size_t)(bp->dbu_ptr - bp->dbu_buf); + size_t adj = roundup(off, align) - off; + + if (bp->dbu_err != 0) { + (void) dt_set_errno(dtp, bp->dbu_err); + return; /* write silently fails */ + } + + if (bp->dbu_ptr + adj + len > bp->dbu_buf + bp->dbu_len) { + size_t new_len = bp->dbu_len * 2; + uchar_t *new_buf; + uint_t r = 1; + + while (bp->dbu_ptr + adj + len > bp->dbu_buf + new_len) { + new_len *= 2; + r++; + } + + if ((new_buf = dt_zalloc(dtp, new_len)) == NULL) { + bp->dbu_err = dtrace_errno(dtp); + return; + } + + bcopy(bp->dbu_buf, new_buf, off); + dt_free(dtp, bp->dbu_buf); + + bp->dbu_buf = new_buf; + bp->dbu_ptr = new_buf + off; + bp->dbu_len = new_len; + bp->dbu_resizes += r; + } + + bp->dbu_ptr += adj; + bcopy(buf, bp->dbu_ptr, len); + bp->dbu_ptr += len; +} + +void +dt_buf_concat(dtrace_hdl_t *dtp, dt_buf_t *dst, + const dt_buf_t *src, size_t align) +{ + if (dst->dbu_err == 0 && src->dbu_err != 0) { + (void) dt_set_errno(dtp, src->dbu_err); + dst->dbu_err = src->dbu_err; + } else { + dt_buf_write(dtp, dst, src->dbu_buf, + (size_t)(src->dbu_ptr - src->dbu_buf), align); + } +} + +size_t +dt_buf_offset(const dt_buf_t *bp, size_t align) +{ + size_t off = (size_t)(bp->dbu_ptr - bp->dbu_buf); + return (roundup(off, align)); +} + +size_t +dt_buf_len(const dt_buf_t *bp) +{ + return (bp->dbu_ptr - bp->dbu_buf); +} + +int +dt_buf_error(const dt_buf_t *bp) +{ + return (bp->dbu_err); +} + +void * +dt_buf_ptr(const dt_buf_t *bp) +{ + return (bp->dbu_buf); +} + +void * +dt_buf_claim(dtrace_hdl_t *dtp, dt_buf_t *bp) +{ + void *buf = bp->dbu_buf; + + if (bp->dbu_err != 0) { + dt_free(dtp, buf); + buf = NULL; + } + + bp->dbu_buf = bp->dbu_ptr = NULL; + bp->dbu_len = 0; + + return (buf); +} diff --git a/lib/libdtrace/common/dt_buf.h b/lib/libdtrace/common/dt_buf.h new file mode 100644 index 000000000000..eb93e13cb751 --- /dev/null +++ b/lib/libdtrace/common/dt_buf.h @@ -0,0 +1,69 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_BUF_H +#define _DT_BUF_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <dtrace.h> + +typedef struct dt_buf { + const char *dbu_name; /* string name for debugging */ + uchar_t *dbu_buf; /* buffer base address */ + uchar_t *dbu_ptr; /* current buffer location */ + size_t dbu_len; /* buffer size in bytes */ + int dbu_err; /* errno value if error */ + int dbu_resizes; /* number of resizes */ +} dt_buf_t; + +extern void dt_buf_create(dtrace_hdl_t *, dt_buf_t *, const char *, size_t); +extern void dt_buf_destroy(dtrace_hdl_t *, dt_buf_t *); +extern void dt_buf_reset(dtrace_hdl_t *, dt_buf_t *); + +extern void dt_buf_write(dtrace_hdl_t *, dt_buf_t *, + const void *, size_t, size_t); + +extern void dt_buf_concat(dtrace_hdl_t *, dt_buf_t *, + const dt_buf_t *, size_t); + +extern size_t dt_buf_offset(const dt_buf_t *, size_t); +extern size_t dt_buf_len(const dt_buf_t *); + +extern int dt_buf_error(const dt_buf_t *); +extern void *dt_buf_ptr(const dt_buf_t *); + +extern void *dt_buf_claim(dtrace_hdl_t *, dt_buf_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_BUF_H */ diff --git a/lib/libdtrace/common/dt_cc.c b/lib/libdtrace/common/dt_cc.c new file mode 100644 index 000000000000..8bd09151d1c4 --- /dev/null +++ b/lib/libdtrace/common/dt_cc.c @@ -0,0 +1,2349 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * DTrace D Language Compiler + * + * The code in this source file implements the main engine for the D language + * compiler. The driver routine for the compiler is dt_compile(), below. The + * compiler operates on either stdio FILEs or in-memory strings as its input + * and can produce either dtrace_prog_t structures from a D program or a single + * dtrace_difo_t structure from a D expression. Multiple entry points are + * provided as wrappers around dt_compile() for the various input/output pairs. + * The compiler itself is implemented across the following source files: + * + * dt_lex.l - lex scanner + * dt_grammar.y - yacc grammar + * dt_parser.c - parse tree creation and semantic checking + * dt_decl.c - declaration stack processing + * dt_xlator.c - D translator lookup and creation + * dt_ident.c - identifier and symbol table routines + * dt_pragma.c - #pragma processing and D pragmas + * dt_printf.c - D printf() and printa() argument checking and processing + * dt_cc.c - compiler driver and dtrace_prog_t construction + * dt_cg.c - DIF code generator + * dt_as.c - DIF assembler + * dt_dof.c - dtrace_prog_t -> DOF conversion + * + * Several other source files provide collections of utility routines used by + * these major files. The compiler itself is implemented in multiple passes: + * + * (1) The input program is scanned and parsed by dt_lex.l and dt_grammar.y + * and parse tree nodes are constructed using the routines in dt_parser.c. + * This node construction pass is described further in dt_parser.c. + * + * (2) The parse tree is "cooked" by assigning each clause a context (see the + * routine dt_setcontext(), below) based on its probe description and then + * recursively descending the tree performing semantic checking. The cook + * routines are also implemented in dt_parser.c and described there. + * + * (3) For actions that are DIF expression statements, the DIF code generator + * and assembler are invoked to create a finished DIFO for the statement. + * + * (4) The dtrace_prog_t data structures for the program clauses and actions + * are built, containing pointers to any DIFOs created in step (3). + * + * (5) The caller invokes a routine in dt_dof.c to convert the finished program + * into DOF format for use in anonymous tracing or enabling in the kernel. + * + * In the implementation, steps 2-4 are intertwined in that they are performed + * in order for each clause as part of a loop that executes over the clauses. + * + * The D compiler currently implements nearly no optimization. The compiler + * implements integer constant folding as part of pass (1), and a set of very + * simple peephole optimizations as part of pass (3). As with any C compiler, + * a large number of optimizations are possible on both the intermediate data + * structures and the generated DIF code. These possibilities should be + * investigated in the context of whether they will have any substantive effect + * on the overall DTrace probe effect before they are undertaken. + */ + +#include <sys/types.h> +#include <sys/wait.h> + +#include <assert.h> +#include <string.h> +#include <strings.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <ucontext.h> +#include <limits.h> +#include <ctype.h> +#include <dirent.h> +#include <dt_module.h> +#include <dt_program.h> +#include <dt_provider.h> +#include <dt_printf.h> +#include <dt_pid.h> +#include <dt_grammar.h> +#include <dt_ident.h> +#include <dt_string.h> +#include <dt_impl.h> + +static const dtrace_diftype_t dt_void_rtype = { + DIF_TYPE_CTF, CTF_K_INTEGER, 0, 0, 0 +}; + +static const dtrace_diftype_t dt_int_rtype = { + DIF_TYPE_CTF, CTF_K_INTEGER, 0, 0, sizeof (uint64_t) +}; + +static void *dt_compile(dtrace_hdl_t *, int, dtrace_probespec_t, void *, + uint_t, int, char *const[], FILE *, const char *); + + +/*ARGSUSED*/ +static int +dt_idreset(dt_idhash_t *dhp, dt_ident_t *idp, void *ignored) +{ + idp->di_flags &= ~(DT_IDFLG_REF | DT_IDFLG_MOD | + DT_IDFLG_DIFR | DT_IDFLG_DIFW); + return (0); +} + +/*ARGSUSED*/ +static int +dt_idpragma(dt_idhash_t *dhp, dt_ident_t *idp, void *ignored) +{ + yylineno = idp->di_lineno; + xyerror(D_PRAGMA_UNUSED, "unused #pragma %s\n", (char *)idp->di_iarg); + return (0); +} + +static dtrace_stmtdesc_t * +dt_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp, + dtrace_attribute_t descattr, dtrace_attribute_t stmtattr) +{ + dtrace_stmtdesc_t *sdp = dtrace_stmt_create(dtp, edp); + + if (sdp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + assert(yypcb->pcb_stmt == NULL); + yypcb->pcb_stmt = sdp; + + sdp->dtsd_descattr = descattr; + sdp->dtsd_stmtattr = stmtattr; + + return (sdp); +} + +static dtrace_actdesc_t * +dt_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *new; + + if ((new = dtrace_stmt_action(dtp, sdp)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + return (new); +} + +/* + * Utility function to determine if a given action description is destructive. + * The dtdo_destructive bit is set for us by the DIF assembler (see dt_as.c). + */ +static int +dt_action_destructive(const dtrace_actdesc_t *ap) +{ + return (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind) || (ap->dtad_kind == + DTRACEACT_DIFEXPR && ap->dtad_difo->dtdo_destructive)); +} + +static void +dt_stmt_append(dtrace_stmtdesc_t *sdp, const dt_node_t *dnp) +{ + dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc; + dtrace_actdesc_t *ap, *tap; + int commit = 0; + int speculate = 0; + int datarec = 0; + + /* + * Make sure that the new statement jibes with the rest of the ECB. + */ + for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) { + if (ap->dtad_kind == DTRACEACT_COMMIT) { + if (commit) { + dnerror(dnp, D_COMM_COMM, "commit( ) may " + "not follow commit( )\n"); + } + + if (datarec) { + dnerror(dnp, D_COMM_DREC, "commit( ) may " + "not follow data-recording action(s)\n"); + } + + for (tap = ap; tap != NULL; tap = tap->dtad_next) { + if (!DTRACEACT_ISAGG(tap->dtad_kind)) + continue; + + dnerror(dnp, D_AGG_COMM, "aggregating actions " + "may not follow commit( )\n"); + } + + commit = 1; + continue; + } + + if (ap->dtad_kind == DTRACEACT_SPECULATE) { + if (speculate) { + dnerror(dnp, D_SPEC_SPEC, "speculate( ) may " + "not follow speculate( )\n"); + } + + if (commit) { + dnerror(dnp, D_SPEC_COMM, "speculate( ) may " + "not follow commit( )\n"); + } + + if (datarec) { + dnerror(dnp, D_SPEC_DREC, "speculate( ) may " + "not follow data-recording action(s)\n"); + } + + speculate = 1; + continue; + } + + if (DTRACEACT_ISAGG(ap->dtad_kind)) { + if (speculate) { + dnerror(dnp, D_AGG_SPEC, "aggregating actions " + "may not follow speculate( )\n"); + } + + datarec = 1; + continue; + } + + if (speculate) { + if (dt_action_destructive(ap)) { + dnerror(dnp, D_ACT_SPEC, "destructive actions " + "may not follow speculate( )\n"); + } + + if (ap->dtad_kind == DTRACEACT_EXIT) { + dnerror(dnp, D_EXIT_SPEC, "exit( ) may not " + "follow speculate( )\n"); + } + } + + /* + * Exclude all non data-recording actions. + */ + if (dt_action_destructive(ap) || + ap->dtad_kind == DTRACEACT_DISCARD) + 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; + + if (commit) { + dnerror(dnp, D_DREC_COMM, "data-recording actions " + "may not follow commit( )\n"); + } + + if (!speculate) + datarec = 1; + } + + if (dtrace_stmt_add(yypcb->pcb_hdl, yypcb->pcb_prog, sdp) != 0) + longjmp(yypcb->pcb_jmpbuf, dtrace_errno(yypcb->pcb_hdl)); + + if (yypcb->pcb_stmt == sdp) + yypcb->pcb_stmt = NULL; +} + +/* + * For the first element of an aggregation tuple or for printa(), we create a + * simple DIF program that simply returns the immediate value that is the ID + * of the aggregation itself. This could be optimized in the future by + * creating a new in-kernel dtad_kind that just returns an integer. + */ +static void +dt_action_difconst(dtrace_actdesc_t *ap, uint_t id, dtrace_actkind_t kind) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dtrace_difo_t *dp = dt_zalloc(dtp, sizeof (dtrace_difo_t)); + + if (dp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + dp->dtdo_buf = dt_alloc(dtp, sizeof (dif_instr_t) * 2); + dp->dtdo_inttab = dt_alloc(dtp, sizeof (uint64_t)); + + if (dp->dtdo_buf == NULL || dp->dtdo_inttab == NULL) { + dt_difo_free(dtp, dp); + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + } + + dp->dtdo_buf[0] = DIF_INSTR_SETX(0, 1); /* setx DIF_INTEGER[0], %r1 */ + dp->dtdo_buf[1] = DIF_INSTR_RET(1); /* ret %r1 */ + dp->dtdo_len = 2; + dp->dtdo_inttab[0] = id; + dp->dtdo_intlen = 1; + dp->dtdo_rtype = dt_int_rtype; + + ap->dtad_difo = dp; + ap->dtad_kind = kind; +} + +static void +dt_action_clear(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dt_ident_t *aid; + dtrace_actdesc_t *ap; + dt_node_t *anp; + + char n[DT_TYPE_NAMELEN]; + int argc = 0; + + for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list) + argc++; /* count up arguments for error messages below */ + + if (argc != 1) { + dnerror(dnp, D_CLEAR_PROTO, + "%s( ) prototype mismatch: %d args passed, 1 expected\n", + dnp->dn_ident->di_name, argc); + } + + anp = dnp->dn_args; + assert(anp != NULL); + + if (anp->dn_kind != DT_NODE_AGG) { + dnerror(dnp, D_CLEAR_AGGARG, + "%s( ) argument #1 is incompatible with prototype:\n" + "\tprototype: aggregation\n\t argument: %s\n", + dnp->dn_ident->di_name, + dt_node_type_name(anp, n, sizeof (n))); + } + + aid = anp->dn_ident; + + if (aid->di_gen == dtp->dt_gen && !(aid->di_flags & DT_IDFLG_MOD)) { + dnerror(dnp, D_CLEAR_AGGBAD, + "undefined aggregation: @%s\n", aid->di_name); + } + + ap = dt_stmt_action(dtp, sdp); + dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_LIBACT); + ap->dtad_arg = DT_ACT_CLEAR; +} + +static void +dt_action_normalize(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dt_ident_t *aid; + dtrace_actdesc_t *ap; + dt_node_t *anp, *normal; + int denormal = (strcmp(dnp->dn_ident->di_name, "denormalize") == 0); + + char n[DT_TYPE_NAMELEN]; + int argc = 0; + + for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list) + argc++; /* count up arguments for error messages below */ + + if ((denormal && argc != 1) || (!denormal && argc != 2)) { + dnerror(dnp, D_NORMALIZE_PROTO, + "%s( ) prototype mismatch: %d args passed, %d expected\n", + dnp->dn_ident->di_name, argc, denormal ? 1 : 2); + } + + anp = dnp->dn_args; + assert(anp != NULL); + + if (anp->dn_kind != DT_NODE_AGG) { + dnerror(dnp, D_NORMALIZE_AGGARG, + "%s( ) argument #1 is incompatible with prototype:\n" + "\tprototype: aggregation\n\t argument: %s\n", + dnp->dn_ident->di_name, + dt_node_type_name(anp, n, sizeof (n))); + } + + if ((normal = anp->dn_list) != NULL && !dt_node_is_scalar(normal)) { + dnerror(dnp, D_NORMALIZE_SCALAR, + "%s( ) argument #2 must be of scalar type\n", + dnp->dn_ident->di_name); + } + + aid = anp->dn_ident; + + if (aid->di_gen == dtp->dt_gen && !(aid->di_flags & DT_IDFLG_MOD)) { + dnerror(dnp, D_NORMALIZE_AGGBAD, + "undefined aggregation: @%s\n", aid->di_name); + } + + ap = dt_stmt_action(dtp, sdp); + dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_LIBACT); + + if (denormal) { + ap->dtad_arg = DT_ACT_DENORMALIZE; + return; + } + + ap->dtad_arg = DT_ACT_NORMALIZE; + + assert(normal != NULL); + ap = dt_stmt_action(dtp, sdp); + dt_cg(yypcb, normal); + + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_LIBACT; + ap->dtad_arg = DT_ACT_NORMALIZE; +} + +static void +dt_action_trunc(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dt_ident_t *aid; + dtrace_actdesc_t *ap; + dt_node_t *anp, *trunc; + + char n[DT_TYPE_NAMELEN]; + int argc = 0; + + for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list) + argc++; /* count up arguments for error messages below */ + + if (argc > 2 || argc < 1) { + dnerror(dnp, D_TRUNC_PROTO, + "%s( ) prototype mismatch: %d args passed, %s expected\n", + dnp->dn_ident->di_name, argc, + argc < 1 ? "at least 1" : "no more than 2"); + } + + anp = dnp->dn_args; + assert(anp != NULL); + trunc = anp->dn_list; + + if (anp->dn_kind != DT_NODE_AGG) { + dnerror(dnp, D_TRUNC_AGGARG, + "%s( ) argument #1 is incompatible with prototype:\n" + "\tprototype: aggregation\n\t argument: %s\n", + dnp->dn_ident->di_name, + dt_node_type_name(anp, n, sizeof (n))); + } + + if (argc == 2) { + assert(trunc != NULL); + if (!dt_node_is_scalar(trunc)) { + dnerror(dnp, D_TRUNC_SCALAR, + "%s( ) argument #2 must be of scalar type\n", + dnp->dn_ident->di_name); + } + } + + aid = anp->dn_ident; + + if (aid->di_gen == dtp->dt_gen && !(aid->di_flags & DT_IDFLG_MOD)) { + dnerror(dnp, D_TRUNC_AGGBAD, + "undefined aggregation: @%s\n", aid->di_name); + } + + ap = dt_stmt_action(dtp, sdp); + dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_LIBACT); + ap->dtad_arg = DT_ACT_TRUNC; + + ap = dt_stmt_action(dtp, sdp); + + if (argc == 1) { + dt_action_difconst(ap, 0, DTRACEACT_LIBACT); + } else { + assert(trunc != NULL); + dt_cg(yypcb, trunc); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_LIBACT; + } + + ap->dtad_arg = DT_ACT_TRUNC; +} + +static void +dt_action_printa(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dt_ident_t *aid, *fid; + dtrace_actdesc_t *ap; + const char *format; + dt_node_t *anp, *proto = NULL; + + char n[DT_TYPE_NAMELEN]; + int argc = 0, argr = 0; + + for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list) + argc++; /* count up arguments for error messages below */ + + switch (dnp->dn_args->dn_kind) { + case DT_NODE_STRING: + format = dnp->dn_args->dn_string; + anp = dnp->dn_args->dn_list; + argr = 2; + break; + case DT_NODE_AGG: + format = NULL; + anp = dnp->dn_args; + argr = 1; + break; + default: + format = NULL; + anp = dnp->dn_args; + argr = 1; + } + + if (argc < argr) { + dnerror(dnp, D_PRINTA_PROTO, + "%s( ) prototype mismatch: %d args passed, %d expected\n", + dnp->dn_ident->di_name, argc, argr); + } + + assert(anp != NULL); + + while (anp != NULL) { + if (anp->dn_kind != DT_NODE_AGG) { + dnerror(dnp, D_PRINTA_AGGARG, + "%s( ) argument #%d is incompatible with " + "prototype:\n\tprototype: aggregation\n" + "\t argument: %s\n", dnp->dn_ident->di_name, argr, + dt_node_type_name(anp, n, sizeof (n))); + } + + aid = anp->dn_ident; + fid = aid->di_iarg; + + if (aid->di_gen == dtp->dt_gen && + !(aid->di_flags & DT_IDFLG_MOD)) { + dnerror(dnp, D_PRINTA_AGGBAD, + "undefined aggregation: @%s\n", aid->di_name); + } + + /* + * If we have multiple aggregations, we must be sure that + * their key signatures match. + */ + if (proto != NULL) { + dt_printa_validate(proto, anp); + } else { + proto = anp; + } + + if (format != NULL) { + yylineno = dnp->dn_line; + + sdp->dtsd_fmtdata = + dt_printf_create(yypcb->pcb_hdl, format); + dt_printf_validate(sdp->dtsd_fmtdata, + DT_PRINTF_AGGREGATION, dnp->dn_ident, 1, + fid->di_id, ((dt_idsig_t *)aid->di_data)->dis_args); + format = NULL; + } + + ap = dt_stmt_action(dtp, sdp); + dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_PRINTA); + + anp = anp->dn_list; + argr++; + } +} + +static void +dt_action_printflike(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp, + dtrace_actkind_t kind) +{ + dt_node_t *anp, *arg1; + dtrace_actdesc_t *ap = NULL; + char n[DT_TYPE_NAMELEN], *str; + + assert(DTRACEACT_ISPRINTFLIKE(kind)); + + if (dnp->dn_args->dn_kind != DT_NODE_STRING) { + dnerror(dnp, D_PRINTF_ARG_FMT, + "%s( ) argument #1 is incompatible with prototype:\n" + "\tprototype: string constant\n\t argument: %s\n", + dnp->dn_ident->di_name, + dt_node_type_name(dnp->dn_args, n, sizeof (n))); + } + + arg1 = dnp->dn_args->dn_list; + yylineno = dnp->dn_line; + str = dnp->dn_args->dn_string; + + + /* + * If this is an freopen(), we use an empty string to denote that + * stdout should be restored. For other printf()-like actions, an + * empty format string is illegal: an empty format string would + * result in malformed DOF, and the compiler thus flags an empty + * format string as a compile-time error. To avoid propagating the + * freopen() special case throughout the system, we simply transpose + * an empty string into a sentinel string (DT_FREOPEN_RESTORE) that + * denotes that stdout should be restored. + */ + if (kind == DTRACEACT_FREOPEN) { + if (strcmp(str, DT_FREOPEN_RESTORE) == 0) { + /* + * Our sentinel is always an invalid argument to + * freopen(), but if it's been manually specified, we + * must fail now instead of when the freopen() is + * actually evaluated. + */ + dnerror(dnp, D_FREOPEN_INVALID, + "%s( ) argument #1 cannot be \"%s\"\n", + dnp->dn_ident->di_name, DT_FREOPEN_RESTORE); + } + + if (str[0] == '\0') + str = DT_FREOPEN_RESTORE; + } + + sdp->dtsd_fmtdata = dt_printf_create(dtp, str); + + dt_printf_validate(sdp->dtsd_fmtdata, DT_PRINTF_EXACTLEN, + dnp->dn_ident, 1, DTRACEACT_AGGREGATION, arg1); + + if (arg1 == NULL) { + dif_instr_t *dbuf; + dtrace_difo_t *dp; + + if ((dbuf = dt_alloc(dtp, sizeof (dif_instr_t))) == NULL || + (dp = dt_zalloc(dtp, sizeof (dtrace_difo_t))) == NULL) { + dt_free(dtp, dbuf); + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + } + + dbuf[0] = DIF_INSTR_RET(DIF_REG_R0); /* ret %r0 */ + + dp->dtdo_buf = dbuf; + dp->dtdo_len = 1; + dp->dtdo_rtype = dt_int_rtype; + + ap = dt_stmt_action(dtp, sdp); + ap->dtad_difo = dp; + ap->dtad_kind = kind; + return; + } + + for (anp = arg1; anp != NULL; anp = anp->dn_list) { + ap = dt_stmt_action(dtp, sdp); + dt_cg(yypcb, anp); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = kind; + } +} + +static void +dt_action_trace(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + if (dt_node_is_void(dnp->dn_args)) { + dnerror(dnp->dn_args, D_TRACE_VOID, + "trace( ) may not be applied to a void expression\n"); + } + + if (dt_node_is_dynamic(dnp->dn_args)) { + dnerror(dnp->dn_args, D_TRACE_DYN, + "trace( ) may not be applied to a dynamic expression\n"); + } + + dt_cg(yypcb, dnp->dn_args); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_DIFEXPR; +} + +static void +dt_action_tracemem(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_node_t *addr = dnp->dn_args; + dt_node_t *size = dnp->dn_args->dn_list; + + char n[DT_TYPE_NAMELEN]; + + if (dt_node_is_integer(addr) == 0 && dt_node_is_pointer(addr) == 0) { + dnerror(addr, D_TRACEMEM_ADDR, + "tracemem( ) argument #1 is incompatible with " + "prototype:\n\tprototype: pointer or integer\n" + "\t argument: %s\n", + dt_node_type_name(addr, n, sizeof (n))); + } + + if (dt_node_is_posconst(size) == 0) { + dnerror(size, D_TRACEMEM_SIZE, "tracemem( ) argument #2 must " + "be a non-zero positive integral constant expression\n"); + } + + dt_cg(yypcb, addr); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_DIFEXPR; + + ap->dtad_difo->dtdo_rtype.dtdt_flags |= DIF_TF_BYREF; + ap->dtad_difo->dtdo_rtype.dtdt_size = size->dn_value; +} + +static void +dt_action_stack_args(dtrace_hdl_t *dtp, dtrace_actdesc_t *ap, dt_node_t *arg0) +{ + ap->dtad_kind = DTRACEACT_STACK; + + if (dtp->dt_options[DTRACEOPT_STACKFRAMES] != DTRACEOPT_UNSET) { + ap->dtad_arg = dtp->dt_options[DTRACEOPT_STACKFRAMES]; + } else { + ap->dtad_arg = 0; + } + + if (arg0 != NULL) { + if (arg0->dn_list != NULL) { + dnerror(arg0, D_STACK_PROTO, "stack( ) prototype " + "mismatch: too many arguments\n"); + } + + if (dt_node_is_posconst(arg0) == 0) { + dnerror(arg0, D_STACK_SIZE, "stack( ) size must be a " + "non-zero positive integral constant expression\n"); + } + + ap->dtad_arg = arg0->dn_value; + } +} + +static void +dt_action_stack(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + dt_action_stack_args(dtp, ap, dnp->dn_args); +} + +static void +dt_action_ustack_args(dtrace_hdl_t *dtp, dtrace_actdesc_t *ap, dt_node_t *dnp) +{ + uint32_t nframes = 0; + uint32_t strsize = 0; /* default string table size */ + dt_node_t *arg0 = dnp->dn_args; + dt_node_t *arg1 = arg0 != NULL ? arg0->dn_list : NULL; + + assert(dnp->dn_ident->di_id == DT_ACT_JSTACK || + dnp->dn_ident->di_id == DT_ACT_USTACK); + + if (dnp->dn_ident->di_id == DT_ACT_JSTACK) { + if (dtp->dt_options[DTRACEOPT_JSTACKFRAMES] != DTRACEOPT_UNSET) + nframes = dtp->dt_options[DTRACEOPT_JSTACKFRAMES]; + + if (dtp->dt_options[DTRACEOPT_JSTACKSTRSIZE] != DTRACEOPT_UNSET) + strsize = dtp->dt_options[DTRACEOPT_JSTACKSTRSIZE]; + + ap->dtad_kind = DTRACEACT_JSTACK; + } else { + assert(dnp->dn_ident->di_id == DT_ACT_USTACK); + + if (dtp->dt_options[DTRACEOPT_USTACKFRAMES] != DTRACEOPT_UNSET) + nframes = dtp->dt_options[DTRACEOPT_USTACKFRAMES]; + + ap->dtad_kind = DTRACEACT_USTACK; + } + + if (arg0 != NULL) { + if (!dt_node_is_posconst(arg0)) { + dnerror(arg0, D_USTACK_FRAMES, "ustack( ) argument #1 " + "must be a non-zero positive integer constant\n"); + } + nframes = (uint32_t)arg0->dn_value; + } + + if (arg1 != NULL) { + if (arg1->dn_kind != DT_NODE_INT || + ((arg1->dn_flags & DT_NF_SIGNED) && + (int64_t)arg1->dn_value < 0)) { + dnerror(arg1, D_USTACK_STRSIZE, "ustack( ) argument #2 " + "must be a positive integer constant\n"); + } + + if (arg1->dn_list != NULL) { + dnerror(arg1, D_USTACK_PROTO, "ustack( ) prototype " + "mismatch: too many arguments\n"); + } + + strsize = (uint32_t)arg1->dn_value; + } + + ap->dtad_arg = DTRACE_USTACK_ARG(nframes, strsize); +} + +static void +dt_action_ustack(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + dt_action_ustack_args(dtp, ap, dnp); +} + +static void +dt_action_setopt(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap; + dt_node_t *arg0, *arg1; + + /* + * The prototype guarantees that we are called with either one or + * two arguments, and that any arguments that are present are strings. + */ + arg0 = dnp->dn_args; + arg1 = arg0->dn_list; + + ap = dt_stmt_action(dtp, sdp); + dt_cg(yypcb, arg0); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_LIBACT; + ap->dtad_arg = DT_ACT_SETOPT; + + ap = dt_stmt_action(dtp, sdp); + + if (arg1 == NULL) { + dt_action_difconst(ap, 0, DTRACEACT_LIBACT); + } else { + dt_cg(yypcb, arg1); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_LIBACT; + } + + ap->dtad_arg = DT_ACT_SETOPT; +} + +/*ARGSUSED*/ +static void +dt_action_symmod_args(dtrace_hdl_t *dtp, dtrace_actdesc_t *ap, + dt_node_t *dnp, dtrace_actkind_t kind) +{ + assert(kind == DTRACEACT_SYM || kind == DTRACEACT_MOD || + kind == DTRACEACT_USYM || kind == DTRACEACT_UMOD || + kind == DTRACEACT_UADDR); + + dt_cg(yypcb, dnp); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = kind; + ap->dtad_difo->dtdo_rtype.dtdt_size = sizeof (uint64_t); +} + +static void +dt_action_symmod(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp, + dtrace_actkind_t kind) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + dt_action_symmod_args(dtp, ap, dnp->dn_args, kind); +} + +/*ARGSUSED*/ +static void +dt_action_ftruncate(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + /* + * Library actions need a DIFO that serves as an argument. As + * ftruncate() doesn't take an argument, we generate the constant 0 + * in a DIFO; this constant will be ignored when the ftruncate() is + * processed. + */ + dt_action_difconst(ap, 0, DTRACEACT_LIBACT); + ap->dtad_arg = DT_ACT_FTRUNCATE; +} + +/*ARGSUSED*/ +static void +dt_action_stop(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + ap->dtad_kind = DTRACEACT_STOP; + ap->dtad_arg = 0; +} + +/*ARGSUSED*/ +static void +dt_action_breakpoint(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + ap->dtad_kind = DTRACEACT_BREAKPOINT; + ap->dtad_arg = 0; +} + +/*ARGSUSED*/ +static void +dt_action_panic(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + ap->dtad_kind = DTRACEACT_PANIC; + ap->dtad_arg = 0; +} + +static void +dt_action_chill(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_cg(yypcb, dnp->dn_args); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_CHILL; +} + +static void +dt_action_raise(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_cg(yypcb, dnp->dn_args); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_RAISE; +} + +static void +dt_action_exit(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_cg(yypcb, dnp->dn_args); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_EXIT; + ap->dtad_difo->dtdo_rtype.dtdt_size = sizeof (int); +} + +static void +dt_action_speculate(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_cg(yypcb, dnp->dn_args); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_SPECULATE; +} + +static void +dt_action_printm(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_node_t *size = dnp->dn_args; + dt_node_t *addr = dnp->dn_args->dn_list; + + char n[DT_TYPE_NAMELEN]; + + if (dt_node_is_posconst(size) == 0) { + dnerror(size, D_PRINTM_SIZE, "printm( ) argument #1 must " + "be a non-zero positive integral constant expression\n"); + } + + if (dt_node_is_pointer(addr) == 0) { + dnerror(addr, D_PRINTM_ADDR, + "printm( ) argument #2 is incompatible with " + "prototype:\n\tprototype: pointer\n" + "\t argument: %s\n", + dt_node_type_name(addr, n, sizeof (n))); + } + + dt_cg(yypcb, addr); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_PRINTM; + + ap->dtad_difo->dtdo_rtype.dtdt_flags |= DIF_TF_BYREF; + ap->dtad_difo->dtdo_rtype.dtdt_size = size->dn_value + sizeof(uintptr_t); +} + +static void +dt_action_printt(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_node_t *size = dnp->dn_args; + dt_node_t *addr = dnp->dn_args->dn_list; + + char n[DT_TYPE_NAMELEN]; + + if (dt_node_is_posconst(size) == 0) { + dnerror(size, D_PRINTT_SIZE, "printt( ) argument #1 must " + "be a non-zero positive integral constant expression\n"); + } + + if (addr == NULL || addr->dn_kind != DT_NODE_FUNC || + addr->dn_ident != dt_idhash_lookup(dtp->dt_globals, "typeref")) { + dnerror(addr, D_PRINTT_ADDR, + "printt( ) argument #2 is incompatible with " + "prototype:\n\tprototype: typeref()\n" + "\t argument: %s\n", + dt_node_type_name(addr, n, sizeof (n))); + } + + dt_cg(yypcb, addr); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_PRINTT; + + ap->dtad_difo->dtdo_rtype.dtdt_flags |= DIF_TF_BYREF; + + /* + * Allow additional buffer space for the data size, type size, + * type string length and a stab in the dark (32 bytes) for the + * type string. The type string is part of the typeref() that + * this action references. + */ + ap->dtad_difo->dtdo_rtype.dtdt_size = size->dn_value + 3 * sizeof(uintptr_t) + 32; + +} + +static void +dt_action_commit(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_cg(yypcb, dnp->dn_args); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_COMMIT; +} + +static void +dt_action_discard(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_cg(yypcb, dnp->dn_args); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_DISCARD; +} + +static void +dt_compile_fun(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + switch (dnp->dn_expr->dn_ident->di_id) { + case DT_ACT_BREAKPOINT: + dt_action_breakpoint(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_CHILL: + dt_action_chill(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_CLEAR: + dt_action_clear(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_COMMIT: + dt_action_commit(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_DENORMALIZE: + dt_action_normalize(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_DISCARD: + dt_action_discard(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_EXIT: + dt_action_exit(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_FREOPEN: + dt_action_printflike(dtp, dnp->dn_expr, sdp, DTRACEACT_FREOPEN); + break; + case DT_ACT_FTRUNCATE: + dt_action_ftruncate(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_MOD: + dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_MOD); + break; + case DT_ACT_NORMALIZE: + dt_action_normalize(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_PANIC: + dt_action_panic(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_PRINTA: + dt_action_printa(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_PRINTF: + dt_action_printflike(dtp, dnp->dn_expr, sdp, DTRACEACT_PRINTF); + break; + case DT_ACT_PRINTM: + dt_action_printm(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_PRINTT: + dt_action_printt(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_RAISE: + dt_action_raise(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_SETOPT: + dt_action_setopt(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_SPECULATE: + dt_action_speculate(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_STACK: + dt_action_stack(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_STOP: + dt_action_stop(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_SYM: + dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_SYM); + break; + case DT_ACT_SYSTEM: + dt_action_printflike(dtp, dnp->dn_expr, sdp, DTRACEACT_SYSTEM); + break; + case DT_ACT_TRACE: + dt_action_trace(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_TRACEMEM: + dt_action_tracemem(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_TRUNC: + dt_action_trunc(dtp, dnp->dn_expr, sdp); + break; + case DT_ACT_UADDR: + dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_UADDR); + break; + case DT_ACT_UMOD: + dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_UMOD); + break; + case DT_ACT_USYM: + dt_action_symmod(dtp, dnp->dn_expr, sdp, DTRACEACT_USYM); + break; + case DT_ACT_USTACK: + case DT_ACT_JSTACK: + dt_action_ustack(dtp, dnp->dn_expr, sdp); + break; + default: + dnerror(dnp->dn_expr, D_UNKNOWN, "tracing function %s( ) is " + "not yet supported\n", dnp->dn_expr->dn_ident->di_name); + } +} + +static void +dt_compile_exp(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dtrace_actdesc_t *ap = dt_stmt_action(dtp, sdp); + + dt_cg(yypcb, dnp->dn_expr); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_difo->dtdo_rtype = dt_void_rtype; + ap->dtad_kind = DTRACEACT_DIFEXPR; +} + +static void +dt_compile_agg(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) +{ + dt_ident_t *aid, *fid; + dt_node_t *anp, *incr = NULL; + dtrace_actdesc_t *ap; + uint_t n = 1, argmax; + uint64_t arg = 0; + + /* + * If the aggregation has no aggregating function applied to it, then + * this statement has no effect. Flag this as a programming error. + */ + if (dnp->dn_aggfun == NULL) { + dnerror(dnp, D_AGG_NULL, "expression has null effect: @%s\n", + dnp->dn_ident->di_name); + } + + aid = dnp->dn_ident; + fid = dnp->dn_aggfun->dn_ident; + + if (dnp->dn_aggfun->dn_args != NULL && + dt_node_is_scalar(dnp->dn_aggfun->dn_args) == 0) { + dnerror(dnp->dn_aggfun, D_AGG_SCALAR, "%s( ) argument #1 must " + "be of scalar type\n", fid->di_name); + } + + /* + * The ID of the aggregation itself is implicitly recorded as the first + * member of each aggregation tuple so we can distinguish them later. + */ + ap = dt_stmt_action(dtp, sdp); + dt_action_difconst(ap, aid->di_id, DTRACEACT_DIFEXPR); + + for (anp = dnp->dn_aggtup; anp != NULL; anp = anp->dn_list) { + ap = dt_stmt_action(dtp, sdp); + n++; + + if (anp->dn_kind == DT_NODE_FUNC) { + if (anp->dn_ident->di_id == DT_ACT_STACK) { + dt_action_stack_args(dtp, ap, anp->dn_args); + continue; + } + + if (anp->dn_ident->di_id == DT_ACT_USTACK || + anp->dn_ident->di_id == DT_ACT_JSTACK) { + dt_action_ustack_args(dtp, ap, anp); + continue; + } + + switch (anp->dn_ident->di_id) { + case DT_ACT_UADDR: + dt_action_symmod_args(dtp, ap, + anp->dn_args, DTRACEACT_UADDR); + continue; + + case DT_ACT_USYM: + dt_action_symmod_args(dtp, ap, + anp->dn_args, DTRACEACT_USYM); + continue; + + case DT_ACT_UMOD: + dt_action_symmod_args(dtp, ap, + anp->dn_args, DTRACEACT_UMOD); + continue; + + case DT_ACT_SYM: + dt_action_symmod_args(dtp, ap, + anp->dn_args, DTRACEACT_SYM); + continue; + + case DT_ACT_MOD: + dt_action_symmod_args(dtp, ap, + anp->dn_args, DTRACEACT_MOD); + continue; + + default: + break; + } + } + + dt_cg(yypcb, anp); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_kind = DTRACEACT_DIFEXPR; + } + + if (fid->di_id == DTRACEAGG_LQUANTIZE) { + /* + * For linear quantization, we have between two and four + * arguments in addition to the expression: + * + * arg1 => Base value + * arg2 => Limit value + * arg3 => Quantization level step size (defaults to 1) + * arg4 => Quantization increment value (defaults to 1) + */ + dt_node_t *arg1 = dnp->dn_aggfun->dn_args->dn_list; + dt_node_t *arg2 = arg1->dn_list; + dt_node_t *arg3 = arg2->dn_list; + dt_idsig_t *isp; + uint64_t nlevels, step = 1, oarg; + int64_t baseval, limitval; + + if (arg1->dn_kind != DT_NODE_INT) { + dnerror(arg1, D_LQUANT_BASETYPE, "lquantize( ) " + "argument #1 must be an integer constant\n"); + } + + baseval = (int64_t)arg1->dn_value; + + if (baseval < INT32_MIN || baseval > INT32_MAX) { + dnerror(arg1, D_LQUANT_BASEVAL, "lquantize( ) " + "argument #1 must be a 32-bit quantity\n"); + } + + if (arg2->dn_kind != DT_NODE_INT) { + dnerror(arg2, D_LQUANT_LIMTYPE, "lquantize( ) " + "argument #2 must be an integer constant\n"); + } + + limitval = (int64_t)arg2->dn_value; + + if (limitval < INT32_MIN || limitval > INT32_MAX) { + dnerror(arg2, D_LQUANT_LIMVAL, "lquantize( ) " + "argument #2 must be a 32-bit quantity\n"); + } + + if (limitval < baseval) { + dnerror(dnp, D_LQUANT_MISMATCH, + "lquantize( ) base (argument #1) must be less " + "than limit (argument #2)\n"); + } + + if (arg3 != NULL) { + if (!dt_node_is_posconst(arg3)) { + dnerror(arg3, D_LQUANT_STEPTYPE, "lquantize( ) " + "argument #3 must be a non-zero positive " + "integer constant\n"); + } + + if ((step = arg3->dn_value) > UINT16_MAX) { + dnerror(arg3, D_LQUANT_STEPVAL, "lquantize( ) " + "argument #3 must be a 16-bit quantity\n"); + } + } + + nlevels = (limitval - baseval) / step; + + if (nlevels == 0) { + dnerror(dnp, D_LQUANT_STEPLARGE, + "lquantize( ) step (argument #3) too large: must " + "have at least one quantization level\n"); + } + + if (nlevels > UINT16_MAX) { + dnerror(dnp, D_LQUANT_STEPSMALL, "lquantize( ) step " + "(argument #3) too small: number of quantization " + "levels must be a 16-bit quantity\n"); + } + + arg = (step << DTRACE_LQUANTIZE_STEPSHIFT) | + (nlevels << DTRACE_LQUANTIZE_LEVELSHIFT) | + ((baseval << DTRACE_LQUANTIZE_BASESHIFT) & + DTRACE_LQUANTIZE_BASEMASK); + + assert(arg != 0); + + isp = (dt_idsig_t *)aid->di_data; + + if (isp->dis_auxinfo == 0) { + /* + * This is the first time we've seen an lquantize() + * for this aggregation; we'll store our argument + * as the auxiliary signature information. + */ + isp->dis_auxinfo = arg; + } else if ((oarg = isp->dis_auxinfo) != arg) { + /* + * If we have seen this lquantize() before and the + * argument doesn't match the original argument, pick + * the original argument apart to concisely report the + * mismatch. + */ + int obaseval = DTRACE_LQUANTIZE_BASE(oarg); + int onlevels = DTRACE_LQUANTIZE_LEVELS(oarg); + int ostep = DTRACE_LQUANTIZE_STEP(oarg); + + if (obaseval != baseval) { + dnerror(dnp, D_LQUANT_MATCHBASE, "lquantize( ) " + "base (argument #1) doesn't match previous " + "declaration: expected %d, found %d\n", + obaseval, (int)baseval); + } + + if (onlevels * ostep != nlevels * step) { + dnerror(dnp, D_LQUANT_MATCHLIM, "lquantize( ) " + "limit (argument #2) doesn't match previous" + " declaration: expected %d, found %d\n", + obaseval + onlevels * ostep, + (int)baseval + (int)nlevels * (int)step); + } + + if (ostep != step) { + dnerror(dnp, D_LQUANT_MATCHSTEP, "lquantize( ) " + "step (argument #3) doesn't match previous " + "declaration: expected %d, found %d\n", + ostep, (int)step); + } + + /* + * We shouldn't be able to get here -- one of the + * parameters must be mismatched if the arguments + * didn't match. + */ + assert(0); + } + + incr = arg3 != NULL ? arg3->dn_list : NULL; + argmax = 5; + } + + if (fid->di_id == DTRACEAGG_QUANTIZE) { + incr = dnp->dn_aggfun->dn_args->dn_list; + argmax = 2; + } + + if (incr != NULL) { + if (!dt_node_is_scalar(incr)) { + dnerror(dnp, D_PROTO_ARG, "%s( ) increment value " + "(argument #%d) must be of scalar type\n", + fid->di_name, argmax); + } + + if ((anp = incr->dn_list) != NULL) { + int argc = argmax; + + for (; anp != NULL; anp = anp->dn_list) + argc++; + + dnerror(incr, D_PROTO_LEN, "%s( ) prototype " + "mismatch: %d args passed, at most %d expected", + fid->di_name, argc, argmax); + } + + ap = dt_stmt_action(dtp, sdp); + n++; + + dt_cg(yypcb, incr); + ap->dtad_difo = dt_as(yypcb); + ap->dtad_difo->dtdo_rtype = dt_void_rtype; + ap->dtad_kind = DTRACEACT_DIFEXPR; + } + + assert(sdp->dtsd_aggdata == NULL); + sdp->dtsd_aggdata = aid; + + ap = dt_stmt_action(dtp, sdp); + assert(fid->di_kind == DT_IDENT_AGGFUNC); + assert(DTRACEACT_ISAGG(fid->di_id)); + ap->dtad_kind = fid->di_id; + ap->dtad_ntuple = n; + ap->dtad_arg = arg; + + if (dnp->dn_aggfun->dn_args != NULL) { + dt_cg(yypcb, dnp->dn_aggfun->dn_args); + ap->dtad_difo = dt_as(yypcb); + } +} + +static void +dt_compile_one_clause(dtrace_hdl_t *dtp, dt_node_t *cnp, dt_node_t *pnp) +{ + dtrace_ecbdesc_t *edp; + dtrace_stmtdesc_t *sdp; + dt_node_t *dnp; + + yylineno = pnp->dn_line; + dt_setcontext(dtp, pnp->dn_desc); + (void) dt_node_cook(cnp, DT_IDFLG_REF); + + if (DT_TREEDUMP_PASS(dtp, 2)) + dt_node_printr(cnp, stderr, 0); + + if ((edp = dt_ecbdesc_create(dtp, pnp->dn_desc)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + assert(yypcb->pcb_ecbdesc == NULL); + yypcb->pcb_ecbdesc = edp; + + if (cnp->dn_pred != NULL) { + dt_cg(yypcb, cnp->dn_pred); + edp->dted_pred.dtpdd_difo = dt_as(yypcb); + } + + if (cnp->dn_acts == NULL) { + dt_stmt_append(dt_stmt_create(dtp, edp, + cnp->dn_ctxattr, _dtrace_defattr), cnp); + } + + for (dnp = cnp->dn_acts; dnp != NULL; dnp = dnp->dn_list) { + assert(yypcb->pcb_stmt == NULL); + sdp = dt_stmt_create(dtp, edp, cnp->dn_ctxattr, cnp->dn_attr); + + switch (dnp->dn_kind) { + case DT_NODE_DEXPR: + if (dnp->dn_expr->dn_kind == DT_NODE_AGG) + dt_compile_agg(dtp, dnp->dn_expr, sdp); + else + dt_compile_exp(dtp, dnp, sdp); + break; + case DT_NODE_DFUNC: + dt_compile_fun(dtp, dnp, sdp); + break; + case DT_NODE_AGG: + dt_compile_agg(dtp, dnp, sdp); + break; + default: + dnerror(dnp, D_UNKNOWN, "internal error -- node kind " + "%u is not a valid statement\n", dnp->dn_kind); + } + + assert(yypcb->pcb_stmt == sdp); + dt_stmt_append(sdp, dnp); + } + + assert(yypcb->pcb_ecbdesc == edp); + dt_ecbdesc_release(dtp, edp); + dt_endcontext(dtp); + yypcb->pcb_ecbdesc = NULL; +} + +static void +dt_compile_clause(dtrace_hdl_t *dtp, dt_node_t *cnp) +{ + dt_node_t *pnp; + + for (pnp = cnp->dn_pdescs; pnp != NULL; pnp = pnp->dn_list) + dt_compile_one_clause(dtp, cnp, pnp); +} + +static void +dt_compile_xlator(dt_node_t *dnp) +{ + dt_xlator_t *dxp = dnp->dn_xlator; + dt_node_t *mnp; + + for (mnp = dnp->dn_members; mnp != NULL; mnp = mnp->dn_list) { + assert(dxp->dx_membdif[mnp->dn_membid] == NULL); + dt_cg(yypcb, mnp); + dxp->dx_membdif[mnp->dn_membid] = dt_as(yypcb); + } +} + +void +dt_setcontext(dtrace_hdl_t *dtp, dtrace_probedesc_t *pdp) +{ + const dtrace_pattr_t *pap; + dt_probe_t *prp; + dt_provider_t *pvp; + dt_ident_t *idp; + char attrstr[8]; + int err; + + /* + * Both kernel and pid based providers are allowed to have names + * ending with what could be interpreted as a number. We assume it's + * a pid and that we may need to dynamically create probes for + * that process if: + * + * (1) The provider doesn't exist, or, + * (2) The provider exists and has DTRACE_PRIV_PROC privilege. + * + * On an error, dt_pid_create_probes() will set the error message + * and tag -- we just have to longjmp() out of here. + */ + if (isdigit(pdp->dtpd_provider[strlen(pdp->dtpd_provider) - 1]) && + ((pvp = dt_provider_lookup(dtp, pdp->dtpd_provider)) == NULL || + pvp->pv_desc.dtvd_priv.dtpp_flags & DTRACE_PRIV_PROC) && + dt_pid_create_probes(pdp, dtp, yypcb) != 0) { + longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); + } + + /* + * Call dt_probe_info() to get the probe arguments and attributes. If + * a representative probe is found, set 'pap' to the probe provider's + * attributes. Otherwise set 'pap' to default Unstable attributes. + */ + if ((prp = dt_probe_info(dtp, pdp, &yypcb->pcb_pinfo)) == NULL) { + pap = &_dtrace_prvdesc; + err = dtrace_errno(dtp); + bzero(&yypcb->pcb_pinfo, sizeof (dtrace_probeinfo_t)); + yypcb->pcb_pinfo.dtp_attr = pap->dtpa_provider; + yypcb->pcb_pinfo.dtp_arga = pap->dtpa_args; + } else { + pap = &prp->pr_pvp->pv_desc.dtvd_attr; + err = 0; + } + + if (err == EDT_NOPROBE && !(yypcb->pcb_cflags & DTRACE_C_ZDEFS)) { + xyerror(D_PDESC_ZERO, "probe description %s:%s:%s:%s does not " + "match any probes\n", pdp->dtpd_provider, pdp->dtpd_mod, + pdp->dtpd_func, pdp->dtpd_name); + } + + if (err != EDT_NOPROBE && err != EDT_UNSTABLE && err != 0) + xyerror(D_PDESC_INVAL, "%s\n", dtrace_errmsg(dtp, err)); + + dt_dprintf("set context to %s:%s:%s:%s [%u] prp=%p attr=%s argc=%d\n", + pdp->dtpd_provider, pdp->dtpd_mod, pdp->dtpd_func, pdp->dtpd_name, + pdp->dtpd_id, (void *)prp, dt_attr_str(yypcb->pcb_pinfo.dtp_attr, + attrstr, sizeof (attrstr)), yypcb->pcb_pinfo.dtp_argc); + + /* + * Reset the stability attributes of D global variables that vary + * based on the attributes of the provider and context itself. + */ + if ((idp = dt_idhash_lookup(dtp->dt_globals, "probeprov")) != NULL) + idp->di_attr = pap->dtpa_provider; + if ((idp = dt_idhash_lookup(dtp->dt_globals, "probemod")) != NULL) + idp->di_attr = pap->dtpa_mod; + if ((idp = dt_idhash_lookup(dtp->dt_globals, "probefunc")) != NULL) + idp->di_attr = pap->dtpa_func; + if ((idp = dt_idhash_lookup(dtp->dt_globals, "probename")) != NULL) + idp->di_attr = pap->dtpa_name; + if ((idp = dt_idhash_lookup(dtp->dt_globals, "args")) != NULL) + idp->di_attr = pap->dtpa_args; + + yypcb->pcb_pdesc = pdp; + yypcb->pcb_probe = prp; +} + +/* + * Reset context-dependent variables and state at the end of cooking a D probe + * definition clause. This ensures that external declarations between clauses + * do not reference any stale context-dependent data from the previous clause. + */ +void +dt_endcontext(dtrace_hdl_t *dtp) +{ + static const char *const cvars[] = { + "probeprov", "probemod", "probefunc", "probename", "args", NULL + }; + + dt_ident_t *idp; + int i; + + for (i = 0; cvars[i] != NULL; i++) { + if ((idp = dt_idhash_lookup(dtp->dt_globals, cvars[i])) != NULL) + idp->di_attr = _dtrace_defattr; + } + + yypcb->pcb_pdesc = NULL; + yypcb->pcb_probe = NULL; +} + +static int +dt_reduceid(dt_idhash_t *dhp, dt_ident_t *idp, dtrace_hdl_t *dtp) +{ + if (idp->di_vers != 0 && idp->di_vers > dtp->dt_vmax) + dt_idhash_delete(dhp, idp); + + return (0); +} + +/* + * When dtrace_setopt() is called for "version", it calls dt_reduce() to remove + * any identifiers or translators that have been previously defined as bound to + * a version greater than the specified version. Therefore, in our current + * version implementation, establishing a binding is a one-way transformation. + * In addition, no versioning is currently provided for types as our .d library + * files do not define any types and we reserve prefixes DTRACE_ and dtrace_ + * for our exclusive use. If required, type versioning will require more work. + */ +int +dt_reduce(dtrace_hdl_t *dtp, dt_version_t v) +{ + char s[DT_VERSION_STRMAX]; + dt_xlator_t *dxp, *nxp; + + if (v > dtp->dt_vmax) + return (dt_set_errno(dtp, EDT_VERSREDUCED)); + else if (v == dtp->dt_vmax) + return (0); /* no reduction necessary */ + + dt_dprintf("reducing api version to %s\n", + dt_version_num2str(v, s, sizeof (s))); + + dtp->dt_vmax = v; + + for (dxp = dt_list_next(&dtp->dt_xlators); dxp != NULL; dxp = nxp) { + nxp = dt_list_next(dxp); + if ((dxp->dx_souid.di_vers != 0 && dxp->dx_souid.di_vers > v) || + (dxp->dx_ptrid.di_vers != 0 && dxp->dx_ptrid.di_vers > v)) + dt_list_delete(&dtp->dt_xlators, dxp); + } + + (void) dt_idhash_iter(dtp->dt_macros, (dt_idhash_f *)dt_reduceid, dtp); + (void) dt_idhash_iter(dtp->dt_aggs, (dt_idhash_f *)dt_reduceid, dtp); + (void) dt_idhash_iter(dtp->dt_globals, (dt_idhash_f *)dt_reduceid, dtp); + (void) dt_idhash_iter(dtp->dt_tls, (dt_idhash_f *)dt_reduceid, dtp); + + return (0); +} + +/* + * Fork and exec the cpp(1) preprocessor to run over the specified input file, + * and return a FILE handle for the cpp output. We use the /dev/fd filesystem + * here to simplify the code by leveraging file descriptor inheritance. + */ +static FILE * +dt_preproc(dtrace_hdl_t *dtp, FILE *ifp) +{ + int argc = dtp->dt_cpp_argc; + char **argv = malloc(sizeof (char *) * (argc + 5)); + FILE *ofp = tmpfile(); + +#if defined(sun) + char ipath[20], opath[20]; /* big enough for /dev/fd/ + INT_MAX + \0 */ +#endif + char verdef[32]; /* big enough for -D__SUNW_D_VERSION=0x%08x + \0 */ + + struct sigaction act, oact; + sigset_t mask, omask; + + int wstat, estat; + pid_t pid; +#if defined(sun) + off64_t off; +#else + off_t off = 0; +#endif + int c; + + if (argv == NULL || ofp == NULL) { + (void) dt_set_errno(dtp, errno); + goto err; + } + + /* + * If the input is a seekable file, see if it is an interpreter file. + * If we see #!, seek past the first line because cpp will choke on it. + * We start cpp just prior to the \n at the end of this line so that + * it still sees the newline, ensuring that #line values are correct. + */ + if (isatty(fileno(ifp)) == 0 && (off = ftello64(ifp)) != -1) { + if ((c = fgetc(ifp)) == '#' && (c = fgetc(ifp)) == '!') { + for (off += 2; c != '\n'; off++) { + if ((c = fgetc(ifp)) == EOF) + break; + } + if (c == '\n') + off--; /* start cpp just prior to \n */ + } + (void) fflush(ifp); + (void) fseeko64(ifp, off, SEEK_SET); + } + +#if defined(sun) + (void) snprintf(ipath, sizeof (ipath), "/dev/fd/%d", fileno(ifp)); + (void) snprintf(opath, sizeof (opath), "/dev/fd/%d", fileno(ofp)); +#endif + + bcopy(dtp->dt_cpp_argv, argv, sizeof (char *) * argc); + + (void) snprintf(verdef, sizeof (verdef), + "-D__SUNW_D_VERSION=0x%08x", dtp->dt_vmax); + argv[argc++] = verdef; + +#if defined(sun) + switch (dtp->dt_stdcmode) { + case DT_STDC_XA: + case DT_STDC_XT: + argv[argc++] = "-D__STDC__=0"; + break; + case DT_STDC_XC: + argv[argc++] = "-D__STDC__=1"; + break; + } + + argv[argc++] = ipath; + argv[argc++] = opath; +#else + argv[argc++] = "-P"; +#endif + argv[argc] = NULL; + + /* + * libdtrace must be able to be embedded in other programs that may + * include application-specific signal handlers. Therefore, if we + * need to fork to run cpp(1), we must avoid generating a SIGCHLD + * that could confuse the containing application. To do this, + * we block SIGCHLD and reset its disposition to SIG_DFL. + * We restore our signal state once we are done. + */ + (void) sigemptyset(&mask); + (void) sigaddset(&mask, SIGCHLD); + (void) sigprocmask(SIG_BLOCK, &mask, &omask); + + bzero(&act, sizeof (act)); + act.sa_handler = SIG_DFL; + (void) sigaction(SIGCHLD, &act, &oact); + + if ((pid = fork1()) == -1) { + (void) sigaction(SIGCHLD, &oact, NULL); + (void) sigprocmask(SIG_SETMASK, &omask, NULL); + (void) dt_set_errno(dtp, EDT_CPPFORK); + goto err; + } + + if (pid == 0) { +#if !defined(sun) + if (isatty(fileno(ifp)) == 0) + lseek(fileno(ifp), off, SEEK_SET); + dup2(fileno(ifp), 0); + dup2(fileno(ofp), 1); +#endif + (void) execvp(dtp->dt_cpp_path, argv); + _exit(errno == ENOENT ? 127 : 126); + } + + do { + dt_dprintf("waiting for %s (PID %d)\n", dtp->dt_cpp_path, + (int)pid); + } while (waitpid(pid, &wstat, 0) == -1 && errno == EINTR); + + (void) sigaction(SIGCHLD, &oact, NULL); + (void) sigprocmask(SIG_SETMASK, &omask, NULL); + + dt_dprintf("%s returned exit status 0x%x\n", dtp->dt_cpp_path, wstat); + estat = WIFEXITED(wstat) ? WEXITSTATUS(wstat) : -1; + + if (estat != 0) { + switch (estat) { + case 126: + (void) dt_set_errno(dtp, EDT_CPPEXEC); + break; + case 127: + (void) dt_set_errno(dtp, EDT_CPPENT); + break; + default: + (void) dt_set_errno(dtp, EDT_CPPERR); + } + goto err; + } + + free(argv); + (void) fflush(ofp); + (void) fseek(ofp, 0, SEEK_SET); + return (ofp); + +err: + free(argv); + (void) fclose(ofp); + return (NULL); +} + +static void +dt_lib_depend_error(dtrace_hdl_t *dtp, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap); + va_end(ap); +} + +int +dt_lib_depend_add(dtrace_hdl_t *dtp, dt_list_t *dlp, const char *arg) +{ + dt_lib_depend_t *dld; + const char *end; + + assert(arg != NULL); + + if ((end = strrchr(arg, '/')) == NULL) + return (dt_set_errno(dtp, EINVAL)); + + if ((dld = dt_zalloc(dtp, sizeof (dt_lib_depend_t))) == NULL) + return (-1); + + if ((dld->dtld_libpath = dt_alloc(dtp, MAXPATHLEN)) == NULL) { + dt_free(dtp, dld); + return (-1); + } + + (void) strlcpy(dld->dtld_libpath, arg, end - arg + 2); + if ((dld->dtld_library = strdup(arg)) == NULL) { + dt_free(dtp, dld->dtld_libpath); + dt_free(dtp, dld); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + dt_list_append(dlp, dld); + return (0); +} + +dt_lib_depend_t * +dt_lib_depend_lookup(dt_list_t *dld, const char *arg) +{ + dt_lib_depend_t *dldn; + + for (dldn = dt_list_next(dld); dldn != NULL; + dldn = dt_list_next(dldn)) { + if (strcmp(dldn->dtld_library, arg) == 0) + return (dldn); + } + + return (NULL); +} + +/* + * Go through all the library files, and, if any library dependencies exist for + * that file, add it to that node's list of dependents. The result of this + * will be a graph which can then be topologically sorted to produce a + * compilation order. + */ +static int +dt_lib_build_graph(dtrace_hdl_t *dtp) +{ + dt_lib_depend_t *dld, *dpld; + + for (dld = dt_list_next(&dtp->dt_lib_dep); dld != NULL; + dld = dt_list_next(dld)) { + char *library = dld->dtld_library; + + for (dpld = dt_list_next(&dld->dtld_dependencies); dpld != NULL; + dpld = dt_list_next(dpld)) { + dt_lib_depend_t *dlda; + + if ((dlda = dt_lib_depend_lookup(&dtp->dt_lib_dep, + dpld->dtld_library)) == NULL) { + dt_lib_depend_error(dtp, + "Invalid library dependency in %s: %s\n", + dld->dtld_library, dpld->dtld_library); + + return (dt_set_errno(dtp, EDT_COMPILER)); + } + + if ((dt_lib_depend_add(dtp, &dlda->dtld_dependents, + library)) != 0) { + return (-1); /* preserve dt_errno */ + } + } + } + return (0); +} + +static int +dt_topo_sort(dtrace_hdl_t *dtp, dt_lib_depend_t *dld, int *count) +{ + dt_lib_depend_t *dpld, *dlda, *new; + + dld->dtld_start = ++(*count); + + for (dpld = dt_list_next(&dld->dtld_dependents); dpld != NULL; + dpld = dt_list_next(dpld)) { + dlda = dt_lib_depend_lookup(&dtp->dt_lib_dep, + dpld->dtld_library); + assert(dlda != NULL); + + if (dlda->dtld_start == 0 && + dt_topo_sort(dtp, dlda, count) == -1) + return (-1); + } + + if ((new = dt_zalloc(dtp, sizeof (dt_lib_depend_t))) == NULL) + return (-1); + + if ((new->dtld_library = strdup(dld->dtld_library)) == NULL) { + dt_free(dtp, new); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + new->dtld_start = dld->dtld_start; + new->dtld_finish = dld->dtld_finish = ++(*count); + dt_list_prepend(&dtp->dt_lib_dep_sorted, new); + + dt_dprintf("library %s sorted (%d/%d)\n", new->dtld_library, + new->dtld_start, new->dtld_finish); + + return (0); +} + +static int +dt_lib_depend_sort(dtrace_hdl_t *dtp) +{ + dt_lib_depend_t *dld, *dpld, *dlda; + int count = 0; + + if (dt_lib_build_graph(dtp) == -1) + return (-1); /* preserve dt_errno */ + + /* + * Perform a topological sort of the graph that hangs off + * dtp->dt_lib_dep. The result of this process will be a + * dependency ordered list located at dtp->dt_lib_dep_sorted. + */ + for (dld = dt_list_next(&dtp->dt_lib_dep); dld != NULL; + dld = dt_list_next(dld)) { + if (dld->dtld_start == 0 && + dt_topo_sort(dtp, dld, &count) == -1) + return (-1); /* preserve dt_errno */; + } + + /* + * Check the graph for cycles. If an ancestor's finishing time is + * less than any of its dependent's finishing times then a back edge + * exists in the graph and this is a cycle. + */ + for (dld = dt_list_next(&dtp->dt_lib_dep); dld != NULL; + dld = dt_list_next(dld)) { + for (dpld = dt_list_next(&dld->dtld_dependents); dpld != NULL; + dpld = dt_list_next(dpld)) { + dlda = dt_lib_depend_lookup(&dtp->dt_lib_dep_sorted, + dpld->dtld_library); + assert(dlda != NULL); + + if (dlda->dtld_finish > dld->dtld_finish) { + dt_lib_depend_error(dtp, + "Cyclic dependency detected: %s => %s\n", + dld->dtld_library, dpld->dtld_library); + + return (dt_set_errno(dtp, EDT_COMPILER)); + } + } + } + + return (0); +} + +static void +dt_lib_depend_free(dtrace_hdl_t *dtp) +{ + dt_lib_depend_t *dld, *dlda; + + while ((dld = dt_list_next(&dtp->dt_lib_dep)) != NULL) { + while ((dlda = dt_list_next(&dld->dtld_dependencies)) != NULL) { + dt_list_delete(&dld->dtld_dependencies, dlda); + dt_free(dtp, dlda->dtld_library); + dt_free(dtp, dlda->dtld_libpath); + dt_free(dtp, dlda); + } + while ((dlda = dt_list_next(&dld->dtld_dependents)) != NULL) { + dt_list_delete(&dld->dtld_dependents, dlda); + dt_free(dtp, dlda->dtld_library); + dt_free(dtp, dlda->dtld_libpath); + dt_free(dtp, dlda); + } + dt_list_delete(&dtp->dt_lib_dep, dld); + dt_free(dtp, dld->dtld_library); + dt_free(dtp, dld->dtld_libpath); + dt_free(dtp, dld); + } + + while ((dld = dt_list_next(&dtp->dt_lib_dep_sorted)) != NULL) { + dt_list_delete(&dtp->dt_lib_dep_sorted, dld); + dt_free(dtp, dld->dtld_library); + dt_free(dtp, dld); + } +} + + +/* + * Open all of the .d library files found in the specified directory and + * compile each one in topological order to cache its inlines and translators, + * etc. We silently ignore any missing directories and other files found + * therein. We only fail (and thereby fail dt_load_libs()) if we fail to + * compile a library and the error is something other than #pragma D depends_on. + * Dependency errors are silently ignored to permit a library directory to + * contain libraries which may not be accessible depending on our privileges. + */ +static int +dt_load_libs_dir(dtrace_hdl_t *dtp, const char *path) +{ + struct dirent *dp; + const char *p; + DIR *dirp; + + char fname[PATH_MAX]; + dtrace_prog_t *pgp; + FILE *fp; + void *rv; + dt_lib_depend_t *dld; + + if ((dirp = opendir(path)) == NULL) { + dt_dprintf("skipping lib dir %s: %s\n", path, strerror(errno)); + return (0); + } + + /* First, parse each file for library dependencies. */ + while ((dp = readdir(dirp)) != NULL) { + if ((p = strrchr(dp->d_name, '.')) == NULL || strcmp(p, ".d")) + continue; /* skip any filename not ending in .d */ + + (void) snprintf(fname, sizeof (fname), + "%s/%s", path, dp->d_name); + + if ((fp = fopen(fname, "r")) == NULL) { + dt_dprintf("skipping library %s: %s\n", + fname, strerror(errno)); + continue; + } + + dtp->dt_filetag = fname; + if (dt_lib_depend_add(dtp, &dtp->dt_lib_dep, fname) != 0) + goto err; + + rv = dt_compile(dtp, DT_CTX_DPROG, + DTRACE_PROBESPEC_NAME, NULL, + DTRACE_C_EMPTY | DTRACE_C_CTL, 0, NULL, fp, NULL); + + if (rv != NULL && dtp->dt_errno && + (dtp->dt_errno != EDT_COMPILER || + dtp->dt_errtag != dt_errtag(D_PRAGMA_DEPEND))) + goto err; + + if (dtp->dt_errno) + dt_dprintf("error parsing library %s: %s\n", + fname, dtrace_errmsg(dtp, dtrace_errno(dtp))); + + (void) fclose(fp); + dtp->dt_filetag = NULL; + } + + (void) closedir(dirp); + /* + * Finish building the graph containing the library dependencies + * and perform a topological sort to generate an ordered list + * for compilation. + */ + if (dt_lib_depend_sort(dtp) == -1) + goto err; + + for (dld = dt_list_next(&dtp->dt_lib_dep_sorted); dld != NULL; + dld = dt_list_next(dld)) { + + if ((fp = fopen(dld->dtld_library, "r")) == NULL) { + dt_dprintf("skipping library %s: %s\n", + dld->dtld_library, strerror(errno)); + continue; + } + + dtp->dt_filetag = dld->dtld_library; + pgp = dtrace_program_fcompile(dtp, fp, DTRACE_C_EMPTY, 0, NULL); + (void) fclose(fp); + dtp->dt_filetag = NULL; + + if (pgp == NULL && (dtp->dt_errno != EDT_COMPILER || + dtp->dt_errtag != dt_errtag(D_PRAGMA_DEPEND))) + goto err; + + if (pgp == NULL) { + dt_dprintf("skipping library %s: %s\n", + dld->dtld_library, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + } else { + dld->dtld_loaded = B_TRUE; + dt_program_destroy(dtp, pgp); + } + } + + dt_lib_depend_free(dtp); + return (0); + +err: + dt_lib_depend_free(dtp); + return (-1); /* preserve dt_errno */ +} + +/* + * Load the contents of any appropriate DTrace .d library files. These files + * contain inlines and translators that will be cached by the compiler. We + * defer this activity until the first compile to permit libdtrace clients to + * add their own library directories and so that we can properly report errors. + */ +static int +dt_load_libs(dtrace_hdl_t *dtp) +{ + dt_dirpath_t *dirp; + + if (dtp->dt_cflags & DTRACE_C_NOLIBS) + return (0); /* libraries already processed */ + + dtp->dt_cflags |= DTRACE_C_NOLIBS; + + for (dirp = dt_list_next(&dtp->dt_lib_path); + dirp != NULL; dirp = dt_list_next(dirp)) { + if (dt_load_libs_dir(dtp, dirp->dir_path) != 0) { + dtp->dt_cflags &= ~DTRACE_C_NOLIBS; + return (-1); /* errno is set for us */ + } + } + + return (0); +} + +static void * +dt_compile(dtrace_hdl_t *dtp, int context, dtrace_probespec_t pspec, void *arg, + uint_t cflags, int argc, char *const argv[], FILE *fp, const char *s) +{ + dt_node_t *dnp; + dt_decl_t *ddp; + dt_pcb_t pcb; + void *rv; + int err; + + if ((fp == NULL && s == NULL) || (cflags & ~DTRACE_C_MASK) != 0) { + (void) dt_set_errno(dtp, EINVAL); + return (NULL); + } + + if (dt_list_next(&dtp->dt_lib_path) != NULL && dt_load_libs(dtp) != 0) + return (NULL); /* errno is set for us */ + + (void) ctf_discard(dtp->dt_cdefs->dm_ctfp); + (void) ctf_discard(dtp->dt_ddefs->dm_ctfp); + + (void) dt_idhash_iter(dtp->dt_globals, dt_idreset, NULL); + (void) dt_idhash_iter(dtp->dt_tls, dt_idreset, NULL); + + if (fp && (cflags & DTRACE_C_CPP) && (fp = dt_preproc(dtp, fp)) == NULL) + return (NULL); /* errno is set for us */ + + dt_pcb_push(dtp, &pcb); + + pcb.pcb_fileptr = fp; + pcb.pcb_string = s; + pcb.pcb_strptr = s; + pcb.pcb_strlen = s ? strlen(s) : 0; + pcb.pcb_sargc = argc; + pcb.pcb_sargv = argv; + pcb.pcb_sflagv = argc ? calloc(argc, sizeof (ushort_t)) : NULL; + pcb.pcb_pspec = pspec; + pcb.pcb_cflags = dtp->dt_cflags | cflags; + pcb.pcb_amin = dtp->dt_amin; + pcb.pcb_yystate = -1; + pcb.pcb_context = context; + pcb.pcb_token = context; + + if (context != DT_CTX_DPROG) + yybegin(YYS_EXPR); + else if (cflags & DTRACE_C_CTL) + yybegin(YYS_CONTROL); + else + yybegin(YYS_CLAUSE); + + if ((err = setjmp(yypcb->pcb_jmpbuf)) != 0) + goto out; + + if (yypcb->pcb_sargc != 0 && yypcb->pcb_sflagv == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + yypcb->pcb_idents = dt_idhash_create("ambiguous", NULL, 0, 0); + yypcb->pcb_locals = dt_idhash_create("clause local", NULL, + DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); + + if (yypcb->pcb_idents == NULL || yypcb->pcb_locals == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + /* + * Invoke the parser to evaluate the D source code. If any errors + * occur during parsing, an error function will be called and we + * will longjmp back to pcb_jmpbuf to abort. If parsing succeeds, + * we optionally display the parse tree if debugging is enabled. + */ + if (yyparse() != 0 || yypcb->pcb_root == NULL) + xyerror(D_EMPTY, "empty D program translation unit\n"); + + yybegin(YYS_DONE); + + if (cflags & DTRACE_C_CTL) + goto out; + + if (context != DT_CTX_DTYPE && DT_TREEDUMP_PASS(dtp, 1)) + dt_node_printr(yypcb->pcb_root, stderr, 0); + + if (yypcb->pcb_pragmas != NULL) + (void) dt_idhash_iter(yypcb->pcb_pragmas, dt_idpragma, NULL); + + if (argc > 1 && !(yypcb->pcb_cflags & DTRACE_C_ARGREF) && + !(yypcb->pcb_sflagv[argc - 1] & DT_IDFLG_REF)) { + xyerror(D_MACRO_UNUSED, "extraneous argument '%s' ($%d is " + "not referenced)\n", yypcb->pcb_sargv[argc - 1], argc - 1); + } + + /* + * If we have successfully created a parse tree for a D program, loop + * over the clauses and actions and instantiate the corresponding + * libdtrace program. If we are parsing a D expression, then we + * simply run the code generator and assembler on the resulting tree. + */ + switch (context) { + case DT_CTX_DPROG: + assert(yypcb->pcb_root->dn_kind == DT_NODE_PROG); + + if ((dnp = yypcb->pcb_root->dn_list) == NULL && + !(yypcb->pcb_cflags & DTRACE_C_EMPTY)) + xyerror(D_EMPTY, "empty D program translation unit\n"); + + if ((yypcb->pcb_prog = dt_program_create(dtp)) == NULL) + longjmp(yypcb->pcb_jmpbuf, dtrace_errno(dtp)); + + for (; dnp != NULL; dnp = dnp->dn_list) { + switch (dnp->dn_kind) { + case DT_NODE_CLAUSE: + dt_compile_clause(dtp, dnp); + break; + case DT_NODE_XLATOR: + if (dtp->dt_xlatemode == DT_XL_DYNAMIC) + dt_compile_xlator(dnp); + break; + case DT_NODE_PROVIDER: + (void) dt_node_cook(dnp, DT_IDFLG_REF); + break; + } + } + + yypcb->pcb_prog->dp_xrefs = yypcb->pcb_asxrefs; + yypcb->pcb_prog->dp_xrefslen = yypcb->pcb_asxreflen; + yypcb->pcb_asxrefs = NULL; + yypcb->pcb_asxreflen = 0; + + rv = yypcb->pcb_prog; + break; + + case DT_CTX_DEXPR: + (void) dt_node_cook(yypcb->pcb_root, DT_IDFLG_REF); + dt_cg(yypcb, yypcb->pcb_root); + rv = dt_as(yypcb); + break; + + case DT_CTX_DTYPE: + ddp = (dt_decl_t *)yypcb->pcb_root; /* root is really a decl */ + err = dt_decl_type(ddp, arg); + dt_decl_free(ddp); + + if (err != 0) + longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); + + rv = NULL; + break; + } + +out: + if (context != DT_CTX_DTYPE && DT_TREEDUMP_PASS(dtp, 3)) + dt_node_printr(yypcb->pcb_root, stderr, 0); + + if (dtp->dt_cdefs_fd != -1 && (ftruncate64(dtp->dt_cdefs_fd, 0) == -1 || + lseek64(dtp->dt_cdefs_fd, 0, SEEK_SET) == -1 || + ctf_write(dtp->dt_cdefs->dm_ctfp, dtp->dt_cdefs_fd) == CTF_ERR)) + dt_dprintf("failed to update CTF cache: %s\n", strerror(errno)); + + if (dtp->dt_ddefs_fd != -1 && (ftruncate64(dtp->dt_ddefs_fd, 0) == -1 || + lseek64(dtp->dt_ddefs_fd, 0, SEEK_SET) == -1 || + ctf_write(dtp->dt_ddefs->dm_ctfp, dtp->dt_ddefs_fd) == CTF_ERR)) + dt_dprintf("failed to update CTF cache: %s\n", strerror(errno)); + + if (yypcb->pcb_fileptr && (cflags & DTRACE_C_CPP)) + (void) fclose(yypcb->pcb_fileptr); /* close dt_preproc() file */ + + dt_pcb_pop(dtp, err); + (void) dt_set_errno(dtp, err); + return (err ? NULL : rv); +} + +dtrace_prog_t * +dtrace_program_strcompile(dtrace_hdl_t *dtp, const char *s, + dtrace_probespec_t spec, uint_t cflags, int argc, char *const argv[]) +{ + return (dt_compile(dtp, DT_CTX_DPROG, + spec, NULL, cflags, argc, argv, NULL, s)); +} + +dtrace_prog_t * +dtrace_program_fcompile(dtrace_hdl_t *dtp, FILE *fp, + uint_t cflags, int argc, char *const argv[]) +{ + return (dt_compile(dtp, DT_CTX_DPROG, + DTRACE_PROBESPEC_NAME, NULL, cflags, argc, argv, fp, NULL)); +} + +int +dtrace_type_strcompile(dtrace_hdl_t *dtp, const char *s, dtrace_typeinfo_t *dtt) +{ + (void) dt_compile(dtp, DT_CTX_DTYPE, + DTRACE_PROBESPEC_NONE, dtt, 0, 0, NULL, NULL, s); + return (dtp->dt_errno ? -1 : 0); +} + +int +dtrace_type_fcompile(dtrace_hdl_t *dtp, FILE *fp, dtrace_typeinfo_t *dtt) +{ + (void) dt_compile(dtp, DT_CTX_DTYPE, + DTRACE_PROBESPEC_NONE, dtt, 0, 0, NULL, fp, NULL); + return (dtp->dt_errno ? -1 : 0); +} diff --git a/lib/libdtrace/common/dt_cg.c b/lib/libdtrace/common/dt_cg.c new file mode 100644 index 000000000000..a33cccd676d2 --- /dev/null +++ b/lib/libdtrace/common/dt_cg.c @@ -0,0 +1,2006 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/sysmacros.h> +#include <sys/isa_defs.h> + +#include <strings.h> +#include <stdlib.h> +#include <setjmp.h> +#include <assert.h> +#include <errno.h> + +#include <dt_impl.h> +#include <dt_grammar.h> +#include <dt_parser.h> +#include <dt_provider.h> + +static void dt_cg_node(dt_node_t *, dt_irlist_t *, dt_regset_t *); + +static dt_irnode_t * +dt_cg_node_alloc(uint_t label, dif_instr_t instr) +{ + dt_irnode_t *dip = malloc(sizeof (dt_irnode_t)); + + if (dip == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + dip->di_label = label; + dip->di_instr = instr; + dip->di_extern = NULL; + dip->di_next = NULL; + + return (dip); +} + +/* + * Code generator wrapper function for ctf_member_info. If we are given a + * reference to a forward declaration tag, search the entire type space for + * the actual definition and then call ctf_member_info on the result. + */ +static ctf_file_t * +dt_cg_membinfo(ctf_file_t *fp, ctf_id_t type, const char *s, ctf_membinfo_t *mp) +{ + while (ctf_type_kind(fp, type) == CTF_K_FORWARD) { + char n[DT_TYPE_NAMELEN]; + dtrace_typeinfo_t dtt; + + if (ctf_type_name(fp, type, n, sizeof (n)) == NULL || + dt_type_lookup(n, &dtt) == -1 || ( + dtt.dtt_ctfp == fp && dtt.dtt_type == type)) + break; /* unable to improve our position */ + + fp = dtt.dtt_ctfp; + type = ctf_type_resolve(fp, dtt.dtt_type); + } + + if (ctf_member_info(fp, type, s, mp) == CTF_ERR) + return (NULL); /* ctf_errno is set for us */ + + return (fp); +} + +static void +dt_cg_xsetx(dt_irlist_t *dlp, dt_ident_t *idp, uint_t lbl, int reg, uint64_t x) +{ + int flag = idp != NULL ? DT_INT_PRIVATE : DT_INT_SHARED; + int intoff = dt_inttab_insert(yypcb->pcb_inttab, x, flag); + dif_instr_t instr = DIF_INSTR_SETX((uint_t)intoff, reg); + + if (intoff == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + if (intoff > DIF_INTOFF_MAX) + longjmp(yypcb->pcb_jmpbuf, EDT_INT2BIG); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl, instr)); + + if (idp != NULL) + dlp->dl_last->di_extern = idp; +} + +static void +dt_cg_setx(dt_irlist_t *dlp, int reg, uint64_t x) +{ + dt_cg_xsetx(dlp, NULL, DT_LBL_NONE, reg, x); +} + +/* + * When loading bit-fields, we want to convert a byte count in the range + * 1-8 to the closest power of 2 (e.g. 3->4, 5->8, etc). The clp2() function + * is a clever implementation from "Hacker's Delight" by Henry Warren, Jr. + */ +static size_t +clp2(size_t x) +{ + x--; + + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + return (x + 1); +} + +/* + * Lookup the correct load opcode to use for the specified node and CTF type. + * We determine the size and convert it to a 3-bit index. Our lookup table + * is constructed to use a 5-bit index, consisting of the 3-bit size 0-7, a + * bit for the sign, and a bit for userland address. For example, a 4-byte + * signed load from userland would be at the following table index: + * user=1 sign=1 size=4 => binary index 11011 = decimal index 27 + */ +static uint_t +dt_cg_load(dt_node_t *dnp, ctf_file_t *ctfp, ctf_id_t type) +{ + static const uint_t ops[] = { + DIF_OP_LDUB, DIF_OP_LDUH, 0, DIF_OP_LDUW, + 0, 0, 0, DIF_OP_LDX, + DIF_OP_LDSB, DIF_OP_LDSH, 0, DIF_OP_LDSW, + 0, 0, 0, DIF_OP_LDX, + DIF_OP_ULDUB, DIF_OP_ULDUH, 0, DIF_OP_ULDUW, + 0, 0, 0, DIF_OP_ULDX, + DIF_OP_ULDSB, DIF_OP_ULDSH, 0, DIF_OP_ULDSW, + 0, 0, 0, DIF_OP_ULDX, + }; + + ctf_encoding_t e; + ssize_t size; + + /* + * If we're loading a bit-field, the size of our load is found by + * rounding cte_bits up to a byte boundary and then finding the + * nearest power of two to this value (see clp2(), above). + */ + if ((dnp->dn_flags & DT_NF_BITFIELD) && + ctf_type_encoding(ctfp, type, &e) != CTF_ERR) + size = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY); + else + size = ctf_type_size(ctfp, type); + + if (size < 1 || size > 8 || (size & (size - 1)) != 0) { + xyerror(D_UNKNOWN, "internal error -- cg cannot load " + "size %ld when passed by value\n", (long)size); + } + + size--; /* convert size to 3-bit index */ + + if (dnp->dn_flags & DT_NF_SIGNED) + size |= 0x08; + if (dnp->dn_flags & DT_NF_USERLAND) + size |= 0x10; + + return (ops[size]); +} + +static void +dt_cg_ptrsize(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, + uint_t op, int dreg) +{ + ctf_file_t *ctfp = dnp->dn_ctfp; + ctf_arinfo_t r; + dif_instr_t instr; + ctf_id_t type; + uint_t kind; + ssize_t size; + int sreg; + + if ((sreg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + type = ctf_type_resolve(ctfp, dnp->dn_type); + kind = ctf_type_kind(ctfp, type); + assert(kind == CTF_K_POINTER || kind == CTF_K_ARRAY); + + if (kind == CTF_K_ARRAY) { + if (ctf_array_info(ctfp, type, &r) != 0) { + yypcb->pcb_hdl->dt_ctferr = ctf_errno(ctfp); + longjmp(yypcb->pcb_jmpbuf, EDT_CTF); + } + type = r.ctr_contents; + } else + type = ctf_type_reference(ctfp, type); + + if ((size = ctf_type_size(ctfp, type)) == 1) + return; /* multiply or divide by one can be omitted */ + + dt_cg_setx(dlp, sreg, size); + instr = DIF_INSTR_FMT(op, dreg, sreg, dreg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, sreg); +} + +/* + * If the result of a "." or "->" operation is a bit-field, we use this routine + * to generate an epilogue to the load instruction that extracts the value. In + * the diagrams below the "ld??" is the load instruction that is generated to + * load the containing word that is generating prior to calling this function. + * + * Epilogue for unsigned fields: Epilogue for signed fields: + * + * ldu? [r1], r1 lds? [r1], r1 + * setx USHIFT, r2 setx 64 - SSHIFT, r2 + * srl r1, r2, r1 sll r1, r2, r1 + * setx (1 << bits) - 1, r2 setx 64 - bits, r2 + * and r1, r2, r1 sra r1, r2, r1 + * + * The *SHIFT constants above changes value depending on the endian-ness of our + * target architecture. Refer to the comments below for more details. + */ +static void +dt_cg_field_get(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, + ctf_file_t *fp, const ctf_membinfo_t *mp) +{ + ctf_encoding_t e; + dif_instr_t instr; + uint64_t shift; + int r1, r2; + + if (ctf_type_encoding(fp, mp->ctm_type, &e) != 0 || e.cte_bits > 64) { + xyerror(D_UNKNOWN, "cg: bad field: off %lu type <%ld> " + "bits %u\n", mp->ctm_offset, mp->ctm_type, e.cte_bits); + } + + assert(dnp->dn_op == DT_TOK_PTR || dnp->dn_op == DT_TOK_DOT); + r1 = dnp->dn_left->dn_reg; + + if ((r2 = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + /* + * On little-endian architectures, ctm_offset counts from the right so + * ctm_offset % NBBY itself is the amount we want to shift right to + * move the value bits to the little end of the register to mask them. + * On big-endian architectures, ctm_offset counts from the left so we + * must subtract (ctm_offset % NBBY + cte_bits) from the size in bits + * we used for the load. The size of our load in turn is found by + * rounding cte_bits up to a byte boundary and then finding the + * nearest power of two to this value (see clp2(), above). These + * properties are used to compute shift as USHIFT or SSHIFT, below. + */ + if (dnp->dn_flags & DT_NF_SIGNED) { +#if BYTE_ORDER == _BIG_ENDIAN + shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - + mp->ctm_offset % NBBY; +#else + shift = mp->ctm_offset % NBBY + e.cte_bits; +#endif + dt_cg_setx(dlp, r2, 64 - shift); + instr = DIF_INSTR_FMT(DIF_OP_SLL, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r2, 64 - e.cte_bits); + instr = DIF_INSTR_FMT(DIF_OP_SRA, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } else { +#if BYTE_ORDER == _BIG_ENDIAN + shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - + (mp->ctm_offset % NBBY + e.cte_bits); +#else + shift = mp->ctm_offset % NBBY; +#endif + dt_cg_setx(dlp, r2, shift); + instr = DIF_INSTR_FMT(DIF_OP_SRL, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r2, (1ULL << e.cte_bits) - 1); + instr = DIF_INSTR_FMT(DIF_OP_AND, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } + + dt_regset_free(drp, r2); +} + +/* + * If the destination of a store operation is a bit-field, we use this routine + * to generate a prologue to the store instruction that loads the surrounding + * bits, clears the destination field, and ORs in the new value of the field. + * In the diagram below the "st?" is the store instruction that is generated to + * store the containing word that is generating after calling this function. + * + * ld [dst->dn_reg], r1 + * setx ~(((1 << cte_bits) - 1) << (ctm_offset % NBBY)), r2 + * and r1, r2, r1 + * + * setx (1 << cte_bits) - 1, r2 + * and src->dn_reg, r2, r2 + * setx ctm_offset % NBBY, r3 + * sll r2, r3, r2 + * + * or r1, r2, r1 + * st? r1, [dst->dn_reg] + * + * This routine allocates a new register to hold the value to be stored and + * returns it. The caller is responsible for freeing this register later. + */ +static int +dt_cg_field_set(dt_node_t *src, dt_irlist_t *dlp, + dt_regset_t *drp, dt_node_t *dst) +{ + uint64_t cmask, fmask, shift; + dif_instr_t instr; + int r1, r2, r3; + + ctf_membinfo_t m; + ctf_encoding_t e; + ctf_file_t *fp, *ofp; + ctf_id_t type; + + assert(dst->dn_op == DT_TOK_PTR || dst->dn_op == DT_TOK_DOT); + assert(dst->dn_right->dn_kind == DT_NODE_IDENT); + + fp = dst->dn_left->dn_ctfp; + type = ctf_type_resolve(fp, dst->dn_left->dn_type); + + if (dst->dn_op == DT_TOK_PTR) { + type = ctf_type_reference(fp, type); + type = ctf_type_resolve(fp, type); + } + + if ((fp = dt_cg_membinfo(ofp = fp, type, + dst->dn_right->dn_string, &m)) == NULL) { + yypcb->pcb_hdl->dt_ctferr = ctf_errno(ofp); + longjmp(yypcb->pcb_jmpbuf, EDT_CTF); + } + + if (ctf_type_encoding(fp, m.ctm_type, &e) != 0 || e.cte_bits > 64) { + xyerror(D_UNKNOWN, "cg: bad field: off %lu type <%ld> " + "bits %u\n", m.ctm_offset, m.ctm_type, e.cte_bits); + } + + if ((r1 = dt_regset_alloc(drp)) == -1 || + (r2 = dt_regset_alloc(drp)) == -1 || + (r3 = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + /* + * Compute shifts and masks. We need to compute "shift" as the amount + * we need to shift left to position our field in the containing word. + * Refer to the comments in dt_cg_field_get(), above, for more info. + * We then compute fmask as the mask that truncates the value in the + * input register to width cte_bits, and cmask as the mask used to + * pass through the containing bits and zero the field bits. + */ +#if BYTE_ORDER == _BIG_ENDIAN + shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - + (m.ctm_offset % NBBY + e.cte_bits); +#else + shift = m.ctm_offset % NBBY; +#endif + fmask = (1ULL << e.cte_bits) - 1; + cmask = ~(fmask << shift); + + instr = DIF_INSTR_LOAD( + dt_cg_load(dst, fp, m.ctm_type), dst->dn_reg, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r2, cmask); + instr = DIF_INSTR_FMT(DIF_OP_AND, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r2, fmask); + instr = DIF_INSTR_FMT(DIF_OP_AND, src->dn_reg, r2, r2); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, r3, shift); + instr = DIF_INSTR_FMT(DIF_OP_SLL, r2, r3, r2); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_FMT(DIF_OP_OR, r1, r2, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_regset_free(drp, r3); + dt_regset_free(drp, r2); + + return (r1); +} + +static void +dt_cg_store(dt_node_t *src, dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *dst) +{ + ctf_encoding_t e; + dif_instr_t instr; + size_t size; + int reg; + + /* + * If we're loading a bit-field, the size of our store is found by + * rounding dst's cte_bits up to a byte boundary and then finding the + * nearest power of two to this value (see clp2(), above). + */ + if ((dst->dn_flags & DT_NF_BITFIELD) && + ctf_type_encoding(dst->dn_ctfp, dst->dn_type, &e) != CTF_ERR) + size = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY); + else + size = dt_node_type_size(src); + + if (src->dn_flags & DT_NF_REF) { + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + dt_cg_setx(dlp, reg, size); + instr = DIF_INSTR_COPYS(src->dn_reg, reg, dst->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); + } else { + if (dst->dn_flags & DT_NF_BITFIELD) + reg = dt_cg_field_set(src, dlp, drp, dst); + else + reg = src->dn_reg; + + switch (size) { + case 1: + instr = DIF_INSTR_STORE(DIF_OP_STB, reg, dst->dn_reg); + break; + case 2: + instr = DIF_INSTR_STORE(DIF_OP_STH, reg, dst->dn_reg); + break; + case 4: + instr = DIF_INSTR_STORE(DIF_OP_STW, reg, dst->dn_reg); + break; + case 8: + instr = DIF_INSTR_STORE(DIF_OP_STX, reg, dst->dn_reg); + break; + default: + xyerror(D_UNKNOWN, "internal error -- cg cannot store " + "size %lu when passed by value\n", (ulong_t)size); + } + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + if (dst->dn_flags & DT_NF_BITFIELD) + dt_regset_free(drp, reg); + } +} + +/* + * Generate code for a typecast or for argument promotion from the type of the + * actual to the type of the formal. We need to generate code for casts when + * a scalar type is being narrowed or changing signed-ness. We first shift the + * desired bits high (losing excess bits if narrowing) and then shift them down + * using logical shift (unsigned result) or arithmetic shift (signed result). + */ +static void +dt_cg_typecast(const dt_node_t *src, const dt_node_t *dst, + dt_irlist_t *dlp, dt_regset_t *drp) +{ + size_t srcsize = dt_node_type_size(src); + size_t dstsize = dt_node_type_size(dst); + + dif_instr_t instr; + int reg, n; + + if (dt_node_is_scalar(dst) && (dstsize < srcsize || + (src->dn_flags & DT_NF_SIGNED) ^ (dst->dn_flags & DT_NF_SIGNED))) { + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + if (dstsize < srcsize) + n = sizeof (uint64_t) * NBBY - dstsize * NBBY; + else + n = sizeof (uint64_t) * NBBY - srcsize * NBBY; + + dt_cg_setx(dlp, reg, n); + + instr = DIF_INSTR_FMT(DIF_OP_SLL, + src->dn_reg, reg, dst->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_FMT((dst->dn_flags & DT_NF_SIGNED) ? + DIF_OP_SRA : DIF_OP_SRL, dst->dn_reg, reg, dst->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); + } +} + +/* + * Generate code to push the specified argument list on to the tuple stack. + * We use this routine for handling subroutine calls and associative arrays. + * We must first generate code for all subexpressions before loading the stack + * because any subexpression could itself require the use of the tuple stack. + * This holds a number of registers equal to the number of arguments, but this + * is not a huge problem because the number of arguments can't exceed the + * number of tuple register stack elements anyway. At most one extra register + * is required (either by dt_cg_typecast() or for dtdt_size, below). This + * implies that a DIF implementation should offer a number of general purpose + * registers at least one greater than the number of tuple registers. + */ +static void +dt_cg_arglist(dt_ident_t *idp, dt_node_t *args, + dt_irlist_t *dlp, dt_regset_t *drp) +{ + const dt_idsig_t *isp = idp->di_data; + dt_node_t *dnp; + int i = 0; + + for (dnp = args; dnp != NULL; dnp = dnp->dn_list) + dt_cg_node(dnp, dlp, drp); + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, DIF_INSTR_FLUSHTS)); + + for (dnp = args; dnp != NULL; dnp = dnp->dn_list, i++) { + dtrace_diftype_t t; + dif_instr_t instr; + uint_t op; + int reg; + + dt_node_diftype(yypcb->pcb_hdl, dnp, &t); + + isp->dis_args[i].dn_reg = dnp->dn_reg; /* re-use register */ + dt_cg_typecast(dnp, &isp->dis_args[i], dlp, drp); + isp->dis_args[i].dn_reg = -1; + + if (t.dtdt_flags & DIF_TF_BYREF) + op = DIF_OP_PUSHTR; + else + op = DIF_OP_PUSHTV; + + if (t.dtdt_size != 0) { + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + dt_cg_setx(dlp, reg, t.dtdt_size); + } else + reg = DIF_REG_R0; + + instr = DIF_INSTR_PUSHTS(op, t.dtdt_kind, reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_reg); + + if (reg != DIF_REG_R0) + dt_regset_free(drp, reg); + } + + if (i > yypcb->pcb_hdl->dt_conf.dtc_diftupregs) + longjmp(yypcb->pcb_jmpbuf, EDT_NOTUPREG); +} + +static void +dt_cg_arithmetic_op(dt_node_t *dnp, dt_irlist_t *dlp, + dt_regset_t *drp, uint_t op) +{ + int is_ptr_op = (dnp->dn_op == DT_TOK_ADD || dnp->dn_op == DT_TOK_SUB || + dnp->dn_op == DT_TOK_ADD_EQ || dnp->dn_op == DT_TOK_SUB_EQ); + + int lp_is_ptr = dt_node_is_pointer(dnp->dn_left); + int rp_is_ptr = dt_node_is_pointer(dnp->dn_right); + + dif_instr_t instr; + + if (lp_is_ptr && rp_is_ptr) { + assert(dnp->dn_op == DT_TOK_SUB); + is_ptr_op = 0; + } + + dt_cg_node(dnp->dn_left, dlp, drp); + if (is_ptr_op && rp_is_ptr) + dt_cg_ptrsize(dnp, dlp, drp, DIF_OP_MUL, dnp->dn_left->dn_reg); + + dt_cg_node(dnp->dn_right, dlp, drp); + if (is_ptr_op && lp_is_ptr) + dt_cg_ptrsize(dnp, dlp, drp, DIF_OP_MUL, dnp->dn_right->dn_reg); + + instr = DIF_INSTR_FMT(op, dnp->dn_left->dn_reg, + dnp->dn_right->dn_reg, dnp->dn_left->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_right->dn_reg); + dnp->dn_reg = dnp->dn_left->dn_reg; + + if (lp_is_ptr && rp_is_ptr) + dt_cg_ptrsize(dnp->dn_right, + dlp, drp, DIF_OP_UDIV, dnp->dn_reg); +} + +static uint_t +dt_cg_stvar(const dt_ident_t *idp) +{ + static const uint_t aops[] = { DIF_OP_STGAA, DIF_OP_STTAA, DIF_OP_NOP }; + static const uint_t sops[] = { DIF_OP_STGS, DIF_OP_STTS, DIF_OP_STLS }; + + uint_t i = (((idp->di_flags & DT_IDFLG_LOCAL) != 0) << 1) | + ((idp->di_flags & DT_IDFLG_TLS) != 0); + + return (idp->di_kind == DT_IDENT_ARRAY ? aops[i] : sops[i]); +} + +static void +dt_cg_prearith_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, uint_t op) +{ + ctf_file_t *ctfp = dnp->dn_ctfp; + dif_instr_t instr; + ctf_id_t type; + ssize_t size = 1; + int reg; + + if (dt_node_is_pointer(dnp)) { + type = ctf_type_resolve(ctfp, dnp->dn_type); + assert(ctf_type_kind(ctfp, type) == CTF_K_POINTER); + size = ctf_type_size(ctfp, ctf_type_reference(ctfp, type)); + } + + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_setx(dlp, reg, size); + + instr = DIF_INSTR_FMT(op, dnp->dn_reg, reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); + + /* + * If we are modifying a variable, generate an stv instruction from + * the variable specified by the identifier. If we are storing to a + * memory address, generate code again for the left-hand side using + * DT_NF_REF to get the address, and then generate a store to it. + * In both paths, we store the value in dnp->dn_reg (the new value). + */ + if (dnp->dn_child->dn_kind == DT_NODE_VAR) { + dt_ident_t *idp = dt_ident_resolve(dnp->dn_child->dn_ident); + + idp->di_flags |= DT_IDFLG_DIFW; + instr = DIF_INSTR_STV(dt_cg_stvar(idp), + idp->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } else { + uint_t rbit = dnp->dn_child->dn_flags & DT_NF_REF; + + assert(dnp->dn_child->dn_flags & DT_NF_WRITABLE); + assert(dnp->dn_child->dn_flags & DT_NF_LVALUE); + + dnp->dn_child->dn_flags |= DT_NF_REF; /* force pass-by-ref */ + dt_cg_node(dnp->dn_child, dlp, drp); + + dt_cg_store(dnp, dlp, drp, dnp->dn_child); + dt_regset_free(drp, dnp->dn_child->dn_reg); + + dnp->dn_left->dn_flags &= ~DT_NF_REF; + dnp->dn_left->dn_flags |= rbit; + } +} + +static void +dt_cg_postarith_op(dt_node_t *dnp, dt_irlist_t *dlp, + dt_regset_t *drp, uint_t op) +{ + ctf_file_t *ctfp = dnp->dn_ctfp; + dif_instr_t instr; + ctf_id_t type; + ssize_t size = 1; + int nreg; + + if (dt_node_is_pointer(dnp)) { + type = ctf_type_resolve(ctfp, dnp->dn_type); + assert(ctf_type_kind(ctfp, type) == CTF_K_POINTER); + size = ctf_type_size(ctfp, ctf_type_reference(ctfp, type)); + } + + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + if ((nreg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_setx(dlp, nreg, size); + instr = DIF_INSTR_FMT(op, dnp->dn_reg, nreg, nreg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + /* + * If we are modifying a variable, generate an stv instruction from + * the variable specified by the identifier. If we are storing to a + * memory address, generate code again for the left-hand side using + * DT_NF_REF to get the address, and then generate a store to it. + * In both paths, we store the value from 'nreg' (the new value). + */ + if (dnp->dn_child->dn_kind == DT_NODE_VAR) { + dt_ident_t *idp = dt_ident_resolve(dnp->dn_child->dn_ident); + + idp->di_flags |= DT_IDFLG_DIFW; + instr = DIF_INSTR_STV(dt_cg_stvar(idp), idp->di_id, nreg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } else { + uint_t rbit = dnp->dn_child->dn_flags & DT_NF_REF; + int oreg = dnp->dn_reg; + + assert(dnp->dn_child->dn_flags & DT_NF_WRITABLE); + assert(dnp->dn_child->dn_flags & DT_NF_LVALUE); + + dnp->dn_child->dn_flags |= DT_NF_REF; /* force pass-by-ref */ + dt_cg_node(dnp->dn_child, dlp, drp); + + dnp->dn_reg = nreg; + dt_cg_store(dnp, dlp, drp, dnp->dn_child); + dnp->dn_reg = oreg; + + dt_regset_free(drp, dnp->dn_child->dn_reg); + dnp->dn_left->dn_flags &= ~DT_NF_REF; + dnp->dn_left->dn_flags |= rbit; + } + + dt_regset_free(drp, nreg); +} + +/* + * Determine if we should perform signed or unsigned comparison for an OP2. + * If both operands are of arithmetic type, perform the usual arithmetic + * conversions to determine the common real type for comparison [ISOC 6.5.8.3]. + */ +static int +dt_cg_compare_signed(dt_node_t *dnp) +{ + dt_node_t dn; + + if (dt_node_is_string(dnp->dn_left) || + dt_node_is_string(dnp->dn_right)) + return (1); /* strings always compare signed */ + else if (!dt_node_is_arith(dnp->dn_left) || + !dt_node_is_arith(dnp->dn_right)) + return (0); /* non-arithmetic types always compare unsigned */ + + bzero(&dn, sizeof (dn)); + dt_node_promote(dnp->dn_left, dnp->dn_right, &dn); + return (dn.dn_flags & DT_NF_SIGNED); +} + +static void +dt_cg_compare_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, uint_t op) +{ + uint_t lbl_true = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + uint_t opc; + + dt_cg_node(dnp->dn_left, dlp, drp); + dt_cg_node(dnp->dn_right, dlp, drp); + + if (dt_node_is_string(dnp->dn_left) || dt_node_is_string(dnp->dn_right)) + opc = DIF_OP_SCMP; + else + opc = DIF_OP_CMP; + + instr = DIF_INSTR_CMP(opc, dnp->dn_left->dn_reg, dnp->dn_right->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_right->dn_reg); + dnp->dn_reg = dnp->dn_left->dn_reg; + + instr = DIF_INSTR_BRANCH(op, lbl_true); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_MOV(DIF_REG_R0, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_xsetx(dlp, NULL, lbl_true, dnp->dn_reg, 1); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +/* + * Code generation for the ternary op requires some trickery with the assembler + * in order to conserve registers. We generate code for dn_expr and dn_left + * and free their registers so they do not have be consumed across codegen for + * dn_right. We insert a dummy MOV at the end of dn_left into the destination + * register, which is not yet known because we haven't done dn_right yet, and + * save the pointer to this instruction node. We then generate code for + * dn_right and use its register as our output. Finally, we reach back and + * patch the instruction for dn_left to move its output into this register. + */ +static void +dt_cg_ternary_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_false = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + dt_irnode_t *dip; + + dt_cg_node(dnp->dn_expr, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_expr->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_expr->dn_reg); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_false); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_node(dnp->dn_left, dlp, drp); + instr = DIF_INSTR_MOV(dnp->dn_left->dn_reg, DIF_REG_R0); + dip = dt_cg_node_alloc(DT_LBL_NONE, instr); /* save dip for below */ + dt_irlist_append(dlp, dip); + dt_regset_free(drp, dnp->dn_left->dn_reg); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_false, DIF_INSTR_NOP)); + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + + /* + * Now that dn_reg is assigned, reach back and patch the correct MOV + * instruction into the tail of dn_left. We know dn_reg was unused + * at that point because otherwise dn_right couldn't have allocated it. + */ + dip->di_instr = DIF_INSTR_MOV(dnp->dn_left->dn_reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +static void +dt_cg_logical_and(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_false = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + + dt_cg_node(dnp->dn_left, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_left->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_left->dn_reg); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_false); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_node(dnp->dn_right, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_right->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dnp->dn_reg = dnp->dn_right->dn_reg; + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_false); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, dnp->dn_reg, 1); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_MOV(DIF_REG_R0, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_false, instr)); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +static void +dt_cg_logical_xor(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_next = dt_irlist_label(dlp); + uint_t lbl_tail = dt_irlist_label(dlp); + + dif_instr_t instr; + + dt_cg_node(dnp->dn_left, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_left->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_next); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_cg_setx(dlp, dnp->dn_left->dn_reg, 1); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_next, DIF_INSTR_NOP)); + dt_cg_node(dnp->dn_right, dlp, drp); + + instr = DIF_INSTR_TST(dnp->dn_right->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_tail); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_cg_setx(dlp, dnp->dn_right->dn_reg, 1); + + instr = DIF_INSTR_FMT(DIF_OP_XOR, dnp->dn_left->dn_reg, + dnp->dn_right->dn_reg, dnp->dn_left->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_tail, instr)); + + dt_regset_free(drp, dnp->dn_right->dn_reg); + dnp->dn_reg = dnp->dn_left->dn_reg; +} + +static void +dt_cg_logical_or(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_true = dt_irlist_label(dlp); + uint_t lbl_false = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + + dt_cg_node(dnp->dn_left, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_left->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, dnp->dn_left->dn_reg); + + instr = DIF_INSTR_BRANCH(DIF_OP_BNE, lbl_true); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_node(dnp->dn_right, dlp, drp); + instr = DIF_INSTR_TST(dnp->dn_right->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dnp->dn_reg = dnp->dn_right->dn_reg; + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_false); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_xsetx(dlp, NULL, lbl_true, dnp->dn_reg, 1); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_MOV(DIF_REG_R0, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_false, instr)); + + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +static void +dt_cg_logical_neg(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + uint_t lbl_zero = dt_irlist_label(dlp); + uint_t lbl_post = dt_irlist_label(dlp); + + dif_instr_t instr; + + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + instr = DIF_INSTR_TST(dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BE, lbl_zero); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_MOV(DIF_REG_R0, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BA, lbl_post); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_xsetx(dlp, NULL, lbl_zero, dnp->dn_reg, 1); + dt_irlist_append(dlp, dt_cg_node_alloc(lbl_post, DIF_INSTR_NOP)); +} + +static void +dt_cg_asgn_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dif_instr_t instr; + dt_ident_t *idp; + + /* + * If we are performing a structure assignment of a translated type, + * we must instantiate all members and create a snapshot of the object + * in scratch space. We allocs a chunk of memory, generate code for + * each member, and then set dnp->dn_reg to the scratch object address. + */ + if ((idp = dt_node_resolve(dnp->dn_right, DT_IDENT_XLSOU)) != NULL) { + ctf_membinfo_t ctm; + dt_xlator_t *dxp = idp->di_data; + dt_node_t *mnp, dn, mn; + int r1, r2; + + /* + * Create two fake dt_node_t's representing operator "." and a + * right-hand identifier child node. These will be repeatedly + * modified according to each instantiated member so that we + * can pass them to dt_cg_store() and effect a member store. + */ + bzero(&dn, sizeof (dt_node_t)); + dn.dn_kind = DT_NODE_OP2; + dn.dn_op = DT_TOK_DOT; + dn.dn_left = dnp; + dn.dn_right = &mn; + + bzero(&mn, sizeof (dt_node_t)); + mn.dn_kind = DT_NODE_IDENT; + mn.dn_op = DT_TOK_IDENT; + + /* + * Allocate a register for our scratch data pointer. First we + * set it to the size of our data structure, and then replace + * it with the result of an allocs of the specified size. + */ + if ((r1 = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_setx(dlp, r1, + ctf_type_size(dxp->dx_dst_ctfp, dxp->dx_dst_base)); + + instr = DIF_INSTR_ALLOCS(r1, r1); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + /* + * When dt_cg_asgn_op() is called, we have already generated + * code for dnp->dn_right, which is the translator input. We + * now associate this register with the translator's input + * identifier so it can be referenced during our member loop. + */ + dxp->dx_ident->di_flags |= DT_IDFLG_CGREG; + dxp->dx_ident->di_id = dnp->dn_right->dn_reg; + + for (mnp = dxp->dx_members; mnp != NULL; mnp = mnp->dn_list) { + /* + * Generate code for the translator member expression, + * and then cast the result to the member type. + */ + dt_cg_node(mnp->dn_membexpr, dlp, drp); + mnp->dn_reg = mnp->dn_membexpr->dn_reg; + dt_cg_typecast(mnp->dn_membexpr, mnp, dlp, drp); + + /* + * Ask CTF for the offset of the member so we can store + * to the appropriate offset. This call has already + * been done once by the parser, so it should succeed. + */ + if (ctf_member_info(dxp->dx_dst_ctfp, dxp->dx_dst_base, + mnp->dn_membname, &ctm) == CTF_ERR) { + yypcb->pcb_hdl->dt_ctferr = + ctf_errno(dxp->dx_dst_ctfp); + longjmp(yypcb->pcb_jmpbuf, EDT_CTF); + } + + /* + * If the destination member is at offset 0, store the + * result directly to r1 (the scratch buffer address). + * Otherwise allocate another temporary for the offset + * and add r1 to it before storing the result. + */ + if (ctm.ctm_offset != 0) { + if ((r2 = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + /* + * Add the member offset rounded down to the + * nearest byte. If the offset was not aligned + * on a byte boundary, this member is a bit- + * field and dt_cg_store() will handle masking. + */ + dt_cg_setx(dlp, r2, ctm.ctm_offset / NBBY); + instr = DIF_INSTR_FMT(DIF_OP_ADD, r1, r2, r2); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_node_type_propagate(mnp, &dn); + dn.dn_right->dn_string = mnp->dn_membname; + dn.dn_reg = r2; + + dt_cg_store(mnp, dlp, drp, &dn); + dt_regset_free(drp, r2); + + } else { + dt_node_type_propagate(mnp, &dn); + dn.dn_right->dn_string = mnp->dn_membname; + dn.dn_reg = r1; + + dt_cg_store(mnp, dlp, drp, &dn); + } + + dt_regset_free(drp, mnp->dn_reg); + } + + dxp->dx_ident->di_flags &= ~DT_IDFLG_CGREG; + dxp->dx_ident->di_id = 0; + + if (dnp->dn_right->dn_reg != -1) + dt_regset_free(drp, dnp->dn_right->dn_reg); + + assert(dnp->dn_reg == dnp->dn_right->dn_reg); + dnp->dn_reg = r1; + } + + /* + * If we are storing to a variable, generate an stv instruction from + * the variable specified by the identifier. If we are storing to a + * memory address, generate code again for the left-hand side using + * DT_NF_REF to get the address, and then generate a store to it. + * In both paths, we assume dnp->dn_reg already has the new value. + */ + if (dnp->dn_left->dn_kind == DT_NODE_VAR) { + idp = dt_ident_resolve(dnp->dn_left->dn_ident); + + if (idp->di_kind == DT_IDENT_ARRAY) + dt_cg_arglist(idp, dnp->dn_left->dn_args, dlp, drp); + + idp->di_flags |= DT_IDFLG_DIFW; + instr = DIF_INSTR_STV(dt_cg_stvar(idp), + idp->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + } else { + uint_t rbit = dnp->dn_left->dn_flags & DT_NF_REF; + + assert(dnp->dn_left->dn_flags & DT_NF_WRITABLE); + assert(dnp->dn_left->dn_flags & DT_NF_LVALUE); + + dnp->dn_left->dn_flags |= DT_NF_REF; /* force pass-by-ref */ + + dt_cg_node(dnp->dn_left, dlp, drp); + dt_cg_store(dnp, dlp, drp, dnp->dn_left); + dt_regset_free(drp, dnp->dn_left->dn_reg); + + dnp->dn_left->dn_flags &= ~DT_NF_REF; + dnp->dn_left->dn_flags |= rbit; + } +} + +static void +dt_cg_assoc_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dif_instr_t instr; + uint_t op; + + assert(dnp->dn_kind == DT_NODE_VAR); + assert(!(dnp->dn_ident->di_flags & DT_IDFLG_LOCAL)); + assert(dnp->dn_args != NULL); + + dt_cg_arglist(dnp->dn_ident, dnp->dn_args, dlp, drp); + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + if (dnp->dn_ident->di_flags & DT_IDFLG_TLS) + op = DIF_OP_LDTAA; + else + op = DIF_OP_LDGAA; + + dnp->dn_ident->di_flags |= DT_IDFLG_DIFR; + instr = DIF_INSTR_LDV(op, dnp->dn_ident->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + /* + * If the associative array is a pass-by-reference type, then we are + * loading its value as a pointer to either load or store through it. + * The array element in question may not have been faulted in yet, in + * which case DIF_OP_LD*AA will return zero. We append an epilogue + * of instructions similar to the following: + * + * ld?aa id, %r1 ! base ld?aa instruction above + * tst %r1 ! start of epilogue + * +--- bne label + * | setx size, %r1 + * | allocs %r1, %r1 + * | st?aa id, %r1 + * | ld?aa id, %r1 + * v + * label: < rest of code > + * + * The idea is that we allocs a zero-filled chunk of scratch space and + * do a DIF_OP_ST*AA to fault in and initialize the array element, and + * then reload it to get the faulted-in address of the new variable + * storage. This isn't cheap, but pass-by-ref associative array values + * are (thus far) uncommon and the allocs cost only occurs once. If + * this path becomes important to DTrace users, we can improve things + * by adding a new DIF opcode to fault in associative array elements. + */ + if (dnp->dn_flags & DT_NF_REF) { + uint_t stvop = op == DIF_OP_LDTAA ? DIF_OP_STTAA : DIF_OP_STGAA; + uint_t label = dt_irlist_label(dlp); + + instr = DIF_INSTR_TST(dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_BRANCH(DIF_OP_BNE, label); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_cg_setx(dlp, dnp->dn_reg, dt_node_type_size(dnp)); + instr = DIF_INSTR_ALLOCS(dnp->dn_reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dnp->dn_ident->di_flags |= DT_IDFLG_DIFW; + instr = DIF_INSTR_STV(stvop, dnp->dn_ident->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_LDV(op, dnp->dn_ident->di_id, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dt_irlist_append(dlp, dt_cg_node_alloc(label, DIF_INSTR_NOP)); + } +} + +static void +dt_cg_array_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dt_probe_t *prp = yypcb->pcb_probe; + uintmax_t saved = dnp->dn_args->dn_value; + dt_ident_t *idp = dnp->dn_ident; + + dif_instr_t instr; + uint_t op; + size_t size; + int reg, n; + + assert(dnp->dn_kind == DT_NODE_VAR); + assert(!(idp->di_flags & DT_IDFLG_LOCAL)); + + assert(dnp->dn_args->dn_kind == DT_NODE_INT); + assert(dnp->dn_args->dn_list == NULL); + + /* + * If this is a reference in the args[] array, temporarily modify the + * array index according to the static argument mapping (if any), + * unless the argument reference is provided by a dynamic translator. + * If we're using a dynamic translator for args[], then just set dn_reg + * to an invalid reg and return: DIF_OP_XLARG will fetch the arg later. + */ + if (idp->di_id == DIF_VAR_ARGS) { + if ((idp->di_kind == DT_IDENT_XLPTR || + idp->di_kind == DT_IDENT_XLSOU) && + dt_xlator_dynamic(idp->di_data)) { + dnp->dn_reg = -1; + return; + } + dnp->dn_args->dn_value = prp->pr_mapping[saved]; + } + + dt_cg_node(dnp->dn_args, dlp, drp); + dnp->dn_args->dn_value = saved; + + dnp->dn_reg = dnp->dn_args->dn_reg; + + if (idp->di_flags & DT_IDFLG_TLS) + op = DIF_OP_LDTA; + else + op = DIF_OP_LDGA; + + idp->di_flags |= DT_IDFLG_DIFR; + + instr = DIF_INSTR_LDA(op, idp->di_id, + dnp->dn_args->dn_reg, dnp->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + /* + * If this is a reference to the args[] array, we need to take the + * additional step of explicitly eliminating any bits larger than the + * type size: the DIF interpreter in the kernel will always give us + * the raw (64-bit) argument value, and any bits larger than the type + * size may be junk. As a practical matter, this arises only on 64-bit + * architectures and only when the argument index is larger than the + * number of arguments passed directly to DTrace: if a 8-, 16- or + * 32-bit argument must be retrieved from the stack, it is possible + * (and it some cases, likely) that the upper bits will be garbage. + */ + if (idp->di_id != DIF_VAR_ARGS || !dt_node_is_scalar(dnp)) + return; + + if ((size = dt_node_type_size(dnp)) == sizeof (uint64_t)) + return; + + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + assert(size < sizeof (uint64_t)); + n = sizeof (uint64_t) * NBBY - size * NBBY; + + dt_cg_setx(dlp, reg, n); + + instr = DIF_INSTR_FMT(DIF_OP_SLL, dnp->dn_reg, reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + instr = DIF_INSTR_FMT((dnp->dn_flags & DT_NF_SIGNED) ? + DIF_OP_SRA : DIF_OP_SRL, dnp->dn_reg, reg, dnp->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); +} + +/* + * Generate code for an inlined variable reference. Inlines can be used to + * define either scalar or associative array substitutions. For scalars, we + * simply generate code for the parse tree saved in the identifier's din_root, + * and then cast the resulting expression to the inline's declaration type. + * For arrays, we take the input parameter subtrees from dnp->dn_args and + * temporarily store them in the din_root of each din_argv[i] identifier, + * which are themselves inlines and were set up for us by the parser. The + * result is that any reference to the inlined parameter inside the top-level + * din_root will turn into a recursive call to dt_cg_inline() for a scalar + * inline whose din_root will refer to the subtree pointed to by the argument. + */ +static void +dt_cg_inline(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + dt_ident_t *idp = dnp->dn_ident; + dt_idnode_t *inp = idp->di_iarg; + + dt_idnode_t *pinp; + dt_node_t *pnp; + int i; + + assert(idp->di_flags & DT_IDFLG_INLINE); + assert(idp->di_ops == &dt_idops_inline); + + if (idp->di_kind == DT_IDENT_ARRAY) { + for (i = 0, pnp = dnp->dn_args; + pnp != NULL; pnp = pnp->dn_list, i++) { + if (inp->din_argv[i] != NULL) { + pinp = inp->din_argv[i]->di_iarg; + pinp->din_root = pnp; + } + } + } + + dt_cg_node(inp->din_root, dlp, drp); + dnp->dn_reg = inp->din_root->dn_reg; + dt_cg_typecast(inp->din_root, dnp, dlp, drp); + + if (idp->di_kind == DT_IDENT_ARRAY) { + for (i = 0; i < inp->din_argc; i++) { + pinp = inp->din_argv[i]->di_iarg; + pinp->din_root = NULL; + } + } +} + +static void +dt_cg_func_typeref(dtrace_hdl_t *dtp, dt_node_t *dnp) +{ + dtrace_typeinfo_t dtt; + dt_node_t *addr = dnp->dn_args; + dt_node_t *nelm = addr->dn_list; + dt_node_t *strp = nelm->dn_list; + dt_node_t *typs = strp->dn_list; + char buf[DT_TYPE_NAMELEN]; + char *p; + + ctf_type_name(addr->dn_ctfp, addr->dn_type, buf, sizeof (buf)); + + /* + * XXX Hack alert! XXX + * The prototype has two dummy args that we munge to represent + * the type string and the type size. + * + * Yes, I hear your grumble, but it works for now. We'll come + * up with a more elegant implementation later. :-) + */ + free(strp->dn_string); + + if ((p = strchr(buf, '*')) != NULL) + *p = '\0'; + + strp->dn_string = strdup(buf); + + if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY, buf, &dtt) < 0) + return; + + typs->dn_value = ctf_type_size(dtt.dtt_ctfp, dtt.dtt_type); +} + +static void +dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp) +{ + ctf_file_t *ctfp = dnp->dn_ctfp; + ctf_file_t *octfp; + ctf_membinfo_t m; + ctf_id_t type; + + dif_instr_t instr; + dt_ident_t *idp; + ssize_t stroff; + uint_t op; + int reg; + + switch (dnp->dn_op) { + case DT_TOK_COMMA: + dt_cg_node(dnp->dn_left, dlp, drp); + dt_regset_free(drp, dnp->dn_left->dn_reg); + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + break; + + case DT_TOK_ASGN: + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_ADD_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_ADD); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_SUB_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_SUB); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_MUL_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_MUL); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_DIV_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SDIV : DIF_OP_UDIV); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_MOD_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SREM : DIF_OP_UREM); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_AND_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_AND); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_XOR_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_XOR); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_OR_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_OR); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_LSH_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_SLL); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_RSH_EQ: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SRA : DIF_OP_SRL); + dt_cg_asgn_op(dnp, dlp, drp); + break; + + case DT_TOK_QUESTION: + dt_cg_ternary_op(dnp, dlp, drp); + break; + + case DT_TOK_LOR: + dt_cg_logical_or(dnp, dlp, drp); + break; + + case DT_TOK_LXOR: + dt_cg_logical_xor(dnp, dlp, drp); + break; + + case DT_TOK_LAND: + dt_cg_logical_and(dnp, dlp, drp); + break; + + case DT_TOK_BOR: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_OR); + break; + + case DT_TOK_XOR: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_XOR); + break; + + case DT_TOK_BAND: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_AND); + break; + + case DT_TOK_EQU: + dt_cg_compare_op(dnp, dlp, drp, DIF_OP_BE); + break; + + case DT_TOK_NEQ: + dt_cg_compare_op(dnp, dlp, drp, DIF_OP_BNE); + break; + + case DT_TOK_LT: + dt_cg_compare_op(dnp, dlp, drp, + dt_cg_compare_signed(dnp) ? DIF_OP_BL : DIF_OP_BLU); + break; + + case DT_TOK_LE: + dt_cg_compare_op(dnp, dlp, drp, + dt_cg_compare_signed(dnp) ? DIF_OP_BLE : DIF_OP_BLEU); + break; + + case DT_TOK_GT: + dt_cg_compare_op(dnp, dlp, drp, + dt_cg_compare_signed(dnp) ? DIF_OP_BG : DIF_OP_BGU); + break; + + case DT_TOK_GE: + dt_cg_compare_op(dnp, dlp, drp, + dt_cg_compare_signed(dnp) ? DIF_OP_BGE : DIF_OP_BGEU); + break; + + case DT_TOK_LSH: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_SLL); + break; + + case DT_TOK_RSH: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SRA : DIF_OP_SRL); + break; + + case DT_TOK_ADD: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_ADD); + break; + + case DT_TOK_SUB: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_SUB); + break; + + case DT_TOK_MUL: + dt_cg_arithmetic_op(dnp, dlp, drp, DIF_OP_MUL); + break; + + case DT_TOK_DIV: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SDIV : DIF_OP_UDIV); + break; + + case DT_TOK_MOD: + dt_cg_arithmetic_op(dnp, dlp, drp, + (dnp->dn_flags & DT_NF_SIGNED) ? DIF_OP_SREM : DIF_OP_UREM); + break; + + case DT_TOK_LNEG: + dt_cg_logical_neg(dnp, dlp, drp); + break; + + case DT_TOK_BNEG: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + instr = DIF_INSTR_NOT(dnp->dn_reg, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + + case DT_TOK_PREINC: + dt_cg_prearith_op(dnp, dlp, drp, DIF_OP_ADD); + break; + + case DT_TOK_POSTINC: + dt_cg_postarith_op(dnp, dlp, drp, DIF_OP_ADD); + break; + + case DT_TOK_PREDEC: + dt_cg_prearith_op(dnp, dlp, drp, DIF_OP_SUB); + break; + + case DT_TOK_POSTDEC: + dt_cg_postarith_op(dnp, dlp, drp, DIF_OP_SUB); + break; + + case DT_TOK_IPOS: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + break; + + case DT_TOK_INEG: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + instr = DIF_INSTR_FMT(DIF_OP_SUB, DIF_REG_R0, + dnp->dn_reg, dnp->dn_reg); + + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + + case DT_TOK_DEREF: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + if (!(dnp->dn_flags & DT_NF_REF)) { + uint_t ubit = dnp->dn_flags & DT_NF_USERLAND; + + /* + * Save and restore DT_NF_USERLAND across dt_cg_load(): + * we need the sign bit from dnp and the user bit from + * dnp->dn_child in order to get the proper opcode. + */ + dnp->dn_flags |= + (dnp->dn_child->dn_flags & DT_NF_USERLAND); + + instr = DIF_INSTR_LOAD(dt_cg_load(dnp, ctfp, + dnp->dn_type), dnp->dn_reg, dnp->dn_reg); + + dnp->dn_flags &= ~DT_NF_USERLAND; + dnp->dn_flags |= ubit; + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + } + break; + + case DT_TOK_ADDROF: { + uint_t rbit = dnp->dn_child->dn_flags & DT_NF_REF; + + dnp->dn_child->dn_flags |= DT_NF_REF; /* force pass-by-ref */ + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + + dnp->dn_child->dn_flags &= ~DT_NF_REF; + dnp->dn_child->dn_flags |= rbit; + break; + } + + case DT_TOK_SIZEOF: { + size_t size = dt_node_sizeof(dnp->dn_child); + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + assert(size != 0); + dt_cg_setx(dlp, dnp->dn_reg, size); + break; + } + + case DT_TOK_STRINGOF: + dt_cg_node(dnp->dn_child, dlp, drp); + dnp->dn_reg = dnp->dn_child->dn_reg; + break; + + case DT_TOK_XLATE: + /* + * An xlate operator appears in either an XLATOR, indicating a + * reference to a dynamic translator, or an OP2, indicating + * use of the xlate operator in the user's program. For the + * dynamic case, generate an xlate opcode with a reference to + * the corresponding member, pre-computed for us in dn_members. + */ + if (dnp->dn_kind == DT_NODE_XLATOR) { + dt_xlator_t *dxp = dnp->dn_xlator; + + assert(dxp->dx_ident->di_flags & DT_IDFLG_CGREG); + assert(dxp->dx_ident->di_id != 0); + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + if (dxp->dx_arg == -1) { + instr = DIF_INSTR_MOV( + dxp->dx_ident->di_id, dnp->dn_reg); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + op = DIF_OP_XLATE; + } else + op = DIF_OP_XLARG; + + instr = DIF_INSTR_XLATE(op, 0, dnp->dn_reg); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + + dlp->dl_last->di_extern = dnp->dn_xmember; + break; + } + + assert(dnp->dn_kind == DT_NODE_OP2); + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + break; + + case DT_TOK_LPAR: + dt_cg_node(dnp->dn_right, dlp, drp); + dnp->dn_reg = dnp->dn_right->dn_reg; + dt_cg_typecast(dnp->dn_right, dnp, dlp, drp); + break; + + case DT_TOK_PTR: + case DT_TOK_DOT: + assert(dnp->dn_right->dn_kind == DT_NODE_IDENT); + dt_cg_node(dnp->dn_left, dlp, drp); + + /* + * If the left-hand side of PTR or DOT is a dynamic variable, + * we expect it to be the output of a D translator. In this + * case, we look up the parse tree corresponding to the member + * that is being accessed and run the code generator over it. + * We then cast the result as if by the assignment operator. + */ + if ((idp = dt_node_resolve( + dnp->dn_left, DT_IDENT_XLSOU)) != NULL || + (idp = dt_node_resolve( + dnp->dn_left, DT_IDENT_XLPTR)) != NULL) { + + dt_xlator_t *dxp; + dt_node_t *mnp; + + dxp = idp->di_data; + mnp = dt_xlator_member(dxp, dnp->dn_right->dn_string); + assert(mnp != NULL); + + dxp->dx_ident->di_flags |= DT_IDFLG_CGREG; + dxp->dx_ident->di_id = dnp->dn_left->dn_reg; + + dt_cg_node(mnp->dn_membexpr, dlp, drp); + dnp->dn_reg = mnp->dn_membexpr->dn_reg; + dt_cg_typecast(mnp->dn_membexpr, dnp, dlp, drp); + + dxp->dx_ident->di_flags &= ~DT_IDFLG_CGREG; + dxp->dx_ident->di_id = 0; + + if (dnp->dn_left->dn_reg != -1) + dt_regset_free(drp, dnp->dn_left->dn_reg); + break; + } + + ctfp = dnp->dn_left->dn_ctfp; + type = ctf_type_resolve(ctfp, dnp->dn_left->dn_type); + + if (dnp->dn_op == DT_TOK_PTR) { + type = ctf_type_reference(ctfp, type); + type = ctf_type_resolve(ctfp, type); + } + + if ((ctfp = dt_cg_membinfo(octfp = ctfp, type, + dnp->dn_right->dn_string, &m)) == NULL) { + yypcb->pcb_hdl->dt_ctferr = ctf_errno(octfp); + longjmp(yypcb->pcb_jmpbuf, EDT_CTF); + } + + if (m.ctm_offset != 0) { + if ((reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + /* + * If the offset is not aligned on a byte boundary, it + * is a bit-field member and we will extract the value + * bits below after we generate the appropriate load. + */ + dt_cg_setx(dlp, reg, m.ctm_offset / NBBY); + + instr = DIF_INSTR_FMT(DIF_OP_ADD, + dnp->dn_left->dn_reg, reg, dnp->dn_left->dn_reg); + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + dt_regset_free(drp, reg); + } + + if (!(dnp->dn_flags & DT_NF_REF)) { + uint_t ubit = dnp->dn_flags & DT_NF_USERLAND; + + /* + * Save and restore DT_NF_USERLAND across dt_cg_load(): + * we need the sign bit from dnp and the user bit from + * dnp->dn_left in order to get the proper opcode. + */ + dnp->dn_flags |= + (dnp->dn_left->dn_flags & DT_NF_USERLAND); + + instr = DIF_INSTR_LOAD(dt_cg_load(dnp, + ctfp, m.ctm_type), dnp->dn_left->dn_reg, + dnp->dn_left->dn_reg); + + dnp->dn_flags &= ~DT_NF_USERLAND; + dnp->dn_flags |= ubit; + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + + if (dnp->dn_flags & DT_NF_BITFIELD) + dt_cg_field_get(dnp, dlp, drp, ctfp, &m); + } + + dnp->dn_reg = dnp->dn_left->dn_reg; + break; + + case DT_TOK_STRING: + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + assert(dnp->dn_kind == DT_NODE_STRING); + stroff = dt_strtab_insert(yypcb->pcb_strtab, dnp->dn_string); + + if (stroff == -1L) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + if (stroff > DIF_STROFF_MAX) + longjmp(yypcb->pcb_jmpbuf, EDT_STR2BIG); + + instr = DIF_INSTR_SETS((ulong_t)stroff, dnp->dn_reg); + dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + + case DT_TOK_IDENT: + /* + * If the specified identifier is a variable on which we have + * set the code generator register flag, then this variable + * has already had code generated for it and saved in di_id. + * Allocate a new register and copy the existing value to it. + */ + if (dnp->dn_kind == DT_NODE_VAR && + (dnp->dn_ident->di_flags & DT_IDFLG_CGREG)) { + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + instr = DIF_INSTR_MOV(dnp->dn_ident->di_id, + dnp->dn_reg); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + } + + /* + * Identifiers can represent function calls, variable refs, or + * symbols. First we check for inlined variables, and handle + * them by generating code for the inline parse tree. + */ + if (dnp->dn_kind == DT_NODE_VAR && + (dnp->dn_ident->di_flags & DT_IDFLG_INLINE)) { + dt_cg_inline(dnp, dlp, drp); + break; + } + + switch (dnp->dn_kind) { + case DT_NODE_FUNC: { + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + + if ((idp = dnp->dn_ident)->di_kind != DT_IDENT_FUNC) { + dnerror(dnp, D_CG_EXPR, "%s %s( ) may not be " + "called from a D expression (D program " + "context required)\n", + dt_idkind_name(idp->di_kind), idp->di_name); + } + + switch (idp->di_id) { + case DIF_SUBR_TYPEREF: + dt_cg_func_typeref(dtp, dnp); + break; + + default: + break; + } + + dt_cg_arglist(dnp->dn_ident, dnp->dn_args, dlp, drp); + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + instr = DIF_INSTR_CALL( + dnp->dn_ident->di_id, dnp->dn_reg); + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + + break; + } + + case DT_NODE_VAR: + if (dnp->dn_ident->di_kind == DT_IDENT_XLSOU || + dnp->dn_ident->di_kind == DT_IDENT_XLPTR) { + /* + * This can only happen if we have translated + * args[]. See dt_idcook_args() for details. + */ + assert(dnp->dn_ident->di_id == DIF_VAR_ARGS); + dt_cg_array_op(dnp, dlp, drp); + break; + } + + if (dnp->dn_ident->di_kind == DT_IDENT_ARRAY) { + if (dnp->dn_ident->di_id > DIF_VAR_ARRAY_MAX) + dt_cg_assoc_op(dnp, dlp, drp); + else + dt_cg_array_op(dnp, dlp, drp); + break; + } + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + if (dnp->dn_ident->di_flags & DT_IDFLG_LOCAL) + op = DIF_OP_LDLS; + else if (dnp->dn_ident->di_flags & DT_IDFLG_TLS) + op = DIF_OP_LDTS; + else + op = DIF_OP_LDGS; + + dnp->dn_ident->di_flags |= DT_IDFLG_DIFR; + + instr = DIF_INSTR_LDV(op, + dnp->dn_ident->di_id, dnp->dn_reg); + + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + break; + + case DT_NODE_SYM: { + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dtrace_syminfo_t *sip = dnp->dn_ident->di_data; + GElf_Sym sym; + + if (dtrace_lookup_by_name(dtp, + sip->dts_object, sip->dts_name, &sym, NULL) == -1) { + xyerror(D_UNKNOWN, "cg failed for symbol %s`%s:" + " %s\n", sip->dts_object, sip->dts_name, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + } + + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_xsetx(dlp, dnp->dn_ident, + DT_LBL_NONE, dnp->dn_reg, sym.st_value); + + if (!(dnp->dn_flags & DT_NF_REF)) { + instr = DIF_INSTR_LOAD(dt_cg_load(dnp, ctfp, + dnp->dn_type), dnp->dn_reg, dnp->dn_reg); + dt_irlist_append(dlp, + dt_cg_node_alloc(DT_LBL_NONE, instr)); + } + break; + } + + default: + xyerror(D_UNKNOWN, "internal error -- node type %u is " + "not valid for an identifier\n", dnp->dn_kind); + } + break; + + case DT_TOK_INT: + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1) + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); + + dt_cg_setx(dlp, dnp->dn_reg, dnp->dn_value); + break; + + default: + xyerror(D_UNKNOWN, "internal error -- token type %u is not a " + "valid D compilation token\n", dnp->dn_op); + } +} + +void +dt_cg(dt_pcb_t *pcb, dt_node_t *dnp) +{ + dif_instr_t instr; + dt_xlator_t *dxp; + + if (pcb->pcb_regs == NULL && (pcb->pcb_regs = + dt_regset_create(pcb->pcb_hdl->dt_conf.dtc_difintregs)) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + dt_regset_reset(pcb->pcb_regs); + (void) dt_regset_alloc(pcb->pcb_regs); /* allocate %r0 */ + + if (pcb->pcb_inttab != NULL) + dt_inttab_destroy(pcb->pcb_inttab); + + if ((pcb->pcb_inttab = dt_inttab_create(yypcb->pcb_hdl)) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + if (pcb->pcb_strtab != NULL) + dt_strtab_destroy(pcb->pcb_strtab); + + if ((pcb->pcb_strtab = dt_strtab_create(BUFSIZ)) == NULL) + longjmp(pcb->pcb_jmpbuf, EDT_NOMEM); + + dt_irlist_destroy(&pcb->pcb_ir); + dt_irlist_create(&pcb->pcb_ir); + + assert(pcb->pcb_dret == NULL); + pcb->pcb_dret = dnp; + + if (dt_node_is_dynamic(dnp)) { + dnerror(dnp, D_CG_DYN, "expression cannot evaluate to result " + "of dynamic type\n"); + } + + /* + * If we're generating code for a translator body, assign the input + * parameter to the first available register (i.e. caller passes %r1). + */ + if (dnp->dn_kind == DT_NODE_MEMBER) { + dxp = dnp->dn_membxlator; + dnp = dnp->dn_membexpr; + + dxp->dx_ident->di_flags |= DT_IDFLG_CGREG; + dxp->dx_ident->di_id = dt_regset_alloc(pcb->pcb_regs); + } + + dt_cg_node(dnp, &pcb->pcb_ir, pcb->pcb_regs); + instr = DIF_INSTR_RET(dnp->dn_reg); + dt_regset_free(pcb->pcb_regs, dnp->dn_reg); + dt_irlist_append(&pcb->pcb_ir, dt_cg_node_alloc(DT_LBL_NONE, instr)); + + if (dnp->dn_kind == DT_NODE_MEMBER) { + dt_regset_free(pcb->pcb_regs, dxp->dx_ident->di_id); + dxp->dx_ident->di_id = 0; + dxp->dx_ident->di_flags &= ~DT_IDFLG_CGREG; + } +} diff --git a/lib/libdtrace/common/dt_consume.c b/lib/libdtrace/common/dt_consume.c new file mode 100644 index 000000000000..776fd17c0309 --- /dev/null +++ b/lib/libdtrace/common/dt_consume.c @@ -0,0 +1,2646 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <unistd.h> +#include <limits.h> +#include <assert.h> +#include <ctype.h> +#if defined(sun) +#include <alloca.h> +#endif +#include <dt_impl.h> + +#define DT_MASK_LO 0x00000000FFFFFFFFULL + +/* + * We declare this here because (1) we need it and (2) we want to avoid a + * dependency on libm in libdtrace. + */ +static long double +dt_fabsl(long double x) +{ + if (x < 0) + return (-x); + + return (x); +} + +/* + * 128-bit arithmetic functions needed to support the stddev() aggregating + * action. + */ +static int +dt_gt_128(uint64_t *a, uint64_t *b) +{ + return (a[1] > b[1] || (a[1] == b[1] && a[0] > b[0])); +} + +static int +dt_ge_128(uint64_t *a, uint64_t *b) +{ + return (a[1] > b[1] || (a[1] == b[1] && a[0] >= b[0])); +} + +static int +dt_le_128(uint64_t *a, uint64_t *b) +{ + return (a[1] < b[1] || (a[1] == b[1] && a[0] <= b[0])); +} + +/* + * Shift the 128-bit value in a by b. If b is positive, shift left. + * If b is negative, shift right. + */ +static void +dt_shift_128(uint64_t *a, int b) +{ + uint64_t mask; + + if (b == 0) + return; + + if (b < 0) { + b = -b; + if (b >= 64) { + a[0] = a[1] >> (b - 64); + a[1] = 0; + } else { + a[0] >>= b; + mask = 1LL << (64 - b); + mask -= 1; + a[0] |= ((a[1] & mask) << (64 - b)); + a[1] >>= b; + } + } else { + if (b >= 64) { + a[1] = a[0] << (b - 64); + a[0] = 0; + } else { + a[1] <<= b; + mask = a[0] >> (64 - b); + a[1] |= mask; + a[0] <<= b; + } + } +} + +static int +dt_nbits_128(uint64_t *a) +{ + int nbits = 0; + uint64_t tmp[2]; + uint64_t zero[2] = { 0, 0 }; + + tmp[0] = a[0]; + tmp[1] = a[1]; + + dt_shift_128(tmp, -1); + while (dt_gt_128(tmp, zero)) { + dt_shift_128(tmp, -1); + nbits++; + } + + return (nbits); +} + +static void +dt_subtract_128(uint64_t *minuend, uint64_t *subtrahend, uint64_t *difference) +{ + uint64_t result[2]; + + result[0] = minuend[0] - subtrahend[0]; + result[1] = minuend[1] - subtrahend[1] - + (minuend[0] < subtrahend[0] ? 1 : 0); + + difference[0] = result[0]; + difference[1] = result[1]; +} + +static void +dt_add_128(uint64_t *addend1, uint64_t *addend2, uint64_t *sum) +{ + uint64_t result[2]; + + result[0] = addend1[0] + addend2[0]; + result[1] = addend1[1] + addend2[1] + + (result[0] < addend1[0] || result[0] < addend2[0] ? 1 : 0); + + sum[0] = result[0]; + sum[1] = result[1]; +} + +/* + * The basic idea is to break the 2 64-bit values into 4 32-bit values, + * use native multiplication on those, and then re-combine into the + * resulting 128-bit value. + * + * (hi1 << 32 + lo1) * (hi2 << 32 + lo2) = + * hi1 * hi2 << 64 + + * hi1 * lo2 << 32 + + * hi2 * lo1 << 32 + + * lo1 * lo2 + */ +static void +dt_multiply_128(uint64_t factor1, uint64_t factor2, uint64_t *product) +{ + uint64_t hi1, hi2, lo1, lo2; + uint64_t tmp[2]; + + hi1 = factor1 >> 32; + hi2 = factor2 >> 32; + + lo1 = factor1 & DT_MASK_LO; + lo2 = factor2 & DT_MASK_LO; + + product[0] = lo1 * lo2; + product[1] = hi1 * hi2; + + tmp[0] = hi1 * lo2; + tmp[1] = 0; + dt_shift_128(tmp, 32); + dt_add_128(product, tmp, product); + + tmp[0] = hi2 * lo1; + tmp[1] = 0; + dt_shift_128(tmp, 32); + dt_add_128(product, tmp, product); +} + +/* + * This is long-hand division. + * + * We initialize subtrahend by shifting divisor left as far as possible. We + * loop, comparing subtrahend to dividend: if subtrahend is smaller, we + * subtract and set the appropriate bit in the result. We then shift + * subtrahend right by one bit for the next comparison. + */ +static void +dt_divide_128(uint64_t *dividend, uint64_t divisor, uint64_t *quotient) +{ + uint64_t result[2] = { 0, 0 }; + uint64_t remainder[2]; + uint64_t subtrahend[2]; + uint64_t divisor_128[2]; + uint64_t mask[2] = { 1, 0 }; + int log = 0; + + assert(divisor != 0); + + divisor_128[0] = divisor; + divisor_128[1] = 0; + + remainder[0] = dividend[0]; + remainder[1] = dividend[1]; + + subtrahend[0] = divisor; + subtrahend[1] = 0; + + while (divisor > 0) { + log++; + divisor >>= 1; + } + + dt_shift_128(subtrahend, 128 - log); + dt_shift_128(mask, 128 - log); + + while (dt_ge_128(remainder, divisor_128)) { + if (dt_ge_128(remainder, subtrahend)) { + dt_subtract_128(remainder, subtrahend, remainder); + result[0] |= mask[0]; + result[1] |= mask[1]; + } + + dt_shift_128(subtrahend, -1); + dt_shift_128(mask, -1); + } + + quotient[0] = result[0]; + quotient[1] = result[1]; +} + +/* + * This is the long-hand method of calculating a square root. + * The algorithm is as follows: + * + * 1. Group the digits by 2 from the right. + * 2. Over the leftmost group, find the largest single-digit number + * whose square is less than that group. + * 3. Subtract the result of the previous step (2 or 4, depending) and + * bring down the next two-digit group. + * 4. For the result R we have so far, find the largest single-digit number + * x such that 2 * R * 10 * x + x^2 is less than the result from step 3. + * (Note that this is doubling R and performing a decimal left-shift by 1 + * and searching for the appropriate decimal to fill the one's place.) + * The value x is the next digit in the square root. + * Repeat steps 3 and 4 until the desired precision is reached. (We're + * dealing with integers, so the above is sufficient.) + * + * In decimal, the square root of 582,734 would be calculated as so: + * + * __7__6__3 + * | 58 27 34 + * -49 (7^2 == 49 => 7 is the first digit in the square root) + * -- + * 9 27 (Subtract and bring down the next group.) + * 146 8 76 (2 * 7 * 10 * 6 + 6^2 == 876 => 6 is the next digit in + * ----- the square root) + * 51 34 (Subtract and bring down the next group.) + * 1523 45 69 (2 * 76 * 10 * 3 + 3^2 == 4569 => 3 is the next digit in + * ----- the square root) + * 5 65 (remainder) + * + * The above algorithm applies similarly in binary, but note that the + * only possible non-zero value for x in step 4 is 1, so step 4 becomes a + * simple decision: is 2 * R * 2 * 1 + 1^2 (aka R << 2 + 1) less than the + * preceding difference? + * + * In binary, the square root of 11011011 would be calculated as so: + * + * __1__1__1__0 + * | 11 01 10 11 + * 01 (0 << 2 + 1 == 1 < 11 => this bit is 1) + * -- + * 10 01 10 11 + * 101 1 01 (1 << 2 + 1 == 101 < 1001 => next bit is 1) + * ----- + * 1 00 10 11 + * 1101 11 01 (11 << 2 + 1 == 1101 < 10010 => next bit is 1) + * ------- + * 1 01 11 + * 11101 1 11 01 (111 << 2 + 1 == 11101 > 10111 => last bit is 0) + * + */ +static uint64_t +dt_sqrt_128(uint64_t *square) +{ + uint64_t result[2] = { 0, 0 }; + uint64_t diff[2] = { 0, 0 }; + uint64_t one[2] = { 1, 0 }; + uint64_t next_pair[2]; + uint64_t next_try[2]; + uint64_t bit_pairs, pair_shift; + int i; + + bit_pairs = dt_nbits_128(square) / 2; + pair_shift = bit_pairs * 2; + + for (i = 0; i <= bit_pairs; i++) { + /* + * Bring down the next pair of bits. + */ + next_pair[0] = square[0]; + next_pair[1] = square[1]; + dt_shift_128(next_pair, -pair_shift); + next_pair[0] &= 0x3; + next_pair[1] = 0; + + dt_shift_128(diff, 2); + dt_add_128(diff, next_pair, diff); + + /* + * next_try = R << 2 + 1 + */ + next_try[0] = result[0]; + next_try[1] = result[1]; + dt_shift_128(next_try, 2); + dt_add_128(next_try, one, next_try); + + if (dt_le_128(next_try, diff)) { + dt_subtract_128(diff, next_try, diff); + dt_shift_128(result, 1); + dt_add_128(result, one, result); + } else { + dt_shift_128(result, 1); + } + + pair_shift -= 2; + } + + assert(result[1] == 0); + + return (result[0]); +} + +uint64_t +dt_stddev(uint64_t *data, uint64_t normal) +{ + uint64_t avg_of_squares[2]; + uint64_t square_of_avg[2]; + int64_t norm_avg; + uint64_t diff[2]; + + /* + * The standard approximation for standard deviation is + * sqrt(average(x**2) - average(x)**2), i.e. the square root + * of the average of the squares minus the square of the average. + */ + dt_divide_128(data + 2, normal, avg_of_squares); + dt_divide_128(avg_of_squares, data[0], avg_of_squares); + + norm_avg = (int64_t)data[1] / (int64_t)normal / (int64_t)data[0]; + + if (norm_avg < 0) + norm_avg = -norm_avg; + + dt_multiply_128((uint64_t)norm_avg, (uint64_t)norm_avg, square_of_avg); + + dt_subtract_128(avg_of_squares, square_of_avg, diff); + + return (dt_sqrt_128(diff)); +} + +static int +dt_flowindent(dtrace_hdl_t *dtp, dtrace_probedata_t *data, dtrace_epid_t last, + dtrace_bufdesc_t *buf, size_t offs) +{ + dtrace_probedesc_t *pd = data->dtpda_pdesc, *npd; + dtrace_eprobedesc_t *epd = data->dtpda_edesc, *nepd; + char *p = pd->dtpd_provider, *n = pd->dtpd_name, *sub; + dtrace_flowkind_t flow = DTRACEFLOW_NONE; + const char *str = NULL; + static const char *e_str[2] = { " -> ", " => " }; + static const char *r_str[2] = { " <- ", " <= " }; + static const char *ent = "entry", *ret = "return"; + static int entlen = 0, retlen = 0; + dtrace_epid_t next, id = epd->dtepd_epid; + int rval; + + if (entlen == 0) { + assert(retlen == 0); + entlen = strlen(ent); + retlen = strlen(ret); + } + + /* + * If the name of the probe is "entry" or ends with "-entry", we + * treat it as an entry; if it is "return" or ends with "-return", + * we treat it as a return. (This allows application-provided probes + * like "method-entry" or "function-entry" to participate in flow + * indentation -- without accidentally misinterpreting popular probe + * names like "carpentry", "gentry" or "Coventry".) + */ + if ((sub = strstr(n, ent)) != NULL && sub[entlen] == '\0' && + (sub == n || sub[-1] == '-')) { + flow = DTRACEFLOW_ENTRY; + str = e_str[strcmp(p, "syscall") == 0]; + } else if ((sub = strstr(n, ret)) != NULL && sub[retlen] == '\0' && + (sub == n || sub[-1] == '-')) { + flow = DTRACEFLOW_RETURN; + str = r_str[strcmp(p, "syscall") == 0]; + } + + /* + * If we're going to indent this, we need to check the ID of our last + * call. If we're looking at the same probe ID but a different EPID, + * we _don't_ want to indent. (Yes, there are some minor holes in + * this scheme -- it's a heuristic.) + */ + if (flow == DTRACEFLOW_ENTRY) { + if ((last != DTRACE_EPIDNONE && id != last && + pd->dtpd_id == dtp->dt_pdesc[last]->dtpd_id)) + flow = DTRACEFLOW_NONE; + } + + /* + * If we're going to unindent this, it's more difficult to see if + * we don't actually want to unindent it -- we need to look at the + * _next_ EPID. + */ + if (flow == DTRACEFLOW_RETURN) { + offs += epd->dtepd_size; + + do { + if (offs >= buf->dtbd_size) { + /* + * We're at the end -- maybe. If the oldest + * record is non-zero, we need to wrap. + */ + if (buf->dtbd_oldest != 0) { + offs = 0; + } else { + goto out; + } + } + + next = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); + + if (next == DTRACE_EPIDNONE) + offs += sizeof (id); + } while (next == DTRACE_EPIDNONE); + + if ((rval = dt_epid_lookup(dtp, next, &nepd, &npd)) != 0) + return (rval); + + if (next != id && npd->dtpd_id == pd->dtpd_id) + flow = DTRACEFLOW_NONE; + } + +out: + if (flow == DTRACEFLOW_ENTRY || flow == DTRACEFLOW_RETURN) { + data->dtpda_prefix = str; + } else { + data->dtpda_prefix = "| "; + } + + if (flow == DTRACEFLOW_RETURN && data->dtpda_indent > 0) + data->dtpda_indent -= 2; + + data->dtpda_flow = flow; + + return (0); +} + +static int +dt_nullprobe() +{ + return (DTRACE_CONSUME_THIS); +} + +static int +dt_nullrec() +{ + return (DTRACE_CONSUME_NEXT); +} + +int +dt_print_quantline(dtrace_hdl_t *dtp, FILE *fp, int64_t val, + uint64_t normal, long double total, char positives, char negatives) +{ + long double f; + uint_t depth, len = 40; + + const char *ats = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"; + const char *spaces = " "; + + assert(strlen(ats) == len && strlen(spaces) == len); + assert(!(total == 0 && (positives || negatives))); + assert(!(val < 0 && !negatives)); + assert(!(val > 0 && !positives)); + assert(!(val != 0 && total == 0)); + + if (!negatives) { + if (positives) { + f = (dt_fabsl((long double)val) * len) / total; + depth = (uint_t)(f + 0.5); + } else { + depth = 0; + } + + return (dt_printf(dtp, fp, "|%s%s %-9lld\n", ats + len - depth, + spaces + depth, (long long)val / normal)); + } + + if (!positives) { + f = (dt_fabsl((long double)val) * len) / total; + depth = (uint_t)(f + 0.5); + + return (dt_printf(dtp, fp, "%s%s| %-9lld\n", spaces + depth, + ats + len - depth, (long long)val / normal)); + } + + /* + * If we're here, we have both positive and negative bucket values. + * To express this graphically, we're going to generate both positive + * and negative bars separated by a centerline. These bars are half + * the size of normal quantize()/lquantize() bars, so we divide the + * length in half before calculating the bar length. + */ + len /= 2; + ats = &ats[len]; + spaces = &spaces[len]; + + f = (dt_fabsl((long double)val) * len) / total; + depth = (uint_t)(f + 0.5); + + if (val <= 0) { + return (dt_printf(dtp, fp, "%s%s|%*s %-9lld\n", spaces + depth, + ats + len - depth, len, "", (long long)val / normal)); + } else { + return (dt_printf(dtp, fp, "%20s|%s%s %-9lld\n", "", + ats + len - depth, spaces + depth, + (long long)val / normal)); + } +} + +int +dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, + size_t size, uint64_t normal) +{ + const int64_t *data = addr; + int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; + long double total = 0; + char positives = 0, negatives = 0; + + if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) + return (dt_set_errno(dtp, EDT_DMISMATCH)); + + while (first_bin < DTRACE_QUANTIZE_NBUCKETS - 1 && data[first_bin] == 0) + first_bin++; + + if (first_bin == DTRACE_QUANTIZE_NBUCKETS - 1) { + /* + * There isn't any data. This is possible if (and only if) + * negative increment values have been used. In this case, + * we'll print the buckets around 0. + */ + first_bin = DTRACE_QUANTIZE_ZEROBUCKET - 1; + last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 1; + } else { + if (first_bin > 0) + first_bin--; + + while (last_bin > 0 && data[last_bin] == 0) + last_bin--; + + if (last_bin < DTRACE_QUANTIZE_NBUCKETS - 1) + last_bin++; + } + + for (i = first_bin; i <= last_bin; i++) { + positives |= (data[i] > 0); + negatives |= (data[i] < 0); + total += dt_fabsl((long double)data[i]); + } + + if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", + "------------- Distribution -------------", "count") < 0) + return (-1); + + for (i = first_bin; i <= last_bin; i++) { + if (dt_printf(dtp, fp, "%16lld ", + (long long)DTRACE_QUANTIZE_BUCKETVAL(i)) < 0) + return (-1); + + if (dt_print_quantline(dtp, fp, data[i], normal, total, + positives, negatives) < 0) + return (-1); + } + + return (0); +} + +int +dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, + size_t size, uint64_t normal) +{ + const int64_t *data = addr; + int i, first_bin, last_bin, base; + uint64_t arg; + long double total = 0; + uint16_t step, levels; + char positives = 0, negatives = 0; + + if (size < sizeof (uint64_t)) + return (dt_set_errno(dtp, EDT_DMISMATCH)); + + arg = *data++; + size -= sizeof (uint64_t); + + base = DTRACE_LQUANTIZE_BASE(arg); + step = DTRACE_LQUANTIZE_STEP(arg); + levels = DTRACE_LQUANTIZE_LEVELS(arg); + + first_bin = 0; + last_bin = levels + 1; + + if (size != sizeof (uint64_t) * (levels + 2)) + return (dt_set_errno(dtp, EDT_DMISMATCH)); + + while (first_bin <= levels + 1 && data[first_bin] == 0) + first_bin++; + + if (first_bin > levels + 1) { + first_bin = 0; + last_bin = 2; + } else { + if (first_bin > 0) + first_bin--; + + while (last_bin > 0 && data[last_bin] == 0) + last_bin--; + + if (last_bin < levels + 1) + last_bin++; + } + + for (i = first_bin; i <= last_bin; i++) { + positives |= (data[i] > 0); + negatives |= (data[i] < 0); + total += dt_fabsl((long double)data[i]); + } + + if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", + "------------- Distribution -------------", "count") < 0) + return (-1); + + for (i = first_bin; i <= last_bin; i++) { + char c[32]; + int err; + + if (i == 0) { + (void) snprintf(c, sizeof (c), "< %d", + base / (uint32_t)normal); + err = dt_printf(dtp, fp, "%16s ", c); + } else if (i == levels + 1) { + (void) snprintf(c, sizeof (c), ">= %d", + base + (levels * step)); + err = dt_printf(dtp, fp, "%16s ", c); + } else { + err = dt_printf(dtp, fp, "%16d ", + base + (i - 1) * step); + } + + if (err < 0 || dt_print_quantline(dtp, fp, data[i], normal, + total, positives, negatives) < 0) + return (-1); + } + + return (0); +} + +/*ARGSUSED*/ +static int +dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, + size_t size, uint64_t normal) +{ + /* LINTED - alignment */ + int64_t *data = (int64_t *)addr; + + return (dt_printf(dtp, fp, " %16lld", data[0] ? + (long long)(data[1] / (int64_t)normal / data[0]) : 0)); +} + +/*ARGSUSED*/ +static int +dt_print_stddev(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, + size_t size, uint64_t normal) +{ + /* LINTED - alignment */ + uint64_t *data = (uint64_t *)addr; + + return (dt_printf(dtp, fp, " %16llu", data[0] ? + (unsigned long long) dt_stddev(data, normal) : 0)); +} + +/*ARGSUSED*/ +int +dt_print_bytes(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, + size_t nbytes, int width, int quiet, int raw) +{ + /* + * If the byte stream is a series of printable characters, followed by + * a terminating byte, we print it out as a string. Otherwise, we + * assume that it's something else and just print the bytes. + */ + int i, j, margin = 5; + char *c = (char *)addr; + + if (nbytes == 0) + return (0); + + if (raw || dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET) + goto raw; + + for (i = 0; i < nbytes; i++) { + /* + * We define a "printable character" to be one for which + * isprint(3C) returns non-zero, isspace(3C) returns non-zero, + * or a character which is either backspace or the bell. + * Backspace and the bell are regrettably special because + * they fail the first two tests -- and yet they are entirely + * printable. These are the only two control characters that + * have meaning for the terminal and for which isprint(3C) and + * isspace(3C) return 0. + */ + if (isprint(c[i]) || isspace(c[i]) || + c[i] == '\b' || c[i] == '\a') + continue; + + if (c[i] == '\0' && i > 0) { + /* + * This looks like it might be a string. Before we + * assume that it is indeed a string, check the + * remainder of the byte range; if it contains + * additional non-nul characters, we'll assume that + * it's a binary stream that just happens to look like + * a string, and we'll print out the individual bytes. + */ + for (j = i + 1; j < nbytes; j++) { + if (c[j] != '\0') + break; + } + + if (j != nbytes) + break; + + if (quiet) + return (dt_printf(dtp, fp, "%s", c)); + else + return (dt_printf(dtp, fp, " %-*s", width, c)); + } + + break; + } + + if (i == nbytes) { + /* + * The byte range is all printable characters, but there is + * no trailing nul byte. We'll assume that it's a string and + * print it as such. + */ + char *s = alloca(nbytes + 1); + bcopy(c, s, nbytes); + s[nbytes] = '\0'; + return (dt_printf(dtp, fp, " %-*s", width, s)); + } + +raw: + if (dt_printf(dtp, fp, "\n%*s ", margin, "") < 0) + return (-1); + + for (i = 0; i < 16; i++) + if (dt_printf(dtp, fp, " %c", "0123456789abcdef"[i]) < 0) + return (-1); + + if (dt_printf(dtp, fp, " 0123456789abcdef\n") < 0) + return (-1); + + + for (i = 0; i < nbytes; i += 16) { + if (dt_printf(dtp, fp, "%*s%5x:", margin, "", i) < 0) + return (-1); + + for (j = i; j < i + 16 && j < nbytes; j++) { + if (dt_printf(dtp, fp, " %02x", (uchar_t)c[j]) < 0) + return (-1); + } + + while (j++ % 16) { + if (dt_printf(dtp, fp, " ") < 0) + return (-1); + } + + if (dt_printf(dtp, fp, " ") < 0) + return (-1); + + for (j = i; j < i + 16 && j < nbytes; j++) { + if (dt_printf(dtp, fp, "%c", + c[j] < ' ' || c[j] > '~' ? '.' : c[j]) < 0) + return (-1); + } + + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + } + + return (0); +} + +int +dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, + caddr_t addr, int depth, int size) +{ + dtrace_syminfo_t dts; + GElf_Sym sym; + int i, indent; + char c[PATH_MAX * 2]; + uint64_t pc; + + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + + if (format == NULL) + format = "%s"; + + if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) + indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; + else + indent = _dtrace_stkindent; + + for (i = 0; i < depth; i++) { + switch (size) { + case sizeof (uint32_t): + /* LINTED - alignment */ + pc = *((uint32_t *)addr); + break; + + case sizeof (uint64_t): + /* LINTED - alignment */ + pc = *((uint64_t *)addr); + break; + + default: + return (dt_set_errno(dtp, EDT_BADSTACKPC)); + } + + if (pc == 0) + break; + + addr += size; + + if (dt_printf(dtp, fp, "%*s", indent, "") < 0) + return (-1); + + if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { + if (pc > sym.st_value) { + (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", + dts.dts_object, dts.dts_name, + pc - sym.st_value); + } else { + (void) snprintf(c, sizeof (c), "%s`%s", + dts.dts_object, dts.dts_name); + } + } else { + /* + * We'll repeat the lookup, but this time we'll specify + * a NULL GElf_Sym -- indicating that we're only + * interested in the containing module. + */ + if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { + (void) snprintf(c, sizeof (c), "%s`0x%llx", + dts.dts_object, pc); + } else { + (void) snprintf(c, sizeof (c), "0x%llx", pc); + } + } + + if (dt_printf(dtp, fp, format, c) < 0) + return (-1); + + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + } + + return (0); +} + +int +dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format, + caddr_t addr, uint64_t arg) +{ + /* LINTED - alignment */ + uint64_t *pc = (uint64_t *)addr; + uint32_t depth = DTRACE_USTACK_NFRAMES(arg); + uint32_t strsize = DTRACE_USTACK_STRSIZE(arg); + const char *strbase = addr + (depth + 1) * sizeof (uint64_t); + const char *str = strsize ? strbase : NULL; + int err = 0; + + char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; + struct ps_prochandle *P; + GElf_Sym sym; + int i, indent; + pid_t pid; + + if (depth == 0) + return (0); + + pid = (pid_t)*pc++; + + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + + if (format == NULL) + format = "%s"; + + if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) + indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; + else + indent = _dtrace_stkindent; + + /* + * Ultimately, we need to add an entry point in the library vector for + * determining <symbol, offset> from <pid, address>. For now, if + * this is a vector open, we just print the raw address or string. + */ + if (dtp->dt_vector == NULL) + P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); + else + P = NULL; + + if (P != NULL) + dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ + + for (i = 0; i < depth && pc[i] != 0; i++) { + const prmap_t *map; + + if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) + break; + +#if defined(sun) + if (P != NULL && Plookup_by_addr(P, pc[i], +#else + if (P != NULL && proc_addr2sym(P, pc[i], +#endif + name, sizeof (name), &sym) == 0) { +#if defined(sun) + (void) Pobjname(P, pc[i], objname, sizeof (objname)); +#else + (void) proc_objname(P, pc[i], objname, sizeof (objname)); +#endif + + if (pc[i] > sym.st_value) { + (void) snprintf(c, sizeof (c), + "%s`%s+0x%llx", dt_basename(objname), name, + (u_longlong_t)(pc[i] - sym.st_value)); + } else { + (void) snprintf(c, sizeof (c), + "%s`%s", dt_basename(objname), name); + } + } else if (str != NULL && str[0] != '\0' && str[0] != '@' && +#if defined(sun) + (P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL || + (map->pr_mflags & MA_WRITE)))) { +#else + (P != NULL && ((map = proc_addr2map(P, pc[i])) == NULL))) { +#endif + /* + * If the current string pointer in the string table + * does not point to an empty string _and_ the program + * counter falls in a writable region, we'll use the + * string from the string table instead of the raw + * address. This last condition is necessary because + * some (broken) ustack helpers will return a string + * even for a program counter that they can't + * identify. If we have a string for a program + * counter that falls in a segment that isn't + * writable, we assume that we have fallen into this + * case and we refuse to use the string. + */ + (void) snprintf(c, sizeof (c), "%s", str); + } else { +#if defined(sun) + if (P != NULL && Pobjname(P, pc[i], objname, +#else + if (P != NULL && proc_objname(P, pc[i], objname, +#endif + sizeof (objname)) != 0) { + (void) snprintf(c, sizeof (c), "%s`0x%llx", + dt_basename(objname), (u_longlong_t)pc[i]); + } else { + (void) snprintf(c, sizeof (c), "0x%llx", + (u_longlong_t)pc[i]); + } + } + + if ((err = dt_printf(dtp, fp, format, c)) < 0) + break; + + if ((err = dt_printf(dtp, fp, "\n")) < 0) + break; + + if (str != NULL && str[0] == '@') { + /* + * If the first character of the string is an "at" sign, + * then the string is inferred to be an annotation -- + * and it is printed out beneath the frame and offset + * with brackets. + */ + if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) + break; + + (void) snprintf(c, sizeof (c), " [ %s ]", &str[1]); + + if ((err = dt_printf(dtp, fp, format, c)) < 0) + break; + + if ((err = dt_printf(dtp, fp, "\n")) < 0) + break; + } + + if (str != NULL) { + str += strlen(str) + 1; + if (str - strbase >= strsize) + str = NULL; + } + } + + if (P != NULL) { + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); + } + + return (err); +} + +static int +dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act) +{ + /* LINTED - alignment */ + uint64_t pid = ((uint64_t *)addr)[0]; + /* LINTED - alignment */ + uint64_t pc = ((uint64_t *)addr)[1]; + const char *format = " %-50s"; + char *s; + int n, len = 256; + + if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) { + struct ps_prochandle *P; + + if ((P = dt_proc_grab(dtp, pid, + PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) { + GElf_Sym sym; + + dt_proc_lock(dtp, P); + +#if defined(sun) + if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0) +#else + if (proc_addr2sym(P, pc, NULL, 0, &sym) == 0) +#endif + pc = sym.st_value; + + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); + } + } + + do { + n = len; + s = alloca(n); + } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) >= n); + + return (dt_printf(dtp, fp, format, s)); +} + +int +dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) +{ + /* LINTED - alignment */ + uint64_t pid = ((uint64_t *)addr)[0]; + /* LINTED - alignment */ + uint64_t pc = ((uint64_t *)addr)[1]; + int err = 0; + + char objname[PATH_MAX], c[PATH_MAX * 2]; + struct ps_prochandle *P; + + if (format == NULL) + format = " %-50s"; + + /* + * See the comment in dt_print_ustack() for the rationale for + * printing raw addresses in the vectored case. + */ + if (dtp->dt_vector == NULL) + P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); + else + P = NULL; + + if (P != NULL) + dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ + +#if defined(sun) + if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != 0) { +#else + if (P != NULL && proc_objname(P, pc, objname, sizeof (objname)) != 0) { +#endif + (void) snprintf(c, sizeof (c), "%s", dt_basename(objname)); + } else { + (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); + } + + err = dt_printf(dtp, fp, format, c); + + if (P != NULL) { + dt_proc_unlock(dtp, P); + dt_proc_release(dtp, P); + } + + return (err); +} + +int +dt_print_memory(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) +{ + int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); + size_t nbytes = *((uintptr_t *) addr); + + return (dt_print_bytes(dtp, fp, addr + sizeof(uintptr_t), + nbytes, 50, quiet, 1)); +} + +typedef struct dt_type_cbdata { + dtrace_hdl_t *dtp; + dtrace_typeinfo_t dtt; + caddr_t addr; + caddr_t addrend; + const char *name; + int f_type; + int indent; + int type_width; + int name_width; + FILE *fp; +} dt_type_cbdata_t; + +static int dt_print_type_data(dt_type_cbdata_t *, ctf_id_t); + +static int +dt_print_type_member(const char *name, ctf_id_t type, ulong_t off, void *arg) +{ + dt_type_cbdata_t cbdata; + dt_type_cbdata_t *cbdatap = arg; + ssize_t ssz; + + if ((ssz = ctf_type_size(cbdatap->dtt.dtt_ctfp, type)) <= 0) + return (0); + + off /= 8; + + cbdata = *cbdatap; + cbdata.name = name; + cbdata.addr += off; + cbdata.addrend = cbdata.addr + ssz; + + return (dt_print_type_data(&cbdata, type)); +} + +static int +dt_print_type_width(const char *name, ctf_id_t type, ulong_t off, void *arg) +{ + char buf[DT_TYPE_NAMELEN]; + char *p; + dt_type_cbdata_t *cbdatap = arg; + size_t sz = strlen(name); + + ctf_type_name(cbdatap->dtt.dtt_ctfp, type, buf, sizeof (buf)); + + if ((p = strchr(buf, '[')) != NULL) + p[-1] = '\0'; + else + p = ""; + + sz += strlen(p); + + if (sz > cbdatap->name_width) + cbdatap->name_width = sz; + + sz = strlen(buf); + + if (sz > cbdatap->type_width) + cbdatap->type_width = sz; + + return (0); +} + +static int +dt_print_type_data(dt_type_cbdata_t *cbdatap, ctf_id_t type) +{ + caddr_t addr = cbdatap->addr; + caddr_t addrend = cbdatap->addrend; + char buf[DT_TYPE_NAMELEN]; + char *p; + int cnt = 0; + uint_t kind = ctf_type_kind(cbdatap->dtt.dtt_ctfp, type); + ssize_t ssz = ctf_type_size(cbdatap->dtt.dtt_ctfp, type); + + ctf_type_name(cbdatap->dtt.dtt_ctfp, type, buf, sizeof (buf)); + + if ((p = strchr(buf, '[')) != NULL) + p[-1] = '\0'; + else + p = ""; + + if (cbdatap->f_type) { + int type_width = roundup(cbdatap->type_width + 1, 4); + int name_width = roundup(cbdatap->name_width + 1, 4); + + name_width -= strlen(cbdatap->name); + + dt_printf(cbdatap->dtp, cbdatap->fp, "%*s%-*s%s%-*s = ",cbdatap->indent * 4,"",type_width,buf,cbdatap->name,name_width,p); + } + + while (addr < addrend) { + dt_type_cbdata_t cbdata; + ctf_arinfo_t arinfo; + ctf_encoding_t cte; + uintptr_t *up; + void *vp = addr; + cbdata = *cbdatap; + cbdata.name = ""; + cbdata.addr = addr; + cbdata.addrend = addr + ssz; + cbdata.f_type = 0; + cbdata.indent++; + cbdata.type_width = 0; + cbdata.name_width = 0; + + if (cnt > 0) + dt_printf(cbdatap->dtp, cbdatap->fp, "%*s", cbdatap->indent * 4,""); + + switch (kind) { + case CTF_K_INTEGER: + if (ctf_type_encoding(cbdatap->dtt.dtt_ctfp, type, &cte) != 0) + return (-1); + if ((cte.cte_format & CTF_INT_SIGNED) != 0) + switch (cte.cte_bits) { + case 8: + if (isprint(*((char *) vp))) + dt_printf(cbdatap->dtp, cbdatap->fp, "'%c', ", *((char *) vp)); + dt_printf(cbdatap->dtp, cbdatap->fp, "%d (0x%x);\n", *((char *) vp), *((char *) vp)); + break; + case 16: + dt_printf(cbdatap->dtp, cbdatap->fp, "%hd (0x%hx);\n", *((short *) vp), *((u_short *) vp)); + break; + case 32: + dt_printf(cbdatap->dtp, cbdatap->fp, "%d (0x%x);\n", *((int *) vp), *((u_int *) vp)); + break; + case 64: + dt_printf(cbdatap->dtp, cbdatap->fp, "%jd (0x%jx);\n", *((long long *) vp), *((unsigned long long *) vp)); + break; + default: + dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_INTEGER: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); + break; + } + else + switch (cte.cte_bits) { + case 8: + dt_printf(cbdatap->dtp, cbdatap->fp, "%u (0x%x);\n", *((uint8_t *) vp) & 0xff, *((uint8_t *) vp) & 0xff); + break; + case 16: + dt_printf(cbdatap->dtp, cbdatap->fp, "%hu (0x%hx);\n", *((u_short *) vp), *((u_short *) vp)); + break; + case 32: + dt_printf(cbdatap->dtp, cbdatap->fp, "%u (0x%x);\n", *((u_int *) vp), *((u_int *) vp)); + break; + case 64: + dt_printf(cbdatap->dtp, cbdatap->fp, "%ju (0x%jx);\n", *((unsigned long long *) vp), *((unsigned long long *) vp)); + break; + default: + dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_INTEGER: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); + break; + } + break; + case CTF_K_FLOAT: + dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_FLOAT: format %x offset %u bits %u\n",cte.cte_format,cte.cte_offset,cte.cte_bits); + break; + case CTF_K_POINTER: + dt_printf(cbdatap->dtp, cbdatap->fp, "%p;\n", *((void **) addr)); + break; + case CTF_K_ARRAY: + if (ctf_array_info(cbdatap->dtt.dtt_ctfp, type, &arinfo) != 0) + return (-1); + dt_printf(cbdatap->dtp, cbdatap->fp, "{\n%*s",cbdata.indent * 4,""); + dt_print_type_data(&cbdata, arinfo.ctr_contents); + dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); + break; + case CTF_K_FUNCTION: + dt_printf(cbdatap->dtp, cbdatap->fp, "CTF_K_FUNCTION:\n"); + break; + case CTF_K_STRUCT: + cbdata.f_type = 1; + if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, + dt_print_type_width, &cbdata) != 0) + return (-1); + dt_printf(cbdatap->dtp, cbdatap->fp, "{\n"); + if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, + dt_print_type_member, &cbdata) != 0) + return (-1); + dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); + break; + case CTF_K_UNION: + cbdata.f_type = 1; + if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, + dt_print_type_width, &cbdata) != 0) + return (-1); + dt_printf(cbdatap->dtp, cbdatap->fp, "{\n"); + if (ctf_member_iter(cbdatap->dtt.dtt_ctfp, type, + dt_print_type_member, &cbdata) != 0) + return (-1); + dt_printf(cbdatap->dtp, cbdatap->fp, "%*s};\n",cbdatap->indent * 4,""); + break; + case CTF_K_ENUM: + dt_printf(cbdatap->dtp, cbdatap->fp, "%s;\n", ctf_enum_name(cbdatap->dtt.dtt_ctfp, type, *((int *) vp))); + break; + case CTF_K_TYPEDEF: + dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); + break; + case CTF_K_VOLATILE: + if (cbdatap->f_type) + dt_printf(cbdatap->dtp, cbdatap->fp, "volatile "); + dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); + break; + case CTF_K_CONST: + if (cbdatap->f_type) + dt_printf(cbdatap->dtp, cbdatap->fp, "const "); + dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); + break; + case CTF_K_RESTRICT: + if (cbdatap->f_type) + dt_printf(cbdatap->dtp, cbdatap->fp, "restrict "); + dt_print_type_data(&cbdata, ctf_type_reference(cbdatap->dtt.dtt_ctfp,type)); + break; + default: + break; + } + + addr += ssz; + cnt++; + } + + return (0); +} + +static int +dt_print_type(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr) +{ + caddr_t addrend; + char *p; + dtrace_typeinfo_t dtt; + dt_type_cbdata_t cbdata; + int num = 0; + int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); + ssize_t ssz; + + if (!quiet) + dt_printf(dtp, fp, "\n"); + + /* Get the total number of bytes of data buffered. */ + size_t nbytes = *((uintptr_t *) addr); + addr += sizeof(uintptr_t); + + /* + * Get the size of the type so that we can check that it matches + * the CTF data we look up and so that we can figure out how many + * type elements are buffered. + */ + size_t typs = *((uintptr_t *) addr); + addr += sizeof(uintptr_t); + + /* + * Point to the type string in the buffer. Get it's string + * length and round it up to become the offset to the start + * of the buffered type data which we would like to be aligned + * for easy access. + */ + char *strp = (char *) addr; + int offset = roundup(strlen(strp) + 1, sizeof(uintptr_t)); + + /* + * The type string might have a format such as 'int [20]'. + * Check if there is an array dimension present. + */ + if ((p = strchr(strp, '[')) != NULL) { + /* Strip off the array dimension. */ + *p++ = '\0'; + + for (; *p != '\0' && *p != ']'; p++) + num = num * 10 + *p - '0'; + } else + /* No array dimension, so default. */ + num = 1; + + /* Lookup the CTF type from the type string. */ + if (dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY, strp, &dtt) < 0) + return (-1); + + /* Offset the buffer address to the start of the data... */ + addr += offset; + + ssz = ctf_type_size(dtt.dtt_ctfp, dtt.dtt_type); + + if (typs != ssz) { + printf("Expected type size from buffer (%lu) to match type size looked up now (%ld)\n", (u_long) typs, (long) ssz); + return (-1); + } + + cbdata.dtp = dtp; + cbdata.dtt = dtt; + cbdata.name = ""; + cbdata.addr = addr; + cbdata.addrend = addr + nbytes; + cbdata.indent = 1; + cbdata.f_type = 1; + cbdata.type_width = 0; + cbdata.name_width = 0; + cbdata.fp = fp; + + return (dt_print_type_data(&cbdata, dtt.dtt_type)); +} + +static int +dt_print_sym(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) +{ + /* LINTED - alignment */ + uint64_t pc = *((uint64_t *)addr); + dtrace_syminfo_t dts; + GElf_Sym sym; + char c[PATH_MAX * 2]; + + if (format == NULL) + format = " %-50s"; + + if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { + (void) snprintf(c, sizeof (c), "%s`%s", + dts.dts_object, dts.dts_name); + } else { + /* + * We'll repeat the lookup, but this time we'll specify a + * NULL GElf_Sym -- indicating that we're only interested in + * the containing module. + */ + if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { + (void) snprintf(c, sizeof (c), "%s`0x%llx", + dts.dts_object, (u_longlong_t)pc); + } else { + (void) snprintf(c, sizeof (c), "0x%llx", + (u_longlong_t)pc); + } + } + + if (dt_printf(dtp, fp, format, c) < 0) + return (-1); + + return (0); +} + +int +dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) +{ + /* LINTED - alignment */ + uint64_t pc = *((uint64_t *)addr); + dtrace_syminfo_t dts; + char c[PATH_MAX * 2]; + + if (format == NULL) + format = " %-50s"; + + if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { + (void) snprintf(c, sizeof (c), "%s", dts.dts_object); + } else { + (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); + } + + if (dt_printf(dtp, fp, format, c) < 0) + return (-1); + + return (0); +} + +typedef struct dt_normal { + dtrace_aggvarid_t dtnd_id; + uint64_t dtnd_normal; +} dt_normal_t; + +static int +dt_normalize_agg(const dtrace_aggdata_t *aggdata, void *arg) +{ + dt_normal_t *normal = arg; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + dtrace_aggvarid_t id = normal->dtnd_id; + + if (agg->dtagd_nrecs == 0) + return (DTRACE_AGGWALK_NEXT); + + if (agg->dtagd_varid != id) + return (DTRACE_AGGWALK_NEXT); + + ((dtrace_aggdata_t *)aggdata)->dtada_normal = normal->dtnd_normal; + return (DTRACE_AGGWALK_NORMALIZE); +} + +static int +dt_normalize(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) +{ + dt_normal_t normal; + caddr_t addr; + + /* + * We (should) have two records: the aggregation ID followed by the + * normalization value. + */ + addr = base + rec->dtrd_offset; + + if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) + return (dt_set_errno(dtp, EDT_BADNORMAL)); + + /* LINTED - alignment */ + normal.dtnd_id = *((dtrace_aggvarid_t *)addr); + rec++; + + if (rec->dtrd_action != DTRACEACT_LIBACT) + return (dt_set_errno(dtp, EDT_BADNORMAL)); + + if (rec->dtrd_arg != DT_ACT_NORMALIZE) + return (dt_set_errno(dtp, EDT_BADNORMAL)); + + addr = base + rec->dtrd_offset; + + switch (rec->dtrd_size) { + case sizeof (uint64_t): + /* LINTED - alignment */ + normal.dtnd_normal = *((uint64_t *)addr); + break; + case sizeof (uint32_t): + /* LINTED - alignment */ + normal.dtnd_normal = *((uint32_t *)addr); + break; + case sizeof (uint16_t): + /* LINTED - alignment */ + normal.dtnd_normal = *((uint16_t *)addr); + break; + case sizeof (uint8_t): + normal.dtnd_normal = *((uint8_t *)addr); + break; + default: + return (dt_set_errno(dtp, EDT_BADNORMAL)); + } + + (void) dtrace_aggregate_walk(dtp, dt_normalize_agg, &normal); + + return (0); +} + +static int +dt_denormalize_agg(const dtrace_aggdata_t *aggdata, void *arg) +{ + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); + + if (agg->dtagd_nrecs == 0) + return (DTRACE_AGGWALK_NEXT); + + if (agg->dtagd_varid != id) + return (DTRACE_AGGWALK_NEXT); + + return (DTRACE_AGGWALK_DENORMALIZE); +} + +static int +dt_clear_agg(const dtrace_aggdata_t *aggdata, void *arg) +{ + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); + + if (agg->dtagd_nrecs == 0) + return (DTRACE_AGGWALK_NEXT); + + if (agg->dtagd_varid != id) + return (DTRACE_AGGWALK_NEXT); + + return (DTRACE_AGGWALK_CLEAR); +} + +typedef struct dt_trunc { + dtrace_aggvarid_t dttd_id; + uint64_t dttd_remaining; +} dt_trunc_t; + +static int +dt_trunc_agg(const dtrace_aggdata_t *aggdata, void *arg) +{ + dt_trunc_t *trunc = arg; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + dtrace_aggvarid_t id = trunc->dttd_id; + + if (agg->dtagd_nrecs == 0) + return (DTRACE_AGGWALK_NEXT); + + if (agg->dtagd_varid != id) + return (DTRACE_AGGWALK_NEXT); + + if (trunc->dttd_remaining == 0) + return (DTRACE_AGGWALK_REMOVE); + + trunc->dttd_remaining--; + return (DTRACE_AGGWALK_NEXT); +} + +static int +dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) +{ + dt_trunc_t trunc; + caddr_t addr; + int64_t remaining; + int (*func)(dtrace_hdl_t *, dtrace_aggregate_f *, void *); + + /* + * We (should) have two records: the aggregation ID followed by the + * number of aggregation entries after which the aggregation is to be + * truncated. + */ + addr = base + rec->dtrd_offset; + + if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) + return (dt_set_errno(dtp, EDT_BADTRUNC)); + + /* LINTED - alignment */ + trunc.dttd_id = *((dtrace_aggvarid_t *)addr); + rec++; + + if (rec->dtrd_action != DTRACEACT_LIBACT) + return (dt_set_errno(dtp, EDT_BADTRUNC)); + + if (rec->dtrd_arg != DT_ACT_TRUNC) + return (dt_set_errno(dtp, EDT_BADTRUNC)); + + addr = base + rec->dtrd_offset; + + switch (rec->dtrd_size) { + case sizeof (uint64_t): + /* LINTED - alignment */ + remaining = *((int64_t *)addr); + break; + case sizeof (uint32_t): + /* LINTED - alignment */ + remaining = *((int32_t *)addr); + break; + case sizeof (uint16_t): + /* LINTED - alignment */ + remaining = *((int16_t *)addr); + break; + case sizeof (uint8_t): + remaining = *((int8_t *)addr); + break; + default: + return (dt_set_errno(dtp, EDT_BADNORMAL)); + } + + if (remaining < 0) { + func = dtrace_aggregate_walk_valsorted; + remaining = -remaining; + } else { + func = dtrace_aggregate_walk_valrevsorted; + } + + assert(remaining >= 0); + trunc.dttd_remaining = remaining; + + (void) func(dtp, dt_trunc_agg, &trunc); + + return (0); +} + +static int +dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, + caddr_t addr, size_t size, uint64_t normal) +{ + int err; + dtrace_actkind_t act = rec->dtrd_action; + + switch (act) { + case DTRACEACT_STACK: + return (dt_print_stack(dtp, fp, NULL, addr, + rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg)); + + case DTRACEACT_USTACK: + case DTRACEACT_JSTACK: + return (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg)); + + case DTRACEACT_USYM: + case DTRACEACT_UADDR: + return (dt_print_usym(dtp, fp, addr, act)); + + case DTRACEACT_UMOD: + return (dt_print_umod(dtp, fp, NULL, addr)); + + case DTRACEACT_SYM: + return (dt_print_sym(dtp, fp, NULL, addr)); + + case DTRACEACT_MOD: + return (dt_print_mod(dtp, fp, NULL, addr)); + + case DTRACEAGG_QUANTIZE: + return (dt_print_quantize(dtp, fp, addr, size, normal)); + + case DTRACEAGG_LQUANTIZE: + return (dt_print_lquantize(dtp, fp, addr, size, normal)); + + case DTRACEAGG_AVG: + return (dt_print_average(dtp, fp, addr, size, normal)); + + case DTRACEAGG_STDDEV: + return (dt_print_stddev(dtp, fp, addr, size, normal)); + + default: + break; + } + + switch (size) { + case sizeof (uint64_t): + err = dt_printf(dtp, fp, " %16lld", + /* LINTED - alignment */ + (long long)*((uint64_t *)addr) / normal); + break; + case sizeof (uint32_t): + /* LINTED - alignment */ + err = dt_printf(dtp, fp, " %8d", *((uint32_t *)addr) / + (uint32_t)normal); + break; + case sizeof (uint16_t): + /* LINTED - alignment */ + err = dt_printf(dtp, fp, " %5d", *((uint16_t *)addr) / + (uint32_t)normal); + break; + case sizeof (uint8_t): + err = dt_printf(dtp, fp, " %3d", *((uint8_t *)addr) / + (uint32_t)normal); + break; + default: + err = dt_print_bytes(dtp, fp, addr, size, 50, 0, 0); + break; + } + + return (err); +} + +int +dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) +{ + int i, aggact = 0; + dt_print_aggdata_t *pd = arg; + const dtrace_aggdata_t *aggdata = aggsdata[0]; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + FILE *fp = pd->dtpa_fp; + dtrace_hdl_t *dtp = pd->dtpa_dtp; + dtrace_recdesc_t *rec; + dtrace_actkind_t act; + caddr_t addr; + size_t size; + + /* + * Iterate over each record description in the key, printing the traced + * data, skipping the first datum (the tuple member created by the + * compiler). + */ + for (i = 1; i < agg->dtagd_nrecs; i++) { + rec = &agg->dtagd_rec[i]; + act = rec->dtrd_action; + addr = aggdata->dtada_data + rec->dtrd_offset; + size = rec->dtrd_size; + + if (DTRACEACT_ISAGG(act)) { + aggact = i; + break; + } + + if (dt_print_datum(dtp, fp, rec, addr, size, 1) < 0) + return (-1); + + if (dt_buffered_flush(dtp, NULL, rec, aggdata, + DTRACE_BUFDATA_AGGKEY) < 0) + return (-1); + } + + assert(aggact != 0); + + for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) { + uint64_t normal; + + aggdata = aggsdata[i]; + agg = aggdata->dtada_desc; + rec = &agg->dtagd_rec[aggact]; + act = rec->dtrd_action; + addr = aggdata->dtada_data + rec->dtrd_offset; + size = rec->dtrd_size; + + assert(DTRACEACT_ISAGG(act)); + normal = aggdata->dtada_normal; + + if (dt_print_datum(dtp, fp, rec, addr, size, normal) < 0) + return (-1); + + if (dt_buffered_flush(dtp, NULL, rec, aggdata, + DTRACE_BUFDATA_AGGVAL) < 0) + return (-1); + + if (!pd->dtpa_allunprint) + agg->dtagd_flags |= DTRACE_AGD_PRINTED; + } + + if (dt_printf(dtp, fp, "\n") < 0) + return (-1); + + if (dt_buffered_flush(dtp, NULL, NULL, aggdata, + DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) + return (-1); + + return (0); +} + +int +dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) +{ + dt_print_aggdata_t *pd = arg; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + dtrace_aggvarid_t aggvarid = pd->dtpa_id; + + if (pd->dtpa_allunprint) { + if (agg->dtagd_flags & DTRACE_AGD_PRINTED) + return (0); + } else { + /* + * If we're not printing all unprinted aggregations, then the + * aggregation variable ID denotes a specific aggregation + * variable that we should print -- skip any other aggregations + * that we encounter. + */ + if (agg->dtagd_nrecs == 0) + return (0); + + if (aggvarid != agg->dtagd_varid) + return (0); + } + + return (dt_print_aggs(&aggdata, 1, arg)); +} + +int +dt_setopt(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, + const char *option, const char *value) +{ + int len, rval; + char *msg; + const char *errstr; + dtrace_setoptdata_t optdata; + + bzero(&optdata, sizeof (optdata)); + (void) dtrace_getopt(dtp, option, &optdata.dtsda_oldval); + + if (dtrace_setopt(dtp, option, value) == 0) { + (void) dtrace_getopt(dtp, option, &optdata.dtsda_newval); + optdata.dtsda_probe = data; + optdata.dtsda_option = option; + optdata.dtsda_handle = dtp; + + if ((rval = dt_handle_setopt(dtp, &optdata)) != 0) + return (rval); + + return (0); + } + + errstr = dtrace_errmsg(dtp, dtrace_errno(dtp)); + len = strlen(option) + strlen(value) + strlen(errstr) + 80; + msg = alloca(len); + + (void) snprintf(msg, len, "couldn't set option \"%s\" to \"%s\": %s\n", + option, value, errstr); + + if ((rval = dt_handle_liberr(dtp, data, msg)) == 0) + return (0); + + return (rval); +} + +static int +dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, dtrace_bufdesc_t *buf, + dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) +{ + dtrace_epid_t id; + size_t offs, start = buf->dtbd_oldest, end = buf->dtbd_size; + int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); + int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); + int rval, i, n; + dtrace_epid_t last = DTRACE_EPIDNONE; + dtrace_probedata_t data; + uint64_t drops; + caddr_t addr; + + bzero(&data, sizeof (data)); + data.dtpda_handle = dtp; + data.dtpda_cpu = cpu; + +again: + for (offs = start; offs < end; ) { + dtrace_eprobedesc_t *epd; + + /* + * We're guaranteed to have an ID. + */ + id = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); + + if (id == DTRACE_EPIDNONE) { + /* + * This is filler to assure proper alignment of the + * next record; we simply ignore it. + */ + offs += sizeof (id); + continue; + } + + if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc, + &data.dtpda_pdesc)) != 0) + return (rval); + + epd = data.dtpda_edesc; + data.dtpda_data = buf->dtbd_data + offs; + + if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) { + rval = dt_handle(dtp, &data); + + if (rval == DTRACE_CONSUME_NEXT) + goto nextepid; + + if (rval == DTRACE_CONSUME_ERROR) + return (-1); + } + + if (flow) + (void) dt_flowindent(dtp, &data, last, buf, offs); + + rval = (*efunc)(&data, arg); + + if (flow) { + if (data.dtpda_flow == DTRACEFLOW_ENTRY) + data.dtpda_indent += 2; + } + + if (rval == DTRACE_CONSUME_NEXT) + goto nextepid; + + if (rval == DTRACE_CONSUME_ABORT) + return (dt_set_errno(dtp, EDT_DIRABORT)); + + if (rval != DTRACE_CONSUME_THIS) + return (dt_set_errno(dtp, EDT_BADRVAL)); + + for (i = 0; i < epd->dtepd_nrecs; i++) { + dtrace_recdesc_t *rec = &epd->dtepd_rec[i]; + dtrace_actkind_t act = rec->dtrd_action; + + data.dtpda_data = buf->dtbd_data + offs + + rec->dtrd_offset; + addr = data.dtpda_data; + + if (act == DTRACEACT_LIBACT) { + uint64_t arg = rec->dtrd_arg; + dtrace_aggvarid_t id; + + switch (arg) { + case DT_ACT_CLEAR: + /* LINTED - alignment */ + id = *((dtrace_aggvarid_t *)addr); + (void) dtrace_aggregate_walk(dtp, + dt_clear_agg, &id); + continue; + + case DT_ACT_DENORMALIZE: + /* LINTED - alignment */ + id = *((dtrace_aggvarid_t *)addr); + (void) dtrace_aggregate_walk(dtp, + dt_denormalize_agg, &id); + continue; + + case DT_ACT_FTRUNCATE: + if (fp == NULL) + continue; + + (void) fflush(fp); + (void) ftruncate(fileno(fp), 0); + (void) fseeko(fp, 0, SEEK_SET); + continue; + + case DT_ACT_NORMALIZE: + if (i == epd->dtepd_nrecs - 1) + return (dt_set_errno(dtp, + EDT_BADNORMAL)); + + if (dt_normalize(dtp, + buf->dtbd_data + offs, rec) != 0) + return (-1); + + i++; + continue; + + case DT_ACT_SETOPT: { + uint64_t *opts = dtp->dt_options; + dtrace_recdesc_t *valrec; + uint32_t valsize; + caddr_t val; + int rv; + + if (i == epd->dtepd_nrecs - 1) { + return (dt_set_errno(dtp, + EDT_BADSETOPT)); + } + + valrec = &epd->dtepd_rec[++i]; + valsize = valrec->dtrd_size; + + if (valrec->dtrd_action != act || + valrec->dtrd_arg != arg) { + return (dt_set_errno(dtp, + EDT_BADSETOPT)); + } + + if (valsize > sizeof (uint64_t)) { + val = buf->dtbd_data + offs + + valrec->dtrd_offset; + } else { + val = "1"; + } + + rv = dt_setopt(dtp, &data, addr, val); + + if (rv != 0) + return (-1); + + flow = (opts[DTRACEOPT_FLOWINDENT] != + DTRACEOPT_UNSET); + quiet = (opts[DTRACEOPT_QUIET] != + DTRACEOPT_UNSET); + + continue; + } + + case DT_ACT_TRUNC: + if (i == epd->dtepd_nrecs - 1) + return (dt_set_errno(dtp, + EDT_BADTRUNC)); + + if (dt_trunc(dtp, + buf->dtbd_data + offs, rec) != 0) + return (-1); + + i++; + continue; + + default: + continue; + } + } + + rval = (*rfunc)(&data, rec, arg); + + if (rval == DTRACE_CONSUME_NEXT) + continue; + + if (rval == DTRACE_CONSUME_ABORT) + return (dt_set_errno(dtp, EDT_DIRABORT)); + + if (rval != DTRACE_CONSUME_THIS) + return (dt_set_errno(dtp, EDT_BADRVAL)); + + if (act == DTRACEACT_STACK) { + int depth = rec->dtrd_arg; + + if (dt_print_stack(dtp, fp, NULL, addr, depth, + rec->dtrd_size / depth) < 0) + return (-1); + goto nextrec; + } + + if (act == DTRACEACT_USTACK || + act == DTRACEACT_JSTACK) { + if (dt_print_ustack(dtp, fp, NULL, + addr, rec->dtrd_arg) < 0) + return (-1); + goto nextrec; + } + + if (act == DTRACEACT_SYM) { + if (dt_print_sym(dtp, fp, NULL, addr) < 0) + return (-1); + goto nextrec; + } + + if (act == DTRACEACT_MOD) { + if (dt_print_mod(dtp, fp, NULL, addr) < 0) + return (-1); + goto nextrec; + } + + if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { + if (dt_print_usym(dtp, fp, addr, act) < 0) + return (-1); + goto nextrec; + } + + if (act == DTRACEACT_UMOD) { + if (dt_print_umod(dtp, fp, NULL, addr) < 0) + return (-1); + goto nextrec; + } + + if (act == DTRACEACT_PRINTM) { + if (dt_print_memory(dtp, fp, addr) < 0) + return (-1); + goto nextrec; + } + + if (act == DTRACEACT_PRINTT) { + if (dt_print_type(dtp, fp, addr) < 0) + return (-1); + goto nextrec; + } + + if (DTRACEACT_ISPRINTFLIKE(act)) { + void *fmtdata; + int (*func)(dtrace_hdl_t *, FILE *, void *, + const dtrace_probedata_t *, + const dtrace_recdesc_t *, uint_t, + const void *buf, size_t); + + if ((fmtdata = dt_format_lookup(dtp, + rec->dtrd_format)) == NULL) + goto nofmt; + + switch (act) { + case DTRACEACT_PRINTF: + func = dtrace_fprintf; + break; + case DTRACEACT_PRINTA: + func = dtrace_fprinta; + break; + case DTRACEACT_SYSTEM: + func = dtrace_system; + break; + case DTRACEACT_FREOPEN: + func = dtrace_freopen; + break; + } + + n = (*func)(dtp, fp, fmtdata, &data, + rec, epd->dtepd_nrecs - i, + (uchar_t *)buf->dtbd_data + offs, + buf->dtbd_size - offs); + + if (n < 0) + return (-1); /* errno is set for us */ + + if (n > 0) + i += n - 1; + goto nextrec; + } + +nofmt: + if (act == DTRACEACT_PRINTA) { + dt_print_aggdata_t pd; + dtrace_aggvarid_t *aggvars; + int j, naggvars = 0; + size_t size = ((epd->dtepd_nrecs - i) * + sizeof (dtrace_aggvarid_t)); + + if ((aggvars = dt_alloc(dtp, size)) == NULL) + return (-1); + + /* + * This might be a printa() with multiple + * aggregation variables. We need to scan + * forward through the records until we find + * a record from a different statement. + */ + for (j = i; j < epd->dtepd_nrecs; j++) { + dtrace_recdesc_t *nrec; + caddr_t naddr; + + nrec = &epd->dtepd_rec[j]; + + if (nrec->dtrd_uarg != rec->dtrd_uarg) + break; + + if (nrec->dtrd_action != act) { + return (dt_set_errno(dtp, + EDT_BADAGG)); + } + + naddr = buf->dtbd_data + offs + + nrec->dtrd_offset; + + aggvars[naggvars++] = + /* LINTED - alignment */ + *((dtrace_aggvarid_t *)naddr); + } + + i = j - 1; + bzero(&pd, sizeof (pd)); + pd.dtpa_dtp = dtp; + pd.dtpa_fp = fp; + + assert(naggvars >= 1); + + if (naggvars == 1) { + pd.dtpa_id = aggvars[0]; + dt_free(dtp, aggvars); + + if (dt_printf(dtp, fp, "\n") < 0 || + dtrace_aggregate_walk_sorted(dtp, + dt_print_agg, &pd) < 0) + return (-1); + goto nextrec; + } + + if (dt_printf(dtp, fp, "\n") < 0 || + dtrace_aggregate_walk_joined(dtp, aggvars, + naggvars, dt_print_aggs, &pd) < 0) { + dt_free(dtp, aggvars); + return (-1); + } + + dt_free(dtp, aggvars); + goto nextrec; + } + + switch (rec->dtrd_size) { + case sizeof (uint64_t): + n = dt_printf(dtp, fp, + quiet ? "%lld" : " %16lld", + /* LINTED - alignment */ + *((unsigned long long *)addr)); + break; + case sizeof (uint32_t): + n = dt_printf(dtp, fp, quiet ? "%d" : " %8d", + /* LINTED - alignment */ + *((uint32_t *)addr)); + break; + case sizeof (uint16_t): + n = dt_printf(dtp, fp, quiet ? "%d" : " %5d", + /* LINTED - alignment */ + *((uint16_t *)addr)); + break; + case sizeof (uint8_t): + n = dt_printf(dtp, fp, quiet ? "%d" : " %3d", + *((uint8_t *)addr)); + break; + default: + n = dt_print_bytes(dtp, fp, addr, + rec->dtrd_size, 33, quiet, 0); + break; + } + + if (n < 0) + return (-1); /* errno is set for us */ + +nextrec: + if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) + return (-1); /* errno is set for us */ + } + + /* + * Call the record callback with a NULL record to indicate + * that we're done processing this EPID. + */ + rval = (*rfunc)(&data, NULL, arg); +nextepid: + offs += epd->dtepd_size; + last = id; + } + + if (buf->dtbd_oldest != 0 && start == buf->dtbd_oldest) { + end = buf->dtbd_oldest; + start = 0; + goto again; + } + + if ((drops = buf->dtbd_drops) == 0) + return (0); + + /* + * Explicitly zero the drops to prevent us from processing them again. + */ + buf->dtbd_drops = 0; + + return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops)); +} + +typedef struct dt_begin { + dtrace_consume_probe_f *dtbgn_probefunc; + dtrace_consume_rec_f *dtbgn_recfunc; + void *dtbgn_arg; + dtrace_handle_err_f *dtbgn_errhdlr; + void *dtbgn_errarg; + int dtbgn_beginonly; +} dt_begin_t; + +static int +dt_consume_begin_probe(const dtrace_probedata_t *data, void *arg) +{ + dt_begin_t *begin = (dt_begin_t *)arg; + dtrace_probedesc_t *pd = data->dtpda_pdesc; + + int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); + int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); + + if (begin->dtbgn_beginonly) { + if (!(r1 && r2)) + return (DTRACE_CONSUME_NEXT); + } else { + if (r1 && r2) + return (DTRACE_CONSUME_NEXT); + } + + /* + * We have a record that we're interested in. Now call the underlying + * probe function... + */ + return (begin->dtbgn_probefunc(data, begin->dtbgn_arg)); +} + +static int +dt_consume_begin_record(const dtrace_probedata_t *data, + const dtrace_recdesc_t *rec, void *arg) +{ + dt_begin_t *begin = (dt_begin_t *)arg; + + return (begin->dtbgn_recfunc(data, rec, begin->dtbgn_arg)); +} + +static int +dt_consume_begin_error(const dtrace_errdata_t *data, void *arg) +{ + dt_begin_t *begin = (dt_begin_t *)arg; + dtrace_probedesc_t *pd = data->dteda_pdesc; + + int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); + int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); + + if (begin->dtbgn_beginonly) { + if (!(r1 && r2)) + return (DTRACE_HANDLE_OK); + } else { + if (r1 && r2) + return (DTRACE_HANDLE_OK); + } + + return (begin->dtbgn_errhdlr(data, begin->dtbgn_errarg)); +} + +static int +dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, dtrace_bufdesc_t *buf, + dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) +{ + /* + * There's this idea that the BEGIN probe should be processed before + * everything else, and that the END probe should be processed after + * anything else. In the common case, this is pretty easy to deal + * with. However, a situation may arise where the BEGIN enabling and + * END enabling are on the same CPU, and some enabling in the middle + * occurred on a different CPU. To deal with this (blech!) we need to + * consume the BEGIN buffer up until the end of the BEGIN probe, and + * then set it aside. We will then process every other CPU, and then + * we'll return to the BEGIN CPU and process the rest of the data + * (which will inevitably include the END probe, if any). Making this + * even more complicated (!) is the library's ERROR enabling. Because + * this enabling is processed before we even get into the consume call + * back, any ERROR firing would result in the library's ERROR enabling + * being processed twice -- once in our first pass (for BEGIN probes), + * and again in our second pass (for everything but BEGIN probes). To + * deal with this, we interpose on the ERROR handler to assure that we + * only process ERROR enablings induced by BEGIN enablings in the + * first pass, and that we only process ERROR enablings _not_ induced + * by BEGIN enablings in the second pass. + */ + dt_begin_t begin; + processorid_t cpu = dtp->dt_beganon; + dtrace_bufdesc_t nbuf; +#if !defined(sun) + dtrace_bufdesc_t *pbuf; +#endif + int rval, i; + static int max_ncpus; + dtrace_optval_t size; + + dtp->dt_beganon = -1; + +#if defined(sun) + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { +#else + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &buf) == -1) { +#endif + /* + * We really don't expect this to fail, but it is at least + * technically possible for this to fail with ENOENT. In this + * case, we just drive on... + */ + if (errno == ENOENT) + return (0); + + return (dt_set_errno(dtp, errno)); + } + + if (!dtp->dt_stopped || buf->dtbd_cpu != dtp->dt_endedon) { + /* + * This is the simple case. We're either not stopped, or if + * we are, we actually processed any END probes on another + * CPU. We can simply consume this buffer and return. + */ + return (dt_consume_cpu(dtp, fp, cpu, buf, pf, rf, arg)); + } + + begin.dtbgn_probefunc = pf; + begin.dtbgn_recfunc = rf; + begin.dtbgn_arg = arg; + begin.dtbgn_beginonly = 1; + + /* + * We need to interpose on the ERROR handler to be sure that we + * only process ERRORs induced by BEGIN. + */ + begin.dtbgn_errhdlr = dtp->dt_errhdlr; + begin.dtbgn_errarg = dtp->dt_errarg; + dtp->dt_errhdlr = dt_consume_begin_error; + dtp->dt_errarg = &begin; + + rval = dt_consume_cpu(dtp, fp, cpu, buf, dt_consume_begin_probe, + dt_consume_begin_record, &begin); + + dtp->dt_errhdlr = begin.dtbgn_errhdlr; + dtp->dt_errarg = begin.dtbgn_errarg; + + if (rval != 0) + return (rval); + + /* + * Now allocate a new buffer. We'll use this to deal with every other + * CPU. + */ + bzero(&nbuf, sizeof (dtrace_bufdesc_t)); + (void) dtrace_getopt(dtp, "bufsize", &size); + if ((nbuf.dtbd_data = malloc(size)) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + if (max_ncpus == 0) + max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; + + for (i = 0; i < max_ncpus; i++) { + nbuf.dtbd_cpu = i; + + if (i == cpu) + continue; + +#if defined(sun) + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &nbuf) == -1) { +#else + pbuf = &nbuf; + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &pbuf) == -1) { +#endif + /* + * If we failed with ENOENT, it may be because the + * CPU was unconfigured -- this is okay. Any other + * error, however, is unexpected. + */ + if (errno == ENOENT) + continue; + + free(nbuf.dtbd_data); + + return (dt_set_errno(dtp, errno)); + } + + if ((rval = dt_consume_cpu(dtp, fp, + i, &nbuf, pf, rf, arg)) != 0) { + free(nbuf.dtbd_data); + return (rval); + } + } + + free(nbuf.dtbd_data); + + /* + * Okay -- we're done with the other buffers. Now we want to + * reconsume the first buffer -- but this time we're looking for + * everything _but_ BEGIN. And of course, in order to only consume + * those ERRORs _not_ associated with BEGIN, we need to reinstall our + * ERROR interposition function... + */ + begin.dtbgn_beginonly = 0; + + assert(begin.dtbgn_errhdlr == dtp->dt_errhdlr); + assert(begin.dtbgn_errarg == dtp->dt_errarg); + dtp->dt_errhdlr = dt_consume_begin_error; + dtp->dt_errarg = &begin; + + rval = dt_consume_cpu(dtp, fp, cpu, buf, dt_consume_begin_probe, + dt_consume_begin_record, &begin); + + dtp->dt_errhdlr = begin.dtbgn_errhdlr; + dtp->dt_errarg = begin.dtbgn_errarg; + + return (rval); +} + +int +dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, + dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) +{ + dtrace_bufdesc_t *buf = &dtp->dt_buf; + dtrace_optval_t size; + static int max_ncpus; + int i, rval; + dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_SWITCHRATE]; + hrtime_t now = gethrtime(); + + if (dtp->dt_lastswitch != 0) { + if (now - dtp->dt_lastswitch < interval) + return (0); + + dtp->dt_lastswitch += interval; + } else { + dtp->dt_lastswitch = now; + } + + if (!dtp->dt_active) + return (dt_set_errno(dtp, EINVAL)); + + if (max_ncpus == 0) + max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; + + if (pf == NULL) + pf = (dtrace_consume_probe_f *)dt_nullprobe; + + if (rf == NULL) + rf = (dtrace_consume_rec_f *)dt_nullrec; + + if (buf->dtbd_data == NULL) { + (void) dtrace_getopt(dtp, "bufsize", &size); + if ((buf->dtbd_data = malloc(size)) == NULL) + return (dt_set_errno(dtp, EDT_NOMEM)); + + buf->dtbd_size = size; + } + + /* + * If we have just begun, we want to first process the CPU that + * executed the BEGIN probe (if any). + */ + if (dtp->dt_active && dtp->dt_beganon != -1) { + buf->dtbd_cpu = dtp->dt_beganon; + if ((rval = dt_consume_begin(dtp, fp, buf, pf, rf, arg)) != 0) + return (rval); + } + + for (i = 0; i < max_ncpus; i++) { + buf->dtbd_cpu = i; + + /* + * If we have stopped, we want to process the CPU on which the + * END probe was processed only _after_ we have processed + * everything else. + */ + if (dtp->dt_stopped && (i == dtp->dt_endedon)) + continue; + +#if defined(sun) + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { +#else + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &buf) == -1) { +#endif + /* + * If we failed with ENOENT, it may be because the + * CPU was unconfigured -- this is okay. Any other + * error, however, is unexpected. + */ + if (errno == ENOENT) + continue; + + return (dt_set_errno(dtp, errno)); + } + + if ((rval = dt_consume_cpu(dtp, fp, i, buf, pf, rf, arg)) != 0) + return (rval); + } + + if (!dtp->dt_stopped) + return (0); + + buf->dtbd_cpu = dtp->dt_endedon; + +#if defined(sun) + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { +#else + if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &buf) == -1) { +#endif + /* + * This _really_ shouldn't fail, but it is strictly speaking + * possible for this to return ENOENT if the CPU that called + * the END enabling somehow managed to become unconfigured. + * It's unclear how the user can possibly expect anything + * rational to happen in this case -- the state has been thrown + * out along with the unconfigured CPU -- so we'll just drive + * on... + */ + if (errno == ENOENT) + return (0); + + return (dt_set_errno(dtp, errno)); + } + + return (dt_consume_cpu(dtp, fp, dtp->dt_endedon, buf, pf, rf, arg)); +} diff --git a/lib/libdtrace/common/dt_decl.c b/lib/libdtrace/common/dt_decl.c new file mode 100644 index 000000000000..bb779840406c --- /dev/null +++ b/lib/libdtrace/common/dt_decl.c @@ -0,0 +1,1127 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <strings.h> +#include <stdlib.h> +#include <limits.h> +#include <alloca.h> +#include <assert.h> + +#include <dt_decl.h> +#include <dt_parser.h> +#include <dt_module.h> +#include <dt_impl.h> + +static dt_decl_t * +dt_decl_check(dt_decl_t *ddp) +{ + if (ddp->dd_kind == CTF_K_UNKNOWN) + return (ddp); /* nothing to check if the type is not yet set */ + + if (ddp->dd_name != NULL && strcmp(ddp->dd_name, "char") == 0 && + (ddp->dd_attr & (DT_DA_SHORT | DT_DA_LONG | DT_DA_LONGLONG))) { + xyerror(D_DECL_CHARATTR, "invalid type declaration: short and " + "long may not be used with char type\n"); + } + + if (ddp->dd_name != NULL && strcmp(ddp->dd_name, "void") == 0 && + (ddp->dd_attr & (DT_DA_SHORT | DT_DA_LONG | DT_DA_LONGLONG | + (DT_DA_SIGNED | DT_DA_UNSIGNED)))) { + xyerror(D_DECL_VOIDATTR, "invalid type declaration: attributes " + "may not be used with void type\n"); + } + + if (ddp->dd_kind != CTF_K_INTEGER && + (ddp->dd_attr & (DT_DA_SIGNED | DT_DA_UNSIGNED))) { + xyerror(D_DECL_SIGNINT, "invalid type declaration: signed and " + "unsigned may only be used with integer type\n"); + } + + if (ddp->dd_kind != CTF_K_INTEGER && ddp->dd_kind != CTF_K_FLOAT && + (ddp->dd_attr & (DT_DA_LONG | DT_DA_LONGLONG))) { + xyerror(D_DECL_LONGINT, "invalid type declaration: long and " + "long long may only be used with integer or " + "floating-point type\n"); + } + + return (ddp); +} + +dt_decl_t * +dt_decl_alloc(ushort_t kind, char *name) +{ + dt_decl_t *ddp = malloc(sizeof (dt_decl_t)); + + if (ddp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + ddp->dd_kind = kind; + ddp->dd_attr = 0; + ddp->dd_ctfp = NULL; + ddp->dd_type = CTF_ERR; + ddp->dd_name = name; + ddp->dd_node = NULL; + ddp->dd_next = NULL; + + return (ddp); +} + +void +dt_decl_free(dt_decl_t *ddp) +{ + dt_decl_t *ndp; + + for (; ddp != NULL; ddp = ndp) { + ndp = ddp->dd_next; + free(ddp->dd_name); + dt_node_list_free(&ddp->dd_node); + free(ddp); + } +} + +void +dt_decl_reset(void) +{ + dt_scope_t *dsp = &yypcb->pcb_dstack; + dt_decl_t *ddp = dsp->ds_decl; + + while (ddp->dd_next != NULL) { + dsp->ds_decl = ddp->dd_next; + ddp->dd_next = NULL; + dt_decl_free(ddp); + ddp = dsp->ds_decl; + } +} + +dt_decl_t * +dt_decl_push(dt_decl_t *ddp) +{ + dt_scope_t *dsp = &yypcb->pcb_dstack; + dt_decl_t *top = dsp->ds_decl; + + if (top != NULL && + top->dd_kind == CTF_K_UNKNOWN && top->dd_name == NULL) { + top->dd_kind = CTF_K_INTEGER; + (void) dt_decl_check(top); + } + + assert(ddp->dd_next == NULL); + ddp->dd_next = top; + dsp->ds_decl = ddp; + + return (ddp); +} + +dt_decl_t * +dt_decl_pop(void) +{ + dt_scope_t *dsp = &yypcb->pcb_dstack; + dt_decl_t *ddp = dt_decl_top(); + + dsp->ds_decl = NULL; + free(dsp->ds_ident); + dsp->ds_ident = NULL; + dsp->ds_ctfp = NULL; + dsp->ds_type = CTF_ERR; + dsp->ds_class = DT_DC_DEFAULT; + dsp->ds_enumval = -1; + + return (ddp); +} + +dt_decl_t * +dt_decl_pop_param(char **idp) +{ + dt_scope_t *dsp = &yypcb->pcb_dstack; + + if (dsp->ds_class != DT_DC_DEFAULT && dsp->ds_class != DT_DC_REGISTER) { + xyerror(D_DECL_PARMCLASS, "inappropriate storage class " + "for function or associative array parameter\n"); + } + + if (idp != NULL && dt_decl_top() != NULL) { + *idp = dsp->ds_ident; + dsp->ds_ident = NULL; + } + + return (dt_decl_pop()); +} + +dt_decl_t * +dt_decl_top(void) +{ + dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; + + if (ddp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NODECL); + + if (ddp->dd_kind == CTF_K_UNKNOWN && ddp->dd_name == NULL) { + ddp->dd_kind = CTF_K_INTEGER; + (void) dt_decl_check(ddp); + } + + return (ddp); +} + +dt_decl_t * +dt_decl_ident(char *name) +{ + dt_scope_t *dsp = &yypcb->pcb_dstack; + dt_decl_t *ddp = dsp->ds_decl; + + if (dsp->ds_ident != NULL) { + free(name); + xyerror(D_DECL_IDENT, "old-style declaration or " + "incorrect type specified\n"); + } + + dsp->ds_ident = name; + + if (ddp == NULL) + ddp = dt_decl_push(dt_decl_alloc(CTF_K_UNKNOWN, NULL)); + + return (ddp); +} + +void +dt_decl_class(dt_dclass_t class) +{ + dt_scope_t *dsp = &yypcb->pcb_dstack; + + if (dsp->ds_class != DT_DC_DEFAULT) { + xyerror(D_DECL_CLASS, "only one storage class allowed " + "in a declaration\n"); + } + + dsp->ds_class = class; +} + +/* + * Set the kind and name of the current declaration. If none is allocated, + * make a new decl and push it on to the top of our stack. If the name or kind + * is already set for the current decl, then we need to fail this declaration. + * This can occur because too many types were given (e.g. "int int"), etc. + */ +dt_decl_t * +dt_decl_spec(ushort_t kind, char *name) +{ + dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; + + if (ddp == NULL) + return (dt_decl_push(dt_decl_alloc(kind, name))); + + /* + * If we already have a type name specified and we see another type + * name, this is an error if the declaration is a typedef. If the + * declaration is not a typedef, then the user may be trying to declare + * a variable whose name has been returned by lex as a TNAME token: + * call dt_decl_ident() as if the grammar's IDENT rule was matched. + */ + if (ddp->dd_name != NULL && kind == CTF_K_TYPEDEF) { + if (yypcb->pcb_dstack.ds_class != DT_DC_TYPEDEF) + return (dt_decl_ident(name)); + xyerror(D_DECL_IDRED, "identifier redeclared: %s\n", name); + } + + if (ddp->dd_name != NULL || ddp->dd_kind != CTF_K_UNKNOWN) + xyerror(D_DECL_COMBO, "invalid type combination\n"); + + ddp->dd_kind = kind; + ddp->dd_name = name; + + if (name != NULL && strchr(name, '`') != NULL) { + xyerror(D_DECL_SCOPE, "D scoping operator may not be used " + "in a type name\n"); + } + + return (dt_decl_check(ddp)); +} + +dt_decl_t * +dt_decl_attr(ushort_t attr) +{ + dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; + + if (ddp == NULL) { + ddp = dt_decl_push(dt_decl_alloc(CTF_K_UNKNOWN, NULL)); + ddp->dd_attr = attr; + return (ddp); + } + + if (attr == DT_DA_LONG && (ddp->dd_attr & DT_DA_LONG)) { + ddp->dd_attr &= ~DT_DA_LONG; + attr = DT_DA_LONGLONG; + } + + ddp->dd_attr |= attr; + return (dt_decl_check(ddp)); +} + +/* + * Examine the list of formal parameters 'flist' and determine if the formal + * name fnp->dn_string is defined in this list (B_TRUE) or not (B_FALSE). + * If 'fnp' is in 'flist', do not search beyond 'fnp' itself in 'flist'. + */ +static int +dt_decl_protoform(dt_node_t *fnp, dt_node_t *flist) +{ + dt_node_t *dnp; + + for (dnp = flist; dnp != fnp && dnp != NULL; dnp = dnp->dn_list) { + if (dnp->dn_string != NULL && + strcmp(dnp->dn_string, fnp->dn_string) == 0) + return (B_TRUE); + } + + return (B_FALSE); +} + +/* + * Common code for parsing array, function, and probe definition prototypes. + * The prototype node list is specified as 'plist'. The formal prototype + * against which to compare the prototype is specified as 'flist'. If plist + * and flist are the same, we require that named parameters are unique. If + * plist and flist are different, we require that named parameters in plist + * match a name that is present in flist. + */ +int +dt_decl_prototype(dt_node_t *plist, + dt_node_t *flist, const char *kind, uint_t flags) +{ + char n[DT_TYPE_NAMELEN]; + int is_void, v = 0, i = 1; + int form = plist != flist; + dt_node_t *dnp; + + for (dnp = plist; dnp != NULL; dnp = dnp->dn_list, i++) { + + if (dnp->dn_type == CTF_ERR && !(flags & DT_DP_VARARGS)) { + dnerror(dnp, D_DECL_PROTO_VARARGS, "%s prototype may " + "not use a variable-length argument list\n", kind); + } + + if (dt_node_is_dynamic(dnp) && !(flags & DT_DP_DYNAMIC)) { + dnerror(dnp, D_DECL_PROTO_TYPE, "%s prototype may not " + "use parameter of type %s: %s, parameter #%d\n", + kind, dt_node_type_name(dnp, n, sizeof (n)), + dnp->dn_string ? dnp->dn_string : "(anonymous)", i); + } + + is_void = dt_node_is_void(dnp); + v += is_void; + + if (is_void && !(flags & DT_DP_VOID)) { + dnerror(dnp, D_DECL_PROTO_TYPE, "%s prototype may not " + "use parameter of type %s: %s, parameter #%d\n", + kind, dt_node_type_name(dnp, n, sizeof (n)), + dnp->dn_string ? dnp->dn_string : "(anonymous)", i); + } + + if (is_void && dnp->dn_string != NULL) { + dnerror(dnp, D_DECL_PROTO_NAME, "void parameter may " + "not have a name: %s\n", dnp->dn_string); + } + + if (dnp->dn_string != NULL && + dt_decl_protoform(dnp, flist) != form) { + dnerror(dnp, D_DECL_PROTO_FORM, "parameter is " + "%s declared in %s prototype: %s, parameter #%d\n", + form ? "not" : "already", kind, dnp->dn_string, i); + } + + if (dnp->dn_string == NULL && + !is_void && !(flags & DT_DP_ANON)) { + dnerror(dnp, D_DECL_PROTO_NAME, "parameter declaration " + "requires a name: parameter #%d\n", i); + } + } + + if (v != 0 && plist->dn_list != NULL) + xyerror(D_DECL_PROTO_VOID, "void must be sole parameter\n"); + + return (v ? 0 : i - 1); /* return zero if sole parameter is 'void' */ +} + +dt_decl_t * +dt_decl_array(dt_node_t *dnp) +{ + dt_decl_t *ddp = dt_decl_push(dt_decl_alloc(CTF_K_ARRAY, NULL)); + dt_scope_t *dsp = &yypcb->pcb_dstack; + dt_decl_t *ndp = ddp; + + /* + * After pushing the array on to the decl stack, scan ahead for multi- + * dimensional array declarations and push the current decl to the + * bottom to match the resulting CTF type tree and data layout. Refer + * to the comments in dt_decl_type() and ISO C 6.5.2.1 for more info. + */ + while (ndp->dd_next != NULL && ndp->dd_next->dd_kind == CTF_K_ARRAY) + ndp = ndp->dd_next; /* skip to bottom-most array declaration */ + + if (ndp != ddp) { + if (dnp != NULL && dnp->dn_kind == DT_NODE_TYPE) { + xyerror(D_DECL_DYNOBJ, + "cannot declare array of associative arrays\n"); + } + dsp->ds_decl = ddp->dd_next; + ddp->dd_next = ndp->dd_next; + ndp->dd_next = ddp; + } + + if (ddp->dd_next->dd_name != NULL && + strcmp(ddp->dd_next->dd_name, "void") == 0) + xyerror(D_DECL_VOIDOBJ, "cannot declare array of void\n"); + + if (dnp != NULL && dnp->dn_kind != DT_NODE_TYPE) { + dnp = ddp->dd_node = dt_node_cook(dnp, DT_IDFLG_REF); + + if (dt_node_is_posconst(dnp) == 0) { + xyerror(D_DECL_ARRSUB, "positive integral constant " + "expression or tuple signature expected as " + "array declaration subscript\n"); + } + + if (dnp->dn_value > UINT_MAX) + xyerror(D_DECL_ARRBIG, "array dimension too big\n"); + + } else if (dnp != NULL) { + ddp->dd_node = dnp; + (void) dt_decl_prototype(dnp, dnp, "array", DT_DP_ANON); + } + + return (ddp); +} + +/* + * When a function is declared, we need to fudge the decl stack a bit if the + * declaration uses the function pointer (*)() syntax. In this case, the + * dt_decl_func() call occurs *after* the dt_decl_ptr() call, even though the + * resulting type is "pointer to function". To make the pointer land on top, + * we check to see if 'pdp' is non-NULL and a pointer. If it is, we search + * backward for a decl tagged with DT_DA_PAREN, and if one is found, the func + * decl is inserted behind this node in the decl list instead of at the top. + * In all cases, the func decl's dd_next pointer is set to the decl chain + * for the function's return type and the function parameter list is discarded. + */ +dt_decl_t * +dt_decl_func(dt_decl_t *pdp, dt_node_t *dnp) +{ + dt_decl_t *ddp = dt_decl_alloc(CTF_K_FUNCTION, NULL); + + ddp->dd_node = dnp; + + (void) dt_decl_prototype(dnp, dnp, "function", + DT_DP_VARARGS | DT_DP_VOID | DT_DP_ANON); + + if (pdp == NULL || pdp->dd_kind != CTF_K_POINTER) + return (dt_decl_push(ddp)); + + while (pdp->dd_next != NULL && !(pdp->dd_next->dd_attr & DT_DA_PAREN)) + pdp = pdp->dd_next; + + if (pdp->dd_next == NULL) + return (dt_decl_push(ddp)); + + ddp->dd_next = pdp->dd_next; + pdp->dd_next = ddp; + + return (pdp); +} + +dt_decl_t * +dt_decl_ptr(void) +{ + return (dt_decl_push(dt_decl_alloc(CTF_K_POINTER, NULL))); +} + +dt_decl_t * +dt_decl_sou(uint_t kind, char *name) +{ + dt_decl_t *ddp = dt_decl_spec(kind, name); + char n[DT_TYPE_NAMELEN]; + ctf_file_t *ctfp; + ctf_id_t type; + uint_t flag; + + if (yypcb->pcb_idepth != 0) + ctfp = yypcb->pcb_hdl->dt_cdefs->dm_ctfp; + else + ctfp = yypcb->pcb_hdl->dt_ddefs->dm_ctfp; + + if (yypcb->pcb_dstack.ds_next != NULL) + flag = CTF_ADD_NONROOT; + else + flag = CTF_ADD_ROOT; + + (void) snprintf(n, sizeof (n), "%s %s", + kind == CTF_K_STRUCT ? "struct" : "union", + name == NULL ? "(anon)" : name); + + if (name != NULL && (type = ctf_lookup_by_name(ctfp, n)) != CTF_ERR && + ctf_type_kind(ctfp, type) != CTF_K_FORWARD) + xyerror(D_DECL_TYPERED, "type redeclared: %s\n", n); + + if (kind == CTF_K_STRUCT) + type = ctf_add_struct(ctfp, flag, name); + else + type = ctf_add_union(ctfp, flag, name); + + if (type == CTF_ERR || ctf_update(ctfp) == CTF_ERR) { + xyerror(D_UNKNOWN, "failed to define %s: %s\n", + n, ctf_errmsg(ctf_errno(ctfp))); + } + + ddp->dd_ctfp = ctfp; + ddp->dd_type = type; + + dt_scope_push(ctfp, type); + return (ddp); +} + +void +dt_decl_member(dt_node_t *dnp) +{ + dt_scope_t *dsp = yypcb->pcb_dstack.ds_next; + dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; + char *ident = yypcb->pcb_dstack.ds_ident; + + const char *idname = ident ? ident : "(anon)"; + char n[DT_TYPE_NAMELEN]; + + dtrace_typeinfo_t dtt; + ctf_encoding_t cte; + ctf_id_t base; + uint_t kind; + ssize_t size; + + if (dsp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOSCOPE); + + if (ddp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NODECL); + + if (dnp == NULL && ident == NULL) + xyerror(D_DECL_MNAME, "member declaration requires a name\n"); + + if (ddp->dd_kind == CTF_K_UNKNOWN && ddp->dd_name == NULL) { + ddp->dd_kind = CTF_K_INTEGER; + (void) dt_decl_check(ddp); + } + + if (dt_decl_type(ddp, &dtt) != 0) + longjmp(yypcb->pcb_jmpbuf, EDT_COMPILER); + + if (ident != NULL && strchr(ident, '`') != NULL) { + xyerror(D_DECL_SCOPE, "D scoping operator may not be used " + "in a member name (%s)\n", ident); + } + + if (dtt.dtt_ctfp == DT_DYN_CTFP(yypcb->pcb_hdl) && + dtt.dtt_type == DT_DYN_TYPE(yypcb->pcb_hdl)) { + xyerror(D_DECL_DYNOBJ, + "cannot have dynamic member: %s\n", ident); + } + + base = ctf_type_resolve(dtt.dtt_ctfp, dtt.dtt_type); + kind = ctf_type_kind(dtt.dtt_ctfp, base); + size = ctf_type_size(dtt.dtt_ctfp, base); + + if (kind == CTF_K_FORWARD || ((kind == CTF_K_STRUCT || + kind == CTF_K_UNION) && size == 0)) { + xyerror(D_DECL_INCOMPLETE, "incomplete struct/union/enum %s: " + "%s\n", dt_type_name(dtt.dtt_ctfp, dtt.dtt_type, + n, sizeof (n)), ident); + } + + if (size == 0) + xyerror(D_DECL_VOIDOBJ, "cannot have void member: %s\n", ident); + + /* + * If a bit-field qualifier was part of the member declaration, create + * a new integer type of the same name and attributes as the base type + * and size equal to the specified number of bits. We reset 'dtt' to + * refer to this new bit-field type and continue on to add the member. + */ + if (dnp != NULL) { + dnp = dt_node_cook(dnp, DT_IDFLG_REF); + + /* + * A bit-field member with no declarator is permitted to have + * size zero and indicates that no more fields are to be packed + * into the current storage unit. We ignore these directives + * as the underlying ctf code currently does so for all fields. + */ + if (ident == NULL && dnp->dn_kind == DT_NODE_INT && + dnp->dn_value == 0) { + dt_node_free(dnp); + goto done; + } + + if (dt_node_is_posconst(dnp) == 0) { + xyerror(D_DECL_BFCONST, "positive integral constant " + "expression expected as bit-field size\n"); + } + + if (ctf_type_kind(dtt.dtt_ctfp, base) != CTF_K_INTEGER || + ctf_type_encoding(dtt.dtt_ctfp, base, &cte) == CTF_ERR || + IS_VOID(cte)) { + xyerror(D_DECL_BFTYPE, "invalid type for " + "bit-field: %s\n", idname); + } + + if (dnp->dn_value > cte.cte_bits) { + xyerror(D_DECL_BFSIZE, "bit-field too big " + "for type: %s\n", idname); + } + + cte.cte_offset = 0; + cte.cte_bits = (uint_t)dnp->dn_value; + + dtt.dtt_type = ctf_add_integer(dsp->ds_ctfp, + CTF_ADD_NONROOT, ctf_type_name(dtt.dtt_ctfp, + dtt.dtt_type, n, sizeof (n)), &cte); + + if (dtt.dtt_type == CTF_ERR || + ctf_update(dsp->ds_ctfp) == CTF_ERR) { + xyerror(D_UNKNOWN, "failed to create type for " + "member '%s': %s\n", idname, + ctf_errmsg(ctf_errno(dsp->ds_ctfp))); + } + + dtt.dtt_ctfp = dsp->ds_ctfp; + dt_node_free(dnp); + } + + /* + * If the member type is not defined in the same CTF container as the + * one associated with the current scope (i.e. the container for the + * struct or union itself) or its parent, copy the member type into + * this container and reset dtt to refer to the copied type. + */ + if (dtt.dtt_ctfp != dsp->ds_ctfp && + dtt.dtt_ctfp != ctf_parent_file(dsp->ds_ctfp)) { + + dtt.dtt_type = ctf_add_type(dsp->ds_ctfp, + dtt.dtt_ctfp, dtt.dtt_type); + dtt.dtt_ctfp = dsp->ds_ctfp; + + if (dtt.dtt_type == CTF_ERR || + ctf_update(dtt.dtt_ctfp) == CTF_ERR) { + xyerror(D_UNKNOWN, "failed to copy type of '%s': %s\n", + idname, ctf_errmsg(ctf_errno(dtt.dtt_ctfp))); + } + } + + if (ctf_add_member(dsp->ds_ctfp, dsp->ds_type, + ident, dtt.dtt_type) == CTF_ERR) { + xyerror(D_UNKNOWN, "failed to define member '%s': %s\n", + idname, ctf_errmsg(ctf_errno(dsp->ds_ctfp))); + } + +done: + free(ident); + yypcb->pcb_dstack.ds_ident = NULL; + dt_decl_reset(); +} + +/*ARGSUSED*/ +static int +dt_decl_hasmembers(const char *name, int value, void *private) +{ + return (1); /* abort search and return true if a member exists */ +} + +dt_decl_t * +dt_decl_enum(char *name) +{ + dt_decl_t *ddp = dt_decl_spec(CTF_K_ENUM, name); + char n[DT_TYPE_NAMELEN]; + ctf_file_t *ctfp; + ctf_id_t type; + uint_t flag; + + if (yypcb->pcb_idepth != 0) + ctfp = yypcb->pcb_hdl->dt_cdefs->dm_ctfp; + else + ctfp = yypcb->pcb_hdl->dt_ddefs->dm_ctfp; + + if (yypcb->pcb_dstack.ds_next != NULL) + flag = CTF_ADD_NONROOT; + else + flag = CTF_ADD_ROOT; + + (void) snprintf(n, sizeof (n), "enum %s", name ? name : "(anon)"); + + if (name != NULL && (type = ctf_lookup_by_name(ctfp, n)) != CTF_ERR) { + if (ctf_enum_iter(ctfp, type, dt_decl_hasmembers, NULL)) + xyerror(D_DECL_TYPERED, "type redeclared: %s\n", n); + } else if ((type = ctf_add_enum(ctfp, flag, name)) == CTF_ERR) { + xyerror(D_UNKNOWN, "failed to define %s: %s\n", + n, ctf_errmsg(ctf_errno(ctfp))); + } + + ddp->dd_ctfp = ctfp; + ddp->dd_type = type; + + dt_scope_push(ctfp, type); + return (ddp); +} + +void +dt_decl_enumerator(char *s, dt_node_t *dnp) +{ + dt_scope_t *dsp = yypcb->pcb_dstack.ds_next; + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + + dt_idnode_t *inp; + dt_ident_t *idp; + char *name; + int value; + + name = alloca(strlen(s) + 1); + (void) strcpy(name, s); + free(s); + + if (dsp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOSCOPE); + + assert(dsp->ds_decl->dd_kind == CTF_K_ENUM); + value = dsp->ds_enumval + 1; /* default is previous value plus one */ + + if (strchr(name, '`') != NULL) { + xyerror(D_DECL_SCOPE, "D scoping operator may not be used in " + "an enumerator name (%s)\n", name); + } + + /* + * If the enumerator is being assigned a value, cook and check the node + * and then free it after we get the value. We also permit references + * to identifiers which are previously defined enumerators in the type. + */ + if (dnp != NULL) { + if (dnp->dn_kind != DT_NODE_IDENT || ctf_enum_value( + dsp->ds_ctfp, dsp->ds_type, dnp->dn_string, &value) != 0) { + dnp = dt_node_cook(dnp, DT_IDFLG_REF); + + if (dnp->dn_kind != DT_NODE_INT) { + xyerror(D_DECL_ENCONST, "enumerator '%s' must " + "be assigned to an integral constant " + "expression\n", name); + } + + if ((intmax_t)dnp->dn_value > INT_MAX || + (intmax_t)dnp->dn_value < INT_MIN) { + xyerror(D_DECL_ENOFLOW, "enumerator '%s' value " + "overflows INT_MAX (%d)\n", name, INT_MAX); + } + + value = (int)dnp->dn_value; + } + dt_node_free(dnp); + } + + if (ctf_add_enumerator(dsp->ds_ctfp, dsp->ds_type, + name, value) == CTF_ERR || ctf_update(dsp->ds_ctfp) == CTF_ERR) { + xyerror(D_UNKNOWN, "failed to define enumerator '%s': %s\n", + name, ctf_errmsg(ctf_errno(dsp->ds_ctfp))); + } + + dsp->ds_enumval = value; /* save most recent value */ + + /* + * If the enumerator name matches an identifier in the global scope, + * flag this as an error. We only do this for "D" enumerators to + * prevent "C" header file enumerators from conflicting with the ever- + * growing list of D built-in global variables and inlines. If a "C" + * enumerator conflicts with a global identifier, we add the enumerator + * but do not insert a corresponding inline (i.e. the D variable wins). + */ + if (dt_idstack_lookup(&yypcb->pcb_globals, name) != NULL) { + if (dsp->ds_ctfp == dtp->dt_ddefs->dm_ctfp) { + xyerror(D_DECL_IDRED, + "identifier redeclared: %s\n", name); + } else + return; + } + + dt_dprintf("add global enumerator %s = %d\n", name, value); + + idp = dt_idhash_insert(dtp->dt_globals, name, DT_IDENT_ENUM, + DT_IDFLG_INLINE | DT_IDFLG_REF, 0, _dtrace_defattr, 0, + &dt_idops_inline, NULL, dtp->dt_gen); + + if (idp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + yyintprefix = 0; + yyintsuffix[0] = '\0'; + yyintdecimal = 0; + + dnp = dt_node_int(value); + dt_node_type_assign(dnp, dsp->ds_ctfp, dsp->ds_type); + + if ((inp = malloc(sizeof (dt_idnode_t))) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + /* + * Remove the INT node from the node allocation list and store it in + * din_list and din_root so it persists with and is freed by the ident. + */ + assert(yypcb->pcb_list == dnp); + yypcb->pcb_list = dnp->dn_link; + dnp->dn_link = NULL; + + bzero(inp, sizeof (dt_idnode_t)); + inp->din_list = dnp; + inp->din_root = dnp; + + idp->di_iarg = inp; + idp->di_ctfp = dsp->ds_ctfp; + idp->di_type = dsp->ds_type; +} + +/* + * Look up the type corresponding to the specified decl stack. The scoping of + * the underlying type names is handled by dt_type_lookup(). We build up the + * name from the specified string and prefixes and then lookup the type. If + * we fail, an errmsg is saved and the caller must abort with EDT_COMPILER. + */ +int +dt_decl_type(dt_decl_t *ddp, dtrace_typeinfo_t *tip) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + + dt_module_t *dmp; + ctf_arinfo_t r; + ctf_id_t type; + + char n[DT_TYPE_NAMELEN]; + uint_t flag; + char *name; + int rv; + + /* + * Based on our current #include depth and decl stack depth, determine + * which dynamic CTF module and scope to use when adding any new types. + */ + dmp = yypcb->pcb_idepth ? dtp->dt_cdefs : dtp->dt_ddefs; + flag = yypcb->pcb_dstack.ds_next ? CTF_ADD_NONROOT : CTF_ADD_ROOT; + + /* + * If we have already cached a CTF type for this decl, then we just + * return the type information for the cached type. + */ + if (ddp->dd_ctfp != NULL && + (dmp = dt_module_lookup_by_ctf(dtp, ddp->dd_ctfp)) != NULL) { + tip->dtt_object = dmp->dm_name; + tip->dtt_ctfp = ddp->dd_ctfp; + tip->dtt_type = ddp->dd_type; + return (0); + } + + /* + * Currently CTF treats all function pointers identically. We cache a + * representative ID of kind CTF_K_FUNCTION and just return that type. + * If we want to support full function declarations, dd_next refers to + * the declaration of the function return type, and the parameter list + * should be parsed and hung off a new pointer inside of this decl. + */ + if (ddp->dd_kind == CTF_K_FUNCTION) { + tip->dtt_object = dtp->dt_ddefs->dm_name; + tip->dtt_ctfp = DT_FUNC_CTFP(dtp); + tip->dtt_type = DT_FUNC_TYPE(dtp); + return (0); + } + + /* + * If the decl is a pointer, resolve the rest of the stack by calling + * dt_decl_type() recursively and then compute a pointer to the result. + * Similar to the code above, we return a cached id for function ptrs. + */ + if (ddp->dd_kind == CTF_K_POINTER) { + if (ddp->dd_next->dd_kind == CTF_K_FUNCTION) { + tip->dtt_object = dtp->dt_ddefs->dm_name; + tip->dtt_ctfp = DT_FPTR_CTFP(dtp); + tip->dtt_type = DT_FPTR_TYPE(dtp); + return (0); + } + + if ((rv = dt_decl_type(ddp->dd_next, tip)) == 0 && + (rv = dt_type_pointer(tip)) != 0) { + xywarn(D_UNKNOWN, "cannot find type: %s*: %s\n", + dt_type_name(tip->dtt_ctfp, tip->dtt_type, + n, sizeof (n)), ctf_errmsg(dtp->dt_ctferr)); + } + + return (rv); + } + + /* + * If the decl is an array, we must find the base type and then call + * dt_decl_type() recursively and then build an array of the result. + * The C and D multi-dimensional array syntax requires that consecutive + * array declarations be processed from right-to-left (i.e. top-down + * from the perspective of the declaration stack). For example, an + * array declaration such as int x[3][5] is stored on the stack as: + * + * (bottom) NULL <- ( INT "int" ) <- ( ARR [3] ) <- ( ARR [5] ) (top) + * + * but means that x is declared to be an array of 3 objects each of + * which is an array of 5 integers, or in CTF representation: + * + * type T1:( content=int, nelems=5 ) type T2:( content=T1, nelems=3 ) + * + * For more details, refer to K&R[5.7] and ISO C 6.5.2.1. Rather than + * overcomplicate the implementation of dt_decl_type(), we push array + * declarations down into the stack in dt_decl_array(), above, so that + * by the time dt_decl_type() is called, the decl stack looks like: + * + * (bottom) NULL <- ( INT "int" ) <- ( ARR [5] ) <- ( ARR [3] ) (top) + * + * which permits a straightforward recursive descent of the decl stack + * to build the corresponding CTF type tree in the appropriate order. + */ + if (ddp->dd_kind == CTF_K_ARRAY) { + /* + * If the array decl has a parameter list associated with it, + * this is an associative array declaration: return <DYN>. + */ + if (ddp->dd_node != NULL && + ddp->dd_node->dn_kind == DT_NODE_TYPE) { + tip->dtt_object = dtp->dt_ddefs->dm_name; + tip->dtt_ctfp = DT_DYN_CTFP(dtp); + tip->dtt_type = DT_DYN_TYPE(dtp); + return (0); + } + + if ((rv = dt_decl_type(ddp->dd_next, tip)) != 0) + return (rv); + + /* + * If the array base type is not defined in the target + * container or its parent, copy the type to the target + * container and reset dtt_ctfp and dtt_type to the copy. + */ + if (tip->dtt_ctfp != dmp->dm_ctfp && + tip->dtt_ctfp != ctf_parent_file(dmp->dm_ctfp)) { + + tip->dtt_type = ctf_add_type(dmp->dm_ctfp, + tip->dtt_ctfp, tip->dtt_type); + tip->dtt_ctfp = dmp->dm_ctfp; + + if (tip->dtt_type == CTF_ERR || + ctf_update(tip->dtt_ctfp) == CTF_ERR) { + xywarn(D_UNKNOWN, "failed to copy type: %s\n", + ctf_errmsg(ctf_errno(tip->dtt_ctfp))); + return (-1); + } + } + + /* + * The array index type is irrelevant in C and D: just set it + * to "long" for all array types that we create on-the-fly. + */ + r.ctr_contents = tip->dtt_type; + r.ctr_index = ctf_lookup_by_name(tip->dtt_ctfp, "long"); + r.ctr_nelems = ddp->dd_node ? + (uint_t)ddp->dd_node->dn_value : 0; + + tip->dtt_object = dmp->dm_name; + tip->dtt_ctfp = dmp->dm_ctfp; + tip->dtt_type = ctf_add_array(dmp->dm_ctfp, CTF_ADD_ROOT, &r); + + if (tip->dtt_type == CTF_ERR || + ctf_update(tip->dtt_ctfp) == CTF_ERR) { + xywarn(D_UNKNOWN, "failed to create array type: %s\n", + ctf_errmsg(ctf_errno(tip->dtt_ctfp))); + return (-1); + } + + return (0); + } + + /* + * Allocate space for the type name and enough space for the maximum + * additional text ("unsigned long long \0" requires 20 more bytes). + */ + name = alloca(ddp->dd_name ? strlen(ddp->dd_name) + 20 : 20); + name[0] = '\0'; + + switch (ddp->dd_kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + if (ddp->dd_attr & DT_DA_SIGNED) + (void) strcat(name, "signed "); + if (ddp->dd_attr & DT_DA_UNSIGNED) + (void) strcat(name, "unsigned "); + if (ddp->dd_attr & DT_DA_SHORT) + (void) strcat(name, "short "); + if (ddp->dd_attr & DT_DA_LONG) + (void) strcat(name, "long "); + if (ddp->dd_attr & DT_DA_LONGLONG) + (void) strcat(name, "long long "); + if (ddp->dd_attr == 0 && ddp->dd_name == NULL) + (void) strcat(name, "int"); + break; + case CTF_K_STRUCT: + (void) strcpy(name, "struct "); + break; + case CTF_K_UNION: + (void) strcpy(name, "union "); + break; + case CTF_K_ENUM: + (void) strcpy(name, "enum "); + break; + case CTF_K_TYPEDEF: + break; + default: + xywarn(D_UNKNOWN, "internal error -- " + "bad decl kind %u\n", ddp->dd_kind); + return (-1); + } + + /* + * Add dd_name unless a short, long, or long long is explicitly + * suffixed by int. We use the C/CTF canonical names for integers. + */ + if (ddp->dd_name != NULL && (ddp->dd_kind != CTF_K_INTEGER || + (ddp->dd_attr & (DT_DA_SHORT | DT_DA_LONG | DT_DA_LONGLONG)) == 0)) + (void) strcat(name, ddp->dd_name); + + /* + * Lookup the type. If we find it, we're done. Otherwise create a + * forward tag for the type if it is a struct, union, or enum. If + * we can't find it and we can't create a tag, return failure. + */ + if ((rv = dt_type_lookup(name, tip)) == 0) + return (rv); + + switch (ddp->dd_kind) { + case CTF_K_STRUCT: + case CTF_K_UNION: + case CTF_K_ENUM: + type = ctf_add_forward(dmp->dm_ctfp, flag, + ddp->dd_name, ddp->dd_kind); + break; + default: + xywarn(D_UNKNOWN, "failed to resolve type %s: %s\n", name, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + return (rv); + } + + if (type == CTF_ERR || ctf_update(dmp->dm_ctfp) == CTF_ERR) { + xywarn(D_UNKNOWN, "failed to add forward tag for %s: %s\n", + name, ctf_errmsg(ctf_errno(dmp->dm_ctfp))); + return (-1); + } + + ddp->dd_ctfp = dmp->dm_ctfp; + ddp->dd_type = type; + + tip->dtt_object = dmp->dm_name; + tip->dtt_ctfp = dmp->dm_ctfp; + tip->dtt_type = type; + + return (0); +} + +void +dt_scope_create(dt_scope_t *dsp) +{ + dsp->ds_decl = NULL; + dsp->ds_next = NULL; + dsp->ds_ident = NULL; + dsp->ds_ctfp = NULL; + dsp->ds_type = CTF_ERR; + dsp->ds_class = DT_DC_DEFAULT; + dsp->ds_enumval = -1; +} + +void +dt_scope_destroy(dt_scope_t *dsp) +{ + dt_scope_t *nsp; + + for (; dsp != NULL; dsp = nsp) { + dt_decl_free(dsp->ds_decl); + free(dsp->ds_ident); + nsp = dsp->ds_next; + if (dsp != &yypcb->pcb_dstack) + free(dsp); + } +} + +void +dt_scope_push(ctf_file_t *ctfp, ctf_id_t type) +{ + dt_scope_t *rsp = &yypcb->pcb_dstack; + dt_scope_t *dsp = malloc(sizeof (dt_scope_t)); + + if (dsp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + dsp->ds_decl = rsp->ds_decl; + dsp->ds_next = rsp->ds_next; + dsp->ds_ident = rsp->ds_ident; + dsp->ds_ctfp = ctfp; + dsp->ds_type = type; + dsp->ds_class = rsp->ds_class; + dsp->ds_enumval = rsp->ds_enumval; + + dt_scope_create(rsp); + rsp->ds_next = dsp; +} + +dt_decl_t * +dt_scope_pop(void) +{ + dt_scope_t *rsp = &yypcb->pcb_dstack; + dt_scope_t *dsp = rsp->ds_next; + + if (dsp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOSCOPE); + + if (dsp->ds_ctfp != NULL && ctf_update(dsp->ds_ctfp) == CTF_ERR) { + xyerror(D_UNKNOWN, "failed to update type definitions: %s\n", + ctf_errmsg(ctf_errno(dsp->ds_ctfp))); + } + + dt_decl_free(rsp->ds_decl); + free(rsp->ds_ident); + + rsp->ds_decl = dsp->ds_decl; + rsp->ds_next = dsp->ds_next; + rsp->ds_ident = dsp->ds_ident; + rsp->ds_ctfp = dsp->ds_ctfp; + rsp->ds_type = dsp->ds_type; + rsp->ds_class = dsp->ds_class; + rsp->ds_enumval = dsp->ds_enumval; + + free(dsp); + return (rsp->ds_decl); +} diff --git a/lib/libdtrace/common/dt_decl.h b/lib/libdtrace/common/dt_decl.h new file mode 100644 index 000000000000..2933155c784f --- /dev/null +++ b/lib/libdtrace/common/dt_decl.h @@ -0,0 +1,126 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_DECL_H +#define _DT_DECL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <libctf.h> +#include <dtrace.h> +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct dt_node; /* forward declaration of dt_node_t */ + +typedef struct dt_decl { + ushort_t dd_kind; /* declaration kind (CTF_K_* kind) */ + ushort_t dd_attr; /* attributes (DT_DA_* flags) */ + ctf_file_t *dd_ctfp; /* CTF container for decl's type */ + ctf_id_t dd_type; /* CTF identifier for decl's type */ + char *dd_name; /* string name of this decl (or NULL) */ + struct dt_node *dd_node; /* node for array size or parm list */ + struct dt_decl *dd_next; /* next declaration in list */ +} dt_decl_t; + +#define DT_DA_SIGNED 0x0001 /* signed integer value */ +#define DT_DA_UNSIGNED 0x0002 /* unsigned integer value */ +#define DT_DA_SHORT 0x0004 /* short integer value */ +#define DT_DA_LONG 0x0008 /* long integer or double */ +#define DT_DA_LONGLONG 0x0010 /* long long integer value */ +#define DT_DA_CONST 0x0020 /* qualify type as const */ +#define DT_DA_RESTRICT 0x0040 /* qualify type as restrict */ +#define DT_DA_VOLATILE 0x0080 /* qualify type as volatile */ +#define DT_DA_PAREN 0x0100 /* parenthesis tag */ + +typedef enum dt_dclass { + DT_DC_DEFAULT, /* no storage class specified */ + DT_DC_AUTO, /* automatic storage */ + DT_DC_REGISTER, /* register storage */ + DT_DC_STATIC, /* static storage */ + DT_DC_EXTERN, /* extern storage */ + DT_DC_TYPEDEF, /* type definition */ + DT_DC_SELF, /* thread-local storage */ + DT_DC_THIS /* clause-local storage */ +} dt_dclass_t; + +typedef struct dt_scope { + dt_decl_t *ds_decl; /* pointer to top of decl stack */ + struct dt_scope *ds_next; /* pointer to next scope */ + char *ds_ident; /* identifier for this scope (if any) */ + ctf_file_t *ds_ctfp; /* CTF container for this scope */ + ctf_id_t ds_type; /* CTF id of enclosing type */ + dt_dclass_t ds_class; /* declaration class for this scope */ + int ds_enumval; /* most recent enumerator value */ +} dt_scope_t; + +extern dt_decl_t *dt_decl_alloc(ushort_t, char *); +extern void dt_decl_free(dt_decl_t *); +extern void dt_decl_reset(void); +extern dt_decl_t *dt_decl_push(dt_decl_t *); +extern dt_decl_t *dt_decl_pop(void); +extern dt_decl_t *dt_decl_pop_param(char **); +extern dt_decl_t *dt_decl_top(void); + +extern dt_decl_t *dt_decl_ident(char *); +extern void dt_decl_class(dt_dclass_t); + +#define DT_DP_VARARGS 0x1 /* permit varargs in prototype */ +#define DT_DP_DYNAMIC 0x2 /* permit dynamic type in prototype */ +#define DT_DP_VOID 0x4 /* permit void type in prototype */ +#define DT_DP_ANON 0x8 /* permit anonymous parameters */ + +extern int dt_decl_prototype(struct dt_node *, struct dt_node *, + const char *, uint_t); + +extern dt_decl_t *dt_decl_spec(ushort_t, char *); +extern dt_decl_t *dt_decl_attr(ushort_t); +extern dt_decl_t *dt_decl_array(struct dt_node *); +extern dt_decl_t *dt_decl_func(dt_decl_t *, struct dt_node *); +extern dt_decl_t *dt_decl_ptr(void); + +extern dt_decl_t *dt_decl_sou(uint_t, char *); +extern void dt_decl_member(struct dt_node *); + +extern dt_decl_t *dt_decl_enum(char *); +extern void dt_decl_enumerator(char *, struct dt_node *); + +extern int dt_decl_type(dt_decl_t *, dtrace_typeinfo_t *); + +extern void dt_scope_create(dt_scope_t *); +extern void dt_scope_destroy(dt_scope_t *); +extern void dt_scope_push(ctf_file_t *, ctf_id_t); +extern dt_decl_t *dt_scope_pop(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_DECL_H */ diff --git a/lib/libdtrace/common/dt_dis.c b/lib/libdtrace/common/dt_dis.c new file mode 100644 index 000000000000..f4bb0c4bbae3 --- /dev/null +++ b/lib/libdtrace/common/dt_dis.c @@ -0,0 +1,511 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <strings.h> +#include <stdio.h> + +#include <dt_impl.h> +#include <dt_ident.h> + +/*ARGSUSED*/ +static void +dt_dis_log(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%-4s %%r%u, %%r%u, %%r%u", name, + DIF_INSTR_R1(in), DIF_INSTR_R2(in), DIF_INSTR_RD(in)); +} + +/*ARGSUSED*/ +static void +dt_dis_branch(const dtrace_difo_t *dp, const char *name, + dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%-4s %u", name, DIF_INSTR_LABEL(in)); +} + +/*ARGSUSED*/ +static void +dt_dis_load(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%-4s [%%r%u], %%r%u", name, + DIF_INSTR_R1(in), DIF_INSTR_RD(in)); +} + +/*ARGSUSED*/ +static void +dt_dis_store(const dtrace_difo_t *dp, const char *name, + dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%-4s %%r%u, [%%r%u]", name, + DIF_INSTR_R1(in), DIF_INSTR_RD(in)); +} + +/*ARGSUSED*/ +static void +dt_dis_str(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%s", name); +} + +/*ARGSUSED*/ +static void +dt_dis_r1rd(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%-4s %%r%u, %%r%u", name, + DIF_INSTR_R1(in), DIF_INSTR_RD(in)); +} + +/*ARGSUSED*/ +static void +dt_dis_cmp(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%-4s %%r%u, %%r%u", name, + DIF_INSTR_R1(in), DIF_INSTR_R2(in)); +} + +/*ARGSUSED*/ +static void +dt_dis_tst(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%-4s %%r%u", name, DIF_INSTR_R1(in)); +} + +static const char * +dt_dis_varname(const dtrace_difo_t *dp, uint_t id, uint_t scope) +{ + const dtrace_difv_t *dvp = dp->dtdo_vartab; + uint_t i; + + for (i = 0; i < dp->dtdo_varlen; i++, dvp++) { + if (dvp->dtdv_id == id && dvp->dtdv_scope == scope) { + if (dvp->dtdv_name < dp->dtdo_strlen) + return (dp->dtdo_strtab + dvp->dtdv_name); + break; + } + } + + return (NULL); +} + +static uint_t +dt_dis_scope(const char *name) +{ + switch (name[2]) { + case 'l': return (DIFV_SCOPE_LOCAL); + case 't': return (DIFV_SCOPE_THREAD); + case 'g': return (DIFV_SCOPE_GLOBAL); + default: return (-1u); + } +} + +static void +dt_dis_lda(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + uint_t var = DIF_INSTR_R1(in); + const char *vname; + + (void) fprintf(fp, "%-4s DT_VAR(%u), %%r%u, %%r%u", name, + var, DIF_INSTR_R2(in), DIF_INSTR_RD(in)); + + if ((vname = dt_dis_varname(dp, var, dt_dis_scope(name))) != NULL) + (void) fprintf(fp, "\t\t! DT_VAR(%u) = \"%s\"", var, vname); +} + +static void +dt_dis_ldv(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + uint_t var = DIF_INSTR_VAR(in); + const char *vname; + + (void) fprintf(fp, "%-4s DT_VAR(%u), %%r%u", + name, var, DIF_INSTR_RD(in)); + + if ((vname = dt_dis_varname(dp, var, dt_dis_scope(name))) != NULL) + (void) fprintf(fp, "\t\t! DT_VAR(%u) = \"%s\"", var, vname); +} + +static void +dt_dis_stv(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + uint_t var = DIF_INSTR_VAR(in); + const char *vname; + + (void) fprintf(fp, "%-4s %%r%u, DT_VAR(%u)", + name, DIF_INSTR_RS(in), var); + + if ((vname = dt_dis_varname(dp, var, dt_dis_scope(name))) != NULL) + (void) fprintf(fp, "\t\t! DT_VAR(%u) = \"%s\"", var, vname); +} + +static void +dt_dis_setx(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + uint_t intptr = DIF_INSTR_INTEGER(in); + + (void) fprintf(fp, "%-4s DT_INTEGER[%u], %%r%u", name, + intptr, DIF_INSTR_RD(in)); + + if (intptr < dp->dtdo_intlen) { + (void) fprintf(fp, "\t\t! 0x%llx", + (u_longlong_t)dp->dtdo_inttab[intptr]); + } +} + +static void +dt_dis_sets(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + uint_t strptr = DIF_INSTR_STRING(in); + + (void) fprintf(fp, "%-4s DT_STRING[%u], %%r%u", name, + strptr, DIF_INSTR_RD(in)); + + if (strptr < dp->dtdo_strlen) + (void) fprintf(fp, "\t\t! \"%s\"", dp->dtdo_strtab + strptr); +} + +/*ARGSUSED*/ +static void +dt_dis_ret(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + (void) fprintf(fp, "%-4s %%r%u", name, DIF_INSTR_RD(in)); +} + +/*ARGSUSED*/ +static void +dt_dis_call(const dtrace_difo_t *dp, const char *name, dif_instr_t in, FILE *fp) +{ + uint_t subr = DIF_INSTR_SUBR(in); + + (void) fprintf(fp, "%-4s DIF_SUBR(%u), %%r%u\t\t! %s", + name, subr, DIF_INSTR_RD(in), dtrace_subrstr(NULL, subr)); +} + +/*ARGSUSED*/ +static void +dt_dis_pushts(const dtrace_difo_t *dp, + const char *name, dif_instr_t in, FILE *fp) +{ + static const char *const tnames[] = { "D type", "string" }; + uint_t type = DIF_INSTR_TYPE(in); + + (void) fprintf(fp, "%-4s DT_TYPE(%u), %%r%u, %%r%u", + name, type, DIF_INSTR_R2(in), DIF_INSTR_RS(in)); + + if (type < sizeof (tnames) / sizeof (tnames[0])) + (void) fprintf(fp, "\t! DT_TYPE(%u) = %s", type, tnames[type]); +} + +static void +dt_dis_xlate(const dtrace_difo_t *dp, + const char *name, dif_instr_t in, FILE *fp) +{ + uint_t xlr = DIF_INSTR_XLREF(in); + + (void) fprintf(fp, "%-4s DT_XLREF[%u], %%r%u", + name, xlr, DIF_INSTR_RD(in)); + + if (xlr < dp->dtdo_xlmlen) { + (void) fprintf(fp, "\t\t! DT_XLREF[%u] = %u.%s", xlr, + (uint_t)dp->dtdo_xlmtab[xlr]->dn_membexpr->dn_xlator->dx_id, + dp->dtdo_xlmtab[xlr]->dn_membname); + } +} + +static char * +dt_dis_typestr(const dtrace_diftype_t *t, char *buf, size_t len) +{ + char kind[16], ckind[16]; + + switch (t->dtdt_kind) { + case DIF_TYPE_CTF: + (void) strcpy(kind, "D type"); + break; + case DIF_TYPE_STRING: + (void) strcpy(kind, "string"); + break; + default: + (void) snprintf(kind, sizeof (kind), "0x%x", t->dtdt_kind); + } + + switch (t->dtdt_ckind) { + case CTF_K_UNKNOWN: + (void) strcpy(ckind, "unknown"); + break; + case CTF_K_INTEGER: + (void) strcpy(ckind, "integer"); + break; + case CTF_K_FLOAT: + (void) strcpy(ckind, "float"); + break; + case CTF_K_POINTER: + (void) strcpy(ckind, "pointer"); + break; + case CTF_K_ARRAY: + (void) strcpy(ckind, "array"); + break; + case CTF_K_FUNCTION: + (void) strcpy(ckind, "function"); + break; + case CTF_K_STRUCT: + (void) strcpy(ckind, "struct"); + break; + case CTF_K_UNION: + (void) strcpy(ckind, "union"); + break; + case CTF_K_ENUM: + (void) strcpy(ckind, "enum"); + break; + case CTF_K_FORWARD: + (void) strcpy(ckind, "forward"); + break; + case CTF_K_TYPEDEF: + (void) strcpy(ckind, "typedef"); + break; + case CTF_K_VOLATILE: + (void) strcpy(ckind, "volatile"); + break; + case CTF_K_CONST: + (void) strcpy(ckind, "const"); + break; + case CTF_K_RESTRICT: + (void) strcpy(ckind, "restrict"); + break; + default: + (void) snprintf(ckind, sizeof (ckind), "0x%x", t->dtdt_ckind); + } + + if (t->dtdt_flags & DIF_TF_BYREF) { + (void) snprintf(buf, len, "%s (%s) by ref (size %lu)", + kind, ckind, (ulong_t)t->dtdt_size); + } else { + (void) snprintf(buf, len, "%s (%s) (size %lu)", + kind, ckind, (ulong_t)t->dtdt_size); + } + + return (buf); +} + +static void +dt_dis_rtab(const char *rtag, const dtrace_difo_t *dp, FILE *fp, + const dof_relodesc_t *rp, uint32_t len) +{ + (void) fprintf(fp, "\n%-4s %-8s %-8s %s\n", + rtag, "OFFSET", "DATA", "NAME"); + + for (; len != 0; len--, rp++) { + (void) fprintf(fp, "%-4u %-8llu %-8llu %s\n", + rp->dofr_type, (u_longlong_t)rp->dofr_offset, + (u_longlong_t)rp->dofr_data, + &dp->dtdo_strtab[rp->dofr_name]); + } +} + +void +dt_dis(const dtrace_difo_t *dp, FILE *fp) +{ + static const struct opent { + const char *op_name; + void (*op_func)(const dtrace_difo_t *, const char *, + dif_instr_t, FILE *); + } optab[] = { + { "(illegal opcode)", dt_dis_str }, + { "or", dt_dis_log }, /* DIF_OP_OR */ + { "xor", dt_dis_log }, /* DIF_OP_XOR */ + { "and", dt_dis_log }, /* DIF_OP_AND */ + { "sll", dt_dis_log }, /* DIF_OP_SLL */ + { "srl", dt_dis_log }, /* DIF_OP_SRL */ + { "sub", dt_dis_log }, /* DIF_OP_SUB */ + { "add", dt_dis_log }, /* DIF_OP_ADD */ + { "mul", dt_dis_log }, /* DIF_OP_MUL */ + { "sdiv", dt_dis_log }, /* DIF_OP_SDIV */ + { "udiv", dt_dis_log }, /* DIF_OP_UDIV */ + { "srem", dt_dis_log }, /* DIF_OP_SREM */ + { "urem", dt_dis_log }, /* DIF_OP_UREM */ + { "not", dt_dis_r1rd }, /* DIF_OP_NOT */ + { "mov", dt_dis_r1rd }, /* DIF_OP_MOV */ + { "cmp", dt_dis_cmp }, /* DIF_OP_CMP */ + { "tst", dt_dis_tst }, /* DIF_OP_TST */ + { "ba", dt_dis_branch }, /* DIF_OP_BA */ + { "be", dt_dis_branch }, /* DIF_OP_BE */ + { "bne", dt_dis_branch }, /* DIF_OP_BNE */ + { "bg", dt_dis_branch }, /* DIF_OP_BG */ + { "bgu", dt_dis_branch }, /* DIF_OP_BGU */ + { "bge", dt_dis_branch }, /* DIF_OP_BGE */ + { "bgeu", dt_dis_branch }, /* DIF_OP_BGEU */ + { "bl", dt_dis_branch }, /* DIF_OP_BL */ + { "blu", dt_dis_branch }, /* DIF_OP_BLU */ + { "ble", dt_dis_branch }, /* DIF_OP_BLE */ + { "bleu", dt_dis_branch }, /* DIF_OP_BLEU */ + { "ldsb", dt_dis_load }, /* DIF_OP_LDSB */ + { "ldsh", dt_dis_load }, /* DIF_OP_LDSH */ + { "ldsw", dt_dis_load }, /* DIF_OP_LDSW */ + { "ldub", dt_dis_load }, /* DIF_OP_LDUB */ + { "lduh", dt_dis_load }, /* DIF_OP_LDUH */ + { "lduw", dt_dis_load }, /* DIF_OP_LDUW */ + { "ldx", dt_dis_load }, /* DIF_OP_LDX */ + { "ret", dt_dis_ret }, /* DIF_OP_RET */ + { "nop", dt_dis_str }, /* DIF_OP_NOP */ + { "setx", dt_dis_setx }, /* DIF_OP_SETX */ + { "sets", dt_dis_sets }, /* DIF_OP_SETS */ + { "scmp", dt_dis_cmp }, /* DIF_OP_SCMP */ + { "ldga", dt_dis_lda }, /* DIF_OP_LDGA */ + { "ldgs", dt_dis_ldv }, /* DIF_OP_LDGS */ + { "stgs", dt_dis_stv }, /* DIF_OP_STGS */ + { "ldta", dt_dis_lda }, /* DIF_OP_LDTA */ + { "ldts", dt_dis_ldv }, /* DIF_OP_LDTS */ + { "stts", dt_dis_stv }, /* DIF_OP_STTS */ + { "sra", dt_dis_log }, /* DIF_OP_SRA */ + { "call", dt_dis_call }, /* DIF_OP_CALL */ + { "pushtr", dt_dis_pushts }, /* DIF_OP_PUSHTR */ + { "pushtv", dt_dis_pushts }, /* DIF_OP_PUSHTV */ + { "popts", dt_dis_str }, /* DIF_OP_POPTS */ + { "flushts", dt_dis_str }, /* DIF_OP_FLUSHTS */ + { "ldgaa", dt_dis_ldv }, /* DIF_OP_LDGAA */ + { "ldtaa", dt_dis_ldv }, /* DIF_OP_LDTAA */ + { "stgaa", dt_dis_stv }, /* DIF_OP_STGAA */ + { "sttaa", dt_dis_stv }, /* DIF_OP_STTAA */ + { "ldls", dt_dis_ldv }, /* DIF_OP_LDLS */ + { "stls", dt_dis_stv }, /* DIF_OP_STLS */ + { "allocs", dt_dis_r1rd }, /* DIF_OP_ALLOCS */ + { "copys", dt_dis_log }, /* DIF_OP_COPYS */ + { "stb", dt_dis_store }, /* DIF_OP_STB */ + { "sth", dt_dis_store }, /* DIF_OP_STH */ + { "stw", dt_dis_store }, /* DIF_OP_STW */ + { "stx", dt_dis_store }, /* DIF_OP_STX */ + { "uldsb", dt_dis_load }, /* DIF_OP_ULDSB */ + { "uldsh", dt_dis_load }, /* DIF_OP_ULDSH */ + { "uldsw", dt_dis_load }, /* DIF_OP_ULDSW */ + { "uldub", dt_dis_load }, /* DIF_OP_ULDUB */ + { "ulduh", dt_dis_load }, /* DIF_OP_ULDUH */ + { "ulduw", dt_dis_load }, /* DIF_OP_ULDUW */ + { "uldx", dt_dis_load }, /* DIF_OP_ULDX */ + { "rldsb", dt_dis_load }, /* DIF_OP_RLDSB */ + { "rldsh", dt_dis_load }, /* DIF_OP_RLDSH */ + { "rldsw", dt_dis_load }, /* DIF_OP_RLDSW */ + { "rldub", dt_dis_load }, /* DIF_OP_RLDUB */ + { "rlduh", dt_dis_load }, /* DIF_OP_RLDUH */ + { "rlduw", dt_dis_load }, /* DIF_OP_RLDUW */ + { "rldx", dt_dis_load }, /* DIF_OP_RLDX */ + { "xlate", dt_dis_xlate }, /* DIF_OP_XLATE */ + { "xlarg", dt_dis_xlate }, /* DIF_OP_XLARG */ + }; + + const struct opent *op; + ulong_t i = 0; + char type[DT_TYPE_NAMELEN]; + + (void) fprintf(fp, "\nDIFO 0x%p returns %s\n", (void *)dp, + dt_dis_typestr(&dp->dtdo_rtype, type, sizeof (type))); + + (void) fprintf(fp, "%-3s %-8s %s\n", + "OFF", "OPCODE", "INSTRUCTION"); + + for (i = 0; i < dp->dtdo_len; i++) { + dif_instr_t instr = dp->dtdo_buf[i]; + dif_instr_t opcode = DIF_INSTR_OP(instr); + + if (opcode >= sizeof (optab) / sizeof (optab[0])) + opcode = 0; /* force invalid opcode message */ + + op = &optab[opcode]; + (void) fprintf(fp, "%02lu: %08x ", i, instr); + op->op_func(dp, op->op_name, instr, fp); + (void) fprintf(fp, "\n"); + } + + if (dp->dtdo_varlen != 0) { + (void) fprintf(fp, "\n%-16s %-4s %-3s %-3s %-4s %s\n", + "NAME", "ID", "KND", "SCP", "FLAG", "TYPE"); + } + + for (i = 0; i < dp->dtdo_varlen; i++) { + dtrace_difv_t *v = &dp->dtdo_vartab[i]; + char kind[4], scope[4], flags[16] = { 0 }; + + switch (v->dtdv_kind) { + case DIFV_KIND_ARRAY: + (void) strcpy(kind, "arr"); + break; + case DIFV_KIND_SCALAR: + (void) strcpy(kind, "scl"); + break; + default: + (void) snprintf(kind, sizeof (kind), + "%u", v->dtdv_kind); + } + + switch (v->dtdv_scope) { + case DIFV_SCOPE_GLOBAL: + (void) strcpy(scope, "glb"); + break; + case DIFV_SCOPE_THREAD: + (void) strcpy(scope, "tls"); + break; + case DIFV_SCOPE_LOCAL: + (void) strcpy(scope, "loc"); + break; + default: + (void) snprintf(scope, sizeof (scope), + "%u", v->dtdv_scope); + } + + if (v->dtdv_flags & ~(DIFV_F_REF | DIFV_F_MOD)) { + (void) snprintf(flags, sizeof (flags), "/0x%x", + v->dtdv_flags & ~(DIFV_F_REF | DIFV_F_MOD)); + } + + if (v->dtdv_flags & DIFV_F_REF) + (void) strcat(flags, "/r"); + if (v->dtdv_flags & DIFV_F_MOD) + (void) strcat(flags, "/w"); + + (void) fprintf(fp, "%-16s %-4x %-3s %-3s %-4s %s\n", + &dp->dtdo_strtab[v->dtdv_name], + v->dtdv_id, kind, scope, flags + 1, + dt_dis_typestr(&v->dtdv_type, type, sizeof (type))); + } + + if (dp->dtdo_xlmlen != 0) { + (void) fprintf(fp, "\n%-4s %-3s %-12s %s\n", + "XLID", "ARG", "MEMBER", "TYPE"); + } + + for (i = 0; i < dp->dtdo_xlmlen; i++) { + dt_node_t *dnp = dp->dtdo_xlmtab[i]; + dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator; + (void) fprintf(fp, "%-4u %-3d %-12s %s\n", + (uint_t)dxp->dx_id, dxp->dx_arg, dnp->dn_membname, + dt_node_type_name(dnp, type, sizeof (type))); + } + + if (dp->dtdo_krelen != 0) + dt_dis_rtab("KREL", dp, fp, dp->dtdo_kreltab, dp->dtdo_krelen); + + if (dp->dtdo_urelen != 0) + dt_dis_rtab("UREL", dp, fp, dp->dtdo_ureltab, dp->dtdo_urelen); +} diff --git a/lib/libdtrace/common/dt_dof.c b/lib/libdtrace/common/dt_dof.c new file mode 100644 index 000000000000..f35a386c5d7d --- /dev/null +++ b/lib/libdtrace/common/dt_dof.c @@ -0,0 +1,969 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#if defined(sun) +#include <sys/sysmacros.h> +#endif + +#include <strings.h> +#if defined(sun) +#include <alloca.h> +#endif +#include <assert.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> + +#include <dt_impl.h> +#include <dt_strtab.h> +#include <dt_program.h> +#include <dt_provider.h> +#include <dt_xlator.h> +#include <dt_dof.h> + +void +dt_dof_init(dtrace_hdl_t *dtp) +{ + dt_dof_t *ddo = &dtp->dt_dof; + + ddo->ddo_hdl = dtp; + ddo->ddo_nsecs = 0; + ddo->ddo_strsec = DOF_SECIDX_NONE; + ddo->ddo_xlimport = NULL; + ddo->ddo_xlexport = NULL; + + dt_buf_create(dtp, &ddo->ddo_secs, "section headers", 0); + dt_buf_create(dtp, &ddo->ddo_strs, "string table", 0); + dt_buf_create(dtp, &ddo->ddo_ldata, "loadable data", 0); + dt_buf_create(dtp, &ddo->ddo_udata, "unloadable data", 0); + + dt_buf_create(dtp, &ddo->ddo_probes, "probe data", 0); + dt_buf_create(dtp, &ddo->ddo_args, "probe args", 0); + dt_buf_create(dtp, &ddo->ddo_offs, "probe offs", 0); + dt_buf_create(dtp, &ddo->ddo_enoffs, "probe is-enabled offs", 0); + dt_buf_create(dtp, &ddo->ddo_rels, "probe rels", 0); + + dt_buf_create(dtp, &ddo->ddo_xlms, "xlate members", 0); +} + +void +dt_dof_fini(dtrace_hdl_t *dtp) +{ + dt_dof_t *ddo = &dtp->dt_dof; + + dt_free(dtp, ddo->ddo_xlimport); + dt_free(dtp, ddo->ddo_xlexport); + + dt_buf_destroy(dtp, &ddo->ddo_secs); + dt_buf_destroy(dtp, &ddo->ddo_strs); + dt_buf_destroy(dtp, &ddo->ddo_ldata); + dt_buf_destroy(dtp, &ddo->ddo_udata); + + dt_buf_destroy(dtp, &ddo->ddo_probes); + dt_buf_destroy(dtp, &ddo->ddo_args); + dt_buf_destroy(dtp, &ddo->ddo_offs); + dt_buf_destroy(dtp, &ddo->ddo_enoffs); + dt_buf_destroy(dtp, &ddo->ddo_rels); + + dt_buf_destroy(dtp, &ddo->ddo_xlms); +} + +static int +dt_dof_reset(dtrace_hdl_t *dtp, dtrace_prog_t *pgp) +{ + dt_dof_t *ddo = &dtp->dt_dof; + uint_t i, nx = dtp->dt_xlatorid; + + assert(ddo->ddo_hdl == dtp); + ddo->ddo_pgp = pgp; + + ddo->ddo_nsecs = 0; + ddo->ddo_strsec = DOF_SECIDX_NONE; + + dt_free(dtp, ddo->ddo_xlimport); + dt_free(dtp, ddo->ddo_xlexport); + + ddo->ddo_xlimport = dt_alloc(dtp, sizeof (dof_secidx_t) * nx); + ddo->ddo_xlexport = dt_alloc(dtp, sizeof (dof_secidx_t) * nx); + + if (nx != 0 && (ddo->ddo_xlimport == NULL || ddo->ddo_xlexport == NULL)) + return (-1); /* errno is set for us */ + + for (i = 0; i < nx; i++) { + ddo->ddo_xlimport[i] = DOF_SECIDX_NONE; + ddo->ddo_xlexport[i] = DOF_SECIDX_NONE; + } + + dt_buf_reset(dtp, &ddo->ddo_secs); + dt_buf_reset(dtp, &ddo->ddo_strs); + dt_buf_reset(dtp, &ddo->ddo_ldata); + dt_buf_reset(dtp, &ddo->ddo_udata); + + dt_buf_reset(dtp, &ddo->ddo_probes); + dt_buf_reset(dtp, &ddo->ddo_args); + dt_buf_reset(dtp, &ddo->ddo_offs); + dt_buf_reset(dtp, &ddo->ddo_enoffs); + dt_buf_reset(dtp, &ddo->ddo_rels); + + dt_buf_reset(dtp, &ddo->ddo_xlms); + return (0); +} + +/* + * Add a loadable DOF section to the file using the specified data buffer and + * the specified DOF section attributes. DOF_SECF_LOAD must be set in flags. + * If 'data' is NULL, the caller is responsible for manipulating the ldata buf. + */ +static dof_secidx_t +dof_add_lsect(dt_dof_t *ddo, const void *data, uint32_t type, + uint32_t align, uint32_t flags, uint32_t entsize, uint64_t size) +{ + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dof_sec_t s; + + s.dofs_type = type; + s.dofs_align = align; + s.dofs_flags = flags | DOF_SECF_LOAD; + s.dofs_entsize = entsize; + s.dofs_offset = dt_buf_offset(&ddo->ddo_ldata, align); + s.dofs_size = size; + + dt_buf_write(dtp, &ddo->ddo_secs, &s, sizeof (s), sizeof (uint64_t)); + + if (data != NULL) + dt_buf_write(dtp, &ddo->ddo_ldata, data, size, align); + + return (ddo->ddo_nsecs++); +} + +/* + * Add an unloadable DOF section to the file using the specified data buffer + * and DOF section attributes. DOF_SECF_LOAD must *not* be set in flags. + * If 'data' is NULL, the caller is responsible for manipulating the udata buf. + */ +static dof_secidx_t +dof_add_usect(dt_dof_t *ddo, const void *data, uint32_t type, + uint32_t align, uint32_t flags, uint32_t entsize, uint64_t size) +{ + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dof_sec_t s; + + s.dofs_type = type; + s.dofs_align = align; + s.dofs_flags = flags & ~DOF_SECF_LOAD; + s.dofs_entsize = entsize; + s.dofs_offset = dt_buf_offset(&ddo->ddo_udata, align); + s.dofs_size = size; + + dt_buf_write(dtp, &ddo->ddo_secs, &s, sizeof (s), sizeof (uint64_t)); + + if (data != NULL) + dt_buf_write(dtp, &ddo->ddo_udata, data, size, align); + + return (ddo->ddo_nsecs++); +} + +/* + * Add a string to the global string table associated with the DOF. The offset + * of the string is returned as an index into the string table. + */ +static dof_stridx_t +dof_add_string(dt_dof_t *ddo, const char *s) +{ + dt_buf_t *bp = &ddo->ddo_strs; + dof_stridx_t i = dt_buf_len(bp); + + if (i != 0 && (s == NULL || *s == '\0')) + return (0); /* string table has \0 at offset 0 */ + + dt_buf_write(ddo->ddo_hdl, bp, s, strlen(s) + 1, sizeof (char)); + return (i); +} + +static dof_attr_t +dof_attr(const dtrace_attribute_t *ap) +{ + return (DOF_ATTR(ap->dtat_name, ap->dtat_data, ap->dtat_class)); +} + +static dof_secidx_t +dof_add_difo(dt_dof_t *ddo, const dtrace_difo_t *dp) +{ + dof_secidx_t dsecs[5]; /* enough for all possible DIFO sections */ + uint_t nsecs = 0; + + dof_difohdr_t *dofd; + dof_relohdr_t dofr; + dof_secidx_t relsec; + + dof_secidx_t strsec = DOF_SECIDX_NONE; + dof_secidx_t intsec = DOF_SECIDX_NONE; + dof_secidx_t hdrsec = DOF_SECIDX_NONE; + + if (dp->dtdo_buf != NULL) { + dsecs[nsecs++] = dof_add_lsect(ddo, dp->dtdo_buf, + DOF_SECT_DIF, sizeof (dif_instr_t), 0, + sizeof (dif_instr_t), sizeof (dif_instr_t) * dp->dtdo_len); + } + + if (dp->dtdo_inttab != NULL) { + dsecs[nsecs++] = intsec = dof_add_lsect(ddo, dp->dtdo_inttab, + DOF_SECT_INTTAB, sizeof (uint64_t), 0, + sizeof (uint64_t), sizeof (uint64_t) * dp->dtdo_intlen); + } + + if (dp->dtdo_strtab != NULL) { + dsecs[nsecs++] = strsec = dof_add_lsect(ddo, dp->dtdo_strtab, + DOF_SECT_STRTAB, sizeof (char), 0, 0, dp->dtdo_strlen); + } + + if (dp->dtdo_vartab != NULL) { + dsecs[nsecs++] = dof_add_lsect(ddo, dp->dtdo_vartab, + DOF_SECT_VARTAB, sizeof (uint_t), 0, sizeof (dtrace_difv_t), + sizeof (dtrace_difv_t) * dp->dtdo_varlen); + } + + if (dp->dtdo_xlmtab != NULL) { + dof_xlref_t *xlt, *xlp; + dt_node_t **pnp; + + xlt = alloca(sizeof (dof_xlref_t) * dp->dtdo_xlmlen); + pnp = dp->dtdo_xlmtab; + + /* + * dtdo_xlmtab contains pointers to the translator members. + * The translator itself is in sect ddo_xlimport[dxp->dx_id]. + * The XLMEMBERS entries are in order by their dn_membid, so + * the member section offset is the population count of bits + * in ddo_pgp->dp_xlrefs[] up to and not including dn_membid. + */ + for (xlp = xlt; xlp < xlt + dp->dtdo_xlmlen; xlp++) { + dt_node_t *dnp = *pnp++; + dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator; + + xlp->dofxr_xlator = ddo->ddo_xlimport[dxp->dx_id]; + xlp->dofxr_member = dt_popcb( + ddo->ddo_pgp->dp_xrefs[dxp->dx_id], dnp->dn_membid); + xlp->dofxr_argn = (uint32_t)dxp->dx_arg; + } + + dsecs[nsecs++] = dof_add_lsect(ddo, xlt, DOF_SECT_XLTAB, + sizeof (dof_secidx_t), 0, sizeof (dof_xlref_t), + sizeof (dof_xlref_t) * dp->dtdo_xlmlen); + } + + /* + * Copy the return type and the array of section indices that form the + * DIFO into a single dof_difohdr_t and then add DOF_SECT_DIFOHDR. + */ + assert(nsecs <= sizeof (dsecs) / sizeof (dsecs[0])); + dofd = alloca(sizeof (dtrace_diftype_t) + sizeof (dsecs)); + bcopy(&dp->dtdo_rtype, &dofd->dofd_rtype, sizeof (dtrace_diftype_t)); + bcopy(dsecs, &dofd->dofd_links, sizeof (dof_secidx_t) * nsecs); + + hdrsec = dof_add_lsect(ddo, dofd, DOF_SECT_DIFOHDR, + sizeof (dof_secidx_t), 0, 0, + sizeof (dtrace_diftype_t) + sizeof (dof_secidx_t) * nsecs); + + /* + * Add any other sections related to dtrace_difo_t. These are not + * referenced in dof_difohdr_t because they are not used by emulation. + */ + if (dp->dtdo_kreltab != NULL) { + relsec = dof_add_lsect(ddo, dp->dtdo_kreltab, DOF_SECT_RELTAB, + sizeof (uint64_t), 0, sizeof (dof_relodesc_t), + sizeof (dof_relodesc_t) * dp->dtdo_krelen); + + /* + * This code assumes the target of all relocations is the + * integer table 'intsec' (DOF_SECT_INTTAB). If other sections + * need relocation in the future this will need to change. + */ + dofr.dofr_strtab = strsec; + dofr.dofr_relsec = relsec; + dofr.dofr_tgtsec = intsec; + + (void) dof_add_lsect(ddo, &dofr, DOF_SECT_KRELHDR, + sizeof (dof_secidx_t), 0, 0, sizeof (dof_relohdr_t)); + } + + if (dp->dtdo_ureltab != NULL) { + relsec = dof_add_lsect(ddo, dp->dtdo_ureltab, DOF_SECT_RELTAB, + sizeof (uint64_t), 0, sizeof (dof_relodesc_t), + sizeof (dof_relodesc_t) * dp->dtdo_urelen); + + /* + * This code assumes the target of all relocations is the + * integer table 'intsec' (DOF_SECT_INTTAB). If other sections + * need relocation in the future this will need to change. + */ + dofr.dofr_strtab = strsec; + dofr.dofr_relsec = relsec; + dofr.dofr_tgtsec = intsec; + + (void) dof_add_lsect(ddo, &dofr, DOF_SECT_URELHDR, + sizeof (dof_secidx_t), 0, 0, sizeof (dof_relohdr_t)); + } + + return (hdrsec); +} + +static void +dof_add_translator(dt_dof_t *ddo, const dt_xlator_t *dxp, uint_t type) +{ + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dof_xlmember_t dofxm; + dof_xlator_t dofxl; + dof_secidx_t *xst; + + char buf[DT_TYPE_NAMELEN]; + dt_node_t *dnp; + uint_t i = 0; + + assert(type == DOF_SECT_XLIMPORT || type == DOF_SECT_XLEXPORT); + xst = type == DOF_SECT_XLIMPORT ? ddo->ddo_xlimport : ddo->ddo_xlexport; + + if (xst[dxp->dx_id] != DOF_SECIDX_NONE) + return; /* translator has already been emitted */ + + dt_buf_reset(dtp, &ddo->ddo_xlms); + + /* + * Generate an array of dof_xlmember_t's into ddo_xlms. If we are + * importing the translator, add only those members referenced by the + * program and set the dofxm_difo reference of each member to NONE. If + * we're exporting the translator, add all members and a DIFO for each. + */ + for (dnp = dxp->dx_members; dnp != NULL; dnp = dnp->dn_list, i++) { + if (type == DOF_SECT_XLIMPORT) { + if (!BT_TEST(ddo->ddo_pgp->dp_xrefs[dxp->dx_id], i)) + continue; /* member is not referenced */ + dofxm.dofxm_difo = DOF_SECIDX_NONE; + } else { + dofxm.dofxm_difo = dof_add_difo(ddo, + dxp->dx_membdif[dnp->dn_membid]); + } + + dofxm.dofxm_name = dof_add_string(ddo, dnp->dn_membname); + dt_node_diftype(dtp, dnp, &dofxm.dofxm_type); + + dt_buf_write(dtp, &ddo->ddo_xlms, + &dofxm, sizeof (dofxm), sizeof (uint32_t)); + } + + dofxl.dofxl_members = dof_add_lsect(ddo, NULL, DOF_SECT_XLMEMBERS, + sizeof (uint32_t), 0, sizeof (dofxm), dt_buf_len(&ddo->ddo_xlms)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_xlms, sizeof (uint32_t)); + + dofxl.dofxl_strtab = ddo->ddo_strsec; + dofxl.dofxl_argv = dof_add_string(ddo, ctf_type_name( + dxp->dx_src_ctfp, dxp->dx_src_type, buf, sizeof (buf))); + dofxl.dofxl_argc = 1; + dofxl.dofxl_type = dof_add_string(ddo, ctf_type_name( + dxp->dx_dst_ctfp, dxp->dx_dst_type, buf, sizeof (buf))); + dofxl.dofxl_attr = dof_attr(&dxp->dx_souid.di_attr); + + xst[dxp->dx_id] = dof_add_lsect(ddo, &dofxl, type, + sizeof (uint32_t), 0, 0, sizeof (dofxl)); +} + +/*ARGSUSED*/ +static int +dof_add_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data) +{ + dt_dof_t *ddo = data; + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dt_probe_t *prp = idp->di_data; + + dof_probe_t dofpr; + dof_relodesc_t dofr; + dt_probe_instance_t *pip; + dt_node_t *dnp; + + char buf[DT_TYPE_NAMELEN]; + uint_t i; + + dofpr.dofpr_addr = 0; + dofpr.dofpr_name = dof_add_string(ddo, prp->pr_name); + dofpr.dofpr_nargv = dt_buf_len(&ddo->ddo_strs); + + for (dnp = prp->pr_nargs; dnp != NULL; dnp = dnp->dn_list) { + (void) dof_add_string(ddo, ctf_type_name(dnp->dn_ctfp, + dnp->dn_type, buf, sizeof (buf))); + } + + dofpr.dofpr_xargv = dt_buf_len(&ddo->ddo_strs); + + for (dnp = prp->pr_xargs; dnp != NULL; dnp = dnp->dn_list) { + (void) dof_add_string(ddo, ctf_type_name(dnp->dn_ctfp, + dnp->dn_type, buf, sizeof (buf))); + } + + dofpr.dofpr_argidx = dt_buf_len(&ddo->ddo_args) / sizeof (uint8_t); + + for (i = 0; i < prp->pr_xargc; i++) { + dt_buf_write(dtp, &ddo->ddo_args, &prp->pr_mapping[i], + sizeof (uint8_t), sizeof (uint8_t)); + } + + dofpr.dofpr_nargc = prp->pr_nargc; + dofpr.dofpr_xargc = prp->pr_xargc; + dofpr.dofpr_pad1 = 0; + dofpr.dofpr_pad2 = 0; + + for (pip = prp->pr_inst; pip != NULL; pip = pip->pi_next) { + dt_dprintf("adding probe for %s:%s\n", pip->pi_fname, + prp->pr_name); + + dofpr.dofpr_func = dof_add_string(ddo, pip->pi_fname); + + /* + * There should be one probe offset or is-enabled probe offset + * or else this probe instance won't have been created. The + * kernel will reject DOF which has a probe with no offsets. + */ + assert(pip->pi_noffs + pip->pi_nenoffs > 0); + + dofpr.dofpr_offidx = + dt_buf_len(&ddo->ddo_offs) / sizeof (uint32_t); + dofpr.dofpr_noffs = pip->pi_noffs; + dt_buf_write(dtp, &ddo->ddo_offs, pip->pi_offs, + pip->pi_noffs * sizeof (uint32_t), sizeof (uint32_t)); + + dofpr.dofpr_enoffidx = + dt_buf_len(&ddo->ddo_enoffs) / sizeof (uint32_t); + dofpr.dofpr_nenoffs = pip->pi_nenoffs; + dt_buf_write(dtp, &ddo->ddo_enoffs, pip->pi_enoffs, + pip->pi_nenoffs * sizeof (uint32_t), sizeof (uint32_t)); + + /* + * If pi_rname isn't set, the relocation will be against the + * function name. If it is, the relocation will be against + * pi_rname. This will be used if the function is scoped + * locally so an alternate symbol is added for the purpose + * of this relocation. + */ + if (pip->pi_rname[0] == '\0') + dofr.dofr_name = dofpr.dofpr_func; + else + dofr.dofr_name = dof_add_string(ddo, pip->pi_rname); + dofr.dofr_type = DOF_RELO_SETX; + dofr.dofr_offset = dt_buf_len(&ddo->ddo_probes); + dofr.dofr_data = 0; + + dt_buf_write(dtp, &ddo->ddo_rels, &dofr, + sizeof (dofr), sizeof (uint64_t)); + + dt_buf_write(dtp, &ddo->ddo_probes, &dofpr, + sizeof (dofpr), sizeof (uint64_t)); + } + + return (0); +} + +static void +dof_add_provider(dt_dof_t *ddo, const dt_provider_t *pvp) +{ + dtrace_hdl_t *dtp = ddo->ddo_hdl; + dof_provider_t dofpv; + dof_relohdr_t dofr; + dof_secidx_t *dofs; + ulong_t xr, nxr; + size_t sz; + id_t i; + + if (pvp->pv_flags & DT_PROVIDER_IMPL) + return; /* ignore providers that are exported by dtrace(7D) */ + + nxr = dt_popcb(pvp->pv_xrefs, pvp->pv_xrmax); + dofs = alloca(sizeof (dof_secidx_t) * (nxr + 1)); + xr = 1; /* reserve dofs[0] for the provider itself */ + + /* + * For each translator referenced by the provider (pv_xrefs), emit an + * exported translator section for it if one hasn't been created yet. + */ + for (i = 0; i < pvp->pv_xrmax; i++) { + if (BT_TEST(pvp->pv_xrefs, i) && + dtp->dt_xlatemode == DT_XL_DYNAMIC) { + dof_add_translator(ddo, + dt_xlator_lookup_id(dtp, i), DOF_SECT_XLEXPORT); + dofs[xr++] = ddo->ddo_xlexport[i]; + } + } + + dt_buf_reset(dtp, &ddo->ddo_probes); + dt_buf_reset(dtp, &ddo->ddo_args); + dt_buf_reset(dtp, &ddo->ddo_offs); + dt_buf_reset(dtp, &ddo->ddo_enoffs); + dt_buf_reset(dtp, &ddo->ddo_rels); + + (void) dt_idhash_iter(pvp->pv_probes, dof_add_probe, ddo); + + dofpv.dofpv_probes = dof_add_lsect(ddo, NULL, DOF_SECT_PROBES, + sizeof (uint64_t), 0, sizeof (dof_probe_t), + dt_buf_len(&ddo->ddo_probes)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, + &ddo->ddo_probes, sizeof (uint64_t)); + + dofpv.dofpv_prargs = dof_add_lsect(ddo, NULL, DOF_SECT_PRARGS, + sizeof (uint8_t), 0, sizeof (uint8_t), dt_buf_len(&ddo->ddo_args)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_args, sizeof (uint8_t)); + + dofpv.dofpv_proffs = dof_add_lsect(ddo, NULL, DOF_SECT_PROFFS, + sizeof (uint_t), 0, sizeof (uint_t), dt_buf_len(&ddo->ddo_offs)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_offs, sizeof (uint_t)); + + if ((sz = dt_buf_len(&ddo->ddo_enoffs)) != 0) { + dofpv.dofpv_prenoffs = dof_add_lsect(ddo, NULL, + DOF_SECT_PRENOFFS, sizeof (uint_t), 0, sizeof (uint_t), sz); + } else { + dofpv.dofpv_prenoffs = DOF_SECT_NONE; + } + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_enoffs, sizeof (uint_t)); + + dofpv.dofpv_strtab = ddo->ddo_strsec; + dofpv.dofpv_name = dof_add_string(ddo, pvp->pv_desc.dtvd_name); + + dofpv.dofpv_provattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_provider); + dofpv.dofpv_modattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_mod); + dofpv.dofpv_funcattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_func); + dofpv.dofpv_nameattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_name); + dofpv.dofpv_argsattr = dof_attr(&pvp->pv_desc.dtvd_attr.dtpa_args); + + dofs[0] = dof_add_lsect(ddo, &dofpv, DOF_SECT_PROVIDER, + sizeof (dof_secidx_t), 0, 0, sizeof (dof_provider_t)); + + dofr.dofr_strtab = dofpv.dofpv_strtab; + dofr.dofr_tgtsec = dofpv.dofpv_probes; + dofr.dofr_relsec = dof_add_lsect(ddo, NULL, DOF_SECT_RELTAB, + sizeof (uint64_t), 0, sizeof (dof_relodesc_t), + dt_buf_len(&ddo->ddo_rels)); + + dt_buf_concat(dtp, &ddo->ddo_ldata, &ddo->ddo_rels, sizeof (uint64_t)); + + (void) dof_add_lsect(ddo, &dofr, DOF_SECT_URELHDR, + sizeof (dof_secidx_t), 0, 0, sizeof (dof_relohdr_t)); + + if (nxr != 0 && dtp->dt_xlatemode == DT_XL_DYNAMIC) { + (void) dof_add_lsect(ddo, dofs, DOF_SECT_PREXPORT, + sizeof (dof_secidx_t), 0, sizeof (dof_secidx_t), + sizeof (dof_secidx_t) * (nxr + 1)); + } +} + +static int +dof_hdr(dtrace_hdl_t *dtp, uint8_t dofversion, dof_hdr_t *hp) +{ + /* + * If our config values cannot fit in a uint8_t, we can't generate a + * DOF header since the values won't fit. This can only happen if the + * user forcibly compiles a program with an artificial configuration. + */ + if (dtp->dt_conf.dtc_difversion > UINT8_MAX || + dtp->dt_conf.dtc_difintregs > UINT8_MAX || + dtp->dt_conf.dtc_diftupregs > UINT8_MAX) + return (dt_set_errno(dtp, EOVERFLOW)); + + bzero(hp, sizeof (dof_hdr_t)); + + hp->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0; + hp->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1; + hp->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2; + hp->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3; + + if (dtp->dt_conf.dtc_ctfmodel == CTF_MODEL_LP64) + hp->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_LP64; + else + hp->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_ILP32; + + hp->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE; + hp->dofh_ident[DOF_ID_VERSION] = dofversion; + hp->dofh_ident[DOF_ID_DIFVERS] = dtp->dt_conf.dtc_difversion; + hp->dofh_ident[DOF_ID_DIFIREG] = dtp->dt_conf.dtc_difintregs; + hp->dofh_ident[DOF_ID_DIFTREG] = dtp->dt_conf.dtc_diftupregs; + + hp->dofh_hdrsize = sizeof (dof_hdr_t); + hp->dofh_secsize = sizeof (dof_sec_t); + hp->dofh_secoff = sizeof (dof_hdr_t); + + return (0); +} + +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 format data, + * add the format string to the global string table. + */ + 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 + 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)); +} + +void +dtrace_dof_destroy(dtrace_hdl_t *dtp, void *dof) +{ + dt_free(dtp, dof); +} + +void * +dtrace_getopt_dof(dtrace_hdl_t *dtp) +{ + dof_hdr_t *dof; + dof_sec_t *sec; + dof_optdesc_t *dofo; + int i, nopts = 0, len = sizeof (dof_hdr_t) + + roundup(sizeof (dof_sec_t), sizeof (uint64_t)); + + for (i = 0; i < DTRACEOPT_MAX; i++) { + if (dtp->dt_options[i] != DTRACEOPT_UNSET) + nopts++; + } + + len += sizeof (dof_optdesc_t) * nopts; + + if ((dof = dt_zalloc(dtp, len)) == NULL || + dof_hdr(dtp, DOF_VERSION, dof) != 0) { + dt_free(dtp, dof); + return (NULL); + } + + dof->dofh_secnum = 1; /* only DOF_SECT_OPTDESC */ + dof->dofh_loadsz = len; + dof->dofh_filesz = len; + + /* + * Fill in the option section header... + */ + sec = (dof_sec_t *)((uintptr_t)dof + sizeof (dof_hdr_t)); + sec->dofs_type = DOF_SECT_OPTDESC; + sec->dofs_align = sizeof (uint64_t); + sec->dofs_flags = DOF_SECF_LOAD; + sec->dofs_entsize = sizeof (dof_optdesc_t); + + dofo = (dof_optdesc_t *)((uintptr_t)sec + + roundup(sizeof (dof_sec_t), sizeof (uint64_t))); + + sec->dofs_offset = (uintptr_t)dofo - (uintptr_t)dof; + sec->dofs_size = sizeof (dof_optdesc_t) * nopts; + + for (i = 0; i < DTRACEOPT_MAX; i++) { + if (dtp->dt_options[i] == DTRACEOPT_UNSET) + continue; + + dofo->dofo_option = i; + dofo->dofo_strtab = DOF_SECIDX_NONE; + dofo->dofo_value = dtp->dt_options[i]; + dofo++; + } + + return (dof); +} + +void * +dtrace_geterr_dof(dtrace_hdl_t *dtp) +{ + if (dtp->dt_errprog != NULL) + return (dtrace_dof_create(dtp, dtp->dt_errprog, 0)); + + (void) dt_set_errno(dtp, EDT_BADERROR); + return (NULL); +} diff --git a/lib/libdtrace/common/dt_dof.h b/lib/libdtrace/common/dt_dof.h new file mode 100644 index 000000000000..e0a4bf52502d --- /dev/null +++ b/lib/libdtrace/common/dt_dof.h @@ -0,0 +1,66 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_DOF_H +#define _DT_DOF_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <dtrace.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <dt_buf.h> + +typedef struct dt_dof { + dtrace_hdl_t *ddo_hdl; /* libdtrace handle */ + dtrace_prog_t *ddo_pgp; /* current program */ + uint_t ddo_nsecs; /* number of sections */ + dof_secidx_t ddo_strsec; /* global strings section index */ + dof_secidx_t *ddo_xlimport; /* imported xlator section indices */ + dof_secidx_t *ddo_xlexport; /* exported xlator section indices */ + dt_buf_t ddo_secs; /* section headers */ + dt_buf_t ddo_strs; /* global strings */ + dt_buf_t ddo_ldata; /* loadable section data */ + dt_buf_t ddo_udata; /* unloadable section data */ + dt_buf_t ddo_probes; /* probe section data */ + dt_buf_t ddo_args; /* probe arguments section data */ + dt_buf_t ddo_offs; /* probe offsets section data */ + dt_buf_t ddo_enoffs; /* is-enabled offsets section data */ + dt_buf_t ddo_rels; /* probe relocation section data */ + dt_buf_t ddo_xlms; /* xlate members section data */ +} dt_dof_t; + +extern void dt_dof_init(dtrace_hdl_t *); +extern void dt_dof_fini(dtrace_hdl_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_DOF_H */ diff --git a/lib/libdtrace/common/dt_error.c b/lib/libdtrace/common/dt_error.c new file mode 100644 index 000000000000..263f70c85ecc --- /dev/null +++ b/lib/libdtrace/common/dt_error.c @@ -0,0 +1,235 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> +#include <strings.h> +#include <dt_impl.h> + +static const struct { + int err; + const char *msg; +} _dt_errlist[] = { + { EDT_VERSION, "Client requested version newer than library" }, + { EDT_VERSINVAL, "Version is not properly formatted or is too large" }, + { EDT_VERSUNDEF, "Requested version is not supported by compiler" }, + { EDT_VERSREDUCED, "Requested version conflicts with earlier setting" }, + { EDT_CTF, "Unexpected libctf error" }, + { EDT_COMPILER, "Error in D program compilation" }, + { EDT_NOREG, "Insufficient registers to generate code" }, + { EDT_NOTUPREG, "Insufficient tuple registers to generate code" }, + { EDT_NOMEM, "Memory allocation failure" }, + { EDT_INT2BIG, "Integer constant table limit exceeded" }, + { EDT_STR2BIG, "String constant table limit exceeded" }, + { EDT_NOMOD, "Unknown module name" }, + { EDT_NOPROV, "Unknown provider name" }, + { EDT_NOPROBE, "No probe matches description" }, + { EDT_NOSYM, "Unknown symbol name" }, + { EDT_NOSYMADDR, "No symbol corresponds to address" }, + { EDT_NOTYPE, "Unknown type name" }, + { EDT_NOVAR, "Unknown variable name" }, + { EDT_NOAGG, "Unknown aggregation name" }, + { EDT_BADSCOPE, "Improper use of scoping operator in type name" }, + { EDT_BADSPEC, "Overspecified probe description" }, + { EDT_BADSPCV, "Undefined macro variable in probe description" }, + { EDT_BADID, "Unknown probe identifier" }, + { EDT_NOTLOADED, "Module is no longer loaded" }, + { EDT_NOCTF, "Module does not contain any CTF data" }, + { EDT_DATAMODEL, "Module and program data models do not match" }, + { EDT_DIFVERS, "Library uses newer DIF version than kernel" }, + { EDT_BADAGG, "Unknown aggregating action" }, + { EDT_FIO, "Error occurred while reading from input stream" }, + { EDT_DIFINVAL, "DIF program content is invalid" }, + { EDT_DIFSIZE, "DIF program exceeds maximum program size" }, + { EDT_DIFFAULT, "DIF program contains invalid pointer" }, + { EDT_BADPROBE, "Invalid probe specification" }, + { EDT_BADPGLOB, "Probe description has too many globbing characters" }, + { EDT_NOSCOPE, "Declaration scope stack underflow" }, + { EDT_NODECL, "Declaration stack underflow" }, + { EDT_DMISMATCH, "Data record list does not match statement" }, + { EDT_DOFFSET, "Data record offset exceeds buffer boundary" }, + { EDT_DALIGN, "Data record has inappropriate alignment" }, + { EDT_BADOPTNAME, "Invalid option name" }, + { EDT_BADOPTVAL, "Invalid value for specified option" }, + { EDT_BADOPTCTX, "Option cannot be used from within a D program" }, + { EDT_CPPFORK, "Failed to fork preprocessor" }, + { EDT_CPPEXEC, "Failed to exec preprocessor" }, + { EDT_CPPENT, "Preprocessor not found" }, + { EDT_CPPERR, "Preprocessor failed to process input program" }, + { EDT_SYMOFLOW, "Symbol table identifier space exhausted" }, + { EDT_ACTIVE, "Operation illegal when tracing is active" }, + { EDT_DESTRUCTIVE, "Destructive actions not allowed" }, + { EDT_NOANON, "No anonymous tracing state" }, + { EDT_ISANON, "Can't claim anonymous state and enable probes" }, + { EDT_ENDTOOBIG, "END enablings exceed size of principal buffer" }, + { EDT_NOCONV, "Failed to load type for printf conversion" }, + { EDT_BADCONV, "Incomplete printf conversion" }, + { EDT_BADERROR, "Invalid library ERROR action" }, + { EDT_ERRABORT, "Abort due to error" }, + { EDT_DROPABORT, "Abort due to drop" }, + { EDT_DIRABORT, "Abort explicitly directed" }, + { EDT_BADRVAL, "Invalid return value from callback" }, + { EDT_BADNORMAL, "Invalid normalization" }, + { EDT_BUFTOOSMALL, "Enabling exceeds size of buffer" }, + { EDT_BADTRUNC, "Invalid truncation" }, + { EDT_BUSY, "DTrace cannot be used when kernel debugger is active" }, + { EDT_ACCESS, "DTrace requires additional privileges" }, + { EDT_NOENT, "DTrace device not available on system" }, + { EDT_BRICKED, "Abort due to systemic unresponsiveness" }, + { EDT_HARDWIRE, "Failed to load language definitions" }, + { EDT_ELFVERSION, "libelf is out-of-date with respect to libdtrace" }, + { EDT_NOBUFFERED, "Attempt to buffer output without handler" }, + { EDT_UNSTABLE, "Description matched an unstable set of probes" }, + { EDT_BADSETOPT, "Invalid setopt() library action" }, + { EDT_BADSTACKPC, "Invalid stack program counter size" }, + { EDT_BADAGGVAR, "Invalid aggregation variable identifier" }, + { EDT_OVERSION, "Client requested deprecated version of library" } +}; + +static const int _dt_nerr = sizeof (_dt_errlist) / sizeof (_dt_errlist[0]); + +const char * +dtrace_errmsg(dtrace_hdl_t *dtp, int error) +{ + const char *str; + int i; + + if (error == EDT_COMPILER && dtp != NULL && dtp->dt_errmsg[0] != '\0') + str = dtp->dt_errmsg; + else if (error == EDT_CTF && dtp != NULL && dtp->dt_ctferr != 0) + str = ctf_errmsg(dtp->dt_ctferr); + else if (error >= EDT_BASE && (error - EDT_BASE) < _dt_nerr) { + for (i = 0; i < _dt_nerr; i++) { + if (_dt_errlist[i].err == error) + return (_dt_errlist[i].msg); + } + str = NULL; + } else + str = strerror(error); + + return (str ? str : "Unknown error"); +} + +int +dtrace_errno(dtrace_hdl_t *dtp) +{ + return (dtp->dt_errno); +} + +#if defined(sun) +int +dt_set_errno(dtrace_hdl_t *dtp, int err) +{ + dtp->dt_errno = err; + return (-1); +} +#else +int +_dt_set_errno(dtrace_hdl_t *dtp, int err, const char *errfile, int errline) +{ + dtp->dt_errno = err; + dtp->dt_errfile = errfile; + dtp->dt_errline = errline; + return (-1); +} + +void dt_get_errloc(dtrace_hdl_t *dtp, const char **p_errfile, int *p_errline) +{ + *p_errfile = dtp->dt_errfile; + *p_errline = dtp->dt_errline; +} +#endif + +void +dt_set_errmsg(dtrace_hdl_t *dtp, const char *errtag, const char *region, + const char *filename, int lineno, const char *format, va_list ap) +{ + size_t len, n; + char *p, *s; + + s = dtp->dt_errmsg; + n = sizeof (dtp->dt_errmsg); + + if (errtag != NULL && (yypcb->pcb_cflags & DTRACE_C_ETAGS)) + (void) snprintf(s, n, "[%s] ", errtag); + else + s[0] = '\0'; + + len = strlen(dtp->dt_errmsg); + s = dtp->dt_errmsg + len; + n = sizeof (dtp->dt_errmsg) - len; + + if (filename == NULL) + filename = dtp->dt_filetag; + + if (filename != NULL) + (void) snprintf(s, n, "\"%s\", line %d: ", filename, lineno); + else if (lineno != 0) + (void) snprintf(s, n, "line %d: ", lineno); + else if (region != NULL) + (void) snprintf(s, n, "in %s: ", region); + + len = strlen(dtp->dt_errmsg); + s = dtp->dt_errmsg + len; + n = sizeof (dtp->dt_errmsg) - len; + (void) vsnprintf(s, n, format, ap); + + if ((p = strrchr(dtp->dt_errmsg, '\n')) != NULL) + *p = '\0'; /* remove trailing \n from message buffer */ + + dtp->dt_errtag = errtag; +} + +/*ARGSUSED*/ +const char * +dtrace_faultstr(dtrace_hdl_t *dtp, int fault) +{ + int i; + + static const struct { + int code; + const char *str; + } faults[] = { + { DTRACEFLT_BADADDR, "invalid address" }, + { DTRACEFLT_BADALIGN, "invalid alignment" }, + { DTRACEFLT_ILLOP, "illegal operation" }, + { DTRACEFLT_DIVZERO, "divide-by-zero" }, + { DTRACEFLT_NOSCRATCH, "out of scratch space" }, + { DTRACEFLT_KPRIV, "invalid kernel access" }, + { DTRACEFLT_UPRIV, "invalid user access" }, + { DTRACEFLT_TUPOFLOW, "tuple stack overflow" }, + { DTRACEFLT_BADSTACK, "bad stack" }, + { DTRACEFLT_LIBRARY, "library-level fault" }, + { 0, NULL } + }; + + for (i = 0; faults[i].str != NULL; i++) { + if (faults[i].code == fault) + return (faults[i].str); + } + + return ("unknown fault"); +} diff --git a/lib/libdtrace/common/dt_errtags.h b/lib/libdtrace/common/dt_errtags.h new file mode 100644 index 000000000000..62f955505711 --- /dev/null +++ b/lib/libdtrace/common/dt_errtags.h @@ -0,0 +1,251 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_ERRTAGS_H +#define _DT_ERRTAGS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This enum definition is used to define a set of error tags associated with + * the D compiler's various error conditions. The shell script mkerrtags.sh is + * used to parse this file and create a corresponding dt_errtags.c source file. + * If you do something other than add a new error tag here, you may need to + * update the mkerrtags shell script as it is based upon simple regexps. + */ +typedef enum { + D_UNKNOWN, /* unknown D compiler error */ + D_SYNTAX, /* syntax error in input stream */ + D_EMPTY, /* empty translation unit */ + D_TYPE_ERR, /* type definition missing */ + D_TYPE_MEMBER, /* type member not found */ + D_ASRELO, /* relocation remains against symbol */ + D_CG_EXPR, /* tracing function called from expr */ + D_CG_DYN, /* expression returns dynamic result */ + D_ATTR_MIN, /* attributes less than amin setting */ + D_ID_OFLOW, /* identifier space overflow */ + D_PDESC_ZERO, /* probedesc matches zero probes */ + D_PDESC_INVAL, /* probedesc is not valid */ + D_PRED_SCALAR, /* predicate must be of scalar type */ + D_FUNC_IDENT, /* function designator is not ident */ + D_FUNC_UNDEF, /* function ident is not defined */ + D_FUNC_IDKIND, /* function ident is of wrong idkind */ + D_OFFSETOF_TYPE, /* offsetof arg is not sou type */ + D_OFFSETOF_BITFIELD, /* offsetof applied to field member */ + D_SIZEOF_TYPE, /* invalid sizeof type */ + D_SIZEOF_BITFIELD, /* sizeof applied to field member */ + D_STRINGOF_TYPE, /* invalid stringof type */ + D_OP_IDENT, /* operand must be an identifier */ + D_OP_INT, /* operand must be integral type */ + D_OP_SCALAR, /* operand must be scalar type */ + D_OP_ARITH, /* operand must be arithmetic type */ + D_OP_WRITE, /* operand must be writable variable */ + D_OP_LVAL, /* operand must be lvalue */ + D_OP_INCOMPAT, /* operand types are not compatible */ + D_OP_VFPTR, /* operand cannot be void or func ptr */ + D_OP_ARRFUN, /* operand cannot be array or func */ + D_OP_PTR, /* operand must be a pointer */ + D_OP_SOU, /* operand must be struct or union */ + D_OP_INCOMPLETE, /* operand is an incomplete type */ + D_OP_DYN, /* operand cannot be of dynamic type */ + D_OP_ACT, /* operand cannot be action */ + D_AGG_REDEF, /* aggregation cannot be redefined */ + D_AGG_FUNC, /* aggregating function required */ + D_AGG_MDIM, /* aggregation used as multi-dim arr */ + D_ARR_BADREF, /* access non-array using tuple */ + D_ARR_LOCAL, /* cannot define local assc array */ + D_DIV_ZERO, /* division by zero detected */ + D_DEREF_NONPTR, /* dereference non-pointer type */ + D_DEREF_VOID, /* dereference void pointer */ + D_DEREF_FUNC, /* dereference function pointer */ + D_ADDROF_LVAL, /* unary & applied to non-lvalue */ + D_ADDROF_VAR, /* unary & applied to variable */ + D_ADDROF_BITFIELD, /* unary & applied to field member */ + D_XLATE_REDECL, /* translator redeclared */ + D_XLATE_NOCONV, /* no conversion for member defined */ + D_XLATE_NONE, /* no translator for type combo */ + D_XLATE_SOU, /* dst must be struct or union type */ + D_XLATE_INCOMPAT, /* translator member type incompat */ + D_XLATE_MEMB, /* translator member is not valid */ + D_CAST_INVAL, /* invalid cast expression */ + D_PRAGERR, /* #pragma error message */ + D_PRAGCTL_INVAL, /* invalid control directive */ + D_PRAGMA_INVAL, /* invalid compiler pragma */ + D_PRAGMA_UNUSED, /* unused compiler pragma */ + D_PRAGMA_MALFORM, /* malformed #pragma argument list */ + D_PRAGMA_OPTSET, /* failed to set #pragma option */ + D_PRAGMA_SCOPE, /* #pragma identifier scope error */ + D_PRAGMA_DEPEND, /* #pragma dependency not satisfied */ + D_MACRO_UNDEF, /* macro parameter is not defined */ + D_MACRO_OFLOW, /* macro parameter integer overflow */ + D_MACRO_UNUSED, /* macro parameter is never used */ + D_INT_OFLOW, /* integer constant overflow */ + D_INT_DIGIT, /* integer digit is not valid */ + D_STR_NL, /* newline in string literal */ + D_CHR_NL, /* newline in character constant */ + D_CHR_NULL, /* empty character constant */ + D_CHR_OFLOW, /* character constant is too long */ + D_IDENT_BADREF, /* identifier expected type mismatch */ + D_IDENT_UNDEF, /* identifier is not known/defined */ + D_IDENT_AMBIG, /* identifier is ambiguous (var/enum) */ + D_SYM_BADREF, /* kernel/user symbol ref mismatch */ + D_SYM_NOTYPES, /* no CTF data available for sym ref */ + D_SYM_MODEL, /* module/program data model mismatch */ + D_VAR_UNDEF, /* reference to undefined variable */ + D_VAR_UNSUP, /* unsupported variable specification */ + D_PROTO_LEN, /* prototype length mismatch */ + D_PROTO_ARG, /* prototype argument mismatch */ + D_ARGS_MULTI, /* description matches unstable set */ + D_ARGS_XLATOR, /* no args[] translator defined */ + D_ARGS_NONE, /* no args[] available */ + D_ARGS_TYPE, /* invalid args[] type */ + D_ARGS_IDX, /* invalid args[] index */ + D_REGS_IDX, /* invalid regs[] index */ + D_KEY_TYPE, /* invalid agg or array key type */ + D_PRINTF_DYN_PROTO, /* dynamic size argument missing */ + D_PRINTF_DYN_TYPE, /* dynamic size type mismatch */ + D_PRINTF_AGG_CONV, /* improper use of %@ conversion */ + D_PRINTF_ARG_PROTO, /* conversion missing value argument */ + D_PRINTF_ARG_TYPE, /* conversion arg has wrong type */ + D_PRINTF_ARG_EXTRA, /* extra arguments specified */ + D_PRINTF_ARG_FMT, /* format string is not a constant */ + D_PRINTF_FMT_EMPTY, /* format string is empty */ + D_DECL_CHARATTR, /* bad attributes for char decl */ + D_DECL_VOIDATTR, /* bad attributes for void decl */ + D_DECL_SIGNINT, /* sign/unsign with non-integer decl */ + D_DECL_LONGINT, /* long with non-arithmetic decl */ + D_DECL_IDENT, /* old-style declaration or bad type */ + D_DECL_CLASS, /* more than one storage class given */ + D_DECL_BADCLASS, /* decl class not supported in D */ + D_DECL_PARMCLASS, /* invalid class for parameter type */ + D_DECL_COMBO, /* bad decl specifier combination */ + D_DECL_ARRSUB, /* const int required for array size */ + D_DECL_ARRNULL, /* array decl requires dim or tuple */ + D_DECL_ARRBIG, /* array size too big */ + D_DECL_IDRED, /* decl identifier redeclared */ + D_DECL_TYPERED, /* decl type redeclared */ + D_DECL_MNAME, /* member name missing */ + D_DECL_SCOPE, /* scoping operator used in decl */ + D_DECL_BFCONST, /* bit-field requires const size expr */ + D_DECL_BFSIZE, /* bit-field size too big for type */ + D_DECL_BFTYPE, /* bit-field type is not valid */ + D_DECL_ENCONST, /* enum tag requires const size expr */ + D_DECL_ENOFLOW, /* enumerator value overflows INT_MAX */ + D_DECL_USELESS, /* useless external declaration */ + D_DECL_LOCASSC, /* attempt to decl local assc array */ + D_DECL_VOIDOBJ, /* attempt to decl void object */ + D_DECL_DYNOBJ, /* attempt to decl dynamic object */ + D_DECL_INCOMPLETE, /* declaration uses incomplete type */ + D_DECL_PROTO_VARARGS, /* varargs not allowed in prototype */ + D_DECL_PROTO_TYPE, /* type not allowed in prototype */ + D_DECL_PROTO_VOID, /* void must be sole parameter */ + D_DECL_PROTO_NAME, /* void parameter may not have a name */ + D_DECL_PROTO_FORM, /* parameter name has no formal */ + D_COMM_COMM, /* commit() after commit() */ + D_COMM_DREC, /* commit() after data action */ + D_SPEC_SPEC, /* speculate() after speculate() */ + D_SPEC_COMM, /* speculate() after commit() */ + D_SPEC_DREC, /* speculate() after data action */ + D_AGG_COMM, /* aggregating act after commit() */ + D_AGG_SPEC, /* aggregating act after speculate() */ + D_AGG_NULL, /* aggregation stmt has null effect */ + D_AGG_SCALAR, /* aggregating function needs scalar */ + D_ACT_SPEC, /* destructive action after speculate */ + D_EXIT_SPEC, /* exit() action after speculate */ + D_DREC_COMM, /* data action after commit() */ + D_PRINTA_PROTO, /* printa() prototype mismatch */ + D_PRINTA_AGGARG, /* aggregation arg type mismatch */ + D_PRINTA_AGGBAD, /* printa() aggregation not defined */ + D_PRINTA_AGGKEY, /* printa() aggregation key mismatch */ + D_PRINTA_AGGPROTO, /* printa() aggregation mismatch */ + D_TRACE_VOID, /* trace() argument has void type */ + D_TRACE_DYN, /* trace() argument has dynamic type */ + D_TRACEMEM_ADDR, /* tracemem() address bad type */ + D_TRACEMEM_SIZE, /* tracemem() size bad type */ + D_STACK_PROTO, /* stack() prototype mismatch */ + D_STACK_SIZE, /* stack() size argument bad type */ + D_USTACK_FRAMES, /* ustack() frames arg bad type */ + D_USTACK_STRSIZE, /* ustack() strsize arg bad type */ + D_USTACK_PROTO, /* ustack() prototype mismatch */ + D_LQUANT_BASETYPE, /* lquantize() bad base type */ + D_LQUANT_BASEVAL, /* lquantize() bad base value */ + D_LQUANT_LIMTYPE, /* lquantize() bad limit type */ + D_LQUANT_LIMVAL, /* lquantize() bad limit value */ + D_LQUANT_MISMATCH, /* lquantize() limit < base */ + D_LQUANT_STEPTYPE, /* lquantize() bad step type */ + D_LQUANT_STEPVAL, /* lquantize() bad step value */ + D_LQUANT_STEPLARGE, /* lquantize() step too large */ + D_LQUANT_STEPSMALL, /* lquantize() step too small */ + D_QUANT_PROTO, /* quantize() prototype mismatch */ + D_PROC_OFF, /* byte offset exceeds function size */ + D_PROC_ALIGN, /* byte offset has invalid alignment */ + D_PROC_NAME, /* invalid process probe name */ + D_PROC_GRAB, /* failed to grab process */ + D_PROC_DYN, /* process is not dynamically linked */ + D_PROC_LIB, /* invalid process library name */ + D_PROC_FUNC, /* no such function in process */ + D_PROC_CREATEFAIL, /* pid probe creation failed */ + D_PROC_NODEV, /* fasttrap device is not installed */ + D_PROC_BADPID, /* user probe pid invalid */ + D_PROC_BADPROV, /* user probe provider invalid */ + D_PROC_USDT, /* problem initializing usdt */ + D_CLEAR_PROTO, /* clear() prototype mismatch */ + D_CLEAR_AGGARG, /* aggregation arg type mismatch */ + D_CLEAR_AGGBAD, /* clear() aggregation not defined */ + D_NORMALIZE_PROTO, /* normalize() prototype mismatch */ + D_NORMALIZE_SCALAR, /* normalize() value must be scalar */ + D_NORMALIZE_AGGARG, /* aggregation arg type mismatch */ + D_NORMALIZE_AGGBAD, /* normalize() aggregation not def. */ + D_TRUNC_PROTO, /* trunc() prototype mismatch */ + D_TRUNC_SCALAR, /* trunc() value must be scalar */ + D_TRUNC_AGGARG, /* aggregation arg type mismatch */ + D_TRUNC_AGGBAD, /* trunc() aggregation not def. */ + D_PROV_BADNAME, /* invalid provider name */ + D_PROV_INCOMPAT, /* provider/probe interface mismatch */ + D_PROV_PRDUP, /* duplicate probe declaration */ + D_PROV_PRARGLEN, /* probe argument list too long */ + D_PROV_PRXLATOR, /* probe argument translator missing */ + D_FREOPEN_INVALID, /* frename() filename is invalid */ + D_LQUANT_MATCHBASE, /* lquantize() mismatch on base */ + D_LQUANT_MATCHLIM, /* lquantize() mismatch on limit */ + D_LQUANT_MATCHSTEP, /* lquantize() mismatch on step */ + D_PRINTM_ADDR, /* printm() memref bad type */ + D_PRINTM_SIZE, /* printm() size bad type */ + D_PRINTT_ADDR, /* printt() typeref bad type */ + D_PRINTT_SIZE /* printt() size bad type */ +} dt_errtag_t; + +extern const char *dt_errtag(dt_errtag_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_ERRTAGS_H */ diff --git a/lib/libdtrace/common/dt_grammar.y b/lib/libdtrace/common/dt_grammar.y new file mode 100644 index 000000000000..0c12623bc422 --- /dev/null +++ b/lib/libdtrace/common/dt_grammar.y @@ -0,0 +1,834 @@ +%{ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <dt_impl.h> + +#define OP1(op, c) dt_node_op1(op, c) +#define OP2(op, l, r) dt_node_op2(op, l, r) +#define OP3(x, y, z) dt_node_op3(x, y, z) +#define LINK(l, r) dt_node_link(l, r) +#define DUP(s) strdup(s) + +%} + +%union { + dt_node_t *l_node; + dt_decl_t *l_decl; + char *l_str; + uintmax_t l_int; + int l_tok; +} + +%token DT_TOK_COMMA DT_TOK_ELLIPSIS +%token DT_TOK_ASGN DT_TOK_ADD_EQ DT_TOK_SUB_EQ DT_TOK_MUL_EQ +%token DT_TOK_DIV_EQ DT_TOK_MOD_EQ DT_TOK_AND_EQ DT_TOK_XOR_EQ DT_TOK_OR_EQ +%token DT_TOK_LSH_EQ DT_TOK_RSH_EQ DT_TOK_QUESTION DT_TOK_COLON +%token DT_TOK_LOR DT_TOK_LXOR DT_TOK_LAND +%token DT_TOK_BOR DT_TOK_XOR DT_TOK_BAND DT_TOK_EQU DT_TOK_NEQ +%token DT_TOK_LT DT_TOK_LE DT_TOK_GT DT_TOK_GE DT_TOK_LSH DT_TOK_RSH +%token DT_TOK_ADD DT_TOK_SUB DT_TOK_MUL DT_TOK_DIV DT_TOK_MOD +%token DT_TOK_LNEG DT_TOK_BNEG DT_TOK_ADDADD DT_TOK_SUBSUB +%token DT_TOK_PREINC DT_TOK_POSTINC DT_TOK_PREDEC DT_TOK_POSTDEC +%token DT_TOK_IPOS DT_TOK_INEG DT_TOK_DEREF DT_TOK_ADDROF +%token DT_TOK_OFFSETOF DT_TOK_SIZEOF DT_TOK_STRINGOF DT_TOK_XLATE +%token DT_TOK_LPAR DT_TOK_RPAR DT_TOK_LBRAC DT_TOK_RBRAC DT_TOK_PTR DT_TOK_DOT + +%token <l_str> DT_TOK_STRING +%token <l_str> DT_TOK_IDENT +%token <l_str> DT_TOK_PSPEC +%token <l_str> DT_TOK_AGG +%token <l_str> DT_TOK_TNAME +%token <l_int> DT_TOK_INT + +%token DT_KEY_AUTO +%token DT_KEY_BREAK +%token DT_KEY_CASE +%token DT_KEY_CHAR +%token DT_KEY_CONST +%token DT_KEY_CONTINUE +%token DT_KEY_COUNTER +%token DT_KEY_DEFAULT +%token DT_KEY_DO +%token DT_KEY_DOUBLE +%token DT_KEY_ELSE +%token DT_KEY_ENUM +%token DT_KEY_EXTERN +%token DT_KEY_FLOAT +%token DT_KEY_FOR +%token DT_KEY_GOTO +%token DT_KEY_IF +%token DT_KEY_IMPORT +%token DT_KEY_INLINE +%token DT_KEY_INT +%token DT_KEY_LONG +%token DT_KEY_PROBE +%token DT_KEY_PROVIDER +%token DT_KEY_REGISTER +%token DT_KEY_RESTRICT +%token DT_KEY_RETURN +%token DT_KEY_SELF +%token DT_KEY_SHORT +%token DT_KEY_SIGNED +%token DT_KEY_STATIC +%token DT_KEY_STRING +%token DT_KEY_STRUCT +%token DT_KEY_SWITCH +%token DT_KEY_THIS +%token DT_KEY_TYPEDEF +%token DT_KEY_UNION +%token DT_KEY_UNSIGNED +%token DT_KEY_VOID +%token DT_KEY_VOLATILE +%token DT_KEY_WHILE +%token DT_KEY_XLATOR + +%token DT_TOK_EPRED +%token DT_CTX_DEXPR +%token DT_CTX_DPROG +%token DT_CTX_DTYPE +%token DT_TOK_EOF 0 + +%left DT_TOK_COMMA +%right DT_TOK_ASGN DT_TOK_ADD_EQ DT_TOK_SUB_EQ DT_TOK_MUL_EQ DT_TOK_DIV_EQ + DT_TOK_MOD_EQ DT_TOK_AND_EQ DT_TOK_XOR_EQ DT_TOK_OR_EQ DT_TOK_LSH_EQ + DT_TOK_RSH_EQ +%left DT_TOK_QUESTION DT_TOK_COLON +%left DT_TOK_LOR +%left DT_TOK_LXOR +%left DT_TOK_LAND +%left DT_TOK_BOR +%left DT_TOK_XOR +%left DT_TOK_BAND +%left DT_TOK_EQU DT_TOK_NEQ +%left DT_TOK_LT DT_TOK_LE DT_TOK_GT DT_TOK_GE +%left DT_TOK_LSH DT_TOK_RSH +%left DT_TOK_ADD DT_TOK_SUB +%left DT_TOK_MUL DT_TOK_DIV DT_TOK_MOD +%right DT_TOK_LNEG DT_TOK_BNEG DT_TOK_ADDADD DT_TOK_SUBSUB + DT_TOK_IPOS DT_TOK_INEG +%right DT_TOK_DEREF DT_TOK_ADDROF DT_TOK_SIZEOF DT_TOK_STRINGOF DT_TOK_XLATE +%left DT_TOK_LPAR DT_TOK_RPAR DT_TOK_LBRAC DT_TOK_RBRAC DT_TOK_PTR DT_TOK_DOT + +%type <l_node> d_expression +%type <l_node> d_program +%type <l_node> d_type + +%type <l_node> translation_unit +%type <l_node> external_declaration +%type <l_node> inline_definition +%type <l_node> translator_definition +%type <l_node> translator_member_list +%type <l_node> translator_member +%type <l_node> provider_definition +%type <l_node> provider_probe_list +%type <l_node> provider_probe +%type <l_node> probe_definition +%type <l_node> probe_specifiers +%type <l_node> probe_specifier_list +%type <l_node> probe_specifier +%type <l_node> statement_list +%type <l_node> statement +%type <l_node> declaration +%type <l_node> init_declarator_list +%type <l_node> init_declarator + +%type <l_decl> type_specifier +%type <l_decl> type_qualifier +%type <l_decl> struct_or_union_specifier +%type <l_decl> specifier_qualifier_list +%type <l_decl> enum_specifier +%type <l_decl> declarator +%type <l_decl> direct_declarator +%type <l_decl> pointer +%type <l_decl> type_qualifier_list +%type <l_decl> type_name +%type <l_decl> abstract_declarator +%type <l_decl> direct_abstract_declarator + +%type <l_node> parameter_type_list +%type <l_node> parameter_list +%type <l_node> parameter_declaration + +%type <l_node> array +%type <l_node> array_parameters +%type <l_node> function +%type <l_node> function_parameters + +%type <l_node> expression +%type <l_node> assignment_expression +%type <l_node> conditional_expression +%type <l_node> constant_expression +%type <l_node> logical_or_expression +%type <l_node> logical_xor_expression +%type <l_node> logical_and_expression +%type <l_node> inclusive_or_expression +%type <l_node> exclusive_or_expression +%type <l_node> and_expression +%type <l_node> equality_expression +%type <l_node> relational_expression +%type <l_node> shift_expression +%type <l_node> additive_expression +%type <l_node> multiplicative_expression +%type <l_node> cast_expression +%type <l_node> unary_expression +%type <l_node> postfix_expression +%type <l_node> primary_expression +%type <l_node> argument_expression_list + +%type <l_tok> assignment_operator +%type <l_tok> unary_operator +%type <l_tok> struct_or_union + +%% + +dtrace_program: d_expression DT_TOK_EOF { return (dt_node_root($1)); } + | d_program DT_TOK_EOF { return (dt_node_root($1)); } + | d_type DT_TOK_EOF { return (dt_node_root($1)); } + ; + +d_expression: DT_CTX_DEXPR { $$ = NULL; } + | DT_CTX_DEXPR expression { $$ = $2; } + ; + +d_program: DT_CTX_DPROG { $$ = dt_node_program(NULL); } + | DT_CTX_DPROG translation_unit { $$ = dt_node_program($2); } + ; + +d_type: DT_CTX_DTYPE { $$ = NULL; } + | DT_CTX_DTYPE type_name { $$ = (dt_node_t *)$2; } + ; + +translation_unit: + external_declaration + | translation_unit external_declaration { $$ = LINK($1, $2); } + ; + +external_declaration: + inline_definition + | translator_definition + | provider_definition + | probe_definition + | declaration + ; + +inline_definition: + DT_KEY_INLINE declaration_specifiers declarator + { dt_scope_push(NULL, CTF_ERR); } DT_TOK_ASGN + assignment_expression ';' { + /* + * We push a new declaration scope before shifting the + * assignment_expression in order to preserve ds_class + * and ds_ident for use in dt_node_inline(). Once the + * entire inline_definition rule is matched, pop the + * scope and construct the inline using the saved decl. + */ + dt_scope_pop(); + $$ = dt_node_inline($6); + } + ; + +translator_definition: + DT_KEY_XLATOR type_name DT_TOK_LT type_name + DT_TOK_IDENT DT_TOK_GT '{' translator_member_list '}' ';' { + $$ = dt_node_xlator($2, $4, $5, $8); + } + | DT_KEY_XLATOR type_name DT_TOK_LT type_name + DT_TOK_IDENT DT_TOK_GT '{' '}' ';' { + $$ = dt_node_xlator($2, $4, $5, NULL); + } + ; + +translator_member_list: + translator_member + | translator_member_list translator_member { $$ = LINK($1,$2); } + ; + +translator_member: + DT_TOK_IDENT DT_TOK_ASGN assignment_expression ';' { + $$ = dt_node_member(NULL, $1, $3); + } + ; + +provider_definition: + DT_KEY_PROVIDER DT_TOK_IDENT '{' provider_probe_list '}' ';' { + $$ = dt_node_provider($2, $4); + } + | DT_KEY_PROVIDER DT_TOK_IDENT '{' '}' ';' { + $$ = dt_node_provider($2, NULL); + } + ; + +provider_probe_list: + provider_probe + | provider_probe_list provider_probe { $$ = LINK($1, $2); } + ; + +provider_probe: + DT_KEY_PROBE DT_TOK_IDENT function DT_TOK_COLON function ';' { + $$ = dt_node_probe($2, 2, $3, $5); + } + | DT_KEY_PROBE DT_TOK_IDENT function ';' { + $$ = dt_node_probe($2, 1, $3, NULL); + } + ; + + +probe_definition: + probe_specifiers { + /* + * If the input stream is a file, do not permit a probe + * specification without / <pred> / or { <act> } after + * it. This can only occur if the next token is EOF or + * an ambiguous predicate was slurped up as a comment. + * We cannot perform this check if input() is a string + * because dtrace(1M) [-fmnP] also use the compiler and + * things like dtrace -n BEGIN have to be accepted. + */ + if (yypcb->pcb_fileptr != NULL) { + dnerror($1, D_SYNTAX, "expected predicate and/" + "or actions following probe description\n"); + } + $$ = dt_node_clause($1, NULL, NULL); + } + | probe_specifiers '{' statement_list '}' { + $$ = dt_node_clause($1, NULL, $3); + } + | probe_specifiers DT_TOK_DIV expression DT_TOK_EPRED { + dnerror($3, D_SYNTAX, "expected actions { } following " + "probe description and predicate\n"); + } + | probe_specifiers DT_TOK_DIV expression DT_TOK_EPRED + '{' statement_list '}' { + $$ = dt_node_clause($1, $3, $6); + } + ; + +probe_specifiers: + probe_specifier_list { yybegin(YYS_EXPR); $$ = $1; } + ; + +probe_specifier_list: + probe_specifier + | probe_specifier_list DT_TOK_COMMA probe_specifier { + $$ = LINK($1, $3); + } + ; + +probe_specifier: + DT_TOK_PSPEC { $$ = dt_node_pdesc_by_name($1); } + | DT_TOK_INT { $$ = dt_node_pdesc_by_id($1); } + ; + +statement_list: statement { $$ = $1; } + | statement_list ';' statement { $$ = LINK($1, $3); } + ; + +statement: /* empty */ { $$ = NULL; } + | expression { $$ = dt_node_statement($1); } + ; + +argument_expression_list: + assignment_expression + | argument_expression_list DT_TOK_COMMA assignment_expression { + $$ = LINK($1, $3); + } + ; + +primary_expression: + DT_TOK_IDENT { $$ = dt_node_ident($1); } + | DT_TOK_AGG { $$ = dt_node_ident($1); } + | DT_TOK_INT { $$ = dt_node_int($1); } + | DT_TOK_STRING { $$ = dt_node_string($1); } + | DT_KEY_SELF { $$ = dt_node_ident(DUP("self")); } + | DT_KEY_THIS { $$ = dt_node_ident(DUP("this")); } + | DT_TOK_LPAR expression DT_TOK_RPAR { $$ = $2; } + ; + +postfix_expression: + primary_expression + | postfix_expression + DT_TOK_LBRAC argument_expression_list DT_TOK_RBRAC { + $$ = OP2(DT_TOK_LBRAC, $1, $3); + } + | postfix_expression DT_TOK_LPAR DT_TOK_RPAR { + $$ = dt_node_func($1, NULL); + } + | postfix_expression + DT_TOK_LPAR argument_expression_list DT_TOK_RPAR { + $$ = dt_node_func($1, $3); + } + | postfix_expression DT_TOK_DOT DT_TOK_IDENT { + $$ = OP2(DT_TOK_DOT, $1, dt_node_ident($3)); + } + | postfix_expression DT_TOK_DOT DT_TOK_TNAME { + $$ = OP2(DT_TOK_DOT, $1, dt_node_ident($3)); + } + | postfix_expression DT_TOK_PTR DT_TOK_IDENT { + $$ = OP2(DT_TOK_PTR, $1, dt_node_ident($3)); + } + | postfix_expression DT_TOK_PTR DT_TOK_TNAME { + $$ = OP2(DT_TOK_PTR, $1, dt_node_ident($3)); + } + | postfix_expression DT_TOK_ADDADD { + $$ = OP1(DT_TOK_POSTINC, $1); + } + | postfix_expression DT_TOK_SUBSUB { + $$ = OP1(DT_TOK_POSTDEC, $1); + } + | DT_TOK_OFFSETOF DT_TOK_LPAR type_name DT_TOK_COMMA + DT_TOK_IDENT DT_TOK_RPAR { + $$ = dt_node_offsetof($3, $5); + } + | DT_TOK_OFFSETOF DT_TOK_LPAR type_name DT_TOK_COMMA + DT_TOK_TNAME DT_TOK_RPAR { + $$ = dt_node_offsetof($3, $5); + } + | DT_TOK_XLATE DT_TOK_LT type_name DT_TOK_GT + DT_TOK_LPAR expression DT_TOK_RPAR { + $$ = OP2(DT_TOK_XLATE, dt_node_type($3), $6); + } + ; + +unary_expression: + postfix_expression + | DT_TOK_ADDADD unary_expression { $$ = OP1(DT_TOK_PREINC, $2); } + | DT_TOK_SUBSUB unary_expression { $$ = OP1(DT_TOK_PREDEC, $2); } + | unary_operator cast_expression { $$ = OP1($1, $2); } + | DT_TOK_SIZEOF unary_expression { $$ = OP1(DT_TOK_SIZEOF, $2); } + | DT_TOK_SIZEOF DT_TOK_LPAR type_name DT_TOK_RPAR { + $$ = OP1(DT_TOK_SIZEOF, dt_node_type($3)); + } + | DT_TOK_STRINGOF unary_expression { + $$ = OP1(DT_TOK_STRINGOF, $2); + } + ; + +unary_operator: DT_TOK_BAND { $$ = DT_TOK_ADDROF; } + | DT_TOK_MUL { $$ = DT_TOK_DEREF; } + | DT_TOK_ADD { $$ = DT_TOK_IPOS; } + | DT_TOK_SUB { $$ = DT_TOK_INEG; } + | DT_TOK_BNEG { $$ = DT_TOK_BNEG; } + | DT_TOK_LNEG { $$ = DT_TOK_LNEG; } + ; + +cast_expression: + unary_expression + | DT_TOK_LPAR type_name DT_TOK_RPAR cast_expression { + $$ = OP2(DT_TOK_LPAR, dt_node_type($2), $4); + } + ; + +multiplicative_expression: + cast_expression + | multiplicative_expression DT_TOK_MUL cast_expression { + $$ = OP2(DT_TOK_MUL, $1, $3); + } + | multiplicative_expression DT_TOK_DIV cast_expression { + $$ = OP2(DT_TOK_DIV, $1, $3); + } + | multiplicative_expression DT_TOK_MOD cast_expression { + $$ = OP2(DT_TOK_MOD, $1, $3); + } + ; + +additive_expression: + multiplicative_expression + | additive_expression DT_TOK_ADD multiplicative_expression { + $$ = OP2(DT_TOK_ADD, $1, $3); + } + | additive_expression DT_TOK_SUB multiplicative_expression { + $$ = OP2(DT_TOK_SUB, $1, $3); + } + ; + +shift_expression: + additive_expression + | shift_expression DT_TOK_LSH additive_expression { + $$ = OP2(DT_TOK_LSH, $1, $3); + } + | shift_expression DT_TOK_RSH additive_expression { + $$ = OP2(DT_TOK_RSH, $1, $3); + } + ; + +relational_expression: + shift_expression + | relational_expression DT_TOK_LT shift_expression { + $$ = OP2(DT_TOK_LT, $1, $3); + } + | relational_expression DT_TOK_GT shift_expression { + $$ = OP2(DT_TOK_GT, $1, $3); + } + | relational_expression DT_TOK_LE shift_expression { + $$ = OP2(DT_TOK_LE, $1, $3); + } + | relational_expression DT_TOK_GE shift_expression { + $$ = OP2(DT_TOK_GE, $1, $3); + } + ; + +equality_expression: + relational_expression + | equality_expression DT_TOK_EQU relational_expression { + $$ = OP2(DT_TOK_EQU, $1, $3); + } + | equality_expression DT_TOK_NEQ relational_expression { + $$ = OP2(DT_TOK_NEQ, $1, $3); + } + ; + +and_expression: + equality_expression + | and_expression DT_TOK_BAND equality_expression { + $$ = OP2(DT_TOK_BAND, $1, $3); + } + ; + +exclusive_or_expression: + and_expression + | exclusive_or_expression DT_TOK_XOR and_expression { + $$ = OP2(DT_TOK_XOR, $1, $3); + } + ; + +inclusive_or_expression: + exclusive_or_expression + | inclusive_or_expression DT_TOK_BOR exclusive_or_expression { + $$ = OP2(DT_TOK_BOR, $1, $3); + } + ; + +logical_and_expression: + inclusive_or_expression + | logical_and_expression DT_TOK_LAND inclusive_or_expression { + $$ = OP2(DT_TOK_LAND, $1, $3); + } + ; + +logical_xor_expression: + logical_and_expression + | logical_xor_expression DT_TOK_LXOR logical_and_expression { + $$ = OP2(DT_TOK_LXOR, $1, $3); + } + ; + +logical_or_expression: + logical_xor_expression + | logical_or_expression DT_TOK_LOR logical_xor_expression { + $$ = OP2(DT_TOK_LOR, $1, $3); + } + ; + +constant_expression: conditional_expression + ; + +conditional_expression: + logical_or_expression + | logical_or_expression DT_TOK_QUESTION expression DT_TOK_COLON + conditional_expression { $$ = OP3($1, $3, $5); } + ; + +assignment_expression: + conditional_expression + | unary_expression assignment_operator assignment_expression { + $$ = OP2($2, $1, $3); + } + ; + +assignment_operator: + DT_TOK_ASGN { $$ = DT_TOK_ASGN; } + | DT_TOK_MUL_EQ { $$ = DT_TOK_MUL_EQ; } + | DT_TOK_DIV_EQ { $$ = DT_TOK_DIV_EQ; } + | DT_TOK_MOD_EQ { $$ = DT_TOK_MOD_EQ; } + | DT_TOK_ADD_EQ { $$ = DT_TOK_ADD_EQ; } + | DT_TOK_SUB_EQ { $$ = DT_TOK_SUB_EQ; } + | DT_TOK_LSH_EQ { $$ = DT_TOK_LSH_EQ; } + | DT_TOK_RSH_EQ { $$ = DT_TOK_RSH_EQ; } + | DT_TOK_AND_EQ { $$ = DT_TOK_AND_EQ; } + | DT_TOK_XOR_EQ { $$ = DT_TOK_XOR_EQ; } + | DT_TOK_OR_EQ { $$ = DT_TOK_OR_EQ; } + ; + +expression: assignment_expression + | expression DT_TOK_COMMA assignment_expression { + $$ = OP2(DT_TOK_COMMA, $1, $3); + } + ; + +declaration: declaration_specifiers ';' { + $$ = dt_node_decl(); + dt_decl_free(dt_decl_pop()); + yybegin(YYS_CLAUSE); + } + | declaration_specifiers init_declarator_list ';' { + $$ = $2; + dt_decl_free(dt_decl_pop()); + yybegin(YYS_CLAUSE); + } + ; + +declaration_specifiers: + d_storage_class_specifier + | d_storage_class_specifier declaration_specifiers + | type_specifier + | type_specifier declaration_specifiers + | type_qualifier + | type_qualifier declaration_specifiers + ; + +parameter_declaration_specifiers: + storage_class_specifier + | storage_class_specifier declaration_specifiers + | type_specifier + | type_specifier declaration_specifiers + | type_qualifier + | type_qualifier declaration_specifiers + ; + +storage_class_specifier: + DT_KEY_AUTO { dt_decl_class(DT_DC_AUTO); } + | DT_KEY_REGISTER { dt_decl_class(DT_DC_REGISTER); } + | DT_KEY_STATIC { dt_decl_class(DT_DC_STATIC); } + | DT_KEY_EXTERN { dt_decl_class(DT_DC_EXTERN); } + | DT_KEY_TYPEDEF { dt_decl_class(DT_DC_TYPEDEF); } + ; + +d_storage_class_specifier: + storage_class_specifier + | DT_KEY_SELF { dt_decl_class(DT_DC_SELF); } + | DT_KEY_THIS { dt_decl_class(DT_DC_THIS); } + ; + +type_specifier: DT_KEY_VOID { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("void")); } + | DT_KEY_CHAR { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("char")); } + | DT_KEY_SHORT { $$ = dt_decl_attr(DT_DA_SHORT); } + | DT_KEY_INT { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("int")); } + | DT_KEY_LONG { $$ = dt_decl_attr(DT_DA_LONG); } + | DT_KEY_FLOAT { $$ = dt_decl_spec(CTF_K_FLOAT, DUP("float")); } + | DT_KEY_DOUBLE { $$ = dt_decl_spec(CTF_K_FLOAT, DUP("double")); } + | DT_KEY_SIGNED { $$ = dt_decl_attr(DT_DA_SIGNED); } + | DT_KEY_UNSIGNED { $$ = dt_decl_attr(DT_DA_UNSIGNED); } + | DT_KEY_STRING { + $$ = dt_decl_spec(CTF_K_TYPEDEF, DUP("string")); + } + | DT_TOK_TNAME { $$ = dt_decl_spec(CTF_K_TYPEDEF, $1); } + | struct_or_union_specifier + | enum_specifier + ; + +type_qualifier: DT_KEY_CONST { $$ = dt_decl_attr(DT_DA_CONST); } + | DT_KEY_RESTRICT { $$ = dt_decl_attr(DT_DA_RESTRICT); } + | DT_KEY_VOLATILE { $$ = dt_decl_attr(DT_DA_VOLATILE); } + ; + +struct_or_union_specifier: + struct_or_union_definition struct_declaration_list '}' { + $$ = dt_scope_pop(); + } + | struct_or_union DT_TOK_IDENT { $$ = dt_decl_spec($1, $2); } + | struct_or_union DT_TOK_TNAME { $$ = dt_decl_spec($1, $2); } + ; + +struct_or_union_definition: + struct_or_union '{' { dt_decl_sou($1, NULL); } + | struct_or_union DT_TOK_IDENT '{' { dt_decl_sou($1, $2); } + | struct_or_union DT_TOK_TNAME '{' { dt_decl_sou($1, $2); } + ; + +struct_or_union: + DT_KEY_STRUCT { $$ = CTF_K_STRUCT; } + | DT_KEY_UNION { $$ = CTF_K_UNION; } + ; + +struct_declaration_list: + struct_declaration + | struct_declaration_list struct_declaration + ; + +init_declarator_list: + init_declarator + | init_declarator_list DT_TOK_COMMA init_declarator { + $$ = LINK($1, $3); + } + ; + +init_declarator: + declarator { + $$ = dt_node_decl(); + dt_decl_reset(); + } + ; + +struct_declaration: + specifier_qualifier_list struct_declarator_list ';' { + dt_decl_free(dt_decl_pop()); + } + ; + +specifier_qualifier_list: + type_specifier + | type_specifier specifier_qualifier_list { $$ = $2; } + | type_qualifier + | type_qualifier specifier_qualifier_list { $$ = $2; } + ; + +struct_declarator_list: + struct_declarator + | struct_declarator_list DT_TOK_COMMA struct_declarator + ; + +struct_declarator: + declarator { dt_decl_member(NULL); } + | DT_TOK_COLON constant_expression { dt_decl_member($2); } + | declarator DT_TOK_COLON constant_expression { + dt_decl_member($3); + } + ; + +enum_specifier: + enum_definition enumerator_list '}' { $$ = dt_scope_pop(); } + | DT_KEY_ENUM DT_TOK_IDENT { $$ = dt_decl_spec(CTF_K_ENUM, $2); } + | DT_KEY_ENUM DT_TOK_TNAME { $$ = dt_decl_spec(CTF_K_ENUM, $2); } + ; + +enum_definition: + DT_KEY_ENUM '{' { dt_decl_enum(NULL); } + | DT_KEY_ENUM DT_TOK_IDENT '{' { dt_decl_enum($2); } + | DT_KEY_ENUM DT_TOK_TNAME '{' { dt_decl_enum($2); } + ; + +enumerator_list: + enumerator + | enumerator_list DT_TOK_COMMA enumerator + ; + +enumerator: DT_TOK_IDENT { dt_decl_enumerator($1, NULL); } + | DT_TOK_IDENT DT_TOK_ASGN expression { + dt_decl_enumerator($1, $3); + } + ; + +declarator: direct_declarator + | pointer direct_declarator + ; + +direct_declarator: + DT_TOK_IDENT { $$ = dt_decl_ident($1); } + | lparen declarator DT_TOK_RPAR { $$ = $2; } + | direct_declarator array { dt_decl_array($2); } + | direct_declarator function { dt_decl_func($1, $2); } + ; + +lparen: DT_TOK_LPAR { dt_decl_top()->dd_attr |= DT_DA_PAREN; } + ; + +pointer: DT_TOK_MUL { $$ = dt_decl_ptr(); } + | DT_TOK_MUL type_qualifier_list { $$ = dt_decl_ptr(); } + | DT_TOK_MUL pointer { $$ = dt_decl_ptr(); } + | DT_TOK_MUL type_qualifier_list pointer { $$ = dt_decl_ptr(); } + ; + +type_qualifier_list: + type_qualifier + | type_qualifier_list type_qualifier { $$ = $2; } + ; + +parameter_type_list: + parameter_list + | DT_TOK_ELLIPSIS { $$ = dt_node_vatype(); } + | parameter_list DT_TOK_COMMA DT_TOK_ELLIPSIS { + $$ = LINK($1, dt_node_vatype()); + } + ; + +parameter_list: parameter_declaration + | parameter_list DT_TOK_COMMA parameter_declaration { + $$ = LINK($1, $3); + } + ; + +parameter_declaration: + parameter_declaration_specifiers { + $$ = dt_node_type(NULL); + } + | parameter_declaration_specifiers declarator { + $$ = dt_node_type(NULL); + } + | parameter_declaration_specifiers abstract_declarator { + $$ = dt_node_type(NULL); + } + ; + +type_name: specifier_qualifier_list { + $$ = dt_decl_pop(); + } + | specifier_qualifier_list abstract_declarator { + $$ = dt_decl_pop(); + } + ; + +abstract_declarator: + pointer + | direct_abstract_declarator + | pointer direct_abstract_declarator + ; + +direct_abstract_declarator: + lparen abstract_declarator DT_TOK_RPAR { $$ = $2; } + | direct_abstract_declarator array { dt_decl_array($2); } + | array { dt_decl_array($1); $$ = NULL; } + | direct_abstract_declarator function { dt_decl_func($1, $2); } + | function { dt_decl_func(NULL, $1); } + ; + +array: DT_TOK_LBRAC { dt_scope_push(NULL, CTF_ERR); } + array_parameters DT_TOK_RBRAC { + dt_scope_pop(); + $$ = $3; + } + ; + +array_parameters: + /* empty */ { $$ = NULL; } + | constant_expression { $$ = $1; } + | parameter_type_list { $$ = $1; } + ; + +function: DT_TOK_LPAR { dt_scope_push(NULL, CTF_ERR); } + function_parameters DT_TOK_RPAR { + dt_scope_pop(); + $$ = $3; + } + ; + +function_parameters: + /* empty */ { $$ = NULL; } + | parameter_type_list { $$ = $1; } + ; + +%% diff --git a/lib/libdtrace/common/dt_handle.c b/lib/libdtrace/common/dt_handle.c new file mode 100644 index 000000000000..ea039e993bd2 --- /dev/null +++ b/lib/libdtrace/common/dt_handle.c @@ -0,0 +1,483 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stddef.h> +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> +#if defined(sun) +#include <alloca.h> +#endif + +#include <dt_impl.h> +#include <dt_program.h> + +static const char _dt_errprog[] = +"dtrace:::ERROR" +"{" +" trace(arg1);" +" trace(arg2);" +" trace(arg3);" +" trace(arg4);" +" trace(arg5);" +"}"; + +int +dtrace_handle_err(dtrace_hdl_t *dtp, dtrace_handle_err_f *hdlr, void *arg) +{ + dtrace_prog_t *pgp = NULL; + dt_stmt_t *stp; + dtrace_ecbdesc_t *edp; + + /* + * We don't currently support multiple error handlers. + */ + if (dtp->dt_errhdlr != NULL) + return (dt_set_errno(dtp, EALREADY)); + + /* + * If the DTRACEOPT_GRABANON is enabled, the anonymous enabling will + * already have a dtrace:::ERROR probe enabled; save 'hdlr' and 'arg' + * but do not bother compiling and enabling _dt_errprog. + */ + if (dtp->dt_options[DTRACEOPT_GRABANON] != DTRACEOPT_UNSET) + goto out; + + if ((pgp = dtrace_program_strcompile(dtp, _dt_errprog, + DTRACE_PROBESPEC_NAME, DTRACE_C_ZDEFS, 0, NULL)) == NULL) + return (dt_set_errno(dtp, dtrace_errno(dtp))); + + stp = dt_list_next(&pgp->dp_stmts); + assert(stp != NULL); + + edp = stp->ds_desc->dtsd_ecbdesc; + assert(edp != NULL); + edp->dted_uarg = DT_ECB_ERROR; + +out: + dtp->dt_errhdlr = hdlr; + dtp->dt_errarg = arg; + dtp->dt_errprog = pgp; + + return (0); +} + +int +dtrace_handle_drop(dtrace_hdl_t *dtp, dtrace_handle_drop_f *hdlr, void *arg) +{ + if (dtp->dt_drophdlr != NULL) + return (dt_set_errno(dtp, EALREADY)); + + dtp->dt_drophdlr = hdlr; + dtp->dt_droparg = arg; + + return (0); +} + +int +dtrace_handle_proc(dtrace_hdl_t *dtp, dtrace_handle_proc_f *hdlr, void *arg) +{ + if (dtp->dt_prochdlr != NULL) + return (dt_set_errno(dtp, EALREADY)); + + dtp->dt_prochdlr = hdlr; + dtp->dt_procarg = arg; + + return (0); +} + +int +dtrace_handle_buffered(dtrace_hdl_t *dtp, dtrace_handle_buffered_f *hdlr, + void *arg) +{ + if (dtp->dt_bufhdlr != NULL) + return (dt_set_errno(dtp, EALREADY)); + + if (hdlr == NULL) + return (dt_set_errno(dtp, EINVAL)); + + dtp->dt_bufhdlr = hdlr; + dtp->dt_bufarg = arg; + + return (0); +} + +int +dtrace_handle_setopt(dtrace_hdl_t *dtp, dtrace_handle_setopt_f *hdlr, + void *arg) +{ + if (hdlr == NULL) + return (dt_set_errno(dtp, EINVAL)); + + dtp->dt_setopthdlr = hdlr; + dtp->dt_setoptarg = arg; + + return (0); +} + +#define DT_REC(type, ndx) *((type *)((uintptr_t)data->dtpda_data + \ + epd->dtepd_rec[(ndx)].dtrd_offset)) + +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 +dt_handle_liberr(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, + const char *faultstr) +{ + dtrace_probedesc_t *errpd = data->dtpda_pdesc; + dtrace_errdata_t err; + const int slop = 80; + char *str; + int len; + + err.dteda_edesc = data->dtpda_edesc; + err.dteda_pdesc = errpd; + err.dteda_cpu = data->dtpda_cpu; + err.dteda_action = -1; + err.dteda_offset = -1; + err.dteda_fault = DTRACEFLT_LIBRARY; + err.dteda_addr = 0; + + len = strlen(faultstr) + + strlen(errpd->dtpd_provider) + strlen(errpd->dtpd_mod) + + strlen(errpd->dtpd_name) + strlen(errpd->dtpd_func) + + slop; + + str = alloca(len); + + (void) snprintf(str, len, "error on enabled probe ID %u " + "(ID %u: %s:%s:%s:%s): %s\n", + data->dtpda_edesc->dtepd_epid, + errpd->dtpd_id, errpd->dtpd_provider, + errpd->dtpd_mod, errpd->dtpd_func, + errpd->dtpd_name, faultstr); + + 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); +} + +#define DROPTAG(x) x, #x + +static const struct { + dtrace_dropkind_t dtdrg_kind; + char *dtdrg_tag; +} _dt_droptags[] = { + { DROPTAG(DTRACEDROP_PRINCIPAL) }, + { DROPTAG(DTRACEDROP_AGGREGATION) }, + { DROPTAG(DTRACEDROP_DYNAMIC) }, + { DROPTAG(DTRACEDROP_DYNRINSE) }, + { DROPTAG(DTRACEDROP_DYNDIRTY) }, + { DROPTAG(DTRACEDROP_SPEC) }, + { DROPTAG(DTRACEDROP_SPECBUSY) }, + { DROPTAG(DTRACEDROP_SPECUNAVAIL) }, + { DROPTAG(DTRACEDROP_DBLERROR) }, + { DROPTAG(DTRACEDROP_STKSTROVERFLOW) }, + { 0, NULL } +}; + +static const char * +dt_droptag(dtrace_dropkind_t kind) +{ + int i; + + for (i = 0; _dt_droptags[i].dtdrg_tag != NULL; i++) { + if (_dt_droptags[i].dtdrg_kind == kind) + return (_dt_droptags[i].dtdrg_tag); + } + + return ("DTRACEDROP_UNKNOWN"); +} + +int +dt_handle_cpudrop(dtrace_hdl_t *dtp, processorid_t cpu, + dtrace_dropkind_t what, uint64_t howmany) +{ + dtrace_dropdata_t drop; + char str[80], *s; + int size; + + assert(what == DTRACEDROP_PRINCIPAL || what == DTRACEDROP_AGGREGATION); + + bzero(&drop, sizeof (drop)); + drop.dtdda_handle = dtp; + drop.dtdda_cpu = cpu; + drop.dtdda_kind = what; + drop.dtdda_drops = howmany; + drop.dtdda_msg = str; + + if (dtp->dt_droptags) { + (void) snprintf(str, sizeof (str), "[%s] ", dt_droptag(what)); + s = &str[strlen(str)]; + size = sizeof (str) - (s - str); + } else { + s = str; + size = sizeof (str); + } + + (void) snprintf(s, size, "%llu %sdrop%s on CPU %d\n", + howmany, what == DTRACEDROP_PRINCIPAL ? "" : "aggregation ", + howmany > 1 ? "s" : "", cpu); + + if (dtp->dt_drophdlr == NULL) + return (dt_set_errno(dtp, EDT_DROPABORT)); + + if ((*dtp->dt_drophdlr)(&drop, dtp->dt_droparg) == DTRACE_HANDLE_ABORT) + return (dt_set_errno(dtp, EDT_DROPABORT)); + + return (0); +} + +static const struct { + dtrace_dropkind_t dtdrt_kind; + uintptr_t dtdrt_offset; + const char *dtdrt_str; + const char *dtdrt_msg; +} _dt_droptab[] = { + { DTRACEDROP_DYNAMIC, + offsetof(dtrace_status_t, dtst_dyndrops), + "dynamic variable drop" }, + + { DTRACEDROP_DYNRINSE, + offsetof(dtrace_status_t, dtst_dyndrops_rinsing), + "dynamic variable drop", " with non-empty rinsing list" }, + + { DTRACEDROP_DYNDIRTY, + offsetof(dtrace_status_t, dtst_dyndrops_dirty), + "dynamic variable drop", " with non-empty dirty list" }, + + { DTRACEDROP_SPEC, + offsetof(dtrace_status_t, dtst_specdrops), + "speculative drop" }, + + { DTRACEDROP_SPECBUSY, + offsetof(dtrace_status_t, dtst_specdrops_busy), + "failed speculation", " (available buffer(s) still busy)" }, + + { DTRACEDROP_SPECUNAVAIL, + offsetof(dtrace_status_t, dtst_specdrops_unavail), + "failed speculation", " (no speculative buffer available)" }, + + { DTRACEDROP_STKSTROVERFLOW, + offsetof(dtrace_status_t, dtst_stkstroverflows), + "jstack()/ustack() string table overflow" }, + + { DTRACEDROP_DBLERROR, + offsetof(dtrace_status_t, dtst_dblerrors), + "error", " in ERROR probe enabling" }, + + { 0, 0, NULL } +}; + +int +dt_handle_status(dtrace_hdl_t *dtp, dtrace_status_t *old, dtrace_status_t *new) +{ + dtrace_dropdata_t drop; + char str[80], *s; + uintptr_t base = (uintptr_t)new, obase = (uintptr_t)old; + int i, size; + + bzero(&drop, sizeof (drop)); + drop.dtdda_handle = dtp; + drop.dtdda_cpu = DTRACE_CPUALL; + drop.dtdda_msg = str; + + /* + * First, check to see if we've been killed -- in which case we abort. + */ + if (new->dtst_killed && !old->dtst_killed) + return (dt_set_errno(dtp, EDT_BRICKED)); + + for (i = 0; _dt_droptab[i].dtdrt_str != NULL; i++) { + uintptr_t naddr = base + _dt_droptab[i].dtdrt_offset; + uintptr_t oaddr = obase + _dt_droptab[i].dtdrt_offset; + + uint64_t nval = *((uint64_t *)naddr); + uint64_t oval = *((uint64_t *)oaddr); + + if (nval == oval) + continue; + + if (dtp->dt_droptags) { + (void) snprintf(str, sizeof (str), "[%s] ", + dt_droptag(_dt_droptab[i].dtdrt_kind)); + s = &str[strlen(str)]; + size = sizeof (str) - (s - str); + } else { + s = str; + size = sizeof (str); + } + + (void) snprintf(s, size, "%llu %s%s%s\n", nval - oval, + _dt_droptab[i].dtdrt_str, (nval - oval > 1) ? "s" : "", + _dt_droptab[i].dtdrt_msg != NULL ? + _dt_droptab[i].dtdrt_msg : ""); + + drop.dtdda_kind = _dt_droptab[i].dtdrt_kind; + drop.dtdda_total = nval; + drop.dtdda_drops = nval - oval; + + if (dtp->dt_drophdlr == NULL) + return (dt_set_errno(dtp, EDT_DROPABORT)); + + if ((*dtp->dt_drophdlr)(&drop, + dtp->dt_droparg) == DTRACE_HANDLE_ABORT) + return (dt_set_errno(dtp, EDT_DROPABORT)); + } + + return (0); +} + +int +dt_handle_setopt(dtrace_hdl_t *dtp, dtrace_setoptdata_t *data) +{ + void *arg = dtp->dt_setoptarg; + + if (dtp->dt_setopthdlr == NULL) + return (0); + + if ((*dtp->dt_setopthdlr)(data, arg) == DTRACE_HANDLE_ABORT) + return (dt_set_errno(dtp, EDT_DIRABORT)); + + return (0); +} + +int +dt_handle(dtrace_hdl_t *dtp, dtrace_probedata_t *data) +{ + dtrace_eprobedesc_t *epd = data->dtpda_edesc; + int rval; + + switch (epd->dtepd_uarg) { + case DT_ECB_ERROR: + rval = dt_handle_err(dtp, data); + break; + + default: + return (DTRACE_CONSUME_THIS); + } + + if (rval == 0) + return (DTRACE_CONSUME_NEXT); + + return (DTRACE_CONSUME_ERROR); +} diff --git a/lib/libdtrace/common/dt_ident.c b/lib/libdtrace/common/dt_ident.c new file mode 100644 index 000000000000..13adbb45e1a7 --- /dev/null +++ b/lib/libdtrace/common/dt_ident.c @@ -0,0 +1,1047 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#if defined(sun) +#include <sys/sysmacros.h> +#endif +#include <strings.h> +#include <stdlib.h> +#if defined(sun) +#include <alloca.h> +#endif +#include <assert.h> +#include <errno.h> +#include <ctype.h> +#if defined(sun) +#include <sys/procfs_isa.h> +#endif +#include <limits.h> + +#include <dt_ident.h> +#include <dt_parser.h> +#include <dt_provider.h> +#include <dt_strtab.h> +#include <dt_impl.h> + +/* + * Common code for cooking an identifier that uses a typed signature list (we + * use this for associative arrays and functions). If the argument list is + * of the same length and types, then return the return type. Otherwise + * print an appropriate compiler error message and abort the compile. + */ +static void +dt_idcook_sign(dt_node_t *dnp, dt_ident_t *idp, + int argc, dt_node_t *args, const char *prefix, const char *suffix) +{ + dt_idsig_t *isp = idp->di_data; + int i, compat, mismatch, arglimit, iskey; + + char n1[DT_TYPE_NAMELEN]; + char n2[DT_TYPE_NAMELEN]; + + iskey = idp->di_kind == DT_IDENT_ARRAY || idp->di_kind == DT_IDENT_AGG; + + if (isp->dis_varargs >= 0) { + mismatch = argc < isp->dis_varargs; + arglimit = isp->dis_varargs; + } else if (isp->dis_optargs >= 0) { + mismatch = (argc < isp->dis_optargs || argc > isp->dis_argc); + arglimit = argc; + } else { + mismatch = argc != isp->dis_argc; + arglimit = isp->dis_argc; + } + + if (mismatch) { + xyerror(D_PROTO_LEN, "%s%s%s prototype mismatch: %d %s%s" + "passed, %s%d expected\n", prefix, idp->di_name, suffix, + argc, iskey ? "key" : "arg", argc == 1 ? " " : "s ", + isp->dis_optargs >= 0 ? "at least " : "", + isp->dis_optargs >= 0 ? isp->dis_optargs : arglimit); + } + + for (i = 0; i < arglimit; i++, args = args->dn_list) { + if (isp->dis_args[i].dn_ctfp != NULL) + compat = dt_node_is_argcompat(&isp->dis_args[i], args); + else + compat = 1; /* "@" matches any type */ + + if (!compat) { + xyerror(D_PROTO_ARG, + "%s%s%s %s #%d is incompatible with " + "prototype:\n\tprototype: %s\n\t%9s: %s\n", + prefix, idp->di_name, suffix, + iskey ? "key" : "argument", i + 1, + dt_node_type_name(&isp->dis_args[i], n1, + sizeof (n1)), + iskey ? "key" : "argument", + dt_node_type_name(args, n2, sizeof (n2))); + } + } + + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); +} + +/* + * Cook an associative array identifier. If this is the first time we are + * cooking this array, create its signature based on the argument list. + * Otherwise validate the argument list against the existing signature. + */ +static void +dt_idcook_assc(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) +{ + if (idp->di_data == NULL) { + dt_idsig_t *isp = idp->di_data = malloc(sizeof (dt_idsig_t)); + char n[DT_TYPE_NAMELEN]; + int i; + + if (isp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + isp->dis_varargs = -1; + isp->dis_optargs = -1; + isp->dis_argc = argc; + isp->dis_args = NULL; + isp->dis_auxinfo = 0; + + if (argc != 0 && (isp->dis_args = calloc(argc, + sizeof (dt_node_t))) == NULL) { + idp->di_data = NULL; + free(isp); + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + } + + /* + * If this identifier has not been explicitly declared earlier, + * set the identifier's base type to be our special type <DYN>. + * If this ident is an aggregation, it will remain as is. If + * this ident is an associative array, it will be reassigned + * based on the result type of the first assignment statement. + */ + if (!(idp->di_flags & DT_IDFLG_DECL)) { + idp->di_ctfp = DT_DYN_CTFP(yypcb->pcb_hdl); + idp->di_type = DT_DYN_TYPE(yypcb->pcb_hdl); + } + + for (i = 0; i < argc; i++, args = args->dn_list) { + if (dt_node_is_dynamic(args) || dt_node_is_void(args)) { + xyerror(D_KEY_TYPE, "%s expression may not be " + "used as %s index: key #%d\n", + dt_node_type_name(args, n, sizeof (n)), + dt_idkind_name(idp->di_kind), i + 1); + } + + dt_node_type_propagate(args, &isp->dis_args[i]); + isp->dis_args[i].dn_list = &isp->dis_args[i + 1]; + } + + if (argc != 0) + isp->dis_args[argc - 1].dn_list = NULL; + + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); + + } else { + dt_idcook_sign(dnp, idp, argc, args, + idp->di_kind == DT_IDENT_AGG ? "@" : "", "[ ]"); + } +} + +/* + * Cook a function call. If this is the first time we are cooking this + * identifier, create its type signature based on predefined prototype stored + * in di_iarg. We then validate the argument list against this signature. + */ +static void +dt_idcook_func(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) +{ + if (idp->di_data == NULL) { + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dtrace_typeinfo_t dtt; + dt_idsig_t *isp; + char *s, *p1, *p2; + int i = 0; + + assert(idp->di_iarg != NULL); + s = alloca(strlen(idp->di_iarg) + 1); + (void) strcpy(s, idp->di_iarg); + + if ((p2 = strrchr(s, ')')) != NULL) + *p2 = '\0'; /* mark end of parameter list string */ + + if ((p1 = strchr(s, '(')) != NULL) + *p1++ = '\0'; /* mark end of return type string */ + + if (p1 == NULL || p2 == NULL) { + xyerror(D_UNKNOWN, "internal error: malformed entry " + "for built-in function %s\n", idp->di_name); + } + + for (p2 = p1; *p2 != '\0'; p2++) { + if (!isspace(*p2)) { + i++; + break; + } + } + + for (p2 = strchr(p2, ','); p2++ != NULL; i++) + p2 = strchr(p2, ','); + + /* + * We first allocate a new ident signature structure with the + * appropriate number of argument entries, and then look up + * the return type and store its CTF data in di_ctfp/type. + */ + if ((isp = idp->di_data = malloc(sizeof (dt_idsig_t))) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + isp->dis_varargs = -1; + isp->dis_optargs = -1; + isp->dis_argc = i; + isp->dis_args = NULL; + isp->dis_auxinfo = 0; + + if (i != 0 && (isp->dis_args = calloc(i, + sizeof (dt_node_t))) == NULL) { + idp->di_data = NULL; + free(isp); + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + } + + if (dt_type_lookup(s, &dtt) == -1) { + xyerror(D_UNKNOWN, "failed to resolve type of %s (%s):" + " %s\n", idp->di_name, s, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + } + + if (idp->di_kind == DT_IDENT_AGGFUNC) { + idp->di_ctfp = DT_DYN_CTFP(dtp); + idp->di_type = DT_DYN_TYPE(dtp); + } else { + idp->di_ctfp = dtt.dtt_ctfp; + idp->di_type = dtt.dtt_type; + } + + /* + * For each comma-delimited parameter in the prototype string, + * we look up the corresponding type and store its CTF data in + * the corresponding location in dis_args[]. We also recognize + * the special type string "@" to indicate that the specified + * parameter may be a D expression of *any* type (represented + * as a dis_args[] element with ctfp = NULL, type == CTF_ERR). + * If a varargs "..." is present, we record the argument index + * in dis_varargs for the benefit of dt_idcook_sign(), above. + * If the type of an argument is enclosed in square brackets + * (e.g. "[int]"), the argument is considered optional: the + * argument may be absent, but if it is present, it must be of + * the specified type. Note that varargs may not optional, + * optional arguments may not follow varargs, and non-optional + * arguments may not follow optional arguments. + */ + for (i = 0; i < isp->dis_argc; i++, p1 = p2) { + while (isspace(*p1)) + p1++; /* skip leading whitespace */ + + if ((p2 = strchr(p1, ',')) == NULL) + p2 = p1 + strlen(p1); + else + *p2++ = '\0'; + + if (strcmp(p1, "@") == 0 || strcmp(p1, "...") == 0) { + isp->dis_args[i].dn_ctfp = NULL; + isp->dis_args[i].dn_type = CTF_ERR; + if (*p1 == '.') + isp->dis_varargs = i; + continue; + } + + if (*p1 == '[' && p1[strlen(p1) - 1] == ']') { + if (isp->dis_varargs != -1) { + xyerror(D_UNKNOWN, "optional arg#%d " + "may not follow variable arg#%d\n", + i + 1, isp->dis_varargs + 1); + } + + if (isp->dis_optargs == -1) + isp->dis_optargs = i; + + p1[strlen(p1) - 1] = '\0'; + p1++; + } else if (isp->dis_optargs != -1) { + xyerror(D_UNKNOWN, "required arg#%d may not " + "follow optional arg#%d\n", i + 1, + isp->dis_optargs + 1); + } + + if (dt_type_lookup(p1, &dtt) == -1) { + xyerror(D_UNKNOWN, "failed to resolve type of " + "%s arg#%d (%s): %s\n", idp->di_name, i + 1, + p1, dtrace_errmsg(dtp, dtrace_errno(dtp))); + } + + dt_node_type_assign(&isp->dis_args[i], + dtt.dtt_ctfp, dtt.dtt_type); + } + } + + dt_idcook_sign(dnp, idp, argc, args, "", "( )"); +} + +/* + * Cook a reference to the dynamically typed args[] array. We verify that the + * reference is using a single integer constant, and then construct a new ident + * representing the appropriate type or translation specifically for this node. + */ +static void +dt_idcook_args(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *ap) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dt_probe_t *prp = yypcb->pcb_probe; + + dt_node_t tag, *nnp, *xnp; + dt_xlator_t *dxp; + dt_ident_t *xidp; + + char n1[DT_TYPE_NAMELEN]; + char n2[DT_TYPE_NAMELEN]; + + if (argc != 1) { + xyerror(D_PROTO_LEN, "%s[ ] prototype mismatch: %d arg%s" + "passed, 1 expected\n", idp->di_name, argc, + argc == 1 ? " " : "s "); + } + + if (ap->dn_kind != DT_NODE_INT) { + xyerror(D_PROTO_ARG, "%s[ ] argument #1 is incompatible with " + "prototype:\n\tprototype: %s\n\t argument: %s\n", + idp->di_name, "integer constant", + dt_type_name(ap->dn_ctfp, ap->dn_type, n1, sizeof (n1))); + } + + if (yypcb->pcb_pdesc == NULL) { + xyerror(D_ARGS_NONE, "%s[ ] may not be referenced outside " + "of a probe clause\n", idp->di_name); + } + + if (prp == NULL) { + xyerror(D_ARGS_MULTI, + "%s[ ] may not be referenced because probe description %s " + "matches an unstable set of probes\n", idp->di_name, + dtrace_desc2str(yypcb->pcb_pdesc, n1, sizeof (n1))); + } + + if (ap->dn_value >= prp->pr_argc) { + xyerror(D_ARGS_IDX, "index %lld is out of range for %s %s[ ]\n", + (longlong_t)ap->dn_value, dtrace_desc2str(yypcb->pcb_pdesc, + n1, sizeof (n1)), idp->di_name); + } + + /* + * Look up the native and translated argument types for the probe. + * If no translation is needed, these will be the same underlying node. + * If translation is needed, look up the appropriate translator. Once + * we have the appropriate node, create a new dt_ident_t for this node, + * assign it the appropriate attributes, and set the type of 'dnp'. + */ + xnp = prp->pr_xargv[ap->dn_value]; + nnp = prp->pr_nargv[prp->pr_mapping[ap->dn_value]]; + + if (xnp->dn_type == CTF_ERR) { + xyerror(D_ARGS_TYPE, "failed to resolve translated type for " + "%s[%lld]\n", idp->di_name, (longlong_t)ap->dn_value); + } + + if (nnp->dn_type == CTF_ERR) { + xyerror(D_ARGS_TYPE, "failed to resolve native type for " + "%s[%lld]\n", idp->di_name, (longlong_t)ap->dn_value); + } + + if (dtp->dt_xlatemode == DT_XL_STATIC && ( + nnp == xnp || dt_node_is_argcompat(nnp, xnp))) { + dnp->dn_ident = dt_ident_create(idp->di_name, idp->di_kind, + idp->di_flags | DT_IDFLG_ORPHAN, idp->di_id, idp->di_attr, + idp->di_vers, idp->di_ops, idp->di_iarg, idp->di_gen); + + if (dnp->dn_ident == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + dt_node_type_assign(dnp, + prp->pr_argv[ap->dn_value].dtt_ctfp, + prp->pr_argv[ap->dn_value].dtt_type); + + } else if ((dxp = dt_xlator_lookup(dtp, + nnp, xnp, DT_XLATE_FUZZY)) != NULL || ( + dxp = dt_xlator_lookup(dtp, dt_probe_tag(prp, ap->dn_value, &tag), + xnp, DT_XLATE_EXACT | DT_XLATE_EXTERN)) != NULL) { + + xidp = dt_xlator_ident(dxp, xnp->dn_ctfp, xnp->dn_type); + + dnp->dn_ident = dt_ident_create(idp->di_name, xidp->di_kind, + xidp->di_flags | DT_IDFLG_ORPHAN, idp->di_id, idp->di_attr, + idp->di_vers, idp->di_ops, idp->di_iarg, idp->di_gen); + + if (dnp->dn_ident == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + if (dt_xlator_dynamic(dxp)) + dxp->dx_arg = (int)ap->dn_value; + + /* + * Propagate relevant members from the translator's internal + * dt_ident_t. This code must be kept in sync with the state + * that is initialized for idents in dt_xlator_create(). + */ + dnp->dn_ident->di_data = xidp->di_data; + dnp->dn_ident->di_ctfp = xidp->di_ctfp; + dnp->dn_ident->di_type = xidp->di_type; + + dt_node_type_assign(dnp, DT_DYN_CTFP(dtp), DT_DYN_TYPE(dtp)); + + } else { + xyerror(D_ARGS_XLATOR, "translator for %s[%lld] from %s to %s " + "is not defined\n", idp->di_name, (longlong_t)ap->dn_value, + dt_node_type_name(nnp, n1, sizeof (n1)), + dt_node_type_name(xnp, n2, sizeof (n2))); + } + + assert(dnp->dn_ident->di_flags & DT_IDFLG_ORPHAN); + assert(dnp->dn_ident->di_id == idp->di_id); +} + +static void +dt_idcook_regs(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *ap) +{ + dtrace_typeinfo_t dtt; + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + char n[DT_TYPE_NAMELEN]; + + if (argc != 1) { + xyerror(D_PROTO_LEN, "%s[ ] prototype mismatch: %d arg%s" + "passed, 1 expected\n", idp->di_name, + argc, argc == 1 ? " " : "s "); + } + + if (ap->dn_kind != DT_NODE_INT) { + xyerror(D_PROTO_ARG, "%s[ ] argument #1 is incompatible with " + "prototype:\n\tprototype: %s\n\t argument: %s\n", + idp->di_name, "integer constant", + dt_type_name(ap->dn_ctfp, ap->dn_type, n, sizeof (n))); + } + + if ((ap->dn_flags & DT_NF_SIGNED) && (int64_t)ap->dn_value < 0) { + xyerror(D_REGS_IDX, "index %lld is out of range for array %s\n", + (longlong_t)ap->dn_value, idp->di_name); + } + + if (dt_type_lookup("uint64_t", &dtt) == -1) { + xyerror(D_UNKNOWN, "failed to resolve type of %s: %s\n", + idp->di_name, dtrace_errmsg(dtp, dtrace_errno(dtp))); + } + + idp->di_ctfp = dtt.dtt_ctfp; + idp->di_type = dtt.dtt_type; + + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); +} + +/*ARGSUSED*/ +static void +dt_idcook_type(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) +{ + if (idp->di_type == CTF_ERR) { + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dtrace_typeinfo_t dtt; + + if (dt_type_lookup(idp->di_iarg, &dtt) == -1) { + xyerror(D_UNKNOWN, + "failed to resolve type %s for identifier %s: %s\n", + (const char *)idp->di_iarg, idp->di_name, + dtrace_errmsg(dtp, dtrace_errno(dtp))); + } + + idp->di_ctfp = dtt.dtt_ctfp; + idp->di_type = dtt.dtt_type; + } + + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); +} + +/*ARGSUSED*/ +static void +dt_idcook_thaw(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) +{ + if (idp->di_ctfp != NULL && idp->di_type != CTF_ERR) + dt_node_type_assign(dnp, idp->di_ctfp, idp->di_type); +} + +static void +dt_idcook_inline(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) +{ + if (idp->di_kind == DT_IDENT_ARRAY) + dt_idcook_assc(dnp, idp, argc, args); + else + dt_idcook_thaw(dnp, idp, argc, args); +} + +static void +dt_iddtor_sign(dt_ident_t *idp) +{ + if (idp->di_data != NULL) + free(((dt_idsig_t *)idp->di_data)->dis_args); + free(idp->di_data); +} + +static void +dt_iddtor_free(dt_ident_t *idp) +{ + free(idp->di_data); +} + +static void +dt_iddtor_inline(dt_ident_t *idp) +{ + dt_idnode_t *inp = idp->di_iarg; + + if (inp != NULL) { + dt_node_link_free(&inp->din_list); + + if (inp->din_hash != NULL) + dt_idhash_destroy(inp->din_hash); + + free(inp->din_argv); + free(inp); + } + + if (idp->di_kind == DT_IDENT_ARRAY) + dt_iddtor_sign(idp); + else + dt_iddtor_free(idp); +} + +/*ARGSUSED*/ +static void +dt_iddtor_none(dt_ident_t *idp) +{ + /* do nothing */ +} + +static void +dt_iddtor_probe(dt_ident_t *idp) +{ + if (idp->di_data != NULL) + dt_probe_destroy(idp->di_data); +} + +static size_t +dt_idsize_type(dt_ident_t *idp) +{ + return (ctf_type_size(idp->di_ctfp, idp->di_type)); +} + +/*ARGSUSED*/ +static size_t +dt_idsize_none(dt_ident_t *idp) +{ + return (0); +} + +const dt_idops_t dt_idops_assc = { + dt_idcook_assc, + dt_iddtor_sign, + dt_idsize_none, +}; + +const dt_idops_t dt_idops_func = { + dt_idcook_func, + dt_iddtor_sign, + dt_idsize_none, +}; + +const dt_idops_t dt_idops_args = { + dt_idcook_args, + dt_iddtor_none, + dt_idsize_none, +}; + +const dt_idops_t dt_idops_regs = { + dt_idcook_regs, + dt_iddtor_free, + dt_idsize_none, +}; + +const dt_idops_t dt_idops_type = { + dt_idcook_type, + dt_iddtor_free, + dt_idsize_type, +}; + +const dt_idops_t dt_idops_thaw = { + dt_idcook_thaw, + dt_iddtor_free, + dt_idsize_type, +}; + +const dt_idops_t dt_idops_inline = { + dt_idcook_inline, + dt_iddtor_inline, + dt_idsize_type, +}; + +const dt_idops_t dt_idops_probe = { + dt_idcook_thaw, + dt_iddtor_probe, + dt_idsize_none, +}; + +static void +dt_idhash_populate(dt_idhash_t *dhp) +{ + const dt_ident_t *idp = dhp->dh_tmpl; + + dhp->dh_tmpl = NULL; /* clear dh_tmpl first to avoid recursion */ + dt_dprintf("populating %s idhash from %p\n", dhp->dh_name, (void *)idp); + + for (; idp->di_name != NULL; idp++) { + if (dt_idhash_insert(dhp, idp->di_name, + idp->di_kind, idp->di_flags, idp->di_id, idp->di_attr, + idp->di_vers, idp->di_ops ? idp->di_ops : &dt_idops_thaw, + idp->di_iarg, 0) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + } +} + +dt_idhash_t * +dt_idhash_create(const char *name, const dt_ident_t *tmpl, + uint_t min, uint_t max) +{ + dt_idhash_t *dhp; + size_t size; + + assert(min <= max); + + size = sizeof (dt_idhash_t) + + sizeof (dt_ident_t *) * (_dtrace_strbuckets - 1); + + if ((dhp = malloc(size)) == NULL) + return (NULL); + + bzero(dhp, size); + dhp->dh_name = name; + dhp->dh_tmpl = tmpl; + dhp->dh_nextid = min; + dhp->dh_minid = min; + dhp->dh_maxid = max; + dhp->dh_hashsz = _dtrace_strbuckets; + + return (dhp); +} + +/* + * Destroy an entire identifier hash. This must be done using two passes with + * an inlined version of dt_ident_destroy() to avoid referencing freed memory. + * In the first pass di_dtor() is called for all identifiers; then the second + * pass frees the actual dt_ident_t's. These must be done separately because + * a di_dtor() may operate on data structures which contain references to other + * identifiers inside of this hash itself (e.g. a global inline definition + * which contains a parse tree that refers to another global variable). + */ +void +dt_idhash_destroy(dt_idhash_t *dhp) +{ + dt_ident_t *idp, *next; + ulong_t i; + + for (i = 0; i < dhp->dh_hashsz; i++) { + for (idp = dhp->dh_hash[i]; idp != NULL; idp = next) { + next = idp->di_next; + idp->di_ops->di_dtor(idp); + } + } + + for (i = 0; i < dhp->dh_hashsz; i++) { + for (idp = dhp->dh_hash[i]; idp != NULL; idp = next) { + next = idp->di_next; + free(idp->di_name); + free(idp); + } + } + + free(dhp); +} + +void +dt_idhash_update(dt_idhash_t *dhp) +{ + uint_t nextid = dhp->dh_minid; + dt_ident_t *idp; + ulong_t i; + + for (i = 0; i < dhp->dh_hashsz; i++) { + for (idp = dhp->dh_hash[i]; idp != NULL; idp = idp->di_next) { + /* + * Right now we're hard coding which types need to be + * reset, but ideally this would be done dynamically. + */ + if (idp->di_kind == DT_IDENT_ARRAY || + idp->di_kind == DT_IDENT_SCALAR || + idp->di_kind == DT_IDENT_AGG) + nextid = MAX(nextid, idp->di_id + 1); + } + } + + dhp->dh_nextid = nextid; +} + +dt_ident_t * +dt_idhash_lookup(dt_idhash_t *dhp, const char *name) +{ + size_t len; + ulong_t h = dt_strtab_hash(name, &len) % dhp->dh_hashsz; + dt_ident_t *idp; + + if (dhp->dh_tmpl != NULL) + dt_idhash_populate(dhp); /* fill hash w/ initial population */ + + for (idp = dhp->dh_hash[h]; idp != NULL; idp = idp->di_next) { + if (strcmp(idp->di_name, name) == 0) + return (idp); + } + + return (NULL); +} + +int +dt_idhash_nextid(dt_idhash_t *dhp, uint_t *p) +{ + if (dhp->dh_nextid >= dhp->dh_maxid) + return (-1); /* no more id's are free to allocate */ + + *p = dhp->dh_nextid++; + return (0); +} + +ulong_t +dt_idhash_size(const dt_idhash_t *dhp) +{ + return (dhp->dh_nelems); +} + +const char * +dt_idhash_name(const dt_idhash_t *dhp) +{ + return (dhp->dh_name); +} + +dt_ident_t * +dt_idhash_insert(dt_idhash_t *dhp, const char *name, ushort_t kind, + ushort_t flags, uint_t id, dtrace_attribute_t attr, uint_t vers, + const dt_idops_t *ops, void *iarg, ulong_t gen) +{ + dt_ident_t *idp; + ulong_t h; + + if (dhp->dh_tmpl != NULL) + dt_idhash_populate(dhp); /* fill hash w/ initial population */ + + idp = dt_ident_create(name, kind, flags, id, + attr, vers, ops, iarg, gen); + + if (idp == NULL) + return (NULL); + + h = dt_strtab_hash(name, NULL) % dhp->dh_hashsz; + idp->di_next = dhp->dh_hash[h]; + + dhp->dh_hash[h] = idp; + dhp->dh_nelems++; + + if (dhp->dh_defer != NULL) + dhp->dh_defer(dhp, idp); + + return (idp); +} + +void +dt_idhash_xinsert(dt_idhash_t *dhp, dt_ident_t *idp) +{ + ulong_t h; + + if (dhp->dh_tmpl != NULL) + dt_idhash_populate(dhp); /* fill hash w/ initial population */ + + h = dt_strtab_hash(idp->di_name, NULL) % dhp->dh_hashsz; + idp->di_next = dhp->dh_hash[h]; + idp->di_flags &= ~DT_IDFLG_ORPHAN; + + dhp->dh_hash[h] = idp; + dhp->dh_nelems++; + + if (dhp->dh_defer != NULL) + dhp->dh_defer(dhp, idp); +} + +void +dt_idhash_delete(dt_idhash_t *dhp, dt_ident_t *key) +{ + size_t len; + ulong_t h = dt_strtab_hash(key->di_name, &len) % dhp->dh_hashsz; + dt_ident_t **pp = &dhp->dh_hash[h]; + dt_ident_t *idp; + + for (idp = dhp->dh_hash[h]; idp != NULL; idp = idp->di_next) { + if (idp == key) + break; + else + pp = &idp->di_next; + } + + assert(idp == key); + *pp = idp->di_next; + + assert(dhp->dh_nelems != 0); + dhp->dh_nelems--; + + if (!(idp->di_flags & DT_IDFLG_ORPHAN)) + dt_ident_destroy(idp); +} + +static int +dt_idhash_comp(const void *lp, const void *rp) +{ + const dt_ident_t *lhs = *((const dt_ident_t **)lp); + const dt_ident_t *rhs = *((const dt_ident_t **)rp); + + if (lhs->di_id != rhs->di_id) + return ((int)(lhs->di_id - rhs->di_id)); + else + return (strcmp(lhs->di_name, rhs->di_name)); +} + +int +dt_idhash_iter(dt_idhash_t *dhp, dt_idhash_f *func, void *data) +{ + dt_ident_t **ids; + dt_ident_t *idp; + ulong_t i, j, n; + int rv; + + if (dhp->dh_tmpl != NULL) + dt_idhash_populate(dhp); /* fill hash w/ initial population */ + + n = dhp->dh_nelems; + ids = alloca(sizeof (dt_ident_t *) * n); + + for (i = 0, j = 0; i < dhp->dh_hashsz; i++) { + for (idp = dhp->dh_hash[i]; idp != NULL; idp = idp->di_next) + ids[j++] = idp; + } + + qsort(ids, dhp->dh_nelems, sizeof (dt_ident_t *), dt_idhash_comp); + + for (i = 0; i < n; i++) { + if ((rv = func(dhp, ids[i], data)) != 0) + return (rv); + } + + return (0); +} + +dt_ident_t * +dt_idstack_lookup(dt_idstack_t *sp, const char *name) +{ + dt_idhash_t *dhp; + dt_ident_t *idp; + + for (dhp = dt_list_prev(&sp->dids_list); + dhp != NULL; dhp = dt_list_prev(dhp)) { + if ((idp = dt_idhash_lookup(dhp, name)) != NULL) + return (idp); + } + + return (NULL); +} + +void +dt_idstack_push(dt_idstack_t *sp, dt_idhash_t *dhp) +{ + dt_list_append(&sp->dids_list, dhp); +} + +void +dt_idstack_pop(dt_idstack_t *sp, dt_idhash_t *dhp) +{ + assert(dt_list_prev(&sp->dids_list) == dhp); + dt_list_delete(&sp->dids_list, dhp); +} + +dt_ident_t * +dt_ident_create(const char *name, ushort_t kind, ushort_t flags, uint_t id, + dtrace_attribute_t attr, uint_t vers, + const dt_idops_t *ops, void *iarg, ulong_t gen) +{ + dt_ident_t *idp; + char *s = NULL; + + if ((name != NULL && (s = strdup(name)) == NULL) || + (idp = malloc(sizeof (dt_ident_t))) == NULL) { + free(s); + return (NULL); + } + + idp->di_name = s; + idp->di_kind = kind; + idp->di_flags = flags; + idp->di_id = id; + idp->di_attr = attr; + idp->di_vers = vers; + idp->di_ops = ops; + idp->di_iarg = iarg; + idp->di_data = NULL; + idp->di_ctfp = NULL; + idp->di_type = CTF_ERR; + idp->di_next = NULL; + idp->di_gen = gen; + idp->di_lineno = yylineno; + + return (idp); +} + +/* + * Destroy an individual identifier. This code must be kept in sync with the + * dt_idhash_destroy() function below, which separates out the call to di_dtor. + */ +void +dt_ident_destroy(dt_ident_t *idp) +{ + idp->di_ops->di_dtor(idp); + free(idp->di_name); + free(idp); +} + +void +dt_ident_morph(dt_ident_t *idp, ushort_t kind, + const dt_idops_t *ops, void *iarg) +{ + idp->di_ops->di_dtor(idp); + idp->di_kind = kind; + idp->di_ops = ops; + idp->di_iarg = iarg; + idp->di_data = NULL; +} + +dtrace_attribute_t +dt_ident_cook(dt_node_t *dnp, dt_ident_t *idp, dt_node_t **pargp) +{ + dtrace_attribute_t attr; + dt_node_t *args, *argp; + int argc = 0; + + attr = dt_node_list_cook(pargp, DT_IDFLG_REF); + args = pargp ? *pargp : NULL; + + for (argp = args; argp != NULL; argp = argp->dn_list) + argc++; + + idp->di_ops->di_cook(dnp, idp, argc, args); + + if (idp->di_flags & DT_IDFLG_USER) + dnp->dn_flags |= DT_NF_USERLAND; + + return (dt_attr_min(attr, idp->di_attr)); +} + +void +dt_ident_type_assign(dt_ident_t *idp, ctf_file_t *fp, ctf_id_t type) +{ + idp->di_ctfp = fp; + idp->di_type = type; +} + +dt_ident_t * +dt_ident_resolve(dt_ident_t *idp) +{ + while (idp->di_flags & DT_IDFLG_INLINE) { + const dt_node_t *dnp = ((dt_idnode_t *)idp->di_iarg)->din_root; + + if (dnp == NULL) + break; /* can't resolve any further yet */ + + switch (dnp->dn_kind) { + case DT_NODE_VAR: + case DT_NODE_SYM: + case DT_NODE_FUNC: + case DT_NODE_AGG: + case DT_NODE_INLINE: + case DT_NODE_PROBE: + idp = dnp->dn_ident; + continue; + } + + if (dt_node_is_dynamic(dnp)) + idp = dnp->dn_ident; + else + break; + } + + return (idp); +} + +size_t +dt_ident_size(dt_ident_t *idp) +{ + idp = dt_ident_resolve(idp); + return (idp->di_ops->di_size(idp)); +} + +int +dt_ident_unref(const dt_ident_t *idp) +{ + return (idp->di_gen == yypcb->pcb_hdl->dt_gen && + (idp->di_flags & (DT_IDFLG_REF|DT_IDFLG_MOD|DT_IDFLG_DECL)) == 0); +} + +const char * +dt_idkind_name(uint_t kind) +{ + switch (kind) { + case DT_IDENT_ARRAY: return ("associative array"); + case DT_IDENT_SCALAR: return ("scalar"); + case DT_IDENT_PTR: return ("pointer"); + case DT_IDENT_FUNC: return ("function"); + case DT_IDENT_AGG: return ("aggregation"); + case DT_IDENT_AGGFUNC: return ("aggregating function"); + case DT_IDENT_ACTFUNC: return ("tracing function"); + case DT_IDENT_XLSOU: return ("translated data"); + case DT_IDENT_XLPTR: return ("pointer to translated data"); + case DT_IDENT_SYMBOL: return ("external symbol reference"); + case DT_IDENT_ENUM: return ("enumerator"); + case DT_IDENT_PRAGAT: return ("#pragma attributes"); + case DT_IDENT_PRAGBN: return ("#pragma binding"); + case DT_IDENT_PROBE: return ("probe definition"); + default: return ("<?>"); + } +} diff --git a/lib/libdtrace/common/dt_ident.h b/lib/libdtrace/common/dt_ident.h new file mode 100644 index 000000000000..cc80d6e98b57 --- /dev/null +++ b/lib/libdtrace/common/dt_ident.h @@ -0,0 +1,183 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_IDENT_H +#define _DT_IDENT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libctf.h> +#include <dtrace.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <dt_list.h> + +struct dt_node; +struct dt_ident; +struct dt_idhash; +struct dt_irlist; +struct dt_regset; + +typedef struct dt_idsig { + int dis_varargs; /* argument index of start of varargs (or -1) */ + int dis_optargs; /* argument index of start of optargs (or -1) */ + int dis_argc; /* number of types in this signature */ + struct dt_node *dis_args; /* array of nodes representing formal types */ + uint64_t dis_auxinfo; /* auxiliary signature information, if any */ +} dt_idsig_t; + +typedef struct dt_idnode { + struct dt_node *din_list; /* allocation list for parse tree nodes */ + struct dt_node *din_root; /* root of this identifier's parse tree */ + struct dt_idhash *din_hash; /* identifiers private to this subtree */ + struct dt_ident **din_argv; /* identifiers in din_hash for arguments */ + int din_argc; /* length of din_argv[] array */ +} dt_idnode_t; + +typedef struct dt_idops { + void (*di_cook)(struct dt_node *, struct dt_ident *, + int, struct dt_node *); + void (*di_dtor)(struct dt_ident *); + size_t (*di_size)(struct dt_ident *); +} dt_idops_t; + +typedef struct dt_ident { + char *di_name; /* identifier name */ + ushort_t di_kind; /* identifier kind (see below) */ + ushort_t di_flags; /* identifier flags (see below) */ + uint_t di_id; /* variable or subr id (see <sys/dtrace.h>) */ + dtrace_attribute_t di_attr; /* identifier stability attributes */ + uint_t di_vers; /* identifier version number (dt_version_t) */ + const dt_idops_t *di_ops; /* identifier's class-specific ops vector */ + void *di_iarg; /* initial argument pointer for ops vector */ + void *di_data; /* private data pointer for ops vector */ + ctf_file_t *di_ctfp; /* CTF container for the variable data type */ + ctf_id_t di_type; /* CTF identifier for the variable data type */ + struct dt_ident *di_next; /* pointer to next ident in hash chain */ + ulong_t di_gen; /* generation number (pass that created me) */ + int di_lineno; /* line number that defined this identifier */ +} dt_ident_t; + +#define DT_IDENT_ARRAY 0 /* identifier is an array variable */ +#define DT_IDENT_SCALAR 1 /* identifier is a scalar variable */ +#define DT_IDENT_PTR 2 /* identifier is a magic pointer */ +#define DT_IDENT_FUNC 3 /* identifier is a built-in function */ +#define DT_IDENT_AGG 4 /* identifier is an aggregation */ +#define DT_IDENT_AGGFUNC 5 /* identifier is an aggregating function */ +#define DT_IDENT_ACTFUNC 6 /* identifier is an action function */ +#define DT_IDENT_XLSOU 7 /* identifier is a translated struct or union */ +#define DT_IDENT_XLPTR 8 /* identifier is a translated pointer */ +#define DT_IDENT_SYMBOL 9 /* identifier is an external symbol */ +#define DT_IDENT_ENUM 10 /* identifier is an enumerator */ +#define DT_IDENT_PRAGAT 11 /* identifier is #pragma attributes */ +#define DT_IDENT_PRAGBN 12 /* identifier is #pragma binding */ +#define DT_IDENT_PROBE 13 /* identifier is a probe definition */ + +#define DT_IDFLG_TLS 0x0001 /* variable is thread-local storage */ +#define DT_IDFLG_LOCAL 0x0002 /* variable is local storage */ +#define DT_IDFLG_WRITE 0x0004 /* variable is writable (can be modified) */ +#define DT_IDFLG_INLINE 0x0008 /* variable is an inline definition */ +#define DT_IDFLG_REF 0x0010 /* variable is referenced by this program */ +#define DT_IDFLG_MOD 0x0020 /* variable is modified by this program */ +#define DT_IDFLG_DIFR 0x0040 /* variable is referenced by current DIFO */ +#define DT_IDFLG_DIFW 0x0080 /* variable is modified by current DIFO */ +#define DT_IDFLG_CGREG 0x0100 /* variable is inlined by code generator */ +#define DT_IDFLG_USER 0x0200 /* variable is associated with userland */ +#define DT_IDFLG_PRIM 0x0400 /* variable is associated with primary object */ +#define DT_IDFLG_DECL 0x0800 /* variable is associated with explicit decl */ +#define DT_IDFLG_ORPHAN 0x1000 /* variable is in a dt_node and not dt_idhash */ + +typedef struct dt_idhash { + dt_list_t dh_list; /* list prev/next pointers for dt_idstack */ + const char *dh_name; /* name of this hash table */ + void (*dh_defer)(struct dt_idhash *, dt_ident_t *); /* defer callback */ + const dt_ident_t *dh_tmpl; /* template for initial ident population */ + uint_t dh_nextid; /* next id to be returned by idhash_nextid() */ + uint_t dh_minid; /* min id to be returned by idhash_nextid() */ + uint_t dh_maxid; /* max id to be returned by idhash_nextid() */ + ulong_t dh_nelems; /* number of identifiers in hash table */ + ulong_t dh_hashsz; /* number of entries in dh_buckets array */ + dt_ident_t *dh_hash[1]; /* array of hash table bucket pointers */ +} dt_idhash_t; + +typedef struct dt_idstack { + dt_list_t dids_list; /* list meta-data for dt_idhash_t stack */ +} dt_idstack_t; + +extern const dt_idops_t dt_idops_assc; /* associative array or aggregation */ +extern const dt_idops_t dt_idops_func; /* function call built-in */ +extern const dt_idops_t dt_idops_args; /* args[] built-in */ +extern const dt_idops_t dt_idops_regs; /* regs[]/uregs[] built-in */ +extern const dt_idops_t dt_idops_type; /* predefined type name string */ +extern const dt_idops_t dt_idops_thaw; /* prefrozen type identifier */ +extern const dt_idops_t dt_idops_inline; /* inline variable */ +extern const dt_idops_t dt_idops_probe; /* probe definition */ + +extern dt_idhash_t *dt_idhash_create(const char *, const dt_ident_t *, + uint_t, uint_t); +extern void dt_idhash_destroy(dt_idhash_t *); +extern void dt_idhash_update(dt_idhash_t *); +extern dt_ident_t *dt_idhash_lookup(dt_idhash_t *, const char *); +extern int dt_idhash_nextid(dt_idhash_t *, uint_t *); +extern ulong_t dt_idhash_size(const dt_idhash_t *); +extern const char *dt_idhash_name(const dt_idhash_t *); + +extern dt_ident_t *dt_idhash_insert(dt_idhash_t *, const char *, ushort_t, + ushort_t, uint_t, dtrace_attribute_t, uint_t, + const dt_idops_t *, void *, ulong_t); + +extern void dt_idhash_xinsert(dt_idhash_t *, dt_ident_t *); +extern void dt_idhash_delete(dt_idhash_t *, dt_ident_t *); + +typedef int dt_idhash_f(dt_idhash_t *, dt_ident_t *, void *); +extern int dt_idhash_iter(dt_idhash_t *, dt_idhash_f *, void *); + +extern dt_ident_t *dt_idstack_lookup(dt_idstack_t *, const char *); +extern void dt_idstack_push(dt_idstack_t *, dt_idhash_t *); +extern void dt_idstack_pop(dt_idstack_t *, dt_idhash_t *); + +extern dt_ident_t *dt_ident_create(const char *, ushort_t, ushort_t, uint_t, + dtrace_attribute_t, uint_t, const dt_idops_t *, void *, ulong_t); +extern void dt_ident_destroy(dt_ident_t *); +extern void dt_ident_morph(dt_ident_t *, ushort_t, const dt_idops_t *, void *); +extern dtrace_attribute_t dt_ident_cook(struct dt_node *, + dt_ident_t *, struct dt_node **); + +extern void dt_ident_type_assign(dt_ident_t *, ctf_file_t *, ctf_id_t); +extern dt_ident_t *dt_ident_resolve(dt_ident_t *); +extern size_t dt_ident_size(dt_ident_t *); +extern int dt_ident_unref(const dt_ident_t *); + +extern const char *dt_idkind_name(uint_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_IDENT_H */ diff --git a/lib/libdtrace/common/dt_impl.h b/lib/libdtrace/common/dt_impl.h new file mode 100644 index 000000000000..6bcc5bc49ab0 --- /dev/null +++ b/lib/libdtrace/common/dt_impl.h @@ -0,0 +1,691 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_IMPL_H +#define _DT_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/objfs.h> +#if !defined(sun) +#include <sys/bitmap.h> +#include <sys/utsname.h> +#include <sys/ioccom.h> +#include <sys/time.h> +#include <string.h> +#endif +#include <setjmp.h> +#include <libctf.h> +#include <dtrace.h> +#include <gelf.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include <dt_parser.h> +#include <dt_regset.h> +#include <dt_inttab.h> +#include <dt_strtab.h> +#include <dt_ident.h> +#include <dt_list.h> +#include <dt_decl.h> +#include <dt_as.h> +#include <dt_proc.h> +#include <dt_dof.h> +#include <dt_pcb.h> + +struct dt_module; /* see below */ +struct dt_pfdict; /* see <dt_printf.h> */ +struct dt_arg; /* see below */ +struct dt_provider; /* see <dt_provider.h> */ +struct dt_xlator; /* see <dt_xlator.h> */ + +typedef struct dt_intrinsic { + const char *din_name; /* string name of the intrinsic type */ + ctf_encoding_t din_data; /* integer or floating-point CTF encoding */ + uint_t din_kind; /* CTF type kind to instantiate */ +} dt_intrinsic_t; + +typedef struct dt_typedef { + const char *dty_src; /* string name of typedef source type */ + const char *dty_dst; /* string name of typedef destination type */ +} dt_typedef_t; + +typedef struct dt_intdesc { + const char *did_name; /* string name of the integer type */ + ctf_file_t *did_ctfp; /* CTF container for this type reference */ + ctf_id_t did_type; /* CTF type reference for this type */ + uintmax_t did_limit; /* maximum positive value held by type */ +} dt_intdesc_t; + +typedef struct dt_modops { + uint_t (*do_syminit)(struct dt_module *); + void (*do_symsort)(struct dt_module *); + GElf_Sym *(*do_symname)(struct dt_module *, + const char *, GElf_Sym *, uint_t *); + GElf_Sym *(*do_symaddr)(struct dt_module *, + GElf_Addr, GElf_Sym *, uint_t *); +} dt_modops_t; + +typedef struct dt_arg { + int da_ndx; /* index of this argument */ + int da_mapping; /* mapping of argument indices to arguments */ + ctf_id_t da_type; /* type of argument */ + ctf_file_t *da_ctfp; /* CTF container for type */ + dt_ident_t *da_xlator; /* translator, if any */ + struct dt_arg *da_next; /* next argument */ +} dt_arg_t; + +typedef struct dt_sym { + uint_t ds_symid; /* id of corresponding symbol */ + uint_t ds_next; /* index of next element in hash chain */ +} dt_sym_t; + +typedef struct dt_module { + dt_list_t dm_list; /* list forward/back pointers */ + char dm_name[DTRACE_MODNAMELEN]; /* string name of module */ + char dm_file[MAXPATHLEN]; /* file path of module (if any) */ + struct dt_module *dm_next; /* pointer to next module in hash chain */ + const dt_modops_t *dm_ops; /* pointer to data model's ops vector */ + Elf *dm_elf; /* libelf handle for module object */ + objfs_info_t dm_info; /* object filesystem private info */ + ctf_sect_t dm_symtab; /* symbol table for module */ + ctf_sect_t dm_strtab; /* string table for module */ + ctf_sect_t dm_ctdata; /* CTF data for module */ + ctf_file_t *dm_ctfp; /* CTF container handle */ + uint_t *dm_symbuckets; /* symbol table hash buckets (chain indices) */ + dt_sym_t *dm_symchains; /* symbol table hash chains buffer */ + void *dm_asmap; /* symbol pointers sorted by value */ + uint_t dm_symfree; /* index of next free hash element */ + uint_t dm_nsymbuckets; /* number of elements in bucket array */ + uint_t dm_nsymelems; /* number of elements in hash table */ + uint_t dm_asrsv; /* actual reserved size of dm_asmap */ + uint_t dm_aslen; /* number of entries in dm_asmap */ + uint_t dm_flags; /* module flags (see below) */ + int dm_modid; /* modinfo(1M) module identifier */ + GElf_Addr dm_text_va; /* virtual address of text section */ + GElf_Xword dm_text_size; /* size in bytes of text section */ + GElf_Addr dm_data_va; /* virtual address of data section */ + GElf_Xword dm_data_size; /* size in bytes of data section */ + GElf_Addr dm_bss_va; /* virtual address of BSS */ + GElf_Xword dm_bss_size; /* size in bytes of BSS */ + dt_idhash_t *dm_extern; /* external symbol definitions */ +#if !defined(sun) + caddr_t dm_reloc_offset; /* Symbol relocation offset. */ +#endif +} dt_module_t; + +#define DT_DM_LOADED 0x1 /* module symbol and type data is loaded */ +#define DT_DM_KERNEL 0x2 /* module is associated with a kernel object */ +#define DT_DM_PRIMARY 0x4 /* module is a krtld primary kernel object */ + +typedef struct dt_provmod { + char *dp_name; /* name of provider module */ + struct dt_provmod *dp_next; /* next module */ +} dt_provmod_t; + +typedef struct dt_ahashent { + struct dt_ahashent *dtahe_prev; /* prev on hash chain */ + struct dt_ahashent *dtahe_next; /* next on hash chain */ + struct dt_ahashent *dtahe_prevall; /* prev on list of all */ + struct dt_ahashent *dtahe_nextall; /* next on list of all */ + uint64_t dtahe_hashval; /* hash value */ + size_t dtahe_size; /* size of data */ + dtrace_aggdata_t dtahe_data; /* data */ + void (*dtahe_aggregate)(int64_t *, int64_t *, size_t); /* function */ +} dt_ahashent_t; + +typedef struct dt_ahash { + dt_ahashent_t **dtah_hash; /* hash table */ + dt_ahashent_t *dtah_all; /* list of all elements */ + size_t dtah_size; /* size of hash table */ +} dt_ahash_t; + +typedef struct dt_aggregate { + dtrace_bufdesc_t dtat_buf; /* buf aggregation snapshot */ + int dtat_flags; /* aggregate flags */ + processorid_t dtat_ncpus; /* number of CPUs in aggregate */ + processorid_t *dtat_cpus; /* CPUs in aggregate */ + processorid_t dtat_ncpu; /* size of dtat_cpus array */ + processorid_t dtat_maxcpu; /* maximum number of CPUs */ + dt_ahash_t dtat_hash; /* aggregate hash table */ +} dt_aggregate_t; + +typedef struct dt_print_aggdata { + dtrace_hdl_t *dtpa_dtp; /* pointer to libdtrace handle */ + dtrace_aggvarid_t dtpa_id; /* aggregation variable of interest */ + FILE *dtpa_fp; /* file pointer */ + int dtpa_allunprint; /* print only unprinted aggregations */ +} dt_print_aggdata_t; + +typedef struct dt_dirpath { + dt_list_t dir_list; /* linked-list forward/back pointers */ + char *dir_path; /* directory pathname */ +} dt_dirpath_t; + +typedef struct dt_lib_depend { + dt_list_t dtld_deplist; /* linked-list forward/back pointers */ + char *dtld_library; /* library name */ + char *dtld_libpath; /* library pathname */ + uint_t dtld_finish; /* completion time in tsort for lib */ + uint_t dtld_start; /* starting time in tsort for lib */ + uint_t dtld_loaded; /* boolean: is this library loaded */ + dt_list_t dtld_dependencies; /* linked-list of lib dependencies */ + dt_list_t dtld_dependents; /* linked-list of lib dependents */ +} dt_lib_depend_t; + +typedef uint32_t dt_version_t; /* encoded version (see below) */ + +struct dtrace_hdl { + const dtrace_vector_t *dt_vector; /* library vector, if vectored open */ + void *dt_varg; /* vector argument, if vectored open */ + dtrace_conf_t dt_conf; /* DTrace driver configuration profile */ + char dt_errmsg[BUFSIZ]; /* buffer for formatted syntax error msgs */ + const char *dt_errtag; /* tag used with last call to dt_set_errmsg() */ + dt_pcb_t *dt_pcb; /* pointer to current parsing control block */ + ulong_t dt_gen; /* compiler generation number */ + dt_list_t dt_programs; /* linked list of dtrace_prog_t's */ + dt_list_t dt_xlators; /* linked list of dt_xlator_t's */ + struct dt_xlator **dt_xlatormap; /* dt_xlator_t's indexed by dx_id */ + id_t dt_xlatorid; /* next dt_xlator_t id to assign */ + dt_ident_t *dt_externs; /* linked list of external symbol identifiers */ + dt_idhash_t *dt_macros; /* hash table of macro variable identifiers */ + dt_idhash_t *dt_aggs; /* hash table of aggregation identifiers */ + dt_idhash_t *dt_globals; /* hash table of global identifiers */ + dt_idhash_t *dt_tls; /* hash table of thread-local identifiers */ + dt_list_t dt_modlist; /* linked list of dt_module_t's */ + dt_module_t **dt_mods; /* hash table of dt_module_t's */ + uint_t dt_modbuckets; /* number of module hash buckets */ + uint_t dt_nmods; /* number of modules in hash and list */ + dt_provmod_t *dt_provmod; /* linked list of provider modules */ + dt_module_t *dt_exec; /* pointer to executable module */ + dt_module_t *dt_rtld; /* pointer to run-time linker module */ + dt_module_t *dt_cdefs; /* pointer to C dynamic type module */ + dt_module_t *dt_ddefs; /* pointer to D dynamic type module */ + dt_list_t dt_provlist; /* linked list of dt_provider_t's */ + struct dt_provider **dt_provs; /* hash table of dt_provider_t's */ + uint_t dt_provbuckets; /* number of provider hash buckets */ + uint_t dt_nprovs; /* number of providers in hash and list */ + dt_proc_hash_t *dt_procs; /* hash table of grabbed process handles */ + dt_intdesc_t dt_ints[6]; /* cached integer type descriptions */ + ctf_id_t dt_type_func; /* cached CTF identifier for function type */ + ctf_id_t dt_type_fptr; /* cached CTF identifier for function pointer */ + ctf_id_t dt_type_str; /* cached CTF identifier for string type */ + ctf_id_t dt_type_dyn; /* cached CTF identifier for <DYN> type */ + ctf_id_t dt_type_stack; /* cached CTF identifier for stack type */ + ctf_id_t dt_type_symaddr; /* cached CTF identifier for _symaddr type */ + ctf_id_t dt_type_usymaddr; /* cached CTF ident. for _usymaddr type */ + size_t dt_maxprobe; /* max enabled probe ID */ + dtrace_eprobedesc_t **dt_edesc; /* enabled probe descriptions */ + dtrace_probedesc_t **dt_pdesc; /* probe descriptions for enabled prbs */ + size_t dt_maxagg; /* max aggregation ID */ + dtrace_aggdesc_t **dt_aggdesc; /* aggregation descriptions */ + int dt_maxformat; /* max format ID */ + void **dt_formats; /* pointer to format array */ + dt_aggregate_t dt_aggregate; /* aggregate */ + dtrace_bufdesc_t dt_buf; /* staging buffer */ + struct dt_pfdict *dt_pfdict; /* dictionary of printf conversions */ + dt_version_t dt_vmax; /* optional ceiling on program API binding */ + dtrace_attribute_t dt_amin; /* optional floor on program attributes */ + char *dt_cpp_path; /* pathname of cpp(1) to invoke if needed */ + char **dt_cpp_argv; /* argument vector for exec'ing cpp(1) */ + int dt_cpp_argc; /* count of initialized cpp(1) arguments */ + int dt_cpp_args; /* size of dt_cpp_argv[] array */ + char *dt_ld_path; /* pathname of ld(1) to invoke if needed */ + dt_list_t dt_lib_path; /* linked-list forming library search path */ + uint_t dt_lazyload; /* boolean: set via -xlazyload */ + uint_t dt_droptags; /* boolean: set via -xdroptags */ + uint_t dt_active; /* boolean: set once tracing is active */ + uint_t dt_stopped; /* boolean: set once tracing is stopped */ + processorid_t dt_beganon; /* CPU that executed BEGIN probe (if any) */ + processorid_t dt_endedon; /* CPU that executed END probe (if any) */ + uint_t dt_oflags; /* dtrace open-time options (see dtrace.h) */ + uint_t dt_cflags; /* dtrace compile-time options (see dtrace.h) */ + uint_t dt_dflags; /* dtrace link-time options (see dtrace.h) */ + uint_t dt_prcmode; /* dtrace process create mode (see dt_proc.h) */ + uint_t dt_linkmode; /* dtrace symbol linking mode (see below) */ + uint_t dt_linktype; /* dtrace link output file type (see below) */ + uint_t dt_xlatemode; /* dtrace translator linking mode (see below) */ + uint_t dt_stdcmode; /* dtrace stdc compatibility mode (see below) */ + uint_t dt_treedump; /* dtrace tree debug bitmap (see below) */ + uint64_t dt_options[DTRACEOPT_MAX]; /* dtrace run-time options */ + int dt_version; /* library version requested by client */ + int dt_ctferr; /* error resulting from last CTF failure */ + int dt_errno; /* error resulting from last failed operation */ +#if !defined(sun) + const char *dt_errfile; + int dt_errline; +#endif + int dt_fd; /* file descriptor for dtrace pseudo-device */ + int dt_ftfd; /* file descriptor for fasttrap pseudo-device */ + int dt_fterr; /* saved errno from failed open of dt_ftfd */ + int dt_cdefs_fd; /* file descriptor for C CTF debugging cache */ + int dt_ddefs_fd; /* file descriptor for D CTF debugging cache */ +#if defined(sun) + int dt_stdout_fd; /* file descriptor for saved stdout */ +#else + FILE *dt_freopen_fp; /* file pointer for freopened stdout */ +#endif + dtrace_handle_err_f *dt_errhdlr; /* error handler, if any */ + void *dt_errarg; /* error handler argument */ + dtrace_prog_t *dt_errprog; /* error handler program, if any */ + dtrace_handle_drop_f *dt_drophdlr; /* drop handler, if any */ + void *dt_droparg; /* drop handler argument */ + dtrace_handle_proc_f *dt_prochdlr; /* proc handler, if any */ + void *dt_procarg; /* proc handler argument */ + dtrace_handle_setopt_f *dt_setopthdlr; /* setopt handler, if any */ + void *dt_setoptarg; /* setopt handler argument */ + dtrace_status_t dt_status[2]; /* status cache */ + int dt_statusgen; /* current status generation */ + hrtime_t dt_laststatus; /* last status */ + hrtime_t dt_lastswitch; /* last switch of buffer data */ + hrtime_t dt_lastagg; /* last snapshot of aggregation data */ + char *dt_sprintf_buf; /* buffer for dtrace_sprintf() */ + int dt_sprintf_buflen; /* length of dtrace_sprintf() buffer */ + const char *dt_filetag; /* default filetag for dt_set_errmsg() */ + char *dt_buffered_buf; /* buffer for buffered output */ + size_t dt_buffered_offs; /* current offset into buffered buffer */ + size_t dt_buffered_size; /* size of buffered buffer */ + dtrace_handle_buffered_f *dt_bufhdlr; /* buffered handler, if any */ + void *dt_bufarg; /* buffered handler argument */ + dt_dof_t dt_dof; /* DOF generation buffers (see dt_dof.c) */ + struct utsname dt_uts; /* uname(2) information for system */ + dt_list_t dt_lib_dep; /* scratch linked-list of lib dependencies */ + dt_list_t dt_lib_dep_sorted; /* dependency sorted library list */ +}; + +/* + * Values for the user arg of the ECB. + */ +#define DT_ECB_DEFAULT 0 +#define DT_ECB_ERROR 1 + +/* + * Values for the dt_linkmode property, which is used by the assembler when + * processing external symbol references. User can set using -xlink=<mode>. + */ +#define DT_LINK_KERNEL 0 /* kernel syms static, user syms dynamic */ +#define DT_LINK_PRIMARY 1 /* primary kernel syms static, others dynamic */ +#define DT_LINK_DYNAMIC 2 /* all symbols dynamic */ +#define DT_LINK_STATIC 3 /* all symbols static */ + +/* + * Values for the dt_linktype property, which is used by dtrace_program_link() + * to determine the type of output file that is desired by the client. + */ +#define DT_LTYP_ELF 0 /* produce ELF containing DOF */ +#define DT_LTYP_DOF 1 /* produce stand-alone DOF */ + +/* + * Values for the dt_xlatemode property, which is used to determine whether + * references to dynamic translators are permitted. Set using -xlate=<mode>. + */ +#define DT_XL_STATIC 0 /* require xlators to be statically defined */ +#define DT_XL_DYNAMIC 1 /* produce references to dynamic translators */ + +/* + * Values for the dt_stdcmode property, which is used by the compiler when + * running cpp to determine the presence and setting of the __STDC__ macro. + */ +#define DT_STDC_XA 0 /* ISO C + K&R C compat w/o ISO: __STDC__=0 */ +#define DT_STDC_XC 1 /* Strict ISO C: __STDC__=1 */ +#define DT_STDC_XS 2 /* K&R C: __STDC__ not defined */ +#define DT_STDC_XT 3 /* ISO C + K&R C compat with ISO: __STDC__=0 */ + +/* + * Macro to test whether a given pass bit is set in the dt_treedump bit-vector. + * If the bit for pass 'p' is set, the D compiler displays the parse tree for + * the program by printing it to stderr at the end of compiler pass 'p'. + */ +#define DT_TREEDUMP_PASS(dtp, p) ((dtp)->dt_treedump & (1 << ((p) - 1))) + +/* + * Macros for accessing the cached CTF container and type ID for the common + * types "int", "string", and <DYN>, which we need to use frequently in the D + * compiler. The DT_INT_* macro relies upon "int" being at index 0 in the + * _dtrace_ints_* tables in dt_open.c; the others are also set up there. + */ +#define DT_INT_CTFP(dtp) ((dtp)->dt_ints[0].did_ctfp) +#define DT_INT_TYPE(dtp) ((dtp)->dt_ints[0].did_type) + +#define DT_FUNC_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) +#define DT_FUNC_TYPE(dtp) ((dtp)->dt_type_func) + +#define DT_FPTR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) +#define DT_FPTR_TYPE(dtp) ((dtp)->dt_type_fptr) + +#define DT_STR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) +#define DT_STR_TYPE(dtp) ((dtp)->dt_type_str) + +#define DT_DYN_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) +#define DT_DYN_TYPE(dtp) ((dtp)->dt_type_dyn) + +#define DT_STACK_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) +#define DT_STACK_TYPE(dtp) ((dtp)->dt_type_stack) + +#define DT_SYMADDR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) +#define DT_SYMADDR_TYPE(dtp) ((dtp)->dt_type_symaddr) + +#define DT_USYMADDR_CTFP(dtp) ((dtp)->dt_ddefs->dm_ctfp) +#define DT_USYMADDR_TYPE(dtp) ((dtp)->dt_type_usymaddr) + +/* + * Actions and subroutines are both DT_NODE_FUNC nodes; to avoid confusing + * an action for a subroutine (or vice versa), we assure that the DT_ACT_* + * constants and the DIF_SUBR_* constants occupy non-overlapping ranges by + * starting the DT_ACT_* constants at DIF_SUBR_MAX + 1. + */ +#define DT_ACT_BASE DIF_SUBR_MAX + 1 +#define DT_ACT(n) (DT_ACT_BASE + (n)) + +#define DT_ACT_PRINTF DT_ACT(0) /* printf() action */ +#define DT_ACT_TRACE DT_ACT(1) /* trace() action */ +#define DT_ACT_TRACEMEM DT_ACT(2) /* tracemem() action */ +#define DT_ACT_STACK DT_ACT(3) /* stack() action */ +#define DT_ACT_STOP DT_ACT(4) /* stop() action */ +#define DT_ACT_BREAKPOINT DT_ACT(5) /* breakpoint() action */ +#define DT_ACT_PANIC DT_ACT(6) /* panic() action */ +#define DT_ACT_SPECULATE DT_ACT(7) /* speculate() action */ +#define DT_ACT_COMMIT DT_ACT(8) /* commit() action */ +#define DT_ACT_DISCARD DT_ACT(9) /* discard() action */ +#define DT_ACT_CHILL DT_ACT(10) /* chill() action */ +#define DT_ACT_EXIT DT_ACT(11) /* exit() action */ +#define DT_ACT_USTACK DT_ACT(12) /* ustack() action */ +#define DT_ACT_PRINTA DT_ACT(13) /* printa() action */ +#define DT_ACT_RAISE DT_ACT(14) /* raise() action */ +#define DT_ACT_CLEAR DT_ACT(15) /* clear() action */ +#define DT_ACT_NORMALIZE DT_ACT(16) /* normalize() action */ +#define DT_ACT_DENORMALIZE DT_ACT(17) /* denormalize() action */ +#define DT_ACT_TRUNC DT_ACT(18) /* trunc() action */ +#define DT_ACT_SYSTEM DT_ACT(19) /* system() action */ +#define DT_ACT_JSTACK DT_ACT(20) /* jstack() action */ +#define DT_ACT_FTRUNCATE DT_ACT(21) /* ftruncate() action */ +#define DT_ACT_FREOPEN DT_ACT(22) /* freopen() action */ +#define DT_ACT_SYM DT_ACT(23) /* sym()/func() actions */ +#define DT_ACT_MOD DT_ACT(24) /* mod() action */ +#define DT_ACT_USYM DT_ACT(25) /* usym()/ufunc() actions */ +#define DT_ACT_UMOD DT_ACT(26) /* umod() action */ +#define DT_ACT_UADDR DT_ACT(27) /* uaddr() action */ +#define DT_ACT_SETOPT DT_ACT(28) /* setopt() action */ +#define DT_ACT_PRINTM DT_ACT(29) /* printm() action */ +#define DT_ACT_PRINTT DT_ACT(30) /* printt() action */ + +/* + * Sentinel to tell freopen() to restore the saved stdout. This must not + * be ever valid for opening for write access via freopen(3C), which of + * course, "." never is. + */ +#define DT_FREOPEN_RESTORE "." + +#define EDT_BASE 1000 /* base value for libdtrace errnos */ + +enum { + EDT_VERSION = EDT_BASE, /* client is requesting unsupported version */ + EDT_VERSINVAL, /* version string is invalid or overflows */ + EDT_VERSUNDEF, /* requested API version is not defined */ + EDT_VERSREDUCED, /* requested API version has been reduced */ + EDT_CTF, /* libctf called failed (dt_ctferr has more) */ + EDT_COMPILER, /* error in D program compilation */ + EDT_NOREG, /* register allocation failure */ + EDT_NOTUPREG, /* tuple register allocation failure */ + EDT_NOMEM, /* memory allocation failure */ + EDT_INT2BIG, /* integer limit exceeded */ + EDT_STR2BIG, /* string limit exceeded */ + EDT_NOMOD, /* unknown module name */ + EDT_NOPROV, /* unknown provider name */ + EDT_NOPROBE, /* unknown probe name */ + EDT_NOSYM, /* unknown symbol name */ + EDT_NOSYMADDR, /* no symbol corresponds to address */ + EDT_NOTYPE, /* unknown type name */ + EDT_NOVAR, /* unknown variable name */ + EDT_NOAGG, /* unknown aggregation name */ + EDT_BADSCOPE, /* improper use of type name scoping operator */ + EDT_BADSPEC, /* overspecified probe description */ + EDT_BADSPCV, /* bad macro variable in probe description */ + EDT_BADID, /* invalid probe identifier */ + EDT_NOTLOADED, /* module is not currently loaded */ + EDT_NOCTF, /* module does not contain any CTF data */ + EDT_DATAMODEL, /* module and program data models don't match */ + EDT_DIFVERS, /* library has newer DIF version than driver */ + EDT_BADAGG, /* unrecognized aggregating action */ + EDT_FIO, /* file i/o error */ + EDT_DIFINVAL, /* invalid DIF program */ + EDT_DIFSIZE, /* invalid DIF size */ + EDT_DIFFAULT, /* failed to copyin DIF program */ + EDT_BADPROBE, /* bad probe description */ + EDT_BADPGLOB, /* bad probe description globbing pattern */ + EDT_NOSCOPE, /* declaration scope stack underflow */ + EDT_NODECL, /* declaration stack underflow */ + EDT_DMISMATCH, /* record list does not match statement */ + EDT_DOFFSET, /* record data offset error */ + EDT_DALIGN, /* record data alignment error */ + EDT_BADOPTNAME, /* invalid dtrace_setopt option name */ + EDT_BADOPTVAL, /* invalid dtrace_setopt option value */ + EDT_BADOPTCTX, /* invalid dtrace_setopt option context */ + EDT_CPPFORK, /* failed to fork preprocessor */ + EDT_CPPEXEC, /* failed to exec preprocessor */ + EDT_CPPENT, /* preprocessor not found */ + EDT_CPPERR, /* unknown preprocessor error */ + EDT_SYMOFLOW, /* external symbol table overflow */ + EDT_ACTIVE, /* operation illegal when tracing is active */ + EDT_DESTRUCTIVE, /* destructive actions not allowed */ + EDT_NOANON, /* no anonymous tracing state */ + EDT_ISANON, /* can't claim anon state and enable probes */ + EDT_ENDTOOBIG, /* END enablings exceed size of prncpl buffer */ + EDT_NOCONV, /* failed to load type for printf conversion */ + EDT_BADCONV, /* incomplete printf conversion */ + EDT_BADERROR, /* invalid library ERROR action */ + EDT_ERRABORT, /* abort due to error */ + EDT_DROPABORT, /* abort due to drop */ + EDT_DIRABORT, /* abort explicitly directed */ + EDT_BADRVAL, /* invalid return value from callback */ + EDT_BADNORMAL, /* invalid normalization */ + EDT_BUFTOOSMALL, /* enabling exceeds size of buffer */ + EDT_BADTRUNC, /* invalid truncation */ + EDT_BUSY, /* device busy (active kernel debugger) */ + EDT_ACCESS, /* insufficient privileges to use DTrace */ + EDT_NOENT, /* dtrace device not available */ + EDT_BRICKED, /* abort due to systemic unresponsiveness */ + EDT_HARDWIRE, /* failed to load hard-wired definitions */ + EDT_ELFVERSION, /* libelf is out-of-date w.r.t libdtrace */ + EDT_NOBUFFERED, /* attempt to buffer output without handler */ + EDT_UNSTABLE, /* description matched unstable set of probes */ + EDT_BADSETOPT, /* invalid setopt library action */ + EDT_BADSTACKPC, /* invalid stack program counter size */ + EDT_BADAGGVAR, /* invalid aggregation variable identifier */ + EDT_OVERSION /* client is requesting deprecated version */ +}; + +/* + * Interfaces for parsing and comparing DTrace attribute tuples, which describe + * stability and architectural binding information. The dtrace_attribute_t + * structure and associated constant definitions are found in <sys/dtrace.h>. + */ +extern dtrace_attribute_t dt_attr_min(dtrace_attribute_t, dtrace_attribute_t); +extern dtrace_attribute_t dt_attr_max(dtrace_attribute_t, dtrace_attribute_t); +extern char *dt_attr_str(dtrace_attribute_t, char *, size_t); +extern int dt_attr_cmp(dtrace_attribute_t, dtrace_attribute_t); + +/* + * Interfaces for parsing and handling DTrace version strings. Version binding + * is a feature of the D compiler that is handled completely independently of + * the DTrace kernel infrastructure, so the definitions are here in libdtrace. + * Version strings are compiled into an encoded uint32_t which can be compared + * using C comparison operators. Version definitions are found in dt_open.c. + */ +#define DT_VERSION_STRMAX 16 /* enough for "255.4095.4095\0" */ +#define DT_VERSION_MAJMAX 0xFF /* maximum major version number */ +#define DT_VERSION_MINMAX 0xFFF /* maximum minor version number */ +#define DT_VERSION_MICMAX 0xFFF /* maximum micro version number */ + +#define DT_VERSION_NUMBER(M, m, u) \ + ((((M) & 0xFF) << 24) | (((m) & 0xFFF) << 12) | ((u) & 0xFFF)) + +#define DT_VERSION_MAJOR(v) (((v) & 0xFF000000) >> 24) +#define DT_VERSION_MINOR(v) (((v) & 0x00FFF000) >> 12) +#define DT_VERSION_MICRO(v) ((v) & 0x00000FFF) + +extern char *dt_version_num2str(dt_version_t, char *, size_t); +extern int dt_version_str2num(const char *, dt_version_t *); +extern int dt_version_defined(dt_version_t); + +/* + * Miscellaneous internal libdtrace interfaces. The definitions below are for + * libdtrace routines that do not yet merit their own separate header file. + */ +extern char *dt_cpp_add_arg(dtrace_hdl_t *, const char *); +extern char *dt_cpp_pop_arg(dtrace_hdl_t *); + +#if defined(sun) +extern int dt_set_errno(dtrace_hdl_t *, int); +#else +int _dt_set_errno(dtrace_hdl_t *, int, const char *, int); +void dt_get_errloc(dtrace_hdl_t *, const char **, int *); +#define dt_set_errno(_a,_b) _dt_set_errno(_a,_b,__FILE__,__LINE__) +#endif +extern void dt_set_errmsg(dtrace_hdl_t *, const char *, const char *, + const char *, int, const char *, va_list); + +#if defined(sun) +extern int dt_ioctl(dtrace_hdl_t *, int, void *); +#else +extern int dt_ioctl(dtrace_hdl_t *, u_long, void *); +#endif +extern int dt_status(dtrace_hdl_t *, processorid_t); +extern long dt_sysconf(dtrace_hdl_t *, int); +extern ssize_t dt_write(dtrace_hdl_t *, int, const void *, size_t); +extern int dt_printf(dtrace_hdl_t *, FILE *, const char *, ...); + +extern void *dt_zalloc(dtrace_hdl_t *, size_t); +extern void *dt_alloc(dtrace_hdl_t *, size_t); +extern void dt_free(dtrace_hdl_t *, void *); +extern void dt_difo_free(dtrace_hdl_t *, dtrace_difo_t *); + +extern int dt_gmatch(const char *, const char *); +extern char *dt_basename(char *); + +extern ulong_t dt_popc(ulong_t); +extern ulong_t dt_popcb(const ulong_t *, ulong_t); + +extern int dt_buffered_enable(dtrace_hdl_t *); +extern int dt_buffered_flush(dtrace_hdl_t *, dtrace_probedata_t *, + const dtrace_recdesc_t *, const dtrace_aggdata_t *, uint32_t flags); +extern void dt_buffered_disable(dtrace_hdl_t *); +extern void dt_buffered_destroy(dtrace_hdl_t *); + +extern int dt_rw_read_held(pthread_rwlock_t *); +extern int dt_rw_write_held(pthread_rwlock_t *); +extern int dt_mutex_held(pthread_mutex_t *); + +extern uint64_t dt_stddev(uint64_t *, uint64_t); + +#define DT_RW_READ_HELD(x) dt_rw_read_held(x) +#define DT_RW_WRITE_HELD(x) dt_rw_write_held(x) +#define DT_RW_LOCK_HELD(x) (DT_RW_READ_HELD(x) || DT_RW_WRITE_HELD(x)) +#define DT_MUTEX_HELD(x) dt_mutex_held(x) + +extern int dt_options_load(dtrace_hdl_t *); + +extern void dt_dprintf(const char *, ...); + +extern void dt_setcontext(dtrace_hdl_t *, dtrace_probedesc_t *); +extern void dt_endcontext(dtrace_hdl_t *); + +extern void dt_pragma(dt_node_t *); +extern int dt_reduce(dtrace_hdl_t *, dt_version_t); +extern void dt_cg(dt_pcb_t *, dt_node_t *); +extern dtrace_difo_t *dt_as(dt_pcb_t *); +extern void dt_dis(const dtrace_difo_t *, FILE *); + +extern int dt_aggregate_go(dtrace_hdl_t *); +extern int dt_aggregate_init(dtrace_hdl_t *); +extern void dt_aggregate_destroy(dtrace_hdl_t *); + +extern int dt_epid_lookup(dtrace_hdl_t *, dtrace_epid_t, + dtrace_eprobedesc_t **, dtrace_probedesc_t **); +extern void dt_epid_destroy(dtrace_hdl_t *); +extern int dt_aggid_lookup(dtrace_hdl_t *, dtrace_aggid_t, dtrace_aggdesc_t **); +extern void dt_aggid_destroy(dtrace_hdl_t *); + +extern void *dt_format_lookup(dtrace_hdl_t *, int); +extern void dt_format_destroy(dtrace_hdl_t *); + +extern int dt_print_quantize(dtrace_hdl_t *, FILE *, + const void *, size_t, uint64_t); +extern int dt_print_lquantize(dtrace_hdl_t *, FILE *, + const void *, size_t, uint64_t); +extern int dt_print_agg(const dtrace_aggdata_t *, void *); + +extern int dt_handle(dtrace_hdl_t *, dtrace_probedata_t *); +extern int dt_handle_liberr(dtrace_hdl_t *, + const dtrace_probedata_t *, const char *); +extern int dt_handle_cpudrop(dtrace_hdl_t *, processorid_t, + dtrace_dropkind_t, uint64_t); +extern int dt_handle_status(dtrace_hdl_t *, + dtrace_status_t *, dtrace_status_t *); +extern int dt_handle_setopt(dtrace_hdl_t *, dtrace_setoptdata_t *); + +extern int dt_lib_depend_add(dtrace_hdl_t *, dt_list_t *, const char *); +extern dt_lib_depend_t *dt_lib_depend_lookup(dt_list_t *, const char *); + +extern dt_pcb_t *yypcb; /* pointer to current parser control block */ +extern char yyintprefix; /* int token prefix for macros (+/-) */ +extern char yyintsuffix[4]; /* int token suffix ([uUlL]*) */ +extern int yyintdecimal; /* int token is decimal (1) or octal/hex (0) */ +extern char yytext[]; /* lex input buffer */ +extern int yylineno; /* lex line number */ +extern int yydebug; /* lex debugging */ +extern dt_node_t *yypragma; /* lex token list for control lines */ + +extern const dtrace_attribute_t _dtrace_maxattr; /* maximum attributes */ +extern const dtrace_attribute_t _dtrace_defattr; /* default attributes */ +extern const dtrace_attribute_t _dtrace_symattr; /* symbol ref attributes */ +extern const dtrace_attribute_t _dtrace_typattr; /* type ref attributes */ +extern const dtrace_attribute_t _dtrace_prvattr; /* provider attributes */ +extern const dtrace_pattr_t _dtrace_prvdesc; /* provider attribute bundle */ + +extern const dt_version_t _dtrace_versions[]; /* array of valid versions */ +extern const char *const _dtrace_version; /* current version string */ + +extern int _dtrace_strbuckets; /* number of hash buckets for strings */ +extern int _dtrace_intbuckets; /* number of hash buckets for ints */ +extern uint_t _dtrace_stkindent; /* default indent for stack/ustack */ +extern uint_t _dtrace_pidbuckets; /* number of hash buckets for pids */ +extern uint_t _dtrace_pidlrulim; /* number of proc handles to cache */ +extern int _dtrace_debug; /* debugging messages enabled */ +extern size_t _dtrace_bufsize; /* default dt_buf_create() size */ +extern int _dtrace_argmax; /* default maximum probe arguments */ + +extern const char *_dtrace_libdir; /* default library directory */ +extern const char *_dtrace_moddir; /* default kernel module directory */ + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_IMPL_H */ diff --git a/lib/libdtrace/common/dt_inttab.c b/lib/libdtrace/common/dt_inttab.c new file mode 100644 index 000000000000..a6ac589ff0dd --- /dev/null +++ b/lib/libdtrace/common/dt_inttab.c @@ -0,0 +1,115 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <assert.h> + +#include <dt_inttab.h> +#include <dt_impl.h> + +dt_inttab_t * +dt_inttab_create(dtrace_hdl_t *dtp) +{ + uint_t len = _dtrace_intbuckets; + dt_inttab_t *ip; + + assert((len & (len - 1)) == 0); + + if ((ip = dt_zalloc(dtp, sizeof (dt_inttab_t))) == NULL || + (ip->int_hash = dt_zalloc(dtp, sizeof (void *) * len)) == NULL) { + dt_free(dtp, ip); + return (NULL); + } + + ip->int_hdl = dtp; + ip->int_hashlen = len; + + return (ip); +} + +void +dt_inttab_destroy(dt_inttab_t *ip) +{ + dt_inthash_t *hp, *np; + + for (hp = ip->int_head; hp != NULL; hp = np) { + np = hp->inh_next; + dt_free(ip->int_hdl, hp); + } + + dt_free(ip->int_hdl, ip->int_hash); + dt_free(ip->int_hdl, ip); +} + +int +dt_inttab_insert(dt_inttab_t *ip, uint64_t value, uint_t flags) +{ + uint_t h = value & (ip->int_hashlen - 1); + dt_inthash_t *hp; + + if (flags & DT_INT_SHARED) { + for (hp = ip->int_hash[h]; hp != NULL; hp = hp->inh_hash) { + if (hp->inh_value == value && hp->inh_flags == flags) + return (hp->inh_index); + } + } + + if ((hp = dt_alloc(ip->int_hdl, sizeof (dt_inthash_t))) == NULL) + return (-1); + + hp->inh_hash = ip->int_hash[h]; + hp->inh_next = NULL; + hp->inh_value = value; + hp->inh_index = ip->int_index++; + hp->inh_flags = flags; + + ip->int_hash[h] = hp; + ip->int_nelems++; + + if (ip->int_head == NULL) + ip->int_head = hp; + else + ip->int_tail->inh_next = hp; + + ip->int_tail = hp; + return (hp->inh_index); +} + +uint_t +dt_inttab_size(const dt_inttab_t *ip) +{ + return (ip->int_nelems); +} + +void +dt_inttab_write(const dt_inttab_t *ip, uint64_t *dst) +{ + const dt_inthash_t *hp; + + for (hp = ip->int_head; hp != NULL; hp = hp->inh_next) + *dst++ = hp->inh_value; +} diff --git a/lib/libdtrace/common/dt_inttab.h b/lib/libdtrace/common/dt_inttab.h new file mode 100644 index 000000000000..c1e86e3eca4e --- /dev/null +++ b/lib/libdtrace/common/dt_inttab.h @@ -0,0 +1,69 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DT_INTTAB_H +#define _DT_INTTAB_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <dtrace.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dt_inthash { + struct dt_inthash *inh_hash; /* next dt_inthash in hash chain */ + struct dt_inthash *inh_next; /* next dt_inthash in output table */ + uint64_t inh_value; /* value associated with this element */ + uint_t inh_index; /* index associated with this element */ + uint_t inh_flags; /* flags (see below) */ +} dt_inthash_t; + +typedef struct dt_inttab { + dtrace_hdl_t *int_hdl; /* pointer back to library handle */ + dt_inthash_t **int_hash; /* array of hash buckets */ + uint_t int_hashlen; /* size of hash bucket array */ + uint_t int_nelems; /* number of elements hashed */ + dt_inthash_t *int_head; /* head of table in index order */ + dt_inthash_t *int_tail; /* tail of table in index order */ + uint_t int_index; /* next index to hand out */ +} dt_inttab_t; + +#define DT_INT_PRIVATE 0 /* only a single ref for this entry */ +#define DT_INT_SHARED 1 /* multiple refs can share entry */ + +extern dt_inttab_t *dt_inttab_create(dtrace_hdl_t *); +extern void dt_inttab_destroy(dt_inttab_t *); +extern int dt_inttab_insert(dt_inttab_t *, uint64_t, uint_t); +extern uint_t dt_inttab_size(const dt_inttab_t *); +extern void dt_inttab_write(const dt_inttab_t *, uint64_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _DT_INTTAB_H */ diff --git a/lib/libdtrace/common/dt_lex.l b/lib/libdtrace/common/dt_lex.l new file mode 100644 index 000000000000..48975274dede --- /dev/null +++ b/lib/libdtrace/common/dt_lex.l @@ -0,0 +1,860 @@ +%{ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> + +#include <dt_impl.h> +#include <dt_grammar.h> +#include <dt_parser.h> +#include <dt_string.h> + +/* + * We need to undefine lex's input and unput macros so that references to these + * call the functions provided at the end of this source file. + */ +#if defined(sun) +#undef input +#undef unput +#else +/* + * Define YY_INPUT for flex since input() can't be re-defined. + */ +#define YY_INPUT(buf,result,max_size) \ + if (yypcb->pcb_fileptr != NULL) { \ + if (((result = fread(buf, 1, max_size, yypcb->pcb_fileptr)) == 0) \ + && ferror(yypcb->pcb_fileptr)) \ + longjmp(yypcb->pcb_jmpbuf, EDT_FIO); \ + } else { \ + int n; \ + for (n = 0; n < max_size && \ + yypcb->pcb_strptr < yypcb->pcb_string + yypcb->pcb_strlen; n++) \ + buf[n] = *yypcb->pcb_strptr++; \ + result = n; \ + } +#endif + +static int id_or_type(const char *); +#if defined(sun) +static int input(void); +static void unput(int); +#endif + +/* + * We first define a set of labeled states for use in the D lexer and then a + * set of regular expressions to simplify things below. The lexer states are: + * + * S0 - D program clause and expression lexing + * S1 - D comments (i.e. skip everything until end of comment) + * S2 - D program outer scope (probe specifiers and declarations) + * S3 - D control line parsing (i.e. after ^# is seen but before \n) + * S4 - D control line scan (locate control directives only and invoke S3) + */ +%} + +%e 1500 /* maximum nodes */ +%p 3700 /* maximum positions */ +%n 600 /* maximum states */ + +%s S0 S1 S2 S3 S4 + +RGX_AGG "@"[a-zA-Z_][0-9a-zA-Z_]* +RGX_PSPEC [-$:a-zA-Z_.?*\\\[\]!][-$:0-9a-zA-Z_.`?*\\\[\]!]* +RGX_IDENT [a-zA-Z_`][0-9a-zA-Z_`]* +RGX_INT ([0-9]+|0[xX][0-9A-Fa-f]+)[uU]?[lL]?[lL]? +RGX_FP ([0-9]+("."?)[0-9]*|"."[0-9]+)((e|E)("+"|-)?[0-9]+)?[fFlL]? +RGX_WS [\f\n\r\t\v ] +RGX_STR ([^"\\\n]|\\[^"\n]|\\\")* +RGX_CHR ([^'\\\n]|\\[^'\n]|\\')* +RGX_INTERP ^[\f\t\v ]*#!.* +RGX_CTL ^[\f\t\v ]*# + +%% + +%{ + +/* + * We insert a special prologue into yylex() itself: if the pcb contains a + * context token, we return that prior to running the normal lexer. This + * allows libdtrace to force yacc into one of our three parsing contexts: D + * expression (DT_CTX_DEXPR), D program (DT_CTX_DPROG) or D type (DT_CTX_DTYPE). + * Once the token is returned, we clear it so this only happens once. + */ +if (yypcb->pcb_token != 0) { + int tok = yypcb->pcb_token; + yypcb->pcb_token = 0; + return (tok); +} + +%} + +<S0>auto return (DT_KEY_AUTO); +<S0>break return (DT_KEY_BREAK); +<S0>case return (DT_KEY_CASE); +<S0>char return (DT_KEY_CHAR); +<S0>const return (DT_KEY_CONST); +<S0>continue return (DT_KEY_CONTINUE); +<S0>counter return (DT_KEY_COUNTER); +<S0>default return (DT_KEY_DEFAULT); +<S0>do return (DT_KEY_DO); +<S0>double return (DT_KEY_DOUBLE); +<S0>else return (DT_KEY_ELSE); +<S0>enum return (DT_KEY_ENUM); +<S0>extern return (DT_KEY_EXTERN); +<S0>float return (DT_KEY_FLOAT); +<S0>for return (DT_KEY_FOR); +<S0>goto return (DT_KEY_GOTO); +<S0>if return (DT_KEY_IF); +<S0>import return (DT_KEY_IMPORT); +<S0>inline return (DT_KEY_INLINE); +<S0>int return (DT_KEY_INT); +<S0>long return (DT_KEY_LONG); +<S0>offsetof return (DT_TOK_OFFSETOF); +<S0>probe return (DT_KEY_PROBE); +<S0>provider return (DT_KEY_PROVIDER); +<S0>register return (DT_KEY_REGISTER); +<S0>restrict return (DT_KEY_RESTRICT); +<S0>return return (DT_KEY_RETURN); +<S0>self return (DT_KEY_SELF); +<S0>short return (DT_KEY_SHORT); +<S0>signed return (DT_KEY_SIGNED); +<S0>sizeof return (DT_TOK_SIZEOF); +<S0>static return (DT_KEY_STATIC); +<S0>string return (DT_KEY_STRING); +<S0>stringof return (DT_TOK_STRINGOF); +<S0>struct return (DT_KEY_STRUCT); +<S0>switch return (DT_KEY_SWITCH); +<S0>this return (DT_KEY_THIS); +<S0>translator return (DT_KEY_XLATOR); +<S0>typedef return (DT_KEY_TYPEDEF); +<S0>union return (DT_KEY_UNION); +<S0>unsigned return (DT_KEY_UNSIGNED); +<S0>void return (DT_KEY_VOID); +<S0>volatile return (DT_KEY_VOLATILE); +<S0>while return (DT_KEY_WHILE); +<S0>xlate return (DT_TOK_XLATE); + +<S2>auto { yybegin(YYS_EXPR); return (DT_KEY_AUTO); } +<S2>char { yybegin(YYS_EXPR); return (DT_KEY_CHAR); } +<S2>const { yybegin(YYS_EXPR); return (DT_KEY_CONST); } +<S2>counter { yybegin(YYS_DEFINE); return (DT_KEY_COUNTER); } +<S2>double { yybegin(YYS_EXPR); return (DT_KEY_DOUBLE); } +<S2>enum { yybegin(YYS_EXPR); return (DT_KEY_ENUM); } +<S2>extern { yybegin(YYS_EXPR); return (DT_KEY_EXTERN); } +<S2>float { yybegin(YYS_EXPR); return (DT_KEY_FLOAT); } +<S2>import { yybegin(YYS_EXPR); return (DT_KEY_IMPORT); } +<S2>inline { yybegin(YYS_DEFINE); return (DT_KEY_INLINE); } +<S2>int { yybegin(YYS_EXPR); return (DT_KEY_INT); } +<S2>long { yybegin(YYS_EXPR); return (DT_KEY_LONG); } +<S2>provider { yybegin(YYS_DEFINE); return (DT_KEY_PROVIDER); } +<S2>register { yybegin(YYS_EXPR); return (DT_KEY_REGISTER); } +<S2>restrict { yybegin(YYS_EXPR); return (DT_KEY_RESTRICT); } +<S2>self { yybegin(YYS_EXPR); return (DT_KEY_SELF); } +<S2>short { yybegin(YYS_EXPR); return (DT_KEY_SHORT); } +<S2>signed { yybegin(YYS_EXPR); return (DT_KEY_SIGNED); } +<S2>static { yybegin(YYS_EXPR); return (DT_KEY_STATIC); } +<S2>string { yybegin(YYS_EXPR); return (DT_KEY_STRING); } +<S2>struct { yybegin(YYS_EXPR); return (DT_KEY_STRUCT); } +<S2>this { yybegin(YYS_EXPR); return (DT_KEY_THIS); } +<S2>translator { yybegin(YYS_DEFINE); return (DT_KEY_XLATOR); } +<S2>typedef { yybegin(YYS_EXPR); return (DT_KEY_TYPEDEF); } +<S2>union { yybegin(YYS_EXPR); return (DT_KEY_UNION); } +<S2>unsigned { yybegin(YYS_EXPR); return (DT_KEY_UNSIGNED); } +<S2>void { yybegin(YYS_EXPR); return (DT_KEY_VOID); } +<S2>volatile { yybegin(YYS_EXPR); return (DT_KEY_VOLATILE); } + +<S0>"$$"[0-9]+ { + int i = atoi(yytext + 2); + char *v = ""; + + /* + * A macro argument reference substitutes the text of + * an argument in place of the current token. When we + * see $$<d> we fetch the saved string from pcb_sargv + * (or use the default argument if the option has been + * set and the argument hasn't been specified) and + * return a token corresponding to this string. + */ + if (i < 0 || (i >= yypcb->pcb_sargc && + !(yypcb->pcb_cflags & DTRACE_C_DEFARG))) { + xyerror(D_MACRO_UNDEF, "macro argument %s is " + "not defined\n", yytext); + } + + if (i < yypcb->pcb_sargc) { + v = yypcb->pcb_sargv[i]; /* get val from pcb */ + yypcb->pcb_sflagv[i] |= DT_IDFLG_REF; + } + + if ((yylval.l_str = strdup(v)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + (void) stresc2chr(yylval.l_str); + return (DT_TOK_STRING); + } + +<S0>"$"[0-9]+ { + int i = atoi(yytext + 1); + char *p, *v = "0"; + + /* + * A macro argument reference substitutes the text of + * one identifier or integer pattern for another. When + * we see $<d> we fetch the saved string from pcb_sargv + * (or use the default argument if the option has been + * set and the argument hasn't been specified) and + * return a token corresponding to this string. + */ + if (i < 0 || (i >= yypcb->pcb_sargc && + !(yypcb->pcb_cflags & DTRACE_C_DEFARG))) { + xyerror(D_MACRO_UNDEF, "macro argument %s is " + "not defined\n", yytext); + } + + if (i < yypcb->pcb_sargc) { + v = yypcb->pcb_sargv[i]; /* get val from pcb */ + yypcb->pcb_sflagv[i] |= DT_IDFLG_REF; + } + + /* + * If the macro text is not a valid integer or ident, + * then we treat it as a string. The string may be + * optionally enclosed in quotes, which we strip. + */ + if (strbadidnum(v)) { + size_t len = strlen(v); + + if (len != 1 && *v == '"' && v[len - 1] == '"') + yylval.l_str = strndup(v + 1, len - 2); + else + yylval.l_str = strndup(v, len); + + if (yylval.l_str == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + (void) stresc2chr(yylval.l_str); + return (DT_TOK_STRING); + } + + /* + * If the macro text is not a string an begins with a + * digit or a +/- sign, process it as an integer token. + */ + if (isdigit(v[0]) || v[0] == '-' || v[0] == '+') { + if (isdigit(v[0])) + yyintprefix = 0; + else + yyintprefix = *v++; + + errno = 0; + yylval.l_int = strtoull(v, &p, 0); + (void) strncpy(yyintsuffix, p, + sizeof (yyintsuffix)); + yyintdecimal = *v != '0'; + + if (errno == ERANGE) { + xyerror(D_MACRO_OFLOW, "macro argument" + " %s constant %s results in integer" + " overflow\n", yytext, v); + } + + return (DT_TOK_INT); + } + + return (id_or_type(v)); + } + +<S0>"$$"{RGX_IDENT} { + dt_ident_t *idp = dt_idhash_lookup( + yypcb->pcb_hdl->dt_macros, yytext + 2); + + char s[16]; /* enough for UINT_MAX + \0 */ + + if (idp == NULL) { + xyerror(D_MACRO_UNDEF, "macro variable %s " + "is not defined\n", yytext); + } + + /* + * For the moment, all current macro variables are of + * type id_t (refer to dtrace_update() for details). + */ + (void) snprintf(s, sizeof (s), "%u", idp->di_id); + if ((yylval.l_str = strdup(s)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + return (DT_TOK_STRING); + } + +<S0>"$"{RGX_IDENT} { + dt_ident_t *idp = dt_idhash_lookup( + yypcb->pcb_hdl->dt_macros, yytext + 1); + + if (idp == NULL) { + xyerror(D_MACRO_UNDEF, "macro variable %s " + "is not defined\n", yytext); + } + + /* + * For the moment, all current macro variables are of + * type id_t (refer to dtrace_update() for details). + */ + yylval.l_int = (intmax_t)(int)idp->di_id; + yyintprefix = 0; + yyintsuffix[0] = '\0'; + yyintdecimal = 1; + + return (DT_TOK_INT); + } + +<S0>{RGX_IDENT} { + return (id_or_type(yytext)); + } + +<S0>{RGX_AGG} { + if ((yylval.l_str = strdup(yytext)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + return (DT_TOK_AGG); + } + +<S0>"@" { + if ((yylval.l_str = strdup("@_")) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + return (DT_TOK_AGG); + } + +<S0>{RGX_INT} | +<S2>{RGX_INT} | +<S3>{RGX_INT} { + char *p; + + errno = 0; + yylval.l_int = strtoull(yytext, &p, 0); + yyintprefix = 0; + (void) strncpy(yyintsuffix, p, sizeof (yyintsuffix)); + yyintdecimal = yytext[0] != '0'; + + if (errno == ERANGE) { + xyerror(D_INT_OFLOW, "constant %s results in " + "integer overflow\n", yytext); + } + + if (*p != '\0' && strchr("uUlL", *p) == NULL) { + xyerror(D_INT_DIGIT, "constant %s contains " + "invalid digit %c\n", yytext, *p); + } + + if ((YYSTATE) != S3) + return (DT_TOK_INT); + + yypragma = dt_node_link(yypragma, + dt_node_int(yylval.l_int)); + } + +<S0>{RGX_FP} yyerror("floating-point constants are not permitted\n"); + +<S0>\"{RGX_STR}$ | +<S3>\"{RGX_STR}$ xyerror(D_STR_NL, "newline encountered in string literal"); + +<S0>\"{RGX_STR}\" | +<S3>\"{RGX_STR}\" { + /* + * Quoted string -- convert C escape sequences and + * return the string as a token. + */ + yylval.l_str = strndup(yytext + 1, yyleng - 2); + + if (yylval.l_str == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + (void) stresc2chr(yylval.l_str); + if ((YYSTATE) != S3) + return (DT_TOK_STRING); + + yypragma = dt_node_link(yypragma, + dt_node_string(yylval.l_str)); + } + +<S0>'{RGX_CHR}$ xyerror(D_CHR_NL, "newline encountered in character constant"); + +<S0>'{RGX_CHR}' { + char *s, *p, *q; + size_t nbytes; + + /* + * Character constant -- convert C escape sequences and + * return the character as an integer immediate value. + */ + if (yyleng == 2) + xyerror(D_CHR_NULL, "empty character constant"); + + s = yytext + 1; + yytext[yyleng - 1] = '\0'; + nbytes = stresc2chr(s); + yylval.l_int = 0; + yyintprefix = 0; + yyintsuffix[0] = '\0'; + yyintdecimal = 1; + + if (nbytes > sizeof (yylval.l_int)) { + xyerror(D_CHR_OFLOW, "character constant is " + "too long"); + } +#if BYTE_ORDER == _LITTLE_ENDIAN + p = ((char *)&yylval.l_int) + nbytes - 1; + for (q = s; nbytes != 0; nbytes--) + *p-- = *q++; +#else + bcopy(s, ((char *)&yylval.l_int) + + sizeof (yylval.l_int) - nbytes, nbytes); +#endif + return (DT_TOK_INT); + } + +<S0>"/*" | +<S2>"/*" { + yypcb->pcb_cstate = (YYSTATE); + BEGIN(S1); + } + +<S0>{RGX_INTERP} | +<S2>{RGX_INTERP} ; /* discard any #! lines */ + +<S0>{RGX_CTL} | +<S2>{RGX_CTL} | +<S4>{RGX_CTL} { + assert(yypragma == NULL); + yypcb->pcb_cstate = (YYSTATE); + BEGIN(S3); + } + +<S4>. ; /* discard */ +<S4>"\n" ; /* discard */ + +<S0>"/" { + int c, tok; + + /* + * The use of "/" as the predicate delimiter and as the + * integer division symbol requires special lookahead + * to avoid a shift/reduce conflict in the D grammar. + * We look ahead to the next non-whitespace character. + * If we encounter EOF, ";", "{", or "/", then this "/" + * closes the predicate and we return DT_TOK_EPRED. + * If we encounter anything else, it's DT_TOK_DIV. + */ + while ((c = input()) != 0) { + if (strchr("\f\n\r\t\v ", c) == NULL) + break; + } + + if (c == 0 || c == ';' || c == '{' || c == '/') { + if (yypcb->pcb_parens != 0) { + yyerror("closing ) expected in " + "predicate before /\n"); + } + if (yypcb->pcb_brackets != 0) { + yyerror("closing ] expected in " + "predicate before /\n"); + } + tok = DT_TOK_EPRED; + } else + tok = DT_TOK_DIV; + + unput(c); + return (tok); + } + +<S0>"(" { + yypcb->pcb_parens++; + return (DT_TOK_LPAR); + } + +<S0>")" { + if (--yypcb->pcb_parens < 0) + yyerror("extra ) in input stream\n"); + return (DT_TOK_RPAR); + } + +<S0>"[" { + yypcb->pcb_brackets++; + return (DT_TOK_LBRAC); + } + +<S0>"]" { + if (--yypcb->pcb_brackets < 0) + yyerror("extra ] in input stream\n"); + return (DT_TOK_RBRAC); + } + +<S0>"{" | +<S2>"{" { + yypcb->pcb_braces++; + return ('{'); + } + +<S0>"}" { + if (--yypcb->pcb_braces < 0) + yyerror("extra } in input stream\n"); + return ('}'); + } + +<S0>"|" return (DT_TOK_BOR); +<S0>"^" return (DT_TOK_XOR); +<S0>"&" return (DT_TOK_BAND); +<S0>"&&" return (DT_TOK_LAND); +<S0>"^^" return (DT_TOK_LXOR); +<S0>"||" return (DT_TOK_LOR); +<S0>"==" return (DT_TOK_EQU); +<S0>"!=" return (DT_TOK_NEQ); +<S0>"<" return (DT_TOK_LT); +<S0>"<=" return (DT_TOK_LE); +<S0>">" return (DT_TOK_GT); +<S0>">=" return (DT_TOK_GE); +<S0>"<<" return (DT_TOK_LSH); +<S0>">>" return (DT_TOK_RSH); +<S0>"+" return (DT_TOK_ADD); +<S0>"-" return (DT_TOK_SUB); +<S0>"*" return (DT_TOK_MUL); +<S0>"%" return (DT_TOK_MOD); +<S0>"~" return (DT_TOK_BNEG); +<S0>"!" return (DT_TOK_LNEG); +<S0>"?" return (DT_TOK_QUESTION); +<S0>":" return (DT_TOK_COLON); +<S0>"." return (DT_TOK_DOT); +<S0>"->" return (DT_TOK_PTR); +<S0>"=" return (DT_TOK_ASGN); +<S0>"+=" return (DT_TOK_ADD_EQ); +<S0>"-=" return (DT_TOK_SUB_EQ); +<S0>"*=" return (DT_TOK_MUL_EQ); +<S0>"/=" return (DT_TOK_DIV_EQ); +<S0>"%=" return (DT_TOK_MOD_EQ); +<S0>"&=" return (DT_TOK_AND_EQ); +<S0>"^=" return (DT_TOK_XOR_EQ); +<S0>"|=" return (DT_TOK_OR_EQ); +<S0>"<<=" return (DT_TOK_LSH_EQ); +<S0>">>=" return (DT_TOK_RSH_EQ); +<S0>"++" return (DT_TOK_ADDADD); +<S0>"--" return (DT_TOK_SUBSUB); +<S0>"..." return (DT_TOK_ELLIPSIS); +<S0>"," return (DT_TOK_COMMA); +<S0>";" return (';'); +<S0>{RGX_WS} ; /* discard */ +<S0>"\\"\n ; /* discard */ +<S0>. yyerror("syntax error near \"%c\"\n", yytext[0]); + +<S1>"/*" yyerror("/* encountered inside a comment\n"); +<S1>"*/" BEGIN(yypcb->pcb_cstate); +<S1>.|\n ; /* discard */ + +<S2>{RGX_PSPEC} { + /* + * S2 has an ambiguity because RGX_PSPEC includes '*' + * as a glob character and '*' also can be DT_TOK_STAR. + * Since lex always matches the longest token, this + * rule can be matched by an input string like "int*", + * which could begin a global variable declaration such + * as "int*x;" or could begin a RGX_PSPEC with globbing + * such as "int* { trace(timestamp); }". If C_PSPEC is + * not set, we must resolve the ambiguity in favor of + * the type and perform lexer pushback if the fragment + * before '*' or entire fragment matches a type name. + * If C_PSPEC is set, we always return a PSPEC token. + * If C_PSPEC is off, the user can avoid ambiguity by + * including a ':' delimiter in the specifier, which + * they should be doing anyway to specify the provider. + */ + if (!(yypcb->pcb_cflags & DTRACE_C_PSPEC) && + strchr(yytext, ':') == NULL) { + + char *p = strchr(yytext, '*'); + char *q = yytext + yyleng - 1; + + if (p != NULL && p > yytext) + *p = '\0'; /* prune yytext */ + + if (dt_type_lookup(yytext, NULL) == 0) { + yylval.l_str = strdup(yytext); + + if (yylval.l_str == NULL) { + longjmp(yypcb->pcb_jmpbuf, + EDT_NOMEM); + } + + if (p != NULL && p > yytext) { + for (*p = '*'; q >= p; q--) + unput(*q); + } + + yybegin(YYS_EXPR); + return (DT_TOK_TNAME); + } + + if (p != NULL && p > yytext) + *p = '*'; /* restore yytext */ + } + + if ((yylval.l_str = strdup(yytext)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + return (DT_TOK_PSPEC); + } + +<S2>"/" return (DT_TOK_DIV); +<S2>"," return (DT_TOK_COMMA); + +<S2>{RGX_WS} ; /* discard */ +<S2>. yyerror("syntax error near \"%c\"\n", yytext[0]); + +<S3>\n { + dt_pragma(yypragma); + yypragma = NULL; + BEGIN(yypcb->pcb_cstate); + } + +<S3>[\f\t\v ]+ ; /* discard */ + +<S3>[^\f\n\t\v "]+ { + dt_node_t *dnp; + + if ((yylval.l_str = strdup(yytext)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + /* + * We want to call dt_node_ident() here, but we can't + * because it will expand inlined identifiers, which we + * don't want to do from #pragma context in order to + * support pragmas that apply to the ident itself. We + * call dt_node_string() and then reset dn_op instead. + */ + dnp = dt_node_string(yylval.l_str); + dnp->dn_kind = DT_NODE_IDENT; + dnp->dn_op = DT_TOK_IDENT; + yypragma = dt_node_link(yypragma, dnp); + } + +<S3>. yyerror("syntax error near \"%c\"\n", yytext[0]); + +%% + +/* + * yybegin provides a wrapper for use from C code around the lex BEGIN() macro. + * We use two main states for lexing because probe descriptions use a syntax + * that is incompatible with the normal D tokens (e.g. names can contain "-"). + * yybegin also handles the job of switching between two lists of dt_nodes + * as we allocate persistent definitions, like inlines, and transient nodes + * that will be freed once we are done parsing the current program file. + */ +void +yybegin(yystate_t state) +{ +#ifdef YYDEBUG + yydebug = _dtrace_debug; +#endif + if (yypcb->pcb_yystate == state) + return; /* nothing to do if we're in the state already */ + + if (yypcb->pcb_yystate == YYS_DEFINE) { + yypcb->pcb_list = yypcb->pcb_hold; + yypcb->pcb_hold = NULL; + } + + switch (state) { + case YYS_CLAUSE: + BEGIN(S2); + break; + case YYS_DEFINE: + assert(yypcb->pcb_hold == NULL); + yypcb->pcb_hold = yypcb->pcb_list; + yypcb->pcb_list = NULL; + /*FALLTHRU*/ + case YYS_EXPR: + BEGIN(S0); + break; + case YYS_DONE: + break; + case YYS_CONTROL: + BEGIN(S4); + break; + default: + xyerror(D_UNKNOWN, "internal error -- bad yystate %d\n", state); + } + + yypcb->pcb_yystate = state; +} + +void +yyinit(dt_pcb_t *pcb) +{ + yypcb = pcb; + yylineno = 1; + yypragma = NULL; +#if defined(sun) + yysptr = yysbuf; +#endif +} + +/* + * Given a lexeme 's' (typically yytext), set yylval and return an appropriate + * token to the parser indicating either an identifier or a typedef name. + * User-defined global variables always take precedence over types, but we do + * use some heuristics because D programs can look at an ever-changing set of + * kernel types and also can implicitly instantiate variables by assignment, + * unlike in C. The code here is ordered carefully as lookups are not cheap. + */ +static int +id_or_type(const char *s) +{ + dtrace_hdl_t *dtp = yypcb->pcb_hdl; + dt_decl_t *ddp = yypcb->pcb_dstack.ds_decl; + int c0, c1, ttok = DT_TOK_TNAME; + dt_ident_t *idp; + + if ((s = yylval.l_str = strdup(s)) == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + + /* + * If the lexeme is a global variable or likely identifier or *not* a + * type_name, then it is an identifier token. + */ + if (dt_idstack_lookup(&yypcb->pcb_globals, s) != NULL || + dt_idhash_lookup(yypcb->pcb_idents, s) != NULL || + dt_type_lookup(s, NULL) != 0) + return (DT_TOK_IDENT); + + /* + * If we're in the midst of parsing a declaration and a type_specifier + * has already been shifted, then return DT_TOK_IDENT instead of TNAME. + * This semantic is necessary to permit valid ISO C code such as: + * + * typedef int foo; + * struct s { foo foo; }; + * + * without causing shift/reduce conflicts in the direct_declarator part + * of the grammar. The result is that we must check for conflicting + * redeclarations of the same identifier as part of dt_node_decl(). + */ + if (ddp != NULL && ddp->dd_name != NULL) + return (DT_TOK_IDENT); + + /* + * If the lexeme is a type name and we are not in a program clause, + * then always interpret it as a type and return DT_TOK_TNAME. + */ + if ((YYSTATE) != S0) + return (DT_TOK_TNAME); + + /* + * If the lexeme matches a type name but is in a program clause, then + * it could be a type or it could be an undefined variable. Peek at + * the next token to decide. If we see ++, --, [, or =, we know there + * might be an assignment that is trying to create a global variable, + * so we optimistically return DT_TOK_IDENT. There is no harm in being + * wrong: a type_name followed by ++, --, [, or = is a syntax error. + */ + while ((c0 = input()) != 0) { + if (strchr("\f\n\r\t\v ", c0) == NULL) + break; + } + + switch (c0) { + case '+': + case '-': + if ((c1 = input()) == c0) + ttok = DT_TOK_IDENT; + unput(c1); + break; + + case '=': + if ((c1 = input()) != c0) + ttok = DT_TOK_IDENT; + unput(c1); + break; + case '[': + ttok = DT_TOK_IDENT; + break; + } + + if (ttok == DT_TOK_IDENT) { + idp = dt_idhash_insert(yypcb->pcb_idents, s, DT_IDENT_SCALAR, 0, + 0, _dtrace_defattr, 0, &dt_idops_thaw, NULL, dtp->dt_gen); + + if (idp == NULL) + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); + } + + unput(c0); + return (ttok); +} + +#if defined(sun) +static int +input(void) +{ + int c; + + if (yysptr > yysbuf) + c = *--yysptr; + else if (yypcb->pcb_fileptr != NULL) + c = fgetc(yypcb->pcb_fileptr); + else if (yypcb->pcb_strptr < yypcb->pcb_string + yypcb->pcb_strlen) + c = *yypcb->pcb_strptr++; + else + c = EOF; + + if (c == '\n') + yylineno++; + + if (c != EOF) + return (c); + + if ((YYSTATE) == S1) + yyerror("end-of-file encountered before matching */\n"); + + if ((YYSTATE) == S3) + yyerror("end-of-file encountered before end of control line\n"); + + if (yypcb->pcb_fileptr != NULL && ferror(yypcb->pcb_fileptr)) + longjmp(yypcb->pcb_jmpbuf, EDT_FIO); + + return (0); /* EOF */ +} + +static void +unput(int c) +{ + if (c == '\n') + yylineno--; + + *yysptr++ = c; + yytchar = c; +} +#endif diff --git a/lib/libdtrace/common/dt_link.c b/lib/libdtrace/common/dt_link.c new file mode 100644 index 000000000000..bc46df577a27 --- /dev/null +++ b/lib/libdtrace/common/dt_link.c @@ -0,0 +1,1774 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#define ELF_TARGET_ALL +#include <elf.h> + +#include <sys/types.h> +#if defined(sun) +#include <sys/sysmacros.h> +#else +#define P2ROUNDUP(x, align) (-(-(x) & -(align))) +#endif + +#include <unistd.h> +#include <strings.h> +#if defined(sun) +#include <alloca.h> +#endif +#include <limits.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#if defined(sun) +#include <wait.h> +#else +#include <sys/wait.h> +#endif +#include <assert.h> +#include <sys/ipc.h> + +#include <dt_impl.h> +#include <dt_provider.h> +#include <dt_program.h> +#include <dt_string.h> + +#define ESHDR_NULL 0 +#define ESHDR_SHSTRTAB 1 +#define ESHDR_DOF 2 +#define ESHDR_STRTAB 3 +#define ESHDR_SYMTAB 4 +#define ESHDR_REL 5 +#define ESHDR_NUM 6 + +#define PWRITE_SCN(index, data) \ + (lseek64(fd, (off64_t)elf_file.shdr[(index)].sh_offset, SEEK_SET) != \ + (off64_t)elf_file.shdr[(index)].sh_offset || \ + dt_write(dtp, fd, (data), elf_file.shdr[(index)].sh_size) != \ + elf_file.shdr[(index)].sh_size) + +static const char DTRACE_SHSTRTAB32[] = "\0" +".shstrtab\0" /* 1 */ +".SUNW_dof\0" /* 11 */ +".strtab\0" /* 21 */ +".symtab\0" /* 29 */ +#ifdef __sparc +".rela.SUNW_dof"; /* 37 */ +#else +".rel.SUNW_dof"; /* 37 */ +#endif + +static const char DTRACE_SHSTRTAB64[] = "\0" +".shstrtab\0" /* 1 */ +".SUNW_dof\0" /* 11 */ +".strtab\0" /* 21 */ +".symtab\0" /* 29 */ +".rela.SUNW_dof"; /* 37 */ + +static const char DOFSTR[] = "__SUNW_dof"; +static const char DOFLAZYSTR[] = "___SUNW_dof"; + +typedef struct dt_link_pair { + struct dt_link_pair *dlp_next; /* next pair in linked list */ + void *dlp_str; /* buffer for string table */ + void *dlp_sym; /* buffer for symbol table */ +} dt_link_pair_t; + +typedef struct dof_elf32 { + uint32_t de_nrel; /* relocation count */ +#ifdef __sparc + Elf32_Rela *de_rel; /* array of relocations for sparc */ +#else + Elf32_Rel *de_rel; /* array of relocations for x86 */ +#endif + uint32_t de_nsym; /* symbol count */ + Elf32_Sym *de_sym; /* array of symbols */ + uint32_t de_strlen; /* size of of string table */ + char *de_strtab; /* string table */ + uint32_t de_global; /* index of the first global symbol */ +} dof_elf32_t; + +static int +prepare_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf32_t *dep) +{ + dof_sec_t *dofs, *s; + dof_relohdr_t *dofrh; + dof_relodesc_t *dofr; + char *strtab; + int i, j, nrel; + size_t strtabsz = 1; + uint32_t count = 0; + size_t base; + Elf32_Sym *sym; +#ifdef __sparc + Elf32_Rela *rel; +#else + Elf32_Rel *rel; +#endif + + /*LINTED*/ + dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff); + + /* + * First compute the size of the string table and the number of + * relocations present in the DOF. + */ + for (i = 0; i < dof->dofh_secnum; i++) { + if (dofs[i].dofs_type != DOF_SECT_URELHDR) + continue; + + /*LINTED*/ + dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); + + s = &dofs[dofrh->dofr_strtab]; + strtab = (char *)dof + s->dofs_offset; + assert(strtab[0] == '\0'); + strtabsz += s->dofs_size - 1; + + s = &dofs[dofrh->dofr_relsec]; + /*LINTED*/ + dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); + count += s->dofs_size / s->dofs_entsize; + } + + dep->de_strlen = strtabsz; + dep->de_nrel = count; + dep->de_nsym = count + 1; /* the first symbol is always null */ + + if (dtp->dt_lazyload) { + dep->de_strlen += sizeof (DOFLAZYSTR); + dep->de_nsym++; + } else { + dep->de_strlen += sizeof (DOFSTR); + dep->de_nsym++; + } + + if ((dep->de_rel = calloc(dep->de_nrel, + sizeof (dep->de_rel[0]))) == NULL) { + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf32_Sym))) == NULL) { + free(dep->de_rel); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) { + free(dep->de_rel); + free(dep->de_sym); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + count = 0; + strtabsz = 1; + dep->de_strtab[0] = '\0'; + rel = dep->de_rel; + sym = dep->de_sym; + dep->de_global = 1; + + /* + * The first symbol table entry must be zeroed and is always ignored. + */ + bzero(sym, sizeof (Elf32_Sym)); + sym++; + + /* + * Take a second pass through the DOF sections filling in the + * memory we allocated. + */ + for (i = 0; i < dof->dofh_secnum; i++) { + if (dofs[i].dofs_type != DOF_SECT_URELHDR) + continue; + + /*LINTED*/ + dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); + + s = &dofs[dofrh->dofr_strtab]; + strtab = (char *)dof + s->dofs_offset; + bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size); + base = strtabsz; + strtabsz += s->dofs_size - 1; + + s = &dofs[dofrh->dofr_relsec]; + /*LINTED*/ + dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); + nrel = s->dofs_size / s->dofs_entsize; + + s = &dofs[dofrh->dofr_tgtsec]; + + for (j = 0; j < nrel; j++) { +#if defined(__arm__) +/* XXX */ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); +#elif defined(__ia64__) +/* XXX */ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); +#elif defined(__i386) || defined(__amd64) + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset; + rel->r_info = ELF32_R_INFO(count + dep->de_global, + R_386_32); +#elif defined(__mips__) +/* XXX */ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); +#elif defined(__powerpc__) +/* XXX */ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); +#elif defined(__sparc) + /* + * Add 4 bytes to hit the low half of this 64-bit + * big-endian address. + */ + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset + 4; + rel->r_info = ELF32_R_INFO(count + dep->de_global, + R_SPARC_32); +#else +#error unknown ISA +#endif + + sym->st_name = base + dofr[j].dofr_name - 1; + sym->st_value = 0; + sym->st_size = 0; + sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC); + sym->st_other = 0; + sym->st_shndx = SHN_UNDEF; + + rel++; + sym++; + count++; + } + } + + /* + * Add a symbol for the DOF itself. We use a different symbol for + * lazily and actively loaded DOF to make them easy to distinguish. + */ + sym->st_name = strtabsz; + sym->st_value = 0; + sym->st_size = dof->dofh_filesz; + sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT); + sym->st_other = 0; + sym->st_shndx = ESHDR_DOF; + sym++; + + if (dtp->dt_lazyload) { + bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz, + sizeof (DOFLAZYSTR)); + strtabsz += sizeof (DOFLAZYSTR); + } else { + bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR)); + strtabsz += sizeof (DOFSTR); + } + + assert(count == dep->de_nrel); + assert(strtabsz == dep->de_strlen); + + return (0); +} + + +typedef struct dof_elf64 { + uint32_t de_nrel; + Elf64_Rela *de_rel; + uint32_t de_nsym; + Elf64_Sym *de_sym; + + uint32_t de_strlen; + char *de_strtab; + + uint32_t de_global; +} dof_elf64_t; + +static int +prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) +{ + dof_sec_t *dofs, *s; + dof_relohdr_t *dofrh; + dof_relodesc_t *dofr; + char *strtab; + int i, j, nrel; + size_t strtabsz = 1; + uint32_t count = 0; + size_t base; + Elf64_Sym *sym; + Elf64_Rela *rel; + + /*LINTED*/ + dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff); + + /* + * First compute the size of the string table and the number of + * relocations present in the DOF. + */ + for (i = 0; i < dof->dofh_secnum; i++) { + if (dofs[i].dofs_type != DOF_SECT_URELHDR) + continue; + + /*LINTED*/ + dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); + + s = &dofs[dofrh->dofr_strtab]; + strtab = (char *)dof + s->dofs_offset; + assert(strtab[0] == '\0'); + strtabsz += s->dofs_size - 1; + + s = &dofs[dofrh->dofr_relsec]; + /*LINTED*/ + dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); + count += s->dofs_size / s->dofs_entsize; + } + + dep->de_strlen = strtabsz; + dep->de_nrel = count; + dep->de_nsym = count + 1; /* the first symbol is always null */ + + if (dtp->dt_lazyload) { + dep->de_strlen += sizeof (DOFLAZYSTR); + dep->de_nsym++; + } else { + dep->de_strlen += sizeof (DOFSTR); + dep->de_nsym++; + } + + if ((dep->de_rel = calloc(dep->de_nrel, + sizeof (dep->de_rel[0]))) == NULL) { + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf64_Sym))) == NULL) { + free(dep->de_rel); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) { + free(dep->de_rel); + free(dep->de_sym); + return (dt_set_errno(dtp, EDT_NOMEM)); + } + + count = 0; + strtabsz = 1; + dep->de_strtab[0] = '\0'; + rel = dep->de_rel; + sym = dep->de_sym; + dep->de_global = 1; + + /* + * The first symbol table entry must be zeroed and is always ignored. + */ + bzero(sym, sizeof (Elf64_Sym)); + sym++; + + /* + * Take a second pass through the DOF sections filling in the + * memory we allocated. + */ + for (i = 0; i < dof->dofh_secnum; i++) { + if (dofs[i].dofs_type != DOF_SECT_URELHDR) + continue; + + /*LINTED*/ + dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); + + s = &dofs[dofrh->dofr_strtab]; + strtab = (char *)dof + s->dofs_offset; + bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size); + base = strtabsz; + strtabsz += s->dofs_size - 1; + + s = &dofs[dofrh->dofr_relsec]; + /*LINTED*/ + dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); + nrel = s->dofs_size / s->dofs_entsize; + + s = &dofs[dofrh->dofr_tgtsec]; + + for (j = 0; j < nrel; j++) { +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); +#ifdef DOODAD +#if defined(__arm__) +/* XXX */ +#elif defined(__ia64__) +/* XXX */ +#elif defined(__mips__) +/* XXX */ +#elif defined(__powerpc__) +/* XXX */ +#elif defined(__i386) || defined(__amd64) + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset; + rel->r_info = ELF64_R_INFO(count + dep->de_global, + R_AMD64_64); +#elif defined(__sparc) + rel->r_offset = s->dofs_offset + + dofr[j].dofr_offset; + rel->r_info = ELF64_R_INFO(count + dep->de_global, + R_SPARC_64); +#else +#error unknown ISA +#endif +#endif + + sym->st_name = base + dofr[j].dofr_name - 1; + sym->st_value = 0; + sym->st_size = 0; + sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_FUNC); + sym->st_other = 0; + sym->st_shndx = SHN_UNDEF; + + rel++; + sym++; + count++; + } + } + + /* + * Add a symbol for the DOF itself. We use a different symbol for + * lazily and actively loaded DOF to make them easy to distinguish. + */ + sym->st_name = strtabsz; + sym->st_value = 0; + sym->st_size = dof->dofh_filesz; + sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_OBJECT); + sym->st_other = 0; + sym->st_shndx = ESHDR_DOF; + sym++; + + if (dtp->dt_lazyload) { + bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz, + sizeof (DOFLAZYSTR)); + strtabsz += sizeof (DOFLAZYSTR); + } else { + bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR)); + strtabsz += sizeof (DOFSTR); + } + + assert(count == dep->de_nrel); + assert(strtabsz == dep->de_strlen); + + return (0); +} + +/* + * Write out an ELF32 file prologue consisting of a header, section headers, + * and a section header string table. The DOF data will follow this prologue + * and complete the contents of the given ELF file. + */ +static int +dump_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) +{ + struct { + Elf32_Ehdr ehdr; + Elf32_Shdr shdr[ESHDR_NUM]; + } elf_file; + + Elf32_Shdr *shp; + Elf32_Off off; + dof_elf32_t de; + int ret = 0; + uint_t nshdr; + + if (prepare_elf32(dtp, dof, &de) != 0) + return (-1); /* errno is set for us */ + + /* + * If there are no relocations, we only need enough sections for + * the shstrtab and the DOF. + */ + nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; + + bzero(&elf_file, sizeof (elf_file)); + + elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; + elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; + elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; + elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; + elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; + elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS32; +#if BYTE_ORDER == _BIG_ENDIAN + elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#else + elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#endif +#if defined(__FreeBSD__) + elf_file.ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; +#endif + elf_file.ehdr.e_type = ET_REL; +#if defined(__arm__) + elf_file.ehdr.e_machine = EM_ARM; +#elif defined(__ia64__) + elf_file.ehdr.e_machine = EM_IA_64; +#elif defined(__mips__) + elf_file.ehdr.e_machine = EM_MIPS; +#elif defined(__powerpc__) + elf_file.ehdr.e_machine = EM_PPC; +#elif defined(__sparc) + elf_file.ehdr.e_machine = EM_SPARC; +#elif defined(__i386) || defined(__amd64) + elf_file.ehdr.e_machine = EM_386; +#endif + elf_file.ehdr.e_version = EV_CURRENT; + elf_file.ehdr.e_shoff = sizeof (Elf32_Ehdr); + elf_file.ehdr.e_ehsize = sizeof (Elf32_Ehdr); + elf_file.ehdr.e_phentsize = sizeof (Elf32_Phdr); + elf_file.ehdr.e_shentsize = sizeof (Elf32_Shdr); + elf_file.ehdr.e_shnum = nshdr; + elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; + off = sizeof (elf_file) + nshdr * sizeof (Elf32_Shdr); + + shp = &elf_file.shdr[ESHDR_SHSTRTAB]; + shp->sh_name = 1; /* DTRACE_SHSTRTAB32[1] = ".shstrtab" */ + shp->sh_type = SHT_STRTAB; + shp->sh_offset = off; + shp->sh_size = sizeof (DTRACE_SHSTRTAB32); + shp->sh_addralign = sizeof (char); + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); + + shp = &elf_file.shdr[ESHDR_DOF]; + shp->sh_name = 11; /* DTRACE_SHSTRTAB32[11] = ".SUNW_dof" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_SUNW_dof; + shp->sh_offset = off; + shp->sh_size = dof->dofh_filesz; + shp->sh_addralign = 8; + off = shp->sh_offset + shp->sh_size; + + shp = &elf_file.shdr[ESHDR_STRTAB]; + shp->sh_name = 21; /* DTRACE_SHSTRTAB32[21] = ".strtab" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_STRTAB; + shp->sh_offset = off; + shp->sh_size = de.de_strlen; + shp->sh_addralign = sizeof (char); + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 4); + + shp = &elf_file.shdr[ESHDR_SYMTAB]; + shp->sh_name = 29; /* DTRACE_SHSTRTAB32[29] = ".symtab" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_SYMTAB; + shp->sh_entsize = sizeof (Elf32_Sym); + shp->sh_link = ESHDR_STRTAB; + shp->sh_offset = off; + shp->sh_info = de.de_global; + shp->sh_size = de.de_nsym * sizeof (Elf32_Sym); + shp->sh_addralign = 4; + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 4); + + if (de.de_nrel == 0) { + if (dt_write(dtp, fd, &elf_file, + sizeof (elf_file)) != sizeof (elf_file) || + PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) || + PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || + PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || + PWRITE_SCN(ESHDR_DOF, dof)) { + ret = dt_set_errno(dtp, errno); + } + } else { + shp = &elf_file.shdr[ESHDR_REL]; + shp->sh_name = 37; /* DTRACE_SHSTRTAB32[37] = ".rel.SUNW_dof" */ + shp->sh_flags = SHF_ALLOC; +#ifdef __sparc + shp->sh_type = SHT_RELA; +#else + shp->sh_type = SHT_REL; +#endif + shp->sh_entsize = sizeof (de.de_rel[0]); + shp->sh_link = ESHDR_SYMTAB; + shp->sh_info = ESHDR_DOF; + shp->sh_offset = off; + shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); + shp->sh_addralign = 4; + + if (dt_write(dtp, fd, &elf_file, + sizeof (elf_file)) != sizeof (elf_file) || + PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) || + PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || + PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || + PWRITE_SCN(ESHDR_REL, de.de_rel) || + PWRITE_SCN(ESHDR_DOF, dof)) { + ret = dt_set_errno(dtp, errno); + } + } + + free(de.de_strtab); + free(de.de_sym); + free(de.de_rel); + + return (ret); +} + +/* + * Write out an ELF64 file prologue consisting of a header, section headers, + * and a section header string table. The DOF data will follow this prologue + * and complete the contents of the given ELF file. + */ +static int +dump_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) +{ + struct { + Elf64_Ehdr ehdr; + Elf64_Shdr shdr[ESHDR_NUM]; + } elf_file; + + Elf64_Shdr *shp; + Elf64_Off off; + dof_elf64_t de; + int ret = 0; + uint_t nshdr; + + if (prepare_elf64(dtp, dof, &de) != 0) + return (-1); /* errno is set for us */ + + /* + * If there are no relocations, we only need enough sections for + * the shstrtab and the DOF. + */ + nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; + + bzero(&elf_file, sizeof (elf_file)); + + elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; + elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; + elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; + elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; + elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; + elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS64; +#if BYTE_ORDER == _BIG_ENDIAN + elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#else + elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#endif +#if defined(__FreeBSD__) + elf_file.ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; +#endif + elf_file.ehdr.e_type = ET_REL; +#if defined(__arm__) + elf_file.ehdr.e_machine = EM_ARM; +#elif defined(__ia64__) + elf_file.ehdr.e_machine = EM_IA_64; +#elif defined(__mips__) + elf_file.ehdr.e_machine = EM_MIPS; +#elif defined(__powerpc__) + elf_file.ehdr.e_machine = EM_PPC; +#elif defined(__sparc) + elf_file.ehdr.e_machine = EM_SPARCV9; +#elif defined(__i386) || defined(__amd64) + elf_file.ehdr.e_machine = EM_AMD64; +#endif + elf_file.ehdr.e_version = EV_CURRENT; + elf_file.ehdr.e_shoff = sizeof (Elf64_Ehdr); + elf_file.ehdr.e_ehsize = sizeof (Elf64_Ehdr); + elf_file.ehdr.e_phentsize = sizeof (Elf64_Phdr); + elf_file.ehdr.e_shentsize = sizeof (Elf64_Shdr); + elf_file.ehdr.e_shnum = nshdr; + elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; + off = sizeof (elf_file) + nshdr * sizeof (Elf64_Shdr); + + shp = &elf_file.shdr[ESHDR_SHSTRTAB]; + shp->sh_name = 1; /* DTRACE_SHSTRTAB64[1] = ".shstrtab" */ + shp->sh_type = SHT_STRTAB; + shp->sh_offset = off; + shp->sh_size = sizeof (DTRACE_SHSTRTAB64); + shp->sh_addralign = sizeof (char); + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); + + shp = &elf_file.shdr[ESHDR_DOF]; + shp->sh_name = 11; /* DTRACE_SHSTRTAB64[11] = ".SUNW_dof" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_SUNW_dof; + shp->sh_offset = off; + shp->sh_size = dof->dofh_filesz; + shp->sh_addralign = 8; + off = shp->sh_offset + shp->sh_size; + + shp = &elf_file.shdr[ESHDR_STRTAB]; + shp->sh_name = 21; /* DTRACE_SHSTRTAB64[21] = ".strtab" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_STRTAB; + shp->sh_offset = off; + shp->sh_size = de.de_strlen; + shp->sh_addralign = sizeof (char); + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); + + shp = &elf_file.shdr[ESHDR_SYMTAB]; + shp->sh_name = 29; /* DTRACE_SHSTRTAB64[29] = ".symtab" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_SYMTAB; + shp->sh_entsize = sizeof (Elf64_Sym); + shp->sh_link = ESHDR_STRTAB; + shp->sh_offset = off; + shp->sh_info = de.de_global; + shp->sh_size = de.de_nsym * sizeof (Elf64_Sym); + shp->sh_addralign = 8; + off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); + + if (de.de_nrel == 0) { + if (dt_write(dtp, fd, &elf_file, + sizeof (elf_file)) != sizeof (elf_file) || + PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || + PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || + PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || + PWRITE_SCN(ESHDR_DOF, dof)) { + ret = dt_set_errno(dtp, errno); + } + } else { + shp = &elf_file.shdr[ESHDR_REL]; + shp->sh_name = 37; /* DTRACE_SHSTRTAB64[37] = ".rel.SUNW_dof" */ + shp->sh_flags = SHF_ALLOC; + shp->sh_type = SHT_RELA; + shp->sh_entsize = sizeof (de.de_rel[0]); + shp->sh_link = ESHDR_SYMTAB; + shp->sh_info = ESHDR_DOF; + shp->sh_offset = off; + shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); + shp->sh_addralign = 8; + + if (dt_write(dtp, fd, &elf_file, + sizeof (elf_file)) != sizeof (elf_file) || + PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || + PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || + PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || + PWRITE_SCN(ESHDR_REL, de.de_rel) || + PWRITE_SCN(ESHDR_DOF, dof)) { + ret = dt_set_errno(dtp, errno); + } + } + + free(de.de_strtab); + free(de.de_sym); + free(de.de_rel); + + return (ret); +} + +static int +dt_symtab_lookup(Elf_Data *data_sym, int nsym, uintptr_t addr, uint_t shn, + GElf_Sym *sym) +{ + int i, ret = -1; + GElf_Sym s; + + for (i = 0; i < nsym && gelf_getsym(data_sym, i, sym) != NULL; i++) { + if (GELF_ST_TYPE(sym->st_info) == STT_FUNC && + shn == sym->st_shndx && + sym->st_value <= addr && + addr < sym->st_value + sym->st_size) { + if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL) + return (0); + + ret = 0; + s = *sym; + } + } + + if (ret == 0) + *sym = s; + return (ret); +} + +#if defined(__arm__) +/* XXX */ +static int +dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, + uint32_t *off) +{ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); + return (0); +} +#elif defined(__ia64__) +/* XXX */ +static int +dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, + uint32_t *off) +{ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); + return (0); +} +#elif defined(__mips__) +/* XXX */ +static int +dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, + uint32_t *off) +{ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); + return (0); +} +#elif defined(__powerpc__) +/* XXX */ +static int +dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, + uint32_t *off) +{ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); + return (0); +} + +#elif defined(__sparc) + +#define DT_OP_RET 0x81c7e008 +#define DT_OP_NOP 0x01000000 +#define DT_OP_CALL 0x40000000 +#define DT_OP_CLR_O0 0x90102000 + +#define DT_IS_MOV_O7(inst) (((inst) & 0xffffe000) == 0x9e100000) +#define DT_IS_RESTORE(inst) (((inst) & 0xc1f80000) == 0x81e80000) +#define DT_IS_RETL(inst) (((inst) & 0xfff83fff) == 0x81c02008) + +#define DT_RS2(inst) ((inst) & 0x1f) +#define DT_MAKE_RETL(reg) (0x81c02008 | ((reg) << 14)) + +/*ARGSUSED*/ +static int +dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, + uint32_t *off) +{ + uint32_t *ip; + + if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0) + return (-1); + + /*LINTED*/ + ip = (uint32_t *)(p + rela->r_offset); + + /* + * We only know about some specific relocation types. + */ + if (GELF_R_TYPE(rela->r_info) != R_SPARC_WDISP30 && + GELF_R_TYPE(rela->r_info) != R_SPARC_WPLT30) + return (-1); + + /* + * We may have already processed this object file in an earlier linker + * invocation. Check to see if the present instruction sequence matches + * the one we would install. + */ + if (isenabled) { + if (ip[0] == DT_OP_CLR_O0) + return (0); + } else { + if (DT_IS_RESTORE(ip[1])) { + if (ip[0] == DT_OP_RET) + return (0); + } else if (DT_IS_MOV_O7(ip[1])) { + if (DT_IS_RETL(ip[0])) + return (0); + } else { + if (ip[0] == DT_OP_NOP) { + (*off) += sizeof (ip[0]); + return (0); + } + } + } + + /* + * We only expect call instructions with a displacement of 0. + */ + if (ip[0] != DT_OP_CALL) { + dt_dprintf("found %x instead of a call instruction at %llx\n", + ip[0], (u_longlong_t)rela->r_offset); + return (-1); + } + + if (isenabled) { + /* + * It would necessarily indicate incorrect usage if an is- + * enabled probe were tail-called so flag that as an error. + * It's also potentially (very) tricky to handle gracefully, + * but could be done if this were a desired use scenario. + */ + if (DT_IS_RESTORE(ip[1]) || DT_IS_MOV_O7(ip[1])) { + dt_dprintf("tail call to is-enabled probe at %llx\n", + (u_longlong_t)rela->r_offset); + return (-1); + } + + ip[0] = DT_OP_CLR_O0; + } else { + /* + * If the call is followed by a restore, it's a tail call so + * change the call to a ret. If the call if followed by a mov + * of a register into %o7, it's a tail call in leaf context + * so change the call to a retl-like instruction that returns + * to that register value + 8 (rather than the typical %o7 + + * 8); the delay slot instruction is left, but should have no + * effect. Otherwise we change the call to be a nop. In the + * first and the last case we adjust the offset to land on what + * was once the delay slot of the call so we correctly get all + * the arguments as they would have been passed in a normal + * function call. + */ + if (DT_IS_RESTORE(ip[1])) { + ip[0] = DT_OP_RET; + (*off) += sizeof (ip[0]); + } else if (DT_IS_MOV_O7(ip[1])) { + ip[0] = DT_MAKE_RETL(DT_RS2(ip[1])); + } else { + ip[0] = DT_OP_NOP; + (*off) += sizeof (ip[0]); + } + } + + return (0); +} + +#elif defined(__i386) || defined(__amd64) + +#define DT_OP_NOP 0x90 +#define DT_OP_RET 0xc3 +#define DT_OP_CALL 0xe8 +#define DT_OP_JMP32 0xe9 +#define DT_OP_REX_RAX 0x48 +#define DT_OP_XOR_EAX_0 0x33 +#define DT_OP_XOR_EAX_1 0xc0 + +static int +dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, + uint32_t *off) +{ + uint8_t *ip = (uint8_t *)(p + rela->r_offset - 1); + uint8_t ret; + + /* + * On x86, the first byte of the instruction is the call opcode and + * the next four bytes are the 32-bit address; the relocation is for + * the address operand. We back up the offset to the first byte of + * the instruction. For is-enabled probes, we later advance the offset + * so that it hits the first nop in the instruction sequence. + */ + (*off) -= 1; + + /* + * We only know about some specific relocation types. Luckily + * these types have the same values on both 32-bit and 64-bit + * x86 architectures. + */ + if (GELF_R_TYPE(rela->r_info) != R_386_PC32 && + GELF_R_TYPE(rela->r_info) != R_386_PLT32) + return (-1); + + /* + * We may have already processed this object file in an earlier linker + * invocation. Check to see if the present instruction sequence matches + * the one we would install. For is-enabled probes, we advance the + * offset to the first nop instruction in the sequence to match the + * text modification code below. + */ + if (!isenabled) { + if ((ip[0] == DT_OP_NOP || ip[0] == DT_OP_RET) && + ip[1] == DT_OP_NOP && ip[2] == DT_OP_NOP && + ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) + return (0); + } else if (dtp->dt_oflags & DTRACE_O_LP64) { + if (ip[0] == DT_OP_REX_RAX && + ip[1] == DT_OP_XOR_EAX_0 && ip[2] == DT_OP_XOR_EAX_1 && + (ip[3] == DT_OP_NOP || ip[3] == DT_OP_RET) && + ip[4] == DT_OP_NOP) { + (*off) += 3; + return (0); + } + } else { + if (ip[0] == DT_OP_XOR_EAX_0 && ip[1] == DT_OP_XOR_EAX_1 && + (ip[2] == DT_OP_NOP || ip[2] == DT_OP_RET) && + ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) { + (*off) += 2; + return (0); + } + } + + /* + * We expect either a call instrution with a 32-bit displacement or a + * jmp instruction with a 32-bit displacement acting as a tail-call. + */ + if (ip[0] != DT_OP_CALL && ip[0] != DT_OP_JMP32) { + dt_dprintf("found %x instead of a call or jmp instruction at " + "%llx\n", ip[0], (u_longlong_t)rela->r_offset); + return (-1); + } + + ret = (ip[0] == DT_OP_JMP32) ? DT_OP_RET : DT_OP_NOP; + + /* + * Establish the instruction sequence -- all nops for probes, and an + * instruction to clear the return value register (%eax/%rax) followed + * by nops for is-enabled probes. For is-enabled probes, we advance + * the offset to the first nop. This isn't stricly necessary but makes + * for more readable disassembly when the probe is enabled. + */ + if (!isenabled) { + ip[0] = ret; + ip[1] = DT_OP_NOP; + ip[2] = DT_OP_NOP; + ip[3] = DT_OP_NOP; + ip[4] = DT_OP_NOP; + } else if (dtp->dt_oflags & DTRACE_O_LP64) { + ip[0] = DT_OP_REX_RAX; + ip[1] = DT_OP_XOR_EAX_0; + ip[2] = DT_OP_XOR_EAX_1; + ip[3] = ret; + ip[4] = DT_OP_NOP; + (*off) += 3; + } else { + ip[0] = DT_OP_XOR_EAX_0; + ip[1] = DT_OP_XOR_EAX_1; + ip[2] = ret; + ip[3] = DT_OP_NOP; + ip[4] = DT_OP_NOP; + (*off) += 2; + } + + return (0); +} + +#else +#error unknown ISA +#endif + +/*PRINTFLIKE5*/ +static int +dt_link_error(dtrace_hdl_t *dtp, Elf *elf, int fd, dt_link_pair_t *bufs, + const char *format, ...) +{ + va_list ap; + dt_link_pair_t *pair; + + va_start(ap, format); + dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap); + va_end(ap); + + if (elf != NULL) + (void) elf_end(elf); + + if (fd >= 0) + (void) close(fd); + + while ((pair = bufs) != NULL) { + bufs = pair->dlp_next; + dt_free(dtp, pair->dlp_str); + dt_free(dtp, pair->dlp_sym); + dt_free(dtp, pair); + } + + return (dt_set_errno(dtp, EDT_COMPILER)); +} + +static int +process_obj(dtrace_hdl_t *dtp, const char *obj, int *eprobesp) +{ + static const char dt_prefix[] = "__dtrace"; + static const char dt_enabled[] = "enabled"; + static const char dt_symprefix[] = "$dtrace"; + static const char dt_symfmt[] = "%s%d.%s"; + int fd, i, ndx, eprobe, mod = 0; + Elf *elf = NULL; + GElf_Ehdr ehdr; + Elf_Scn *scn_rel, *scn_sym, *scn_str, *scn_tgt; + Elf_Data *data_rel, *data_sym, *data_str, *data_tgt; + GElf_Shdr shdr_rel, shdr_sym, shdr_str, shdr_tgt; + GElf_Sym rsym, fsym, dsym; + GElf_Rela rela; + char *s, *p, *r; + char pname[DTRACE_PROVNAMELEN]; + dt_provider_t *pvp; + dt_probe_t *prp; + uint32_t off, eclass, emachine1, emachine2; + size_t symsize, nsym, isym, istr, len; + key_t objkey; + dt_link_pair_t *pair, *bufs = NULL; + dt_strtab_t *strtab; + + if ((fd = open64(obj, O_RDWR)) == -1) { + return (dt_link_error(dtp, elf, fd, bufs, + "failed to open %s: %s", obj, strerror(errno))); + } + + if ((elf = elf_begin(fd, ELF_C_RDWR, NULL)) == NULL) { + return (dt_link_error(dtp, elf, fd, bufs, + "failed to process %s: %s", obj, elf_errmsg(elf_errno()))); + } + + switch (elf_kind(elf)) { + case ELF_K_ELF: + break; + case ELF_K_AR: + return (dt_link_error(dtp, elf, fd, bufs, "archives are not " + "permitted; use the contents of the archive instead: %s", + obj)); + default: + return (dt_link_error(dtp, elf, fd, bufs, + "invalid file type: %s", obj)); + } + + if (gelf_getehdr(elf, &ehdr) == NULL) { + return (dt_link_error(dtp, elf, fd, bufs, "corrupt file: %s", + obj)); + } + + if (dtp->dt_oflags & DTRACE_O_LP64) { + eclass = ELFCLASS64; +#if defined(__ia64__) + emachine1 = emachine2 = EM_IA_64; +#elif defined(__mips__) + emachine1 = emachine2 = EM_MIPS; +#elif defined(__powerpc__) + emachine1 = emachine2 = EM_PPC64; +#elif defined(__sparc) + emachine1 = emachine2 = EM_SPARCV9; +#elif defined(__i386) || defined(__amd64) + emachine1 = emachine2 = EM_AMD64; +#endif + symsize = sizeof (Elf64_Sym); + } else { + eclass = ELFCLASS32; +#if defined(__arm__) + emachine1 = emachine2 = EM_ARM; +#elif defined(__mips__) + emachine1 = emachine2 = EM_MIPS; +#elif defined(__powerpc__) + emachine1 = emachine2 = EM_PPC; +#elif defined(__sparc) + emachine1 = EM_SPARC; + emachine2 = EM_SPARC32PLUS; +#elif defined(__i386) || defined(__amd64) || defined(__ia64__) + emachine1 = emachine2 = EM_386; +#endif + symsize = sizeof (Elf32_Sym); + } + + if (ehdr.e_ident[EI_CLASS] != eclass) { + return (dt_link_error(dtp, elf, fd, bufs, + "incorrect ELF class for object file: %s", obj)); + } + + if (ehdr.e_machine != emachine1 && ehdr.e_machine != emachine2) { + return (dt_link_error(dtp, elf, fd, bufs, + "incorrect ELF machine type for object file: %s", obj)); + } + + /* + * We use this token as a relatively unique handle for this file on the + * system in order to disambiguate potential conflicts between files of + * the same name which contain identially named local symbols. + */ + if ((objkey = ftok(obj, 0)) == (key_t)-1) { + return (dt_link_error(dtp, elf, fd, bufs, + "failed to generate unique key for object file: %s", obj)); + } + + scn_rel = NULL; + while ((scn_rel = elf_nextscn(elf, scn_rel)) != NULL) { + if (gelf_getshdr(scn_rel, &shdr_rel) == NULL) + goto err; + + /* + * Skip any non-relocation sections. + */ + if (shdr_rel.sh_type != SHT_RELA && shdr_rel.sh_type != SHT_REL) + continue; + + if ((data_rel = elf_getdata(scn_rel, NULL)) == NULL) + goto err; + + /* + * Grab the section, section header and section data for the + * symbol table that this relocation section references. + */ + if ((scn_sym = elf_getscn(elf, shdr_rel.sh_link)) == NULL || + gelf_getshdr(scn_sym, &shdr_sym) == NULL || + (data_sym = elf_getdata(scn_sym, NULL)) == NULL) + goto err; + + /* + * Ditto for that symbol table's string table. + */ + if ((scn_str = elf_getscn(elf, shdr_sym.sh_link)) == NULL || + gelf_getshdr(scn_str, &shdr_str) == NULL || + (data_str = elf_getdata(scn_str, NULL)) == NULL) + goto err; + + /* + * Grab the section, section header and section data for the + * target section for the relocations. For the relocations + * we're looking for -- this will typically be the text of the + * object file. + */ + if ((scn_tgt = elf_getscn(elf, shdr_rel.sh_info)) == NULL || + gelf_getshdr(scn_tgt, &shdr_tgt) == NULL || + (data_tgt = elf_getdata(scn_tgt, NULL)) == NULL) + goto err; + + /* + * We're looking for relocations to symbols matching this form: + * + * __dtrace[enabled]_<prov>___<probe> + * + * For the generated object, we need to record the location + * identified by the relocation, and create a new relocation + * in the generated object that will be resolved at link time + * to the location of the function in which the probe is + * embedded. In the target object, we change the matched symbol + * so that it will be ignored at link time, and we modify the + * target (text) section to replace the call instruction with + * one or more nops. + * + * If the function containing the probe is locally scoped + * (static), we create an alias used by the relocation in the + * generated object. The alias, a new symbol, will be global + * (so that the relocation from the generated object can be + * resolved), and hidden (so that it is converted to a local + * symbol at link time). Such aliases have this form: + * + * $dtrace<key>.<function> + * + * We take a first pass through all the relocations to + * populate our string table and count the number of extra + * symbols we'll require. + */ + strtab = dt_strtab_create(1); + nsym = 0; + isym = data_sym->d_size / symsize; + istr = data_str->d_size; + + for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { + + if (shdr_rel.sh_type == SHT_RELA) { + if (gelf_getrela(data_rel, i, &rela) == NULL) + continue; + } else { + GElf_Rel rel; + if (gelf_getrel(data_rel, i, &rel) == NULL) + continue; + rela.r_offset = rel.r_offset; + rela.r_info = rel.r_info; + rela.r_addend = 0; + } + + if (gelf_getsym(data_sym, GELF_R_SYM(rela.r_info), + &rsym) == NULL) { + dt_strtab_destroy(strtab); + goto err; + } + + s = (char *)data_str->d_buf + rsym.st_name; + + if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) + continue; + + if (dt_symtab_lookup(data_sym, isym, rela.r_offset, + shdr_rel.sh_info, &fsym) != 0) { + dt_strtab_destroy(strtab); + goto err; + } + + if (GELF_ST_BIND(fsym.st_info) != STB_LOCAL) + continue; + + if (fsym.st_name > data_str->d_size) { + dt_strtab_destroy(strtab); + goto err; + } + + s = (char *)data_str->d_buf + fsym.st_name; + + /* + * If this symbol isn't of type function, we've really + * driven off the rails or the object file is corrupt. + */ + if (GELF_ST_TYPE(fsym.st_info) != STT_FUNC) { + dt_strtab_destroy(strtab); + return (dt_link_error(dtp, elf, fd, bufs, + "expected %s to be of type function", s)); + } + + len = snprintf(NULL, 0, dt_symfmt, dt_symprefix, + objkey, s) + 1; + if ((p = dt_alloc(dtp, len)) == NULL) { + dt_strtab_destroy(strtab); + goto err; + } + (void) snprintf(p, len, dt_symfmt, dt_symprefix, + objkey, s); + + if (dt_strtab_index(strtab, p) == -1) { + nsym++; + (void) dt_strtab_insert(strtab, p); + } + + dt_free(dtp, p); + } + + /* + * If needed, allocate the additional space for the symbol + * table and string table copying the old data into the new + * buffers, and marking the buffers as dirty. We inject those + * newly allocated buffers into the libelf data structures, but + * are still responsible for freeing them once we're done with + * the elf handle. + */ + if (nsym > 0) { + /* + * The first byte of the string table is reserved for + * the \0 entry. + */ + len = dt_strtab_size(strtab) - 1; + + assert(len > 0); + assert(dt_strtab_index(strtab, "") == 0); + + dt_strtab_destroy(strtab); + + if ((pair = dt_alloc(dtp, sizeof (*pair))) == NULL) + goto err; + + if ((pair->dlp_str = dt_alloc(dtp, data_str->d_size + + len)) == NULL) { + dt_free(dtp, pair); + goto err; + } + + if ((pair->dlp_sym = dt_alloc(dtp, data_sym->d_size + + nsym * symsize)) == NULL) { + dt_free(dtp, pair->dlp_str); + dt_free(dtp, pair); + goto err; + } + + pair->dlp_next = bufs; + bufs = pair; + + bcopy(data_str->d_buf, pair->dlp_str, data_str->d_size); + data_str->d_buf = pair->dlp_str; + data_str->d_size += len; + (void) elf_flagdata(data_str, ELF_C_SET, ELF_F_DIRTY); + + shdr_str.sh_size += len; + (void) gelf_update_shdr(scn_str, &shdr_str); + + bcopy(data_sym->d_buf, pair->dlp_sym, data_sym->d_size); + data_sym->d_buf = pair->dlp_sym; + data_sym->d_size += nsym * symsize; + (void) elf_flagdata(data_sym, ELF_C_SET, ELF_F_DIRTY); + + shdr_sym.sh_size += nsym * symsize; + (void) gelf_update_shdr(scn_sym, &shdr_sym); + + nsym += isym; + } else { + dt_strtab_destroy(strtab); + } + + /* + * Now that the tables have been allocated, perform the + * modifications described above. + */ + for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { + + if (shdr_rel.sh_type == SHT_RELA) { + if (gelf_getrela(data_rel, i, &rela) == NULL) + continue; + } else { + GElf_Rel rel; + if (gelf_getrel(data_rel, i, &rel) == NULL) + continue; + rela.r_offset = rel.r_offset; + rela.r_info = rel.r_info; + rela.r_addend = 0; + } + + ndx = GELF_R_SYM(rela.r_info); + + if (gelf_getsym(data_sym, ndx, &rsym) == NULL || + rsym.st_name > data_str->d_size) + goto err; + + s = (char *)data_str->d_buf + rsym.st_name; + + if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) + continue; + + s += sizeof (dt_prefix) - 1; + + /* + * Check to see if this is an 'is-enabled' check as + * opposed to a normal probe. + */ + if (strncmp(s, dt_enabled, + sizeof (dt_enabled) - 1) == 0) { + s += sizeof (dt_enabled) - 1; + eprobe = 1; + *eprobesp = 1; + dt_dprintf("is-enabled probe\n"); + } else { + eprobe = 0; + dt_dprintf("normal probe\n"); + } + + if (*s++ != '_') + goto err; + + if ((p = strstr(s, "___")) == NULL || + p - s >= sizeof (pname)) + goto err; + + bcopy(s, pname, p - s); + pname[p - s] = '\0'; + + p = strhyphenate(p + 3); /* strlen("___") */ + + if (dt_symtab_lookup(data_sym, isym, rela.r_offset, + shdr_rel.sh_info, &fsym) != 0) + goto err; + + if (fsym.st_name > data_str->d_size) + goto err; + + assert(GELF_ST_TYPE(fsym.st_info) == STT_FUNC); + + /* + * If a NULL relocation name is passed to + * dt_probe_define(), the function name is used for the + * relocation. The relocation needs to use a mangled + * name if the symbol is locally scoped; the function + * name may need to change if we've found the global + * alias for the locally scoped symbol (we prefer + * global symbols to locals in dt_symtab_lookup()). + */ + s = (char *)data_str->d_buf + fsym.st_name; + r = NULL; + + if (GELF_ST_BIND(fsym.st_info) == STB_LOCAL) { + dsym = fsym; + dsym.st_name = istr; + dsym.st_info = GELF_ST_INFO(STB_GLOBAL, + STT_FUNC); + dsym.st_other = + ELF64_ST_VISIBILITY(STV_ELIMINATE); + (void) gelf_update_sym(data_sym, isym, &dsym); + + r = (char *)data_str->d_buf + istr; + istr += 1 + sprintf(r, dt_symfmt, + dt_symprefix, objkey, s); + isym++; + assert(isym <= nsym); + + } else if (strncmp(s, dt_symprefix, + strlen(dt_symprefix)) == 0) { + r = s; + if ((s = strchr(s, '.')) == NULL) + goto err; + s++; + } + + if ((pvp = dt_provider_lookup(dtp, pname)) == NULL) { + return (dt_link_error(dtp, elf, fd, bufs, + "no such provider %s", pname)); + } + + if ((prp = dt_probe_lookup(pvp, p)) == NULL) { + return (dt_link_error(dtp, elf, fd, bufs, + "no such probe %s", p)); + } + + assert(fsym.st_value <= rela.r_offset); + + off = rela.r_offset - fsym.st_value; + if (dt_modtext(dtp, data_tgt->d_buf, eprobe, + &rela, &off) != 0) { + goto err; + } + + if (dt_probe_define(pvp, prp, s, r, off, eprobe) != 0) { + return (dt_link_error(dtp, elf, fd, bufs, + "failed to allocate space for probe")); + } + + mod = 1; + (void) elf_flagdata(data_tgt, ELF_C_SET, ELF_F_DIRTY); + + /* + * This symbol may already have been marked to + * be ignored by another relocation referencing + * the same symbol or if this object file has + * already been processed by an earlier link + * invocation. + */ +printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); +#ifdef DOODAD + if (rsym.st_shndx != SHN_SUNW_IGNORE) { + rsym.st_shndx = SHN_SUNW_IGNORE; + (void) gelf_update_sym(data_sym, ndx, &rsym); + } +#endif + } + } + + if (mod && elf_update(elf, ELF_C_WRITE) == -1) + goto err; + + (void) elf_end(elf); + (void) close(fd); + + while ((pair = bufs) != NULL) { + bufs = pair->dlp_next; + dt_free(dtp, pair->dlp_str); + dt_free(dtp, pair->dlp_sym); + dt_free(dtp, pair); + } + + return (0); + +err: + return (dt_link_error(dtp, elf, fd, bufs, + "an error was encountered while processing %s", obj)); +} + +int +dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, + const char *file, int objc, char *const objv[]) +{ +#if !defined(sun) + char tfile[PATH_MAX]; +#endif + char drti[PATH_MAX]; + dof_hdr_t *dof; + int fd, status, i, cur; + char *cmd, tmp; + size_t len; + int eprobes = 0, ret = 0; + +#if !defined(sun) + /* XXX Should get a temp file name here. */ + snprintf(tfile, sizeof(tfile), "%s.tmp", file); +#endif + + /* + * A NULL program indicates a special use in which we just link + * together a bunch of object files specified in objv and then + * unlink(2) those object files. + */ + if (pgp == NULL) { + |