aboutsummaryrefslogtreecommitdiff
path: root/lib/libdtrace/common
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libdtrace/common')
-rw-r--r--lib/libdtrace/common/drti.c206
-rw-r--r--lib/libdtrace/common/dt_aggregate.c1886
-rw-r--r--lib/libdtrace/common/dt_as.c501
-rw-r--r--lib/libdtrace/common/dt_as.h64
-rw-r--r--lib/libdtrace/common/dt_buf.c177
-rw-r--r--lib/libdtrace/common/dt_buf.h69
-rw-r--r--lib/libdtrace/common/dt_cc.c2349
-rw-r--r--lib/libdtrace/common/dt_cg.c2006
-rw-r--r--lib/libdtrace/common/dt_consume.c2646
-rw-r--r--lib/libdtrace/common/dt_decl.c1127
-rw-r--r--lib/libdtrace/common/dt_decl.h126
-rw-r--r--lib/libdtrace/common/dt_dis.c511
-rw-r--r--lib/libdtrace/common/dt_dof.c969
-rw-r--r--lib/libdtrace/common/dt_dof.h66
-rw-r--r--lib/libdtrace/common/dt_error.c235
-rw-r--r--lib/libdtrace/common/dt_errtags.h251
-rw-r--r--lib/libdtrace/common/dt_grammar.y834
-rw-r--r--lib/libdtrace/common/dt_handle.c483
-rw-r--r--lib/libdtrace/common/dt_ident.c1047
-rw-r--r--lib/libdtrace/common/dt_ident.h183
-rw-r--r--lib/libdtrace/common/dt_impl.h691
-rw-r--r--lib/libdtrace/common/dt_inttab.c115
-rw-r--r--lib/libdtrace/common/dt_inttab.h69
-rw-r--r--lib/libdtrace/common/dt_lex.l860
-rw-r--r--lib/libdtrace/common/dt_link.c1774
-rw-r--r--lib/libdtrace/common/dt_list.c111
-rw-r--r--lib/libdtrace/common/dt_list.h53
-rw-r--r--lib/libdtrace/common/dt_map.c442
-rw-r--r--lib/libdtrace/common/dt_module.c1383
-rw-r--r--lib/libdtrace/common/dt_module.h56
-rw-r--r--lib/libdtrace/common/dt_open.c1643
-rw-r--r--lib/libdtrace/common/dt_options.c1031
-rw-r--r--lib/libdtrace/common/dt_parser.c4893
-rw-r--r--lib/libdtrace/common/dt_parser.h285
-rw-r--r--lib/libdtrace/common/dt_pcb.c187
-rw-r--r--lib/libdtrace/common/dt_pcb.h103
-rw-r--r--lib/libdtrace/common/dt_pid.c863
-rw-r--r--lib/libdtrace/common/dt_pid.h64
-rw-r--r--lib/libdtrace/common/dt_pragma.c507
-rw-r--r--lib/libdtrace/common/dt_printf.c1979
-rw-r--r--lib/libdtrace/common/dt_printf.h135
-rw-r--r--lib/libdtrace/common/dt_proc.c1223
-rw-r--r--lib/libdtrace/common/dt_proc.h118
-rw-r--r--lib/libdtrace/common/dt_program.c606
-rw-r--r--lib/libdtrace/common/dt_program.h63
-rw-r--r--lib/libdtrace/common/dt_provider.c883
-rw-r--r--lib/libdtrace/common/dt_provider.h118
-rw-r--r--lib/libdtrace/common/dt_regset.c107
-rw-r--r--lib/libdtrace/common/dt_regset.h53
-rw-r--r--lib/libdtrace/common/dt_string.c325
-rw-r--r--lib/libdtrace/common/dt_string.h51
-rw-r--r--lib/libdtrace/common/dt_strtab.c293
-rw-r--r--lib/libdtrace/common/dt_strtab.h72
-rw-r--r--lib/libdtrace/common/dt_subr.c997
-rw-r--r--lib/libdtrace/common/dt_work.c319
-rw-r--r--lib/libdtrace/common/dt_xlator.c383
-rw-r--r--lib/libdtrace/common/dt_xlator.h87
-rw-r--r--lib/libdtrace/common/dtrace.h580
-rw-r--r--lib/libdtrace/common/mkerrtags.sh59
-rw-r--r--lib/libdtrace/common/mknames.sh55
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) {
+