[DTrace-devel] [PATCH] Implementation of the printa() action

Kris Van Hees kris.van.hees at oracle.com
Mon Dec 7 13:07:18 PST 2020


Given the design for the handling of aggregation data, there is no
further need for cummulative data retrieval using snapshots.  When we
are ready to display aggregation data, we can retrieve the data by
performing a key 0 BPF map lookup on the 'aggs' map.  It gives us all
data for all CPUs in a single buffer.

Change summary:

  Global
    - General code cleanup
    - Replace dtrace_aggvarid_t by dtrace_aggid_t
    - Replace DTRACEAGG_* constants with DT_AGG_* constants
  cmd/dtrace.c
    - Remove disabling of default agg printing
  include/dtrace/metadesc.h
    struct dtrace_aggdesc
      - Removed dtagd_epid and dtagd_pad
      - Added dtagd_sig (lquantize and llquantize parameters)
      - Replaced dtagd_rec[1] with *dtagd_recs
  libdtrace/dt_aggregate.c
    - Switch from retrieving per-CPU aggregation data using ioctl() to
      retrieving the BPF map value for key 0 from the 'aggs' map.  The
      order of aggregation data blocks is defined by the order of the
      aggregations (by ID) in the dt_aggs idhash.
  libdtrace/dt_cg.c
    - Add support for storing aggregation IDs in dt_cg_store_val()
    - Implement printa()
  libdtrace/dt_consume.c
    - Remove obsolete suport for reading parameters from first data value
    - Pass encoded agg function parameters to dt_print_datum()
    - Pass encoded lquantize parameters to dt_print_lquantize()
    - Pass encoded llquantize parameters to dt_print_llquantize()
    - Implement dt_printa()
  libdtrace/dt_map.c
    - Rewrite dt_aggid_add() to not try to retrieve data from the kernel
      using ioctl() calls but instead construct the data description
      based on the aggregation identifier.
  libdtrace/dt_printf.c
    - Adjust pfprint_*() functions to passing agg func parameters
  libdtrace/dtrace.h
    struct dtrace_aggdata
      - Renamed dtada_handle -> dtada_hdl
      - Removed dtada_ddesc and dtada_pdesc
  Tests
    - Various tests have been renamed to make their meaning more clear
    - Various tests have been adjusted to more accurately test things

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 cmd/dtrace.c                                  |   9 +-
 include/dtrace/metadesc.h                     |   7 +-
 include/dtrace/universal.h                    |   1 -
 libdtrace/dt_aggregate.c                      | 972 +++++++-----------
 libdtrace/dt_bpf.c                            |  35 +-
 libdtrace/dt_cg.c                             | 129 ++-
 libdtrace/dt_consume.c                        | 299 +++---
 libdtrace/dt_ident.c                          |  18 +-
 libdtrace/dt_ident.h                          |   2 +-
 libdtrace/dt_impl.h                           |  30 +-
 libdtrace/dt_map.c                            | 173 ++--
 libdtrace/dt_printf.c                         | 219 ++--
 libdtrace/dt_printf.h                         |   8 +-
 libdtrace/dt_work.c                           |   4 +-
 libdtrace/dtrace.h                            |   8 +-
 .../printa/err.D_PRINTA_AGGARG.fmt_int_arg.d  |  18 +
 .../err.D_PRINTA_AGGARG.fmt_mixed_args.d      |  22 +
 .../printa/err.D_PRINTA_AGGARG.int_arg.d      |  18 +
 .../printa/err.D_PRINTA_AGGARG.mixed_args.d   |  22 +
 .../actions/printa/err.D_PRINTA_AGGKEY.d      |  21 +
 .../actions/printa/err.D_PRINTA_AGGPROTO.d    |  21 +
 .../printa/err.D_PRINTA_PROTO.fmt_no_arg.d    |  18 +
 .../err.D_PRINTF_ARG_PROTO.key_no_val.d       |  20 +
 .../actions/printa/err.D_PROTO_LEN.no_arg.d   |  19 +
 .../aggs/err.D_AGG_REDEF.idx_diff_funcs.d     |  19 +
 ....D_LLQUANT_STEPVAL.steps_divides_factor.d} |   0
 test/unittest/aggs/tst.count.d                |   5 +-
 test/unittest/aggs/tst.lquantround.d          |   1 -
 .../printa/err.D_PRINTA_AGGARG.badagg.d       |   2 +-
 .../printa/err.D_PRINTA_AGGARG.badfmt.d       |   2 +-
 .../printa/err.D_PRINTA_AGGARG.badval.d       |   2 +-
 test/unittest/printa/err.D_PRINTA_PROTO.bad.d |   2 +-
 test/unittest/printa/tst.def.d                |   5 +-
 test/unittest/printa/tst.fmt.d                |   4 +-
 test/unittest/printa/tst.manyval.d            |   7 +-
 35 files changed, 1090 insertions(+), 1052 deletions(-)
 create mode 100644 test/unittest/actions/printa/err.D_PRINTA_AGGARG.fmt_int_arg.d
 create mode 100644 test/unittest/actions/printa/err.D_PRINTA_AGGARG.fmt_mixed_args.d
 create mode 100644 test/unittest/actions/printa/err.D_PRINTA_AGGARG.int_arg.d
 create mode 100644 test/unittest/actions/printa/err.D_PRINTA_AGGARG.mixed_args.d
 create mode 100644 test/unittest/actions/printa/err.D_PRINTA_AGGKEY.d
 create mode 100644 test/unittest/actions/printa/err.D_PRINTA_AGGPROTO.d
 create mode 100644 test/unittest/actions/printa/err.D_PRINTA_PROTO.fmt_no_arg.d
 create mode 100644 test/unittest/actions/printa/err.D_PRINTF_ARG_PROTO.key_no_val.d
 create mode 100644 test/unittest/actions/printa/err.D_PROTO_LEN.no_arg.d
 create mode 100644 test/unittest/aggs/err.D_AGG_REDEF.idx_diff_funcs.d
 rename test/unittest/aggs/{err.D_LLQUANT_STEPVAL.lt_factor.d => err.D_LLQUANT_STEPVAL.steps_divides_factor.d} (100%)

diff --git a/cmd/dtrace.c b/cmd/dtrace.c
index 673b8fab..e9e25a47 100644
--- a/cmd/dtrace.c
+++ b/cmd/dtrace.c
@@ -603,13 +603,10 @@ bufhandler(const dtrace_bufdata_t *bufdata, void *arg)
 	    { NULL }
 	};
 
-	if (bufdata->dtbda_probe != NULL) {
+	if (bufdata->dtbda_probe != NULL)
 		pd = bufdata->dtbda_probe->dtpda_pdesc;
-	} else if (agg != NULL) {
-		pd = agg->dtada_pdesc;
-	} else {
+	else
 		pd = NULL;
-	}
 
 	BUFDUMPHDR(">>> Called buffer handler");
 	BUFDUMPHDR("");
@@ -1556,13 +1553,11 @@ main(int argc, char *argv[])
 
 	oprintf("\n");
 
-#if 0
 	if (!g_impatient) {
 		if (dtrace_aggregate_print(g_dtp, g_ofp, NULL) == -1 &&
 		    dtrace_errno(g_dtp) != EINTR)
 			dfatal("failed to print aggregations");
 	}
-#endif
 
 release_procs:
 	for (i = 0; i < g_psc; i++)
diff --git a/include/dtrace/metadesc.h b/include/dtrace/metadesc.h
index 7f530ba2..7b29c107 100644
--- a/include/dtrace/metadesc.h
+++ b/include/dtrace/metadesc.h
@@ -52,14 +52,13 @@ typedef struct dtrace_datadesc {
 
 typedef struct dtrace_aggdesc {
 	DTRACE_PTR(char, dtagd_name);		/* not filled in by kernel */
-	dtrace_aggvarid_t dtagd_varid;		/* not filled in by kernel */
+	dtrace_aggid_t dtagd_varid;		/* not filled in by kernel */
 	int dtagd_flags;			/* not filled in by kernel */
 	dtrace_aggid_t dtagd_id;		/* aggregation ID */
-	dtrace_epid_t dtagd_epid;		/* enabled probe ID */
+	uint64_t dtagd_sig;			/* aggregation signature */
 	uint32_t dtagd_size;			/* size in bytes */
 	int dtagd_nrecs;			/* number of records */
-	uint32_t dtagd_pad;			/* explicit padding */
-	dtrace_recdesc_t dtagd_rec[1];		/* record descriptions */
+	dtrace_recdesc_t *dtagd_recs;		/* record descriptions */
 } dtrace_aggdesc_t;
 
 typedef struct dtrace_fmtdesc {
diff --git a/include/dtrace/universal.h b/include/dtrace/universal.h
index b2381e88..405e517d 100644
--- a/include/dtrace/universal.h
+++ b/include/dtrace/universal.h
@@ -39,7 +39,6 @@ typedef uint32_t	dtrace_epid_t;		/* enabled probe identifier */
 typedef uint32_t	dtrace_optid_t;		/* option identifier */
 typedef uint32_t	dtrace_specid_t;	/* speculation identifier */
 
-typedef uint64_t	dtrace_aggvarid_t;	/* aggregation variable id */
 typedef uint64_t	dtrace_genid_t;		/* generation identifier */
 typedef uint64_t	dtrace_optval_t;	/* option value */
 
diff --git a/libdtrace/dt_aggregate.c b/libdtrace/dt_aggregate.c
index 04d059b0..5fd79dee 100644
--- a/libdtrace/dt_aggregate.c
+++ b/libdtrace/dt_aggregate.c
@@ -16,6 +16,7 @@
 
 #include <libproc.h>
 #include <port.h>
+#include <dt_bpf.h>
 
 #define	DTRACE_AHASHSIZE	32779		/* big 'ol prime */
 
@@ -33,15 +34,6 @@ 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)
-{
-	int 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)
 {
@@ -49,28 +41,12 @@ dt_aggregate_countcmp(int64_t *lhs, int64_t *rhs)
 	int64_t rvar = *rhs;
 
 	if (lvar < rvar)
-		return (DT_LESSTHAN);
+		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;
-}
+		return DT_GREATERTHAN;
 
-/*ARGSUSED*/
-static void
-dt_aggregate_max(int64_t *existing, int64_t *new, size_t size)
-{
-	if (*new > *existing)
-		*existing = *new;
+	return 0;
 }
 
 static int
@@ -80,12 +56,12 @@ dt_aggregate_averagecmp(int64_t *lhs, int64_t *rhs)
 	int64_t ravg = rhs[0] ? (rhs[1] / rhs[0]) : 0;
 
 	if (lavg < ravg)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lavg > ravg)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
-	return (0);
+	return 0;
 }
 
 static int
@@ -95,24 +71,12 @@ dt_aggregate_stddevcmp(int64_t *lhs, int64_t *rhs)
 	uint64_t rsd = dt_stddev((uint64_t *)rhs, 1);
 
 	if (lsd < rsd)
-		return (DT_LESSTHAN);
+		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;
+		return DT_GREATERTHAN;
 
-	for (i = 0; i <= levels + 1; i++)
-		existing[i] = existing[i] + new[i + 1];
+	return 0;
 }
 
 static long double
@@ -127,8 +91,8 @@ dt_aggregate_lquantizedsum(int64_t *lquanta)
 	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));
+	return total +
+	       (long double)lquanta[levels + 1] * (long double)(base + 1);
 }
 
 static int64_t
@@ -140,19 +104,19 @@ dt_aggregate_lquantizedzero(int64_t *lquanta)
 	uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg), i;
 
 	if (base - 1 == 0)
-		return (lquanta[0]);
+		return lquanta[0];
 
 	for (i = 0; i < levels; base += step, i++) {
 		if (base != 0)
 			continue;
 
-		return (lquanta[i + 1]);
+		return lquanta[i + 1];
 	}
 
 	if (base + 1 == 0)
-		return (lquanta[levels + 1]);
+		return lquanta[levels + 1];
 
-	return (0);
+	return 0;
 }
 
 static int
@@ -163,10 +127,10 @@ dt_aggregate_lquantizedcmp(int64_t *lhs, int64_t *rhs)
 	int64_t lzero, rzero;
 
 	if (lsum < rsum)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lsum > rsum)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
 	/*
 	 * If they're both equal, then we will compare based on the weights at
@@ -178,37 +142,12 @@ dt_aggregate_lquantizedcmp(int64_t *lhs, int64_t *rhs)
 	rzero = dt_aggregate_lquantizedzero(rhs);
 
 	if (lzero < rzero)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lzero > rzero)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
-	return (0);
-}
-
-static void
-dt_aggregate_llquantize(int64_t *existing, int64_t *new, size_t size)
-{
-	int64_t arg = *existing++;
-	uint16_t factor = DTRACE_LLQUANTIZE_FACTOR(arg);
-	uint16_t lmag = DTRACE_LLQUANTIZE_LMAG(arg);
-	uint16_t hmag = DTRACE_LLQUANTIZE_HMAG(arg);
-	uint16_t steps = DTRACE_LLQUANTIZE_STEPS(arg);
-	int i, limit;
-
-	/*
-	 * The rest of the buffer contains:
-	 *
-	 *   < 0   overflow bin
-	 *   < 0   (hmag-lmag+1) log ranges, (steps - steps/factor) bins/range
-	 *         underflow bin
-	 *   > 0   (hmag-lmag+1) log ranges, (steps - steps/factor) bins/range
-	 *   > 0   overflow bin
-	 */
-	limit = (hmag-lmag+1) * (steps-steps/factor) * 2 + 2 + 1;
-
-	for (i = 0; i < limit; i++)
-		existing[i] = existing[i] + new[i + 1];
+	return 0;
 }
 
 /* called by dt_aggregate_llquantizedcmp() */
@@ -247,7 +186,7 @@ dt_aggregate_llquantizedsum(int64_t *llquanta)
 		scale *= factor;
 	}
 
-	return (total);
+	return total;
 }
 
 /* called by dt_aggregate_llquantizedcmp() */
@@ -264,7 +203,7 @@ dt_aggregate_llquantizedzero(int64_t *llquanta)
 	/*
 	 * We'll define "zero" here as being the underflow bin.
 	 */
-	return (llquanta[underflow_bin]);
+	return llquanta[underflow_bin];
 }
 
 /*
@@ -281,10 +220,10 @@ dt_aggregate_llquantizedcmp(int64_t *lhs, int64_t *rhs)
 	int64_t lzero, rzero;
 
 	if (lsum < rsum)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lsum > rsum)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
 	/*
 	 * If they're both equal, then we will compare based on the weights at
@@ -296,12 +235,12 @@ dt_aggregate_llquantizedcmp(int64_t *lhs, int64_t *rhs)
 	rzero = dt_aggregate_llquantizedzero(rhs);
 
 	if (lzero < rzero)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lzero > rzero)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
-	return (0);
+	return 0;
 }
 
 static int
@@ -324,10 +263,10 @@ dt_aggregate_quantizedcmp(int64_t *lhs, int64_t *rhs)
 	}
 
 	if (ltotal < rtotal)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (ltotal > rtotal)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
 	/*
 	 * If they're both equal, then we will compare based on the weights at
@@ -335,12 +274,12 @@ dt_aggregate_quantizedcmp(int64_t *lhs, int64_t *rhs)
 	 * tie and will be resolved based on the key comparison.
 	 */
 	if (lzero < rzero)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lzero > rzero)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
-	return (0);
+	return 0;
 }
 
 static void
@@ -422,7 +361,7 @@ dt_aggregate_mod(dtrace_hdl_t *dtp, uint64_t *addr)
 		 */
 		if (dmp->dm_text_addrs != NULL &&
 		    bsearch(addr, dmp->dm_text_addrs, dmp->dm_text_addrs_size,
-			    sizeof (struct dtrace_addr_range),
+			    sizeof(struct dtrace_addr_range),
 			    dtrace_addr_range_cmp) != NULL) {
 
 			*addr = dmp->dm_text_addrs[0].dar_va;
@@ -431,7 +370,7 @@ dt_aggregate_mod(dtrace_hdl_t *dtp, uint64_t *addr)
 
 		if (dmp->dm_data_addrs != NULL &&
 		    bsearch(addr, dmp->dm_data_addrs, dmp->dm_data_addrs_size,
-			    sizeof (struct dtrace_addr_range),
+			    sizeof(struct dtrace_addr_range),
 			    dtrace_addr_range_cmp) != NULL) {
 
 			if (dmp->dm_text_addrs != NULL)
@@ -444,12 +383,12 @@ dt_aggregate_mod(dtrace_hdl_t *dtp, uint64_t *addr)
 	}
 }
 
-static dtrace_aggvarid_t
-dt_aggregate_aggvarid(dt_ahashent_t *ent)
+static dtrace_aggid_t
+dt_aggregate_aggid(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;
+	dtrace_recdesc_t *rec = agg->dtagd_recs;
 
 	/*
 	 * First, we'll check the variable ID in the aggdesc.  If it's valid,
@@ -457,313 +396,156 @@ dt_aggregate_aggvarid(dt_ahashent_t *ent)
 	 * present as the first record.
 	 */
 	if (agg->dtagd_varid != DTRACE_AGGVARIDNONE)
-		return (agg->dtagd_varid);
+		return agg->dtagd_varid;
 
-	agg->dtagd_varid = *((dtrace_aggvarid_t *)(uintptr_t)(data +
-	    rec->dtrd_offset));
+	agg->dtagd_varid = *((dtrace_aggid_t *)
+					(uintptr_t)(data + rec->dtrd_offset));
 
-	return (agg->dtagd_varid);
+	return agg->dtagd_varid;
 }
 
+typedef struct dt_snapstate {
+	dtrace_hdl_t	*dtp;
+	processorid_t	cpu;
+	char		*buf;
+	dt_aggregate_t	*agp;
+} dt_snapstate_t;
 
 static int
-dt_aggregate_snap_cpu(dtrace_hdl_t *dtp, processorid_t cpu)
+dt_aggregate_snap_one(dt_idhash_t *dhp, dt_ident_t *aid, dt_snapstate_t *st)
 {
-	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 (dt_ioctl(dtp, DTRACEIOC_AGGSNAP, buf) == -1) {
-		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));
-
-		memset(hash->dtah_hash, 0, 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);
+	dt_ahash_t		*agh = &st->agp->dtat_hash;
+	dt_ahashent_t		*h;
+	dtrace_aggdesc_t	*agg;
+	dtrace_aggdata_t	*agd;
+	uint64_t		hval = aid->di_id;
+	size_t			ndx = hval % agh->dtah_size;
+	int			rval;
+
+	rval = dt_aggid_lookup(st->dtp, aid->di_id, &agg);
+	if (rval != 0)
+		return rval;
+
+	/* See if we already have an entry for this aggregation. */
+	for (h = agh->dtah_hash[ndx]; h != NULL; h = h->dtahe_next) {
+		if (h->dtahe_hval != hval)
 			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:
+		if (h->dtahe_size != aid->di_size)
 			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));
-		memset(h, 0, sizeof (dt_ahashent_t));
-		aggdata = &h->dtahe_data;
-
-		if ((aggdata->dtada_data = malloc(size)) == NULL) {
-			free(h);
-			return (dt_set_errno(dtp, EDT_NOMEM));
-		}
-
-		memcpy(aggdata->dtada_data, addr, size);
-		aggdata->dtada_size = size;
-		aggdata->dtada_desc = agg;
-		aggdata->dtada_handle = dtp;
-		dt_epid_lookup(dtp, agg->dtagd_epid, &aggdata->dtada_ddesc,
-			       &aggdata->dtada_pdesc);
-		aggdata->dtada_normal = 1;
-
-		h->dtahe_hashval = hashval;
-		h->dtahe_size = size;
-		(void) dt_aggregate_aggvarid(h);
+		/* Entry found - process the data. */
+		agd = &h->dtahe_data;
+		memcpy(agd->dtada_data, st->buf + aid->di_offset, aid->di_size);
 
-		rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1];
+		/* If we keep per-CPU data - process that as well. */
+		if (agd->dtada_percpu != NULL)
+			memcpy(agd->dtada_percpu[st->cpu],
+			       st->buf + aid->di_offset, aid->di_size);
 
-		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));
-			}
+		return 0;
+	}
 
-			for (j = 0; j < max_cpus; j++) {
-				percpu[j] = malloc(rec->dtrd_size);
+	/* Not found - add it. */
+	h = dt_zalloc(st->dtp, sizeof(dt_ahashent_t));
+	if (h == NULL)
+		return dt_set_errno(st->dtp, EDT_NOMEM);
 
-				if (percpu[j] == NULL) {
-					while (--j >= 0)
-						free(percpu[j]);
+	agd = &h->dtahe_data;
+	agd->dtada_data = dt_alloc(st->dtp, aid->di_size);
+	if (agd->dtada_data == NULL) {
+		dt_free(st->dtp, h);
+		return dt_set_errno(st->dtp, EDT_NOMEM);
+	}
 
-					free(aggdata->dtada_data);
-					free(h);
-					return (dt_set_errno(dtp, EDT_NOMEM));
-				}
+	memcpy(agd->dtada_data, st->buf + aid->di_offset, aid->di_size);
+	agd->dtada_size = aid->di_size;
+	agd->dtada_desc = agg;
+	agd->dtada_hdl = st->dtp;
+	agd->dtada_normal = 1;
 
-				if (j == cpu) {
-					memcpy(percpu[j],
-					    &addr[rec->dtrd_offset],
-					    rec->dtrd_size);
-				} else {
-					memset(percpu[j], 0, rec->dtrd_size);
-				}
-			}
+	h->dtahe_hval = hval;
+	h->dtahe_size = aid->di_size;
 
-			aggdata->dtada_percpu = percpu;
-		}
+	if (st->agp->dtat_flags & DTRACE_A_PERCPU) {
+		/* FIXME - INCOMPLETE */
+	}
 
-		switch (rec->dtrd_action) {
-		case DTRACEAGG_MIN:
-			h->dtahe_aggregate = dt_aggregate_min;
-			break;
+	/* FIXME - Do we need to set dtahe_aggregate? */
 
-		case DTRACEAGG_MAX:
-			h->dtahe_aggregate = dt_aggregate_max;
-			break;
+	/* Add the new entry to the hashtable. */
+	if (agh->dtah_hash[ndx] != NULL)
+		agh->dtah_hash[ndx]->dtahe_prev = h;
 
-		case DTRACEAGG_LQUANTIZE:
-			h->dtahe_aggregate = dt_aggregate_lquantize;
-			break;
+	h->dtahe_next = agh->dtah_hash[ndx];
+	agh->dtah_hash[ndx] = h;
 
-		case DTRACEAGG_LLQUANTIZE:
-			h->dtahe_aggregate = dt_aggregate_llquantize;
-			break;
+	if (agh->dtah_all != NULL)
+		agh->dtah_all->dtahe_prevall = h;
 
-		case DTRACEAGG_COUNT:
-		case DTRACEAGG_SUM:
-		case DTRACEAGG_AVG:
-		case DTRACEAGG_STDDEV:
-		case DTRACEAGG_QUANTIZE:
-			h->dtahe_aggregate = dt_aggregate_count;
-			break;
+	h->dtahe_nextall = agh->dtah_all;
+	agh->dtah_all = h;
 
-		default:
-			return (dt_set_errno(dtp, EDT_BADAGG));
-		}
+	return 0;
+}
 
-		if (hash->dtah_hash[ndx] != NULL)
-			hash->dtah_hash[ndx]->dtahe_prev = h;
+static int
+dt_aggregate_snap_cpu(dtrace_hdl_t *dtp, processorid_t cpu)
+{
+	dt_aggregate_t	*agp = &dtp->dt_aggregate;
+	char		*buf = agp->dtat_cpu_buf[cpu];
+	uint64_t	*seq = (uint64_t *)buf;
+	dt_snapstate_t	st;
 
-		h->dtahe_next = hash->dtah_hash[ndx];
-		hash->dtah_hash[ndx] = h;
+	/* Nothing to be done? */
+	if (*seq == 0)
+		return 0;
 
-		if (hash->dtah_all != NULL)
-			hash->dtah_all->dtahe_prevall = h;
+	/*
+	 * Process all static aggregation identifiers in the CPU buffer.  We
+	 * skip over the latch sequence number because from this point on we
+	 * are simply processing data.
+	 */
+	st.dtp = dtp;
+	st.cpu = cpu;
+	st.buf = buf + sizeof(*seq);
+	st.agp = agp;
 
-		h->dtahe_nextall = hash->dtah_all;
-		hash->dtah_all = h;
-bufnext:
-		offs += agg->dtagd_size;
-	}
+	return dt_idhash_iter(dtp->dt_aggs,
+			      (dt_idhash_f *)dt_aggregate_snap_one, &st);
 
-	return (0);
+	return 0;
 }
 
+/*
+ * Retrieve all aggregation data from the enabled CPUs and aggregate it into
+ * its global values.  Note that we do not clear the per-CPU aggregation data
+ * is not reset after retrieval, so trievals are not cumulative.
+ */
 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;
-	}
+	dt_aggregate_t	*agp = &dtp->dt_aggregate;
+	uint32_t	key = 0;
+	int		i, rval;
 
-	if (!dtp->dt_active)
-		return (dt_set_errno(dtp, EINVAL));
+	/*
+	 * If we do not have a buffer initialized, we will not be processing
+	 * aggregations, so there is nothing to be done here.
+	 */
+	if (agp->dtat_cpu_buf == NULL)
+		return 0;
 
-	if (agp->dtat_buf.dtbd_size == 0)
-		return (0);
+	rval = dt_bpf_map_lookup(dtp->dt_aggmap_fd, &key, agp->dtat_buf);
+	if (rval != 0)
+		return dt_set_errno(dtp, -rval);
 
-	for (i = 0; i < agp->dtat_ncpus; i++) {
-		if ((rval = dt_aggregate_snap_cpu(dtp, agp->dtat_cpus[i])) != 0)
-			return (rval);
+	for (i = 0; i < dtp->dt_conf.num_online_cpus; i++) {
+		rval = dt_aggregate_snap_cpu(dtp, dtp->dt_conf.cpus[i].cpu_id);
+		if (rval != 0)
+			return rval;
 	}
 
-	return (0);
+	return 0;
 }
 
 static int
@@ -775,31 +557,31 @@ dt_aggregate_hashcmp(const void *lhs, const void *rhs)
 	dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc;
 
 	if (lagg->dtagd_nrecs < ragg->dtagd_nrecs)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lagg->dtagd_nrecs > ragg->dtagd_nrecs)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
-	return (0);
+	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;
+	dt_ahashent_t	*lh = *((dt_ahashent_t **)lhs);
+	dt_ahashent_t	*rh = *((dt_ahashent_t **)rhs);
+	dtrace_aggid_t	lid, rid;
 
-	lid = dt_aggregate_aggvarid(lh);
-	rid = dt_aggregate_aggvarid(rh);
+	lid = dt_aggregate_aggid(lh);
+	rid = dt_aggregate_aggid(rh);
 
 	if (lid < rid)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lid > rid)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
-	return (0);
+	return 0;
 }
 
 static int
@@ -814,7 +596,7 @@ dt_aggregate_keycmp(const void *lhs, const void *rhs)
 	int rval, i, j, keypos, nrecs;
 
 	if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0)
-		return (rval);
+		return rval;
 
 	nrecs = lagg->dtagd_nrecs - 1;
 	assert(nrecs == ragg->dtagd_nrecs - 1);
@@ -828,41 +610,41 @@ dt_aggregate_keycmp(const void *lhs, const void *rhs)
 		if (ndx >= nrecs)
 			ndx = ndx - nrecs + 1;
 
-		lrec = &lagg->dtagd_rec[ndx];
-		rrec = &ragg->dtagd_rec[ndx];
+		lrec = &lagg->dtagd_recs[ndx];
+		rrec = &ragg->dtagd_recs[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);
+			return DT_LESSTHAN;
 
 		if (lrec->dtrd_size > rrec->dtrd_size)
-			return (DT_GREATERTHAN);
+			return DT_GREATERTHAN;
 
 		switch (lrec->dtrd_size) {
-		case sizeof (uint64_t):
+		case sizeof(uint64_t):
 			/* LINTED - alignment */
 			lval = *((uint64_t *)ldata);
 			/* LINTED - alignment */
 			rval = *((uint64_t *)rdata);
 			break;
 
-		case sizeof (uint32_t):
+		case sizeof(uint32_t):
 			/* LINTED - alignment */
 			lval = *((uint32_t *)ldata);
 			/* LINTED - alignment */
 			rval = *((uint32_t *)rdata);
 			break;
 
-		case sizeof (uint16_t):
+		case sizeof(uint16_t):
 			/* LINTED - alignment */
 			lval = *((uint16_t *)ldata);
 			/* LINTED - alignment */
 			rval = *((uint16_t *)rdata);
 			break;
 
-		case sizeof (uint8_t):
+		case sizeof(uint8_t):
 			lval = *((uint8_t *)ldata);
 			rval = *((uint8_t *)rdata);
 			break;
@@ -879,10 +661,10 @@ dt_aggregate_keycmp(const void *lhs, const void *rhs)
 					rval = ((uint64_t *)rdata)[j];
 
 					if (lval < rval)
-						return (DT_LESSTHAN);
+						return DT_LESSTHAN;
 
 					if (lval > rval)
-						return (DT_GREATERTHAN);
+						return DT_GREATERTHAN;
 				}
 
 				break;
@@ -893,10 +675,10 @@ dt_aggregate_keycmp(const void *lhs, const void *rhs)
 					rval = ((uint8_t *)rdata)[j];
 
 					if (lval < rval)
-						return (DT_LESSTHAN);
+						return DT_LESSTHAN;
 
 					if (lval > rval)
-						return (DT_GREATERTHAN);
+						return DT_GREATERTHAN;
 				}
 			}
 
@@ -904,13 +686,13 @@ dt_aggregate_keycmp(const void *lhs, const void *rhs)
 		}
 
 		if (lval < rval)
-			return (DT_LESSTHAN);
+			return DT_LESSTHAN;
 
 		if (lval > rval)
-			return (DT_GREATERTHAN);
+			return DT_GREATERTHAN;
 	}
 
-	return (0);
+	return 0;
 }
 
 static int
@@ -927,32 +709,32 @@ dt_aggregate_valcmp(const void *lhs, const void *rhs)
 	int rval, i;
 
 	if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0)
-		return (rval);
+		return rval;
 
 	if (lagg->dtagd_nrecs > ragg->dtagd_nrecs)
-		return (DT_GREATERTHAN);
+		return DT_GREATERTHAN;
 
 	if (lagg->dtagd_nrecs < ragg->dtagd_nrecs)
-		return (DT_LESSTHAN);
+		return DT_LESSTHAN;
 
 	if (lagg->dtagd_nrecs <= 0)
 	    return 0;
 
 	for (i = 0; i < lagg->dtagd_nrecs; i++) {
-		lrec = &lagg->dtagd_rec[i];
-		rrec = &ragg->dtagd_rec[i];
+		lrec = &lagg->dtagd_recs[i];
+		rrec = &ragg->dtagd_recs[i];
 
 		if (lrec->dtrd_offset < rrec->dtrd_offset)
-			return (DT_LESSTHAN);
+			return DT_LESSTHAN;
 
 		if (lrec->dtrd_offset > rrec->dtrd_offset)
-			return (DT_GREATERTHAN);
+			return DT_GREATERTHAN;
 
 		if (lrec->dtrd_action < rrec->dtrd_action)
-			return (DT_LESSTHAN);
+			return DT_LESSTHAN;
 
 		if (lrec->dtrd_action > rrec->dtrd_action)
-			return (DT_GREATERTHAN);
+			return DT_GREATERTHAN;
 	}
 
 	laddr = (int64_t *)(uintptr_t)(ldata + lrec->dtrd_offset);
@@ -990,7 +772,7 @@ dt_aggregate_valcmp(const void *lhs, const void *rhs)
 		assert(0);
 	}
 
-	return (rval);
+	return rval;
 }
 
 static int
@@ -999,14 +781,14 @@ dt_aggregate_valkeycmp(const void *lhs, const void *rhs)
 	int rval;
 
 	if ((rval = dt_aggregate_valcmp(lhs, rhs)) != 0)
-		return (rval);
+		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));
+	return dt_aggregate_keycmp(lhs, rhs);
 }
 
 static int
@@ -1015,9 +797,9 @@ dt_aggregate_keyvarcmp(const void *lhs, const void *rhs)
 	int rval;
 
 	if ((rval = dt_aggregate_keycmp(lhs, rhs)) != 0)
-		return (rval);
+		return rval;
 
-	return (dt_aggregate_varcmp(lhs, rhs));
+	return dt_aggregate_varcmp(lhs, rhs);
 }
 
 static int
@@ -1026,9 +808,9 @@ dt_aggregate_varkeycmp(const void *lhs, const void *rhs)
 	int rval;
 
 	if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0)
-		return (rval);
+		return rval;
 
-	return (dt_aggregate_keycmp(lhs, rhs));
+	return dt_aggregate_keycmp(lhs, rhs);
 }
 
 static int
@@ -1037,9 +819,9 @@ dt_aggregate_valvarcmp(const void *lhs, const void *rhs)
 	int rval;
 
 	if ((rval = dt_aggregate_valkeycmp(lhs, rhs)) != 0)
-		return (rval);
+		return rval;
 
-	return (dt_aggregate_varcmp(lhs, rhs));
+	return dt_aggregate_varcmp(lhs, rhs);
 }
 
 static int
@@ -1048,33 +830,33 @@ dt_aggregate_varvalcmp(const void *lhs, const void *rhs)
 	int rval;
 
 	if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0)
-		return (rval);
+		return rval;
 
-	return (dt_aggregate_valkeycmp(lhs, rhs));
+	return dt_aggregate_valkeycmp(lhs, rhs);
 }
 
 static int
 dt_aggregate_keyvarrevcmp(const void *lhs, const void *rhs)
 {
-	return (dt_aggregate_keyvarcmp(rhs, lhs));
+	return dt_aggregate_keyvarcmp(rhs, lhs);
 }
 
 static int
 dt_aggregate_varkeyrevcmp(const void *lhs, const void *rhs)
 {
-	return (dt_aggregate_varkeycmp(rhs, lhs));
+	return dt_aggregate_varkeycmp(rhs, lhs);
 }
 
 static int
 dt_aggregate_valvarrevcmp(const void *lhs, const void *rhs)
 {
-	return (dt_aggregate_valvarcmp(rhs, lhs));
+	return dt_aggregate_valvarcmp(rhs, lhs);
 }
 
 static int
 dt_aggregate_varvalrevcmp(const void *lhs, const void *rhs)
 {
-	return (dt_aggregate_varvalcmp(rhs, lhs));
+	return dt_aggregate_varvalcmp(rhs, lhs);
 }
 
 static int
@@ -1099,7 +881,7 @@ dt_aggregate_bundlecmp(const void *lhs, const void *rhs)
 		assert(rh[i + 1] == NULL);
 
 		if ((rval = dt_aggregate_keycmp(&lh[i], &rh[i])) != 0)
-			return (rval);
+			return rval;
 	}
 
 	for (i = 0; ; i++) {
@@ -1113,14 +895,14 @@ dt_aggregate_bundlecmp(const void *lhs, const void *rhs)
 			 * tie-breaker.
 			 */
 			if (dt_keysort)
-				return (0);
+				return 0;
 
 			assert(i != 0);
 			assert(rh[i + 1] == NULL);
-			return (dt_aggregate_keycmp(&lh[i], &rh[i]));
+			return dt_aggregate_keycmp(&lh[i], &rh[i]);
 		} else {
 			if ((rval = dt_aggregate_valcmp(&lh[i], &rh[i])) != 0)
-				return (rval);
+				return rval;
 		}
 	}
 }
@@ -1128,61 +910,53 @@ dt_aggregate_bundlecmp(const void *lhs, const void *rhs)
 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;
+	dt_aggregate_t	*agp = &dtp->dt_aggregate;
+	dt_ahash_t	*agh = &agp->dtat_hash;
+	int		aggsz, i;
 
-	assert(agp->dtat_maxcpu == 0);
-	assert(agp->dtat_ncpu == 0);
-	assert(agp->dtat_cpus == NULL);
-
-	agp->dtat_maxcpu = dtp->dt_conf.dtc_maxbufs;
-	agp->dtat_ncpu = dtp->dt_conf.dtc_maxbufs;
-	agp->dtat_cpus = malloc(agp->dtat_ncpu * sizeof (processorid_t));
-
-	if (agp->dtat_cpus == NULL)
-		return (dt_set_errno(dtp, EDT_NOMEM));
+	/* If there are no aggregations there is nothing to do. */
+	aggsz = dt_idhash_datasize(dtp->dt_aggs);
+	if (aggsz <= 0)
+		return 0;
 
 	/*
-	 * 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.
+	 * Allocate a buffer to hold the aggregation data for all possible
+	 * CPUs, and initialize the per-CPU data pointers for CPUs that are
+	 * currently enabled.
+	 *
+	 * The size of the aggregation data is to be increased by the size of
+	 * the latch sequence number.  That yields the actual size of the data
+	 * allocated per CPU.
 	 */
-	rval = dtrace_getopt(dtp, "cpu", &cpu);
-	assert(rval == 0 && cpu != DTRACEOPT_UNSET);
+	aggsz += sizeof(uint64_t);
+	agp->dtat_buf = dt_zalloc(dtp, dtp->dt_conf.num_possible_cpus * aggsz);
+	if (agp->dtat_buf == NULL)
+		return dt_set_errno(dtp, EDT_NOMEM);
+
+	agp->dtat_cpu_buf = dt_calloc(dtp, dtp->dt_conf.max_cpuid + 1,
+				      sizeof(char *));
+	if (agp->dtat_cpu_buf == NULL) {
+		free(agp->dtat_buf);
+		return dt_set_errno(dtp, EDT_NOMEM);
+	}
 
-	if (cpu != DTRACE_CPUALL) {
-		assert(cpu < agp->dtat_ncpu);
-		agp->dtat_cpus[agp->dtat_ncpus++] = (processorid_t)cpu;
+	for (i = 0; i < dtp->dt_conf.num_online_cpus; i++) {
+		int	cpu = dtp->dt_conf.cpus[i].cpu_id;
 
-		return (0);
+		agp->dtat_cpu_buf[cpu] = agp->dtat_buf + cpu * aggsz;
 	}
 
-	agp->dtat_ncpus = 0;
-	for (i = 0; i < agp->dtat_maxcpu; i++) {
-		if (dt_cpu_status(dtp, i) == -1)
-			continue;
-
-		agp->dtat_cpus[agp->dtat_ncpus++] = i;
+	/* Create the aggregation hash. */
+	agh->dtah_size = DTRACE_AHASHSIZE;
+	agh->dtah_hash = dt_zalloc(dtp,
+				   agh->dtah_size * sizeof(dt_ahashent_t *));
+	if (agh->dtah_hash == NULL) {
+		free(agp->dtat_cpu_buf);
+		free(agp->dtat_buf);
+		return dt_set_errno(dtp, EDT_NOMEM);
 	}
 
-	return (0);
+	return 0;
 }
 
 static int
@@ -1202,13 +976,13 @@ dt_aggwalk_rval(dtrace_hdl_t *dtp, dt_ahashent_t *h, int rval)
 		uint32_t size, offs = 0;
 
 		aggdesc = h->dtahe_data.dtada_desc;
-		rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1];
+		rec = &aggdesc->dtagd_recs[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);
+			offs = sizeof(uint64_t);
+			size -= sizeof(uint64_t);
 		}
 
 		memset(&data->dtada_data[rec->dtrd_offset] + offs, 0, size);
@@ -1225,22 +999,22 @@ dt_aggwalk_rval(dtrace_hdl_t *dtp, dt_ahashent_t *h, int rval)
 		/*
 		 * We assume that errno is already set in this case.
 		 */
-		return (dt_set_errno(dtp, errno));
+		return dt_set_errno(dtp, errno);
 
 	case DTRACE_AGGWALK_ABORT:
-		return (dt_set_errno(dtp, EDT_DIRABORT));
+		return dt_set_errno(dtp, EDT_DIRABORT);
 
 	case DTRACE_AGGWALK_DENORMALIZE:
 		h->dtahe_data.dtada_normal = 1;
-		return (0);
+		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 dt_set_errno(dtp, EDT_BADRVAL);
 		}
 
-		return (0);
+		return 0;
 
 	case DTRACE_AGGWALK_REMOVE: {
 		dtrace_aggdata_t *aggdata = &h->dtahe_data;
@@ -1253,7 +1027,7 @@ dt_aggwalk_rval(dtrace_hdl_t *dtp, dt_ahashent_t *h, int rval)
 			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;
+			size_t ndx = h->dtahe_hval % hash->dtah_size;
 
 			assert(hash->dtah_hash[ndx] == h);
 			hash->dtah_hash[ndx] = h->dtahe_next;
@@ -1289,14 +1063,14 @@ dt_aggwalk_rval(dtrace_hdl_t *dtp, dt_ahashent_t *h, int rval)
 		free(aggdata->dtada_data);
 		free(h);
 
-		return (0);
+		return 0;
 	}
 
 	default:
-		return (dt_set_errno(dtp, EDT_BADRVAL));
+		return dt_set_errno(dtp, EDT_BADRVAL);
 	}
 
-	return (0);
+	return 0;
 }
 
 void
@@ -1345,135 +1119,143 @@ dtrace_aggregate_walk(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg)
 		next = h->dtahe_nextall;
 
 		if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1)
-			return (-1);
+			return -1;
 	}
 
-	return (0);
+	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_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;
 
+	dtrace_aggregate_snap(dtp);
+
+	/*
+	 * Count how many aggregations have data in the buffers.  If there are
+	 * aggregations but none have recorded data yet, there is nothing to do.
+	 */
 	for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall)
 		nentries++;
+	if (nentries == 0)
+		return 0;
 
 	sorted = dt_calloc(dtp, nentries, sizeof(dt_ahashent_t *));
 	if (sorted == NULL)
-		return (-1);
+		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);
+	pthread_mutex_lock(&dt_qsort_lock);
 
 	if (sfunc == NULL) {
 		dt_aggregate_qsort(dtp, sorted, nentries,
-		    sizeof (dt_ahashent_t *), NULL);
+		    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);
+		qsort(sorted, nentries, sizeof(dt_ahashent_t *), sfunc);
 	}
 
-	(void) pthread_mutex_unlock(&dt_qsort_lock);
+	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);
+			return -1;
 		}
 	}
 
 	dt_free(dtp, sorted);
-	return (0);
+	return 0;
 }
 
 int
-dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp,
-    dtrace_aggregate_f *func, void *arg)
+dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func,
+			     void *arg)
 {
-	return (dt_aggregate_walk_sorted(dtp, func, arg, NULL));
+	return dt_aggregate_walk_sorted(dtp, func, arg, NULL);
 }
 
 int
-dtrace_aggregate_walk_keysorted(dtrace_hdl_t *dtp,
-    dtrace_aggregate_f *func, void *arg)
+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));
+	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)
+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));
+	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)
+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));
+	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)
+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));
+	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)
+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));
+	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)
+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));
+	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)
+				      dtrace_aggregate_f *func, void *arg)
 {
-	return (dt_aggregate_walk_sorted(dtp, func,
-	    arg, dt_aggregate_keyvarrevcmp));
+	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)
+				      dtrace_aggregate_f *func, void *arg)
 {
-	return (dt_aggregate_walk_sorted(dtp, func,
-	    arg, dt_aggregate_valvarrevcmp));
+	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)
+dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggid_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;
@@ -1481,7 +1263,7 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 	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;
+	dtrace_aggid_t max = 0, aggvar;
 	int rval = -1, *map, *remap = NULL;
 	int i, j;
 	dtrace_optval_t sortpos = dtp->dt_options[DTRACEOPT_AGGSORTPOS];
@@ -1500,7 +1282,7 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 	 */
 	for (i = 0; i < naggvars; i++) {
 		if (aggvars[i] == DTRACE_AGGVARIDNONE || aggvars[i] < 0)
-			return (dt_set_errno(dtp, EDT_BADAGGVAR));
+			return dt_set_errno(dtp, EDT_BADAGGVAR);
 
 		if (aggvars[i] > max)
 			max = aggvars[i];
@@ -1508,10 +1290,9 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 
 	map = dt_calloc(dtp, max + 1, sizeof(int));
 	if (map == NULL)
-		return (-1);
+		return -1;
 
 	zaggdata = dt_calloc(dtp, naggvars, sizeof(dt_ahashent_t));
-
 	if (zaggdata == NULL)
 		goto out;
 
@@ -1537,7 +1318,6 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 			 */
 			if (remap == NULL) {
 				remap = dt_calloc(dtp, naggvars, sizeof(int));
-
 				if (remap == NULL)
 					goto out;
 			}
@@ -1558,16 +1338,21 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 		map[aggvar] = i + 1;
 	}
 
+	/*
+	 * Retrieve the aggregation data.
+	 */
+	dtrace_aggregate_snap(dtp);
+
 	/*
 	 * 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;
+		dtrace_aggid_t	id;
+		int		ndx;
 
-		if ((id = dt_aggregate_aggvarid(h)) > max || !(ndx = map[id]))
+		if ((id = dt_aggregate_aggid(h)) > max || !(ndx = map[id]))
 			continue;
 
 		if (zaggdata[ndx - 1].dtahe_size == 0) {
@@ -1608,7 +1393,7 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 	 */
 	for (i = 0; i < naggvars; i++) {
 		if (zaggdata[i].dtahe_size == 0) {
-			dtrace_aggvarid_t aggvar;
+			dtrace_aggid_t	aggvar;
 
 			aggvar = aggvars[(i - sortpos + naggvars) % naggvars];
 			assert(zaggdata[i].dtahe_data.dtada_data == NULL);
@@ -1630,12 +1415,9 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 				aggdata = &zaggdata[i].dtahe_data;
 				aggdata->dtada_size = agg->dtagd_size;
 				aggdata->dtada_desc = agg;
-				aggdata->dtada_handle = dtp;
-				dt_epid_lookup(dtp, agg->dtagd_epid,
-					       &aggdata->dtada_ddesc,
-					       &aggdata->dtada_pdesc);
+				aggdata->dtada_hdl = dtp;
 				aggdata->dtada_normal = 1;
-				zaggdata[i].dtahe_hashval = 0;
+				zaggdata[i].dtahe_hval = 0;
 				zaggdata[i].dtahe_size = agg->dtagd_size;
 				break;
 			}
@@ -1674,9 +1456,6 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 	 */
 	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;
@@ -1694,63 +1473,6 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 		}
 
 		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;
 	}
 
@@ -1764,9 +1486,9 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 		goto out;
 
 	for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) {
-		dtrace_aggvarid_t id;
+		dtrace_aggid_t	id;
 
-		if ((id = dt_aggregate_aggvarid(h)) > max || !map[id])
+		if ((id = dt_aggregate_aggid(h)) > max || !map[id])
 			continue;
 
 		sorted[i++] = h;
@@ -1780,9 +1502,9 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 	 * dt_qsort_lock here, and hold it across all of our subsequent
 	 * comparison and sorting.
 	 */
-	(void) pthread_mutex_lock(&dt_qsort_lock);
+	pthread_mutex_lock(&dt_qsort_lock);
 
-	qsort(sorted, nentries, sizeof (dt_ahashent_t *),
+	qsort(sorted, nentries, sizeof(dt_ahashent_t *),
 	    dt_aggregate_keyvarcmp);
 
 	/*
@@ -1806,15 +1528,15 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 		 * (i - 1) belongs in one bundle.
 		 */
 		assert(i - start <= naggvars);
-		bundlesize = (naggvars + 2) * sizeof (dt_ahashent_t *);
+		bundlesize = (naggvars + 2) * sizeof(dt_ahashent_t *);
 
 		if ((nbundle = dt_zalloc(dtp, bundlesize)) == NULL) {
-			(void) pthread_mutex_unlock(&dt_qsort_lock);
+			pthread_mutex_unlock(&dt_qsort_lock);
 			goto out;
 		}
 
 		for (j = start; j < i; j++) {
-			dtrace_aggvarid_t id = dt_aggregate_aggvarid(sorted[j]);
+			dtrace_aggid_t	id = dt_aggregate_aggid(sorted[j]);
 
 			assert(id <= max);
 			assert(map[id] != 0);
@@ -1844,9 +1566,8 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 				assert(remap[j] - 1 < j);
 				assert(nbundle[remap[j] - 1] != NULL);
 				nbundle[j] = nbundle[remap[j] - 1];
-			} else {
+			} else
 				nbundle[j] = &zaggdata[j];
-			}
 		}
 
 		bundle[nbundles++] = nbundle;
@@ -1856,16 +1577,16 @@ dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars,
 	/*
 	 * Now we need to re-sort based on the first value.
 	 */
-	dt_aggregate_qsort(dtp, bundle, nbundles, sizeof (dt_ahashent_t **),
+	dt_aggregate_qsort(dtp, bundle, nbundles, sizeof(dt_ahashent_t **),
 	    dt_aggregate_bundlecmp);
 
-	(void) pthread_mutex_unlock(&dt_qsort_lock);
+	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 *));
+	data = alloca((naggvars + 1) * sizeof(dtrace_aggdata_t *));
 
 	for (i = 0; i < nbundles; i++) {
 		for (j = 0; j < naggvars; j++)
@@ -1911,7 +1632,7 @@ out:
 	dt_free(dtp, remap);
 	dt_free(dtp, map);
 
-	return (rval);
+	return rval;
 }
 
 int
@@ -1920,6 +1641,9 @@ dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp,
 {
 	dtrace_print_aggdata_t pd;
 
+	if (dt_idhash_datasize(dtp->dt_aggs) == 0)
+		return 0;
+
 	pd.dtpa_dtp = dtp;
 	pd.dtpa_fp = fp;
 	pd.dtpa_allunprint = 1;
@@ -1928,9 +1652,9 @@ dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp,
 		func = dtrace_aggregate_walk_sorted;
 
 	if ((*func)(dtp, dt_print_agg, &pd) == -1)
-		return (dt_set_errno(dtp, dtp->dt_errno));
+		return dt_set_errno(dtp, dtp->dt_errno);
 
-	return (0);
+	return 0;
 }
 
 void
@@ -1946,7 +1670,7 @@ dtrace_aggregate_clear(dtrace_hdl_t *dtp)
 
 	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];
+		rec = &aggdesc->dtagd_recs[aggdesc->dtagd_nrecs - 1];
 		data = &h->dtahe_data;
 
 		memset(&data->dtada_data[rec->dtrd_offset], 0, rec->dtrd_size);
@@ -1971,7 +1695,7 @@ dt_aggregate_destroy(dtrace_hdl_t *dtp)
 	if (hash->dtah_hash == NULL) {
 		assert(hash->dtah_all == NULL);
 	} else {
-		free(hash->dtah_hash);
+		dt_free(dtp, hash->dtah_hash);
 
 		for (h = hash->dtah_all; h != NULL; h = next) {
 			next = h->dtahe_nextall;
@@ -1980,12 +1704,12 @@ dt_aggregate_destroy(dtrace_hdl_t *dtp)
 
 			if (aggdata->dtada_percpu != NULL) {
 				for (i = 0; i < max_cpus; i++)
-					free(aggdata->dtada_percpu[i]);
-				free(aggdata->dtada_percpu);
+					dt_free(dtp, aggdata->dtada_percpu[i]);
+				dt_free(dtp, aggdata->dtada_percpu);
 			}
 
-			free(aggdata->dtada_data);
-			free(h);
+			dt_free(dtp, aggdata->dtada_data);
+			dt_free(dtp, h);
 		}
 
 		hash->dtah_hash = NULL;
@@ -1993,6 +1717,6 @@ dt_aggregate_destroy(dtrace_hdl_t *dtp)
 		hash->dtah_size = 0;
 	}
 
-	free(agp->dtat_buf.dtbd_data);
-	free(agp->dtat_cpus);
+	dt_free(dtp, agp->dtat_cpu_buf);
+	dt_free(dtp, agp->dtat_buf);
 }
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index f7028b90..c6102f15 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -208,7 +208,7 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
 	dt_gmap_done = 1;
 
 	/* Determine the aggregation buffer size.  */
-	aggsz = dt_idhash_nextoff(dtp->dt_aggs, 1, 0);
+	aggsz = dt_idhash_datasize(dtp->dt_aggs);
 
 	/* Determine the number of global and TLS variables. */
 	gvarc = dt_idhash_peekid(dtp->dt_globals) - DIF_VAR_OTHER_UBASE;
@@ -220,47 +220,52 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
 				       sizeof(DT_STATE_VAL_TYPE),
 				       DT_STATE_NUM_ELEMS);
 	if (dtp->dt_stmap_fd == -1)
-		return -1;	/* dt_errno is set for us */
+		return -1;		/* dt_errno is set for us */
 
 	/*
-	 * If there is aggregation data to be collected, we need to add a
-	 * uint64_t to the map value size to hold a latch sequence number (seq)
-	 * for concurrent access to the data.
+	 * If there is aggregation data to be collected, we need to create the
+	 * 'aggs' BPF map, and account for a uint64_t in the map value size to
+	 * hold a latch sequence number (seq) for concurrent access to the
+	 * data.
 	 */
-	if (aggsz > 0 &&
-	    create_gmap(dtp, "aggs", BPF_MAP_TYPE_PERCPU_ARRAY,
-			sizeof(uint32_t), sizeof(uint64_t) +  aggsz, 1) == -1)
-		return -1;	/* dt_errno is set for us */
+	if (aggsz > 0) {
+		dtp->dt_aggmap_fd = create_gmap(dtp, "aggs",
+						BPF_MAP_TYPE_PERCPU_ARRAY,
+						sizeof(uint32_t),
+						sizeof(uint64_t) + aggsz, 1);
+		if (dtp->dt_aggmap_fd == -1)
+			return -1;	/* dt_errno is set for us */
+	}
 
 	if (create_gmap(dtp, "buffers", BPF_MAP_TYPE_PERF_EVENT_ARRAY,
 			sizeof(uint32_t), sizeof(uint32_t),
 			dtp->dt_conf.num_online_cpus) == -1)
-		return -1;	/* dt_errno is set for us */
+		return -1;		/* dt_errno is set for us */
 
 	ci_mapfd = create_gmap(dtp, "cpuinfo", BPF_MAP_TYPE_PERCPU_ARRAY,
 			       sizeof(uint32_t), sizeof(cpuinfo_t), 1);
 	if (ci_mapfd == -1)
-		return -1;	/* dt_errno is set for us */
+		return -1;		/* dt_errno is set for us */
 
 	if (create_gmap(dtp, "mem", BPF_MAP_TYPE_PERCPU_ARRAY,
 			sizeof(uint32_t),
 			roundup(sizeof(dt_mstate_t), 8) + 8 +
 				roundup(dtp->dt_maxreclen, 8), 1) == -1)
-		return -1;	/* dt_errno is set for us */
+		return -1;		/* dt_errno is set for us */
 
 	if (create_gmap(dtp, "strtab", BPF_MAP_TYPE_ARRAY,
 			sizeof(uint32_t), dtp->dt_strlen, 1) == -1)
-		return -1;	/* dt_errno is set for us */
+		return -1;		/* dt_errno is set for us */
 
 	if (gvarc > 0 &&
 	    create_gmap(dtp, "gvars", BPF_MAP_TYPE_ARRAY,
 			sizeof(uint32_t), sizeof(uint64_t), gvarc) == -1)
-		return -1;	/* dt_errno is set for us */
+		return -1;		/* dt_errno is set for us */
 
 	if (tvarc > 0 &&
 	    create_gmap(dtp, "tvars", BPF_MAP_TYPE_ARRAY,
 			sizeof(uint32_t), sizeof(uint64_t), tvarc) == -1)
-		return -1;	/* dt_errno is set for us */
+		return -1;		/* dt_errno is set for us */
 
 	/* Populate the 'cpuinfo' map. */
 	dt_bpf_map_update(ci_mapfd, &key, dtp->dt_conf.cpus);
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 463ba63d..2d73b434 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -129,7 +129,7 @@ dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act)
 	emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8));
 	emit(dlp, BPF_STORE(BPF_DW, BPF_REG_FP, DCTX_FP(DCTX_BUF), BPF_REG_0));
 
-	if (dt_idhash_nextoff(dtp->dt_aggs, 1, 0) > 0) {
+	if (dt_idhash_datasize(dtp->dt_aggs) > 0) {
 		dt_ident_t	*aggs = dt_dlib_get_map(dtp, "aggs");
 
 		/*
@@ -457,12 +457,25 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind,
 {
 	dtrace_diftype_t	vtype;
 	dt_irlist_t		*dlp = &pcb->pcb_ir;
+	dt_regset_t		*drp = pcb->pcb_regs;
 	uint_t			off;
 
-	dt_cg_node(dnp, &pcb->pcb_ir, pcb->pcb_regs);
-	dt_node_diftype(pcb->pcb_hdl, dnp, &vtype);
+	/*
+	 * Special case for aggregations: we store the aggregation id.  We
+	 * cannot just generate code for the dnp node because it has no type.
+	 */
+	if (dnp->dn_kind == DT_NODE_AGG) {
+		if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
+			longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+		emit(dlp, BPF_MOV_IMM(dnp->dn_reg, dnp->dn_ident->di_id));
+		vtype.dtdt_size = sizeof(dnp->dn_ident->di_id);
+	} else {
+		dt_cg_node(dnp, &pcb->pcb_ir, drp);
+		dt_node_diftype(pcb->pcb_hdl, dnp, &vtype);
+	}
 
-	if (dt_node_is_scalar(dnp) || dt_node_is_float(dnp)) {
+	if (dt_node_is_scalar(dnp) || dt_node_is_float(dnp) ||
+	    dnp->dn_kind == DT_NODE_AGG) {
 		int	sz = 8;
 
 		off = dt_rec_add(pcb->pcb_hdl, dt_cg_fill_gap, kind,
@@ -485,7 +498,7 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind,
 		}
 
 		emit(dlp, BPF_STORE(sz, BPF_REG_9, off, dnp->dn_reg));
-		dt_regset_free(pcb->pcb_regs, dnp->dn_reg);
+		dt_regset_free(drp, dnp->dn_reg);
 
 		return 0;
 #if 0
@@ -505,7 +518,7 @@ dt_cg_store_val(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind,
 		sz = P2ROUNDUP(sz, sizeof(uint64_t));
 		emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, off, sz & ((1UL << 32)-1)));
 		emit(dlp, BPF_STORE_IMM(BPF_W, BPF_REG_9, off + 4, sz >> 32));
-		dt_regset_free(pcb->pcb_regs, dnp->dn_args->dn_reg);
+		dt_regset_free(drp, dnp->dn_args->dn_reg);
 
 		return sz + sizeof(uint64_t);
 #endif
@@ -732,6 +745,86 @@ dt_cg_act_pcap(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 static void
 dt_cg_act_printa(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 {
+	dt_node_t	*anp, *proto = NULL;
+	dt_pfargv_t	*pfp = NULL;
+	int		argc = 0, argr;
+	const char	*fmt;
+	char		n[DT_TYPE_NAMELEN];
+	dt_ident_t	*aid, *fid;
+	int		*cfp = &pcb->pcb_stmt->dtsd_clauseflags;
+
+	/* process clause flags */
+	if (*cfp & DT_CLSFLAG_COMMIT)
+		dnerror(dnp, D_DREC_COMM,
+		    "data-recording actions may not follow commit( )\n");
+	*cfp |= DT_CLSFLAG_DATAREC;
+
+	/* Count the arguments. */
+	for (anp = dnp->dn_args; anp != NULL; anp = anp->dn_list)
+		argc++;
+
+	/* Get format string (if any). */
+	if (dnp->dn_args->dn_kind == DT_NODE_STRING) {
+		fmt = dnp->dn_args->dn_string;
+		anp = dnp->dn_args->dn_list;
+		argr = 2;
+	} else {
+		fmt = 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 == pcb->pcb_hdl->dt_gen &&
+		    !(aid->di_flags & DT_IDFLG_MOD))
+			dnerror(dnp, D_PRINTA_AGGBAD,
+				"undefined aggregation: @%s\n", aid->di_name);
+
+		/*
+		 * If multiple aggregations are specified, their signatures
+		 * must match.
+		 */
+		if (proto != NULL)
+			dt_printa_validate(proto, anp);
+		else
+			proto = anp;
+
+		/*
+		 * Validate the format string and the datatypes of the keys, if
+		 * there is a format string specified.
+		 */
+		if (fmt != NULL) {
+			yylineno = dnp->dn_line;
+
+			pfp = dt_printf_create(pcb->pcb_hdl, fmt);
+			dt_printf_validate(pfp, DT_PRINTF_AGGREGATION,
+					   dnp->dn_ident, 1, fid->di_id,
+					   ((dt_idsig_t *)aid->di_data)->dis_args);
+		}
+
+		dt_cg_store_val(pcb, anp, DTRACEACT_PRINTA, pfp, (uint64_t)dnp);
+		pfp = NULL;
+
+		anp = anp->dn_list;
+		argr++;
+	}
 }
 
 static void
@@ -3573,16 +3666,15 @@ typedef void dt_cg_aggfunc_f(dt_pcb_t *, dt_ident_t *, dt_node_t *,
 			     dt_irlist_t *, dt_regset_t *);
 
 static dt_cg_aggfunc_f *_dt_cg_agg[DT_AGG_NUM] = {
-	[0 ... DT_AGG_NUM - 1]	= NULL,
-	[DT_AGG_AVG]		= &dt_cg_agg_avg,
-	[DT_AGG_COUNT]		= &dt_cg_agg_count,
-	[DT_AGG_LLQUANTIZE]	= &dt_cg_agg_llquantize,
-	[DT_AGG_LQUANTIZE]	= &dt_cg_agg_lquantize,
-	[DT_AGG_MAX]		= &dt_cg_agg_max,
-	[DT_AGG_MIN]		= &dt_cg_agg_min,
-	[DT_AGG_QUANTIZE]	= &dt_cg_agg_quantize,
-	[DT_AGG_STDDEV]		= &dt_cg_agg_stddev,
-	[DT_AGG_SUM]		= &dt_cg_agg_sum,
+	[DT_AGG_IDX(DT_AGG_AVG)]	= &dt_cg_agg_avg,
+	[DT_AGG_IDX(DT_AGG_COUNT)]	= &dt_cg_agg_count,
+	[DT_AGG_IDX(DT_AGG_LLQUANTIZE)]	= &dt_cg_agg_llquantize,
+	[DT_AGG_IDX(DT_AGG_LQUANTIZE)]	= &dt_cg_agg_lquantize,
+	[DT_AGG_IDX(DT_AGG_MAX)]	= &dt_cg_agg_max,
+	[DT_AGG_IDX(DT_AGG_MIN)]	= &dt_cg_agg_min,
+	[DT_AGG_IDX(DT_AGG_QUANTIZE)]	= &dt_cg_agg_quantize,
+	[DT_AGG_IDX(DT_AGG_STDDEV)]	= &dt_cg_agg_stddev,
+	[DT_AGG_IDX(DT_AGG_SUM)]	= &dt_cg_agg_sum,
 };
 
 static void
@@ -3613,12 +3705,13 @@ dt_cg_agg(dt_pcb_t *pcb, dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 			"supported yet: @%s\n", dnp->dn_ident->di_name);
 
 
-	assert(fid->di_id >= 0 && fid->di_id < DT_AGG_NUM);
+	assert(fid->di_id >= DT_AGG_BASE && fid->di_id < DT_AGG_HIGHEST);
 
-	aggfp = _dt_cg_agg[fid->di_id];
+	aggfp = _dt_cg_agg[DT_AGG_IDX(fid->di_id)];
 	assert(aggfp != NULL);
 
 	(*aggfp)(pcb, aid, dnp, dlp, drp);
+	dt_aggid_add(pcb->pcb_hdl, aid);
 }
 
 void
diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c
index a2dffc5c..55ba6d05 100644
--- a/libdtrace/dt_consume.c
+++ b/libdtrace/dt_consume.c
@@ -574,31 +574,27 @@ dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
 }
 
 int
-dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
-    size_t size, uint64_t normal)
+dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size,
+		   uint64_t normal, uint64_t sig)
 {
 	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);
+	if (size < sizeof(uint64_t))
+		return dt_set_errno(dtp, EDT_DMISMATCH);
 
-	base = DTRACE_LQUANTIZE_BASE(arg);
-	step = DTRACE_LQUANTIZE_STEP(arg);
-	levels = DTRACE_LQUANTIZE_LEVELS(arg);
+	base = DTRACE_LQUANTIZE_BASE(sig);
+	step = DTRACE_LQUANTIZE_STEP(sig);
+	levels = DTRACE_LQUANTIZE_LEVELS(sig);
 
 	first_bin = 0;
 	last_bin = levels + 1;
 
-	if (size != sizeof (uint64_t) * (levels + 2))
-		return (dt_set_errno(dtp, EDT_DMISMATCH));
+	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++;
@@ -653,8 +649,8 @@ dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
 }
 
 int
-dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
-    size_t size, uint64_t normal)
+dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, size_t size,
+		    uint64_t normal, uint64_t sig)
 {
 	const int64_t *data = addr;
 	int factor, lmag, hmag, steps, steps_factor, step, bin0;
@@ -662,20 +658,16 @@ dt_print_llquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
 	int cwidth = 16, pad = 0;
 	char *c;
 	uint64_t scale;
-	uint64_t arg;
 	long double total = 0;
 	char positives = 0, negatives = 0;
 
 	if (size < sizeof (uint64_t))
 		return (dt_set_errno(dtp, EDT_DMISMATCH));
 
-	arg = *data++;
-	size -= sizeof (uint64_t);
-
-	factor = DTRACE_LLQUANTIZE_FACTOR(arg);
-	lmag = DTRACE_LLQUANTIZE_LMAG(arg);
-	hmag = DTRACE_LLQUANTIZE_HMAG(arg);
-	steps = DTRACE_LLQUANTIZE_STEPS(arg);
+	factor = DTRACE_LLQUANTIZE_FACTOR(sig);
+	lmag = DTRACE_LLQUANTIZE_LMAG(sig);
+	hmag = DTRACE_LLQUANTIZE_HMAG(sig);
+	steps = DTRACE_LLQUANTIZE_STEPS(sig);
 	steps_factor = steps / factor;
 	bin0 = 1 + (hmag-lmag+1) * (steps-steps/factor);
 
@@ -1422,16 +1414,16 @@ dt_print_pcap(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
 
 #ifdef FIXME
 typedef struct dt_normal {
-	dtrace_aggvarid_t dtnd_id;
-	uint64_t dtnd_normal;
+	dtrace_aggid_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;
+	dt_normal_t		*normal = arg;
+	dtrace_aggdesc_t	*agg = aggdata->dtada_desc;
+	dtrace_aggid_t		id = normal->dtnd_id;
 
 	if (agg->dtagd_nrecs == 0)
 		return (DTRACE_AGGWALK_NEXT);
@@ -1455,18 +1447,18 @@ dt_normalize(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec)
 	 */
 	addr = base + rec->dtrd_offset;
 
-	if (rec->dtrd_size != sizeof (dtrace_aggvarid_t))
-		return (dt_set_errno(dtp, EDT_BADNORMAL));
+	if (rec->dtrd_size != sizeof(dtrace_aggid_t))
+		return dt_set_errno(dtp, EDT_BADNORMAL);
 
 	/* LINTED - alignment */
-	normal.dtnd_id = *((dtrace_aggvarid_t *)addr);
+	normal.dtnd_id = *((dtrace_aggid_t *)addr);
 	rec++;
 
 	if (rec->dtrd_action != DTRACEACT_LIBACT)
-		return (dt_set_errno(dtp, EDT_BADNORMAL));
+		return dt_set_errno(dtp, EDT_BADNORMAL);
 
 	if (rec->dtrd_arg != DT_ACT_NORMALIZE)
-		return (dt_set_errno(dtp, EDT_BADNORMAL));
+		return dt_set_errno(dtp, EDT_BADNORMAL);
 
 	addr = base + rec->dtrd_offset;
 
@@ -1487,55 +1479,55 @@ dt_normalize(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec)
 		normal.dtnd_normal = *((uint8_t *)addr);
 		break;
 	default:
-		return (dt_set_errno(dtp, EDT_BADNORMAL));
+		return dt_set_errno(dtp, EDT_BADNORMAL);
 	}
 
-	(void) dtrace_aggregate_walk(dtp, dt_normalize_agg, &normal);
+	dtrace_aggregate_walk(dtp, dt_normalize_agg, &normal);
 
-	return (0);
+	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);
+	dtrace_aggdesc_t	*agg = aggdata->dtada_desc;
+	dtrace_aggid_t		id = *((dtrace_aggid_t *)arg);
 
 	if (agg->dtagd_nrecs == 0)
-		return (DTRACE_AGGWALK_NEXT);
+		return DTRACE_AGGWALK_NEXT;
 
 	if (agg->dtagd_varid != id)
-		return (DTRACE_AGGWALK_NEXT);
+		return DTRACE_AGGWALK_NEXT;
 
-	return (DTRACE_AGGWALK_DENORMALIZE);
+	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);
+	dtrace_aggdesc_t	*agg = aggdata->dtada_desc;
+	dtrace_aggid_t		id = *((dtrace_aggid_t *)arg);
 
 	if (agg->dtagd_nrecs == 0)
-		return (DTRACE_AGGWALK_NEXT);
+		return DTRACE_AGGWALK_NEXT;
 
 	if (agg->dtagd_varid != id)
-		return (DTRACE_AGGWALK_NEXT);
+		return DTRACE_AGGWALK_NEXT;
 
-	return (DTRACE_AGGWALK_CLEAR);
+	return DTRACE_AGGWALK_CLEAR;
 }
 
 typedef struct dt_trunc {
-	dtrace_aggvarid_t dttd_id;
-	uint64_t dttd_remaining;
+	dtrace_aggid_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;
+	dt_trunc_t		*trunc = arg;
+	dtrace_aggdesc_t	*agg = aggdata->dtada_desc;
+	dtrace_aggid_t		id = trunc->dttd_id;
 
 	if (agg->dtagd_nrecs == 0)
 		return (DTRACE_AGGWALK_NEXT);
@@ -1565,18 +1557,18 @@ dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec)
 	 */
 	addr = base + rec->dtrd_offset;
 
-	if (rec->dtrd_size != sizeof (dtrace_aggvarid_t))
-		return (dt_set_errno(dtp, EDT_BADTRUNC));
+	if (rec->dtrd_size != sizeof(dtrace_aggid_t))
+		return dt_set_errno(dtp, EDT_BADTRUNC);
 
 	/* LINTED - alignment */
-	trunc.dttd_id = *((dtrace_aggvarid_t *)addr);
+	trunc.dttd_id = *((dtrace_aggid_t *)addr);
 	rec++;
 
 	if (rec->dtrd_action != DTRACEACT_LIBACT)
-		return (dt_set_errno(dtp, EDT_BADTRUNC));
+		return dt_set_errno(dtp, EDT_BADTRUNC);
 
 	if (rec->dtrd_arg != DT_ACT_TRUNC)
-		return (dt_set_errno(dtp, EDT_BADTRUNC));
+		return dt_set_errno(dtp, EDT_BADTRUNC);
 
 	addr = base + rec->dtrd_offset;
 
@@ -1597,68 +1589,67 @@ dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec)
 		remaining = *((int8_t *)addr);
 		break;
 	default:
-		return (dt_set_errno(dtp, EDT_BADNORMAL));
+		return dt_set_errno(dtp, EDT_BADNORMAL);
 	}
 
 	if (remaining < 0) {
 		func = dtrace_aggregate_walk_valsorted;
 		remaining = -remaining;
-	} else {
+	} else
 		func = dtrace_aggregate_walk_valrevsorted;
-	}
 
 	assert(remaining >= 0);
 	trunc.dttd_remaining = remaining;
 
-	(void) func(dtp, dt_trunc_agg, &trunc);
+	func(dtp, dt_trunc_agg, &trunc);
 
-	return (0);
+	return 0;
 }
 #endif
 
 static int
 dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
-    caddr_t addr, size_t size, uint64_t normal)
+	       caddr_t addr, size_t size, uint64_t normal, uint64_t sig)
 {
-	int err;
-	dtrace_actkind_t act = rec->dtrd_action;
+	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));
+		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));
+		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));
+		return dt_print_usym(dtp, fp, addr, act);
 
 	case DTRACEACT_UMOD:
-		return (dt_print_umod(dtp, fp, NULL, addr));
+		return dt_print_umod(dtp, fp, NULL, addr);
 
 	case DTRACEACT_SYM:
-		return (dt_print_sym(dtp, fp, NULL, addr));
+		return dt_print_sym(dtp, fp, NULL, addr);
 
 	case DTRACEACT_MOD:
-		return (dt_print_mod(dtp, fp, NULL, addr));
+		return dt_print_mod(dtp, fp, NULL, addr);
 
-	case DTRACEAGG_QUANTIZE:
-		return (dt_print_quantize(dtp, fp, addr, size, normal));
+	case DT_AGG_QUANTIZE:
+		return dt_print_quantize(dtp, fp, addr, size, normal);
 
-	case DTRACEAGG_LQUANTIZE:
-		return (dt_print_lquantize(dtp, fp, addr, size, normal));
+	case DT_AGG_LQUANTIZE:
+		return dt_print_lquantize(dtp, fp, addr, size, normal, sig);
 
-	case DTRACEAGG_LLQUANTIZE:
-		return (dt_print_llquantize(dtp, fp, addr, size, normal));
+	case DT_AGG_LLQUANTIZE:
+		return dt_print_llquantize(dtp, fp, addr, size, normal, sig);
 
-	case DTRACEAGG_AVG:
-		return (dt_print_average(dtp, fp, addr, size, normal));
+	case DT_AGG_AVG:
+		return dt_print_average(dtp, fp, addr, size, normal);
 
-	case DTRACEAGG_STDDEV:
-		return (dt_print_stddev(dtp, fp, addr, size, normal));
+	case DT_AGG_STDDEV:
+		return dt_print_stddev(dtp, fp, addr, size, normal);
 
 	default:
 		break;
@@ -1689,93 +1680,89 @@ dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
 		break;
 	}
 
-	return (err);
+	return err;
 }
 
-int
+static int
 dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
 {
-	int i, aggact = 0;
-	dtrace_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;
+	int			i, aggact = 0;
+	dtrace_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;
+	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).
+	 * data.
 	 */
-	for (i = 1; i < agg->dtagd_nrecs; i++) {
-		rec = &agg->dtagd_rec[i];
-		act = rec->dtrd_action;
+	for (i = 0; i < agg->dtagd_nrecs; i++) {
+		rec = &agg->dtagd_recs[i];
 		addr = aggdata->dtada_data + rec->dtrd_offset;
 		size = rec->dtrd_size;
 
-		if (DTRACEACT_ISAGG(act)) {
+		if (DTRACEACT_ISAGG(rec->dtrd_action)) {
 			aggact = i;
 			break;
 		}
 
-		if (dt_print_datum(dtp, fp, rec, addr, size, 1) < 0)
-			return (-1);
+		if (dt_print_datum(dtp, fp, rec, addr, size, 1, 0) < 0)
+			return -1;
 
 		if (dt_buffered_flush(dtp, NULL, rec, aggdata,
-		    DTRACE_BUFDATA_AGGKEY) < 0)
-			return (-1);
+				      DTRACE_BUFDATA_AGGKEY) < 0)
+			return -1;
 	}
 
-	assert(aggact != 0);
-
 	for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) {
-		uint64_t normal;
+		uint64_t	normal;
 
 		aggdata = aggsdata[i];
 		agg = aggdata->dtada_desc;
-		rec = &agg->dtagd_rec[aggact];
-		act = rec->dtrd_action;
+		rec = &agg->dtagd_recs[aggact];
 		addr = aggdata->dtada_data + rec->dtrd_offset;
-		size = rec->dtrd_size;
+		size = agg->dtagd_size;
 
-		assert(DTRACEACT_ISAGG(act));
+		assert(DTRACEACT_ISAGG(rec->dtrd_action));
 		normal = aggdata->dtada_normal;
 
-		if (dt_print_datum(dtp, fp, rec, addr, size, normal) < 0)
-			return (-1);
+		if (dt_print_datum(dtp, fp, rec, addr, size, normal,
+				   agg->dtagd_sig) < 0)
+			return -1;
 
 		if (dt_buffered_flush(dtp, NULL, rec, aggdata,
-		    DTRACE_BUFDATA_AGGVAL) < 0)
-			return (-1);
+				      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);
+		return -1;
 
 	if (dt_buffered_flush(dtp, NULL, NULL, aggdata,
-	    DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0)
-		return (-1);
+			      DTRACE_BUFDATA_AGGFORMAT |
+			      DTRACE_BUFDATA_AGGLAST) < 0)
+		return -1;
 
-	return (0);
+	return 0;
 }
 
 int
 dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg)
 {
-	dtrace_print_aggdata_t *pd = arg;
-	dtrace_aggdesc_t *agg = aggdata->dtada_desc;
-	dtrace_aggvarid_t aggvarid = pd->dtpa_id;
+	dtrace_print_aggdata_t	*pd = arg;
+	dtrace_aggdesc_t	*agg = aggdata->dtada_desc;
+	dtrace_aggid_t		aggvarid = pd->dtpa_id;
 
 	if (pd->dtpa_allunprint) {
 		if (agg->dtagd_flags & DTRACE_AGD_PRINTED)
-			return (0);
+			return 0;
 	} else {
 		/*
 		 * If we're not printing all unprinted aggregations, then the
@@ -1784,13 +1771,68 @@ dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg)
 		 * that we encounter.
 		 */
 		if (agg->dtagd_nrecs == 0)
-			return (0);
+			return 0;
 
 		if (aggvarid != agg->dtagd_varid)
-			return (0);
+			return 0;
+	}
+
+	return dt_print_aggs(&aggdata, 1, arg);
+}
+
+static int
+dt_printa(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
+	  const dtrace_probedata_t *data, const dtrace_recdesc_t *recs,
+	  uint_t nrecs, const void *buf, size_t len)
+{
+	dtrace_print_aggdata_t	pd;
+	dtrace_aggid_t		*aggvars;
+	int			i, naggvars = 0;
+
+	aggvars = alloca(nrecs * sizeof(dtrace_aggid_t));
+
+	/*
+	 * This might be a printa() with multiple aggregation variables.  We
+	 * need to scan forward through the records until we find a record that
+	 * does not belong to this printa() statement.
+	 */
+	for (i = 0; i < nrecs; i++) {
+		const dtrace_recdesc_t *nrec = &recs[i];
+
+		if (nrec->dtrd_arg != recs->dtrd_arg)
+			break;
+
+		if (nrec->dtrd_action != recs->dtrd_action)
+			return dt_set_errno(dtp, EDT_BADAGG);
+
+		aggvars[naggvars++] =
+		    /* LINTED - alignment */
+		    *((dtrace_aggid_t *)((caddr_t)buf + nrec->dtrd_offset));
+	}
+
+	if (naggvars == 0)
+		return dt_set_errno(dtp, EDT_BADAGG);
+
+	pd.dtpa_dtp = dtp;
+	pd.dtpa_fp = fp;
+	pd.dtpa_allunprint = 0;
+
+	if (naggvars == 1) {
+		pd.dtpa_id = aggvars[0];
+
+		if (dt_printf(dtp, fp, "\n") < 0 ||
+		    dtrace_aggregate_walk_sorted(dtp, dt_print_agg, &pd) < 0)
+			return -1;		/* errno is set for us */
+	} else {
+		pd.dtpa_id = 0;
+
+		if (dt_printf(dtp, fp, "\n") < 0 ||
+		    dtrace_aggregate_walk_joined(dtp, aggvars, naggvars,
+						 dt_print_aggs, &pd) < 0)
+			return -1;		/* errno is set for us */
 	}
 
-	return (dt_print_aggs(&aggdata, 1, arg));
+	return i;
 }
 
 int
@@ -2001,11 +2043,12 @@ dt_consume_one(dtrace_hdl_t *dtp, FILE *fp, char *buf,
 			case DTRACEACT_PRINTF:
 				func = dtrace_fprintf;
 				break;
-#ifdef FIXME
 			case DTRACEACT_PRINTA:
-				func = dtrace_fprinta;
+				if (rec->dtrd_format != NULL)
+					func = dtrace_fprinta;
+				else
+					func = dt_printa;
 				break;
-#endif
 			case DTRACEACT_SYSTEM:
 				func = dtrace_system;
 				break;
diff --git a/libdtrace/dt_ident.c b/libdtrace/dt_ident.c
index aa436057..eb59ad10 100644
--- a/libdtrace/dt_ident.c
+++ b/libdtrace/dt_ident.c
@@ -770,25 +770,21 @@ dt_idhash_peekid(dt_idhash_t *dhp)
 }
 
 uint_t
-dt_idhash_nextoff(dt_idhash_t *dhp, uint_t alignment, uint_t size)
+dt_idhash_datasize(const dt_idhash_t *dhp)
 {
-	uint_t	off = (dhp->dh_nextoff + (alignment - 1)) & ~(alignment - 1);
-
-	dhp->dh_nextoff = off + size;
-
-	return off;
+	return dhp->dh_nextoff;
 }
 
 ulong_t
 dt_idhash_size(const dt_idhash_t *dhp)
 {
-	return (dhp->dh_nelems);
+	return dhp->dh_nelems;
 }
 
 const char *
 dt_idhash_name(const dt_idhash_t *dhp)
 {
-	return (dhp->dh_name);
+	return dhp->dh_name;
 }
 
 dt_ident_t *
@@ -1030,8 +1026,12 @@ dt_ident_set_data(dt_ident_t *idp, void *data)
 void
 dt_ident_set_storage(dt_ident_t *idp, uint_t alignment, uint_t size)
 {
-	idp->di_offset = dt_idhash_nextoff(idp->di_hash, alignment, size);
+	dt_idhash_t	*dhp = idp->di_hash;
+
+	idp->di_offset = (dhp->dh_nextoff + (alignment - 1)) & ~(alignment - 1);
 	idp->di_size = size;
+
+	dhp->dh_nextoff = idp->di_offset + size;
 }
 
 void
diff --git a/libdtrace/dt_ident.h b/libdtrace/dt_ident.h
index 770d99e2..0e986b57 100644
--- a/libdtrace/dt_ident.h
+++ b/libdtrace/dt_ident.h
@@ -132,7 +132,7 @@ 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 uint_t dt_idhash_peekid(dt_idhash_t *);
-extern uint_t dt_idhash_nextoff(dt_idhash_t *, uint_t, uint_t);
+extern uint_t dt_idhash_datasize(const dt_idhash_t *);
 extern ulong_t dt_idhash_size(const dt_idhash_t *);
 extern const char *dt_idhash_name(const dt_idhash_t *);
 
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 0978aa01..a526bf7a 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -177,7 +177,7 @@ typedef struct dt_ahashent {
 	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 */
+	uint64_t dtahe_hval;			/* 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 */
@@ -190,7 +190,8 @@ typedef struct dt_ahash {
 } dt_ahash_t;
 
 typedef struct dt_aggregate {
-	dtrace_bufdesc_t dtat_buf; 	/* buf aggregation snapshot */
+	char **dtat_cpu_buf;		/* mmap()'d per-CPU agg buffers */
+	char *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 */
@@ -294,7 +295,7 @@ struct dtrace_hdl {
 	dtrace_datadesc_t **dt_ddesc; /* probe data 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 */
+	dtrace_aggdesc_t **dt_adesc; /* aggregation descriptions */
 	int dt_maxformat;	/* max format ID */
 	dt_aggregate_t dt_aggregate; /* aggregate */
 	struct dt_pebset *dt_pebset; /* perf event buffers set */
@@ -336,6 +337,7 @@ struct dtrace_hdl {
 	int dt_stdout_fd;	/* file descriptor for saved stdout */
 	int dt_poll_fd;		/* file descriptor for event polling */
 	int dt_stmap_fd;	/* file descriptor for the 'state' BPF map */
+	int dt_aggmap_fd;	/* file descriptor for the 'aggs' BPF map */
 	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 */
@@ -504,8 +506,13 @@ struct dtrace_hdl {
 /*
  * Aggregation functions.
  */
+#define	DT_AGG_BASE		DTRACEACT_AGGREGATION
+#define	DT_AGG(n)		(DT_AGG_BASE + (n))
+#define DT_AGG_IDX(n)		((n) - DT_AGG_BASE)
+#define DT_AGG_NUM		(DT_AGG_IDX(DT_AGG_HIGHEST))
+
 typedef enum dt_aggfid {
-	DT_AGG_AVG = 0,
+	DT_AGG_AVG = DT_AGG_BASE,
 	DT_AGG_COUNT,
 	DT_AGG_LLQUANTIZE,
 	DT_AGG_LQUANTIZE,
@@ -515,7 +522,7 @@ typedef enum dt_aggfid {
 	DT_AGG_STDDEV,
 	DT_AGG_SUM,
 
-	DT_AGG_NUM
+	DT_AGG_HIGHEST
 } dt_aggfid_t;
 
 /*
@@ -727,15 +734,16 @@ extern void dt_epid_destroy(dtrace_hdl_t *);
 typedef void (*dt_cg_gap_f)(dt_pcb_t *, int);
 extern uint32_t dt_rec_add(dtrace_hdl_t *, dt_cg_gap_f, dtrace_actkind_t,
 			   uint32_t, uint16_t, dt_pfargv_t *, uint64_t);
+extern int dt_aggid_add(dtrace_hdl_t *, const dt_ident_t *);
 extern int dt_aggid_lookup(dtrace_hdl_t *, dtrace_aggid_t, dtrace_aggdesc_t **);
 extern void dt_aggid_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_llquantize(dtrace_hdl_t *, FILE *,
-    const void *, size_t, uint64_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, uint64_t);
+extern int dt_print_llquantize(dtrace_hdl_t *, FILE *, const void *, size_t,
+			       uint64_t, uint64_t);
 extern int dt_print_agg(const dtrace_aggdata_t *, void *);
 
 extern int dt_handle(dtrace_hdl_t *, dtrace_probedata_t *);
diff --git a/libdtrace/dt_map.c b/libdtrace/dt_map.c
index 145b8b7a..0c9495d7 100644
--- a/libdtrace/dt_map.c
+++ b/libdtrace/dt_map.c
@@ -94,8 +94,8 @@ dt_datadesc_finalize(dtrace_hdl_t *dtp, dtrace_datadesc_t *ddp)
 dtrace_epid_t
 dt_epid_add(dtrace_hdl_t *dtp, dtrace_datadesc_t *ddp, dtrace_id_t prid)
 {
-	dtrace_id_t		max = dtp->dt_maxprobe;
-	dtrace_epid_t		epid;
+	dtrace_id_t	max = dtp->dt_maxprobe;
+	dtrace_epid_t	epid;
 
 	epid = dtp->dt_nextepid++;
 	if (epid >= max || dtp->dt_ddesc == NULL) {
@@ -236,123 +236,86 @@ dt_rec_add(dtrace_hdl_t *dtp, dt_cg_gap_f gapf, dtrace_actkind_t kind,
 	return off;
 }
 
-static int
-dt_aggid_add(dtrace_hdl_t *dtp, dtrace_aggid_t id)
+int
+dt_aggid_add(dtrace_hdl_t *dtp, const dt_ident_t *aid)
 {
-	dtrace_id_t max;
-	int rval;
+	dtrace_id_t		max;
+	dtrace_aggdesc_t	*agg;
+	dtrace_recdesc_t	*recs;
+	dtrace_aggid_t		id = aid->di_id;
+	dt_ident_t		*fid = aid->di_iarg;
+	int			i;
+	uint_t			off = 0;
 
-	while (id >= (max = dtp->dt_maxagg) || dtp->dt_aggdesc == NULL) {
-		dtrace_id_t new_max = max ? (max << 1) : 1;
-		size_t nsize = new_max * sizeof (void *);
-		dtrace_aggdesc_t **new_aggdesc;
+	while (id >= (max = dtp->dt_maxagg) || dtp->dt_adesc == NULL) {
+		dtrace_id_t		nmax = max ? (max << 1) : 1;
+		dtrace_aggdesc_t	**nadesc;
 
-		if ((new_aggdesc = malloc(nsize)) == NULL)
-			return (dt_set_errno(dtp, EDT_NOMEM));
+		nadesc = dt_calloc(dtp, nmax, sizeof(void *));
+		if (nadesc == NULL)
+			return dt_set_errno(dtp, EDT_NOMEM);
 
-		memset(new_aggdesc, 0, nsize);
+		if (dtp->dt_adesc != NULL) {
+			size_t	osize = max * sizeof(void *);
 
-		if (dtp->dt_aggdesc != NULL) {
-			memcpy(new_aggdesc, dtp->dt_aggdesc,
-			    max * sizeof (void *));
-			free(dtp->dt_aggdesc);
+			memcpy(nadesc, dtp->dt_adesc, osize);
+			free(dtp->dt_adesc);
 		}
 
-		dtp->dt_aggdesc = new_aggdesc;
-		dtp->dt_maxagg = new_max;
+		dtp->dt_adesc = nadesc;
+		dtp->dt_maxagg = nmax;
 	}
 
-	if (dtp->dt_aggdesc[id] == NULL) {
-		dtrace_aggdesc_t *agg, *nagg;
-
-		if ((agg = malloc(sizeof (dtrace_aggdesc_t))) == NULL)
-			return (dt_set_errno(dtp, EDT_NOMEM));
-
-		memset(agg, 0, sizeof (dtrace_aggdesc_t));
-		agg->dtagd_id = id;
-		agg->dtagd_nrecs = 1;
-
-		if (dt_ioctl(dtp, DTRACEIOC_AGGDESC, agg) == -1) {
-			rval = dt_set_errno(dtp, errno);
-			free(agg);
-			return (rval);
-		}
-
-		if (DTRACE_SIZEOF_AGGDESC(agg) != sizeof (*agg)) {
-			/*
-			 * There must be more than one action.  Allocate the
-			 * appropriate amount of space and try again.
-			 */
-			if ((nagg = malloc(DTRACE_SIZEOF_AGGDESC(agg))) != NULL)
-				memcpy(nagg, agg, sizeof (*agg));
-
-			free(agg);
-
-			if ((agg = nagg) == NULL)
-				return (dt_set_errno(dtp, EDT_NOMEM));
-
-			rval = dt_ioctl(dtp, DTRACEIOC_AGGDESC, agg);
+	/* Already added? */
+	if (dtp->dt_adesc[id] != NULL)
+		return 0;
+
+	agg = dt_zalloc(dtp, sizeof(dtrace_aggdesc_t));
+	if (agg == NULL)
+		return dt_set_errno(dtp, EDT_NOMEM);
+
+	agg->dtagd_id = id;
+	agg->dtagd_name = aid->di_name;
+	agg->dtagd_sig = ((dt_idsig_t *)aid->di_data)->dis_auxinfo;
+	agg->dtagd_varid = aid->di_id;
+	agg->dtagd_size = aid->di_size / 2;
+	agg->dtagd_nrecs = agg->dtagd_size / sizeof(uint64_t);
+
+	recs = dt_calloc(dtp, agg->dtagd_nrecs, sizeof(dtrace_recdesc_t));
+	if (recs == NULL) {
+		dt_free(dtp, agg);
+		return dt_set_errno(dtp, EDT_NOMEM);
+	}
 
-			if (rval == -1) {
-				rval = dt_set_errno(dtp, errno);
-				free(agg);
-				return (rval);
-			}
-		}
+	agg->dtagd_recs = recs;
 
-		/*
-		 * If we have a uarg, it's a pointer to the compiler-generated
-		 * statement; we'll use this value to get the name and
-		 * compiler-generated variable ID for the aggregation.  If
-		 * we're grabbing an anonymous enabling, this pointer value
-		 * is obviously meaningless -- and in this case, we can't
-		 * provide the compiler-generated aggregation information.
-		 */
-		if (dtp->dt_options[DTRACEOPT_GRABANON] == DTRACEOPT_UNSET &&
-		    agg->dtagd_rec[0].dtrd_uarg != 0) {
-			dtrace_stmtdesc_t *sdp;
-			dt_ident_t *aid;
-
-			sdp = (dtrace_stmtdesc_t *)(uintptr_t)
-			    agg->dtagd_rec[0].dtrd_uarg;
-			aid = sdp->dtsd_aggdata;
-			agg->dtagd_name = aid->di_name;
-			agg->dtagd_varid = aid->di_id;
-		} else {
-			agg->dtagd_varid = DTRACE_AGGVARIDNONE;
-		}
+	for (i = 0; i < agg->dtagd_nrecs; i++) {
+		dtrace_recdesc_t	*rec = &recs[i];
 
-#if 0
-		if ((epid = agg->dtagd_epid) >= dtp->dt_maxprobe ||
-		    dtp->dt_pdesc[epid] == NULL) {
-			if ((rval = dt_epid_add(dtp, epid)) != 0) {
-				free(agg);
-				return (rval);
-			}
-		}
-#endif
+		rec->dtrd_action = fid->di_id;
+		rec->dtrd_size = sizeof(uint64_t);
+		rec->dtrd_offset = off;
+		rec->dtrd_alignment = sizeof(uint64_t);
+		rec->dtrd_format = NULL;
+		rec->dtrd_arg = 1;
 
-		dtp->dt_aggdesc[id] = agg;
+		off += sizeof(uint64_t);
 	}
 
-	return (0);
+	dtp->dt_adesc[id] = agg;
+
+	return 0;
 }
 
 int
 dt_aggid_lookup(dtrace_hdl_t *dtp, dtrace_aggid_t aggid, dtrace_aggdesc_t **adp)
 {
-	int rval;
-
-	if (aggid >= dtp->dt_maxagg || dtp->dt_aggdesc[aggid] == NULL) {
-		if ((rval = dt_aggid_add(dtp, aggid)) != 0)
-			return (rval);
-	}
+	if (aggid >= dtp->dt_maxagg || dtp->dt_adesc[aggid] == NULL)
+		return -1;
 
-	assert(aggid < dtp->dt_maxagg);
-	assert(dtp->dt_aggdesc[aggid] != NULL);
-	*adp = dtp->dt_aggdesc[aggid];
+	*adp = dtp->dt_adesc[aggid];
 
-	return (0);
+	return 0;
 }
 
 void
@@ -360,18 +323,20 @@ dt_aggid_destroy(dtrace_hdl_t *dtp)
 {
 	size_t i;
 
-	assert((dtp->dt_aggdesc != NULL && dtp->dt_maxagg != 0) ||
-	    (dtp->dt_aggdesc == NULL && dtp->dt_maxagg == 0));
+	assert((dtp->dt_adesc != NULL && dtp->dt_maxagg != 0) ||
+	       (dtp->dt_adesc == NULL && dtp->dt_maxagg == 0));
 
-	if (dtp->dt_aggdesc == NULL)
+	if (dtp->dt_adesc == NULL)
 		return;
 
 	for (i = 0; i < dtp->dt_maxagg; i++) {
-		if (dtp->dt_aggdesc[i] != NULL)
-			free(dtp->dt_aggdesc[i]);
+		if (dtp->dt_adesc[i] != NULL) {
+			dt_free(dtp, dtp->dt_adesc[i]->dtagd_recs);
+			dt_free(dtp, dtp->dt_adesc[i]);
+		}
 	}
 
-	free(dtp->dt_aggdesc);
-	dtp->dt_aggdesc = NULL;
+	dt_free(dtp, dtp->dt_adesc);
+	dtp->dt_adesc = NULL;
 	dtp->dt_maxagg = 0;
 }
diff --git a/libdtrace/dt_printf.c b/libdtrace/dt_printf.c
index 37e21141..65915371 100644
--- a/libdtrace/dt_printf.c
+++ b/libdtrace/dt_printf.c
@@ -205,7 +205,8 @@ pfcheck_type(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
 /*ARGSUSED*/
 static int
 pfprint_sint(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t unormal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t unormal, uint64_t sig)
 {
 	int64_t normal = (int64_t)unormal;
 	int32_t n = (int32_t)normal;
@@ -231,7 +232,8 @@ pfprint_sint(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_uint(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
 	uint32_t n = (uint32_t)normal;
 
@@ -255,19 +257,22 @@ pfprint_uint(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 
 static int
 pfprint_dint(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
 	if (pfd->pfd_flags & DT_PFCONV_SIGNED)
-		return (pfprint_sint(dtp, fp, format, pfd, addr, size, normal));
+		return pfprint_sint(dtp, fp, format, pfd, addr, size, normal,
+				    sig);
 	else
-		return (pfprint_uint(dtp, fp, format, pfd, addr, size, normal));
+		return pfprint_uint(dtp, fp, format, pfd, addr, size, normal,
+				    sig);
 }
 
 /*ARGSUSED*/
 static int
 pfprint_fp(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
-{
+	   const dt_pfargd_t *pfd, const void *addr, size_t size,
+	   uint64_t normal, uint64_t sig) {
 	double n = (double)normal;
 	long double ldn = (long double)normal;
 
@@ -289,7 +294,8 @@ pfprint_fp(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_addr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
 	char *s;
 	int n, len = 256;
@@ -317,23 +323,26 @@ pfprint_addr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	    const dt_pfargd_t *pfd, const void *addr, size_t size,
+	    uint64_t normal, uint64_t sig)
 {
-	return (dt_print_mod(dtp, fp, format, (caddr_t)addr));
+	return dt_print_mod(dtp, fp, format, (caddr_t)addr);
 }
 
 /*ARGSUSED*/
 static int
 pfprint_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
-	return (dt_print_umod(dtp, fp, format, (caddr_t)addr));
+	return dt_print_umod(dtp, fp, format, (caddr_t)addr);
 }
 
 /*ARGSUSED*/
 static int
 pfprint_uaddr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	      const dt_pfargd_t *pfd, const void *addr, size_t size,
+	      uint64_t normal, uint64_t sig)
 {
 	char *s;
 	int n, len = 256;
@@ -370,7 +379,8 @@ pfprint_uaddr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *vaddr, size_t size, uint64_t normal)
+	      const dt_pfargd_t *pfd, const void *vaddr, size_t size,
+	      uint64_t normal, uint64_t sig)
 {
 	int width;
 	dtrace_optval_t saved = dtp->dt_options[DTRACEOPT_STACKINDENT];
@@ -421,7 +431,8 @@ pfprint_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_time(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
 	char src[32], buf[32], *dst = buf;
 	hrtime_t time = *((uint64_t *)addr);
@@ -459,7 +470,8 @@ pfprint_time(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_time822(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	        const dt_pfargd_t *pfd, const void *addr, size_t size,
+		uint64_t normal, uint64_t sig)
 {
 	hrtime_t time = *((uint64_t *)addr);
 	time_t sec = (time_t)(time / NANOSEC);
@@ -474,7 +486,8 @@ pfprint_time822(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_cstr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
 	char *s = alloca(size + 1);
 
@@ -486,7 +499,8 @@ pfprint_cstr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_wstr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
 	wchar_t *ws = alloca(size + sizeof (wchar_t));
 
@@ -498,7 +512,8 @@ pfprint_wstr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 /*ARGSUSED*/
 static int
 pfprint_estr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
 	char *s;
 	int n;
@@ -513,7 +528,8 @@ pfprint_estr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 
 static int
 pfprint_echr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	     const dt_pfargd_t *pfd, const void *addr, size_t size,
+	     uint64_t normal, uint64_t sig)
 {
 	char c;
 
@@ -528,16 +544,17 @@ pfprint_echr(dtrace_hdl_t *dtp, FILE *fp, const char *format,
 		c = *(int32_t *)addr;
 		break;
 	default:
-		return (dt_set_errno(dtp, EDT_DMISMATCH));
+		return dt_set_errno(dtp, EDT_DMISMATCH);
 	}
 
-	return (pfprint_estr(dtp, fp, format, pfd, &c, 1, normal));
+	return pfprint_estr(dtp, fp, format, pfd, &c, 1, normal, sig);
 }
 
 /*ARGSUSED*/
 static int
 pfprint_pct(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	    const dt_pfargd_t *pfd, const void *addr, size_t size,
+	    uint64_t normal, uint64_t sig)
 {
 	return (dt_printf(dtp, fp, "%%"));
 }
@@ -1192,52 +1209,56 @@ dt_printf_getint(dtrace_hdl_t *dtp, const dtrace_recdesc_t *recp,
 /*ARGSUSED*/
 static int
 pfprint_average(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+		const dt_pfargd_t *pfd, const void *addr, size_t size,
+		uint64_t normal, uint64_t sig)
 {
 	const uint64_t *data = addr;
 
 	if (size != sizeof (uint64_t) * 2)
-		return (dt_set_errno(dtp, EDT_DMISMATCH));
+		return dt_set_errno(dtp, EDT_DMISMATCH);
 
-	return (dt_printf(dtp, fp, format,
-	    data[0] ? data[1] / normal / data[0] : 0));
+	return dt_printf(dtp, fp, format,
+			 data[0] ? data[1] / normal / data[0] : 0);
 }
 
 /*ARGSUSED*/
 static int
 pfprint_stddev(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+	       const dt_pfargd_t *pfd, const void *addr, size_t size,
+	       uint64_t normal, uint64_t sig)
 {
 	const uint64_t *data = addr;
 
 	if (size != sizeof (uint64_t) * 4)
-		return (dt_set_errno(dtp, EDT_DMISMATCH));
+		return dt_set_errno(dtp, EDT_DMISMATCH);
 
-	return (dt_printf(dtp, fp, format,
-	    dt_stddev((uint64_t *)data, normal)));
+	return dt_printf(dtp, fp, format, dt_stddev((uint64_t *)data, normal));
 }
 
 /*ARGSUSED*/
 static int
 pfprint_quantize(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+		 const dt_pfargd_t *pfd, const void *addr, size_t size,
+		 uint64_t normal, uint64_t sig)
 {
-	return (dt_print_quantize(dtp, fp, addr, size, normal));
+	return dt_print_quantize(dtp, fp, addr, size, normal);
 }
 
 /*ARGSUSED*/
 static int
 pfprint_lquantize(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+		  const dt_pfargd_t *pfd, const void *addr, size_t size,
+		  uint64_t normal, uint64_t sig)
 {
-	return (dt_print_lquantize(dtp, fp, addr, size, normal));
+	return dt_print_lquantize(dtp, fp, addr, size, normal, sig);
 }
 
 static int
 pfprint_llquantize(dtrace_hdl_t *dtp, FILE *fp, const char *format,
-    const dt_pfargd_t *pfd, const void *addr, size_t size, uint64_t normal)
+		   const dt_pfargd_t *pfd, const void *addr, size_t size,
+		   uint64_t normal, uint64_t sig)
 {
-	return (dt_print_llquantize(dtp, fp, addr, size, normal));
+	return dt_print_llquantize(dtp, fp, addr, size, normal, sig);
 }
 
 static int
@@ -1252,7 +1273,7 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv,
 	caddr_t lim = (caddr_t)buf + len, limit;
 	char format[64] = "%";
 	int i, aggrec = 0, curagg = -1;
-	uint64_t normal;
+	uint64_t normal, sig;
 
 	/*
 	 * If we are formatting an aggregation, set 'aggrec' to the index of
@@ -1326,23 +1347,22 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv,
 		 * with no data record and continue; it consumes no record.
 		 */
 		if (pfc->pfc_print == &pfprint_pct) {
-			if (pfc->pfc_print(dtp, fp, NULL, pfd, NULL, 0, 1) >= 0)
+			if (pfc->pfc_print(dtp, fp, NULL, pfd, NULL, 0, 1, 0) >= 0)
 				continue;
-			return (-1); /* errno is set for us */
+			return -1;		/* errno is set for us */
 		}
 
 		if (pfd->pfd_flags & DT_PFCONV_DYNWIDTH) {
 			if (dt_printf_getint(dtp, recp++, nrecs--, buf,
-			    len, &width) == -1)
-				return (-1); /* errno is set for us */
+					     len, &width) == -1)
+				return -1;	/* errno is set for us */
 			pfd->pfd_dynwidth = width;
-		} else {
+		} else
 			pfd->pfd_dynwidth = 0;
-		}
 
-		if ((pfd->pfd_flags & DT_PFCONV_DYNPREC) && dt_printf_getint(
-		    dtp, recp++, nrecs--, buf, len, &prec) == -1)
-			return (-1); /* errno is set for us */
+		if ((pfd->pfd_flags & DT_PFCONV_DYNPREC) &&
+		    dt_printf_getint(dtp, recp++, nrecs--, buf, len, &prec) == -1)
+			return -1;		/* errno is set for us */
 
 		if (pfd->pfd_flags & DT_PFCONV_AGG) {
 			/*
@@ -1368,10 +1388,11 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv,
 			if (curagg < naggvars - 1)
 				curagg++;
 
-			rec = &agg->dtagd_rec[aggrec];
+			rec = &agg->dtagd_recs[aggrec];
 			addr = aggdata->dtada_data + rec->dtrd_offset;
 			limit = addr + aggdata->dtada_size;
 			normal = aggdata->dtada_normal;
+			sig = agg->dtagd_sig;
 			flags = DTRACE_BUFDATA_AGGVAL;
 		} else {
 			if (nrecs == 0)
@@ -1395,6 +1416,7 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv,
 			addr = (caddr_t)buf + rec->dtrd_offset;
 			limit = lim;
 			normal = 1;
+			sig = 0;
 		}
 
 		size = rec->dtrd_size;
@@ -1413,19 +1435,19 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv,
 		}
 
 		switch (rec->dtrd_action) {
-		case DTRACEAGG_AVG:
+		case DT_AGG_AVG:
 			func = pfprint_average;
 			break;
-		case DTRACEAGG_STDDEV:
+		case DT_AGG_STDDEV:
 			func = pfprint_stddev;
 			break;
-		case DTRACEAGG_QUANTIZE:
+		case DT_AGG_QUANTIZE:
 			func = pfprint_quantize;
 			break;
-		case DTRACEAGG_LQUANTIZE:
+		case DT_AGG_LQUANTIZE:
 			func = pfprint_lquantize;
 			break;
-		case DTRACEAGG_LLQUANTIZE:
+		case DT_AGG_LLQUANTIZE:
 			func = pfprint_llquantize;
 			break;
 		case DTRACEACT_MOD:
@@ -1472,7 +1494,7 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv,
 		(void) strcpy(f, pfd->pfd_fmt);
 		pfd->pfd_rec = rec;
 
-		if (func(dtp, fp, format, pfd, addr, size, normal) < 0)
+		if (func(dtp, fp, format, pfd, addr, size, normal, sig) < 0)
 			return (-1); /* errno is set for us */
 
 		if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) {
@@ -1794,19 +1816,17 @@ dtrace_printf_format(dtrace_hdl_t *dtp, void *fmtdata, char *s, size_t len)
 static int
 dt_fprinta(const dtrace_aggdata_t *adp, void *arg)
 {
-	const dtrace_aggdesc_t *agg = adp->dtada_desc;
-	const dtrace_recdesc_t *recp = &agg->dtagd_rec[0];
-	uint_t nrecs = agg->dtagd_nrecs;
-	dt_pfwalk_t *pfw = arg;
-	dtrace_hdl_t *dtp = pfw->pfw_argv->pfv_dtp;
-	int id;
-
-	if (dt_printf_getint(dtp, recp++, nrecs--,
-	    adp->dtada_data, adp->dtada_size, &id) != 0 || pfw->pfw_aid != id)
-		return (0); /* no aggregation id or id does not match */
-
-	if (dt_printf_format(dtp, pfw->pfw_fp, pfw->pfw_argv,
-	    recp, nrecs, adp->dtada_data, adp->dtada_size, &adp, 1) == -1)
+	const dtrace_aggdesc_t	*agg = adp->dtada_desc;
+	const dtrace_recdesc_t	*rec = agg->dtagd_recs;
+	uint_t			nrecs = agg->dtagd_nrecs;
+	dt_pfwalk_t		*pfw = arg;
+	dtrace_hdl_t		*dtp = pfw->pfw_argv->pfv_dtp;
+
+	if (pfw->pfw_aid != agg->dtagd_id)
+		return 0;	/* id does not match */
+
+	if (dt_printf_format(dtp, pfw->pfw_fp, pfw->pfw_argv, rec, nrecs,
+			     adp->dtada_data, adp->dtada_size, &adp, 1) == -1)
 		return (pfw->pfw_err = dtp->dt_errno);
 
 	/*
@@ -1815,23 +1835,23 @@ dt_fprinta(const dtrace_aggdata_t *adp, void *arg)
 	 */
 	((dtrace_aggdesc_t *)agg)->dtagd_flags |= DTRACE_AGD_PRINTED;
 
-	return (0);
+	return 0;
 }
 
 static int
 dt_fprintas(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
 {
-	const dtrace_aggdata_t *aggdata = aggsdata[0];
-	const dtrace_aggdesc_t *agg = aggdata->dtada_desc;
-	const dtrace_recdesc_t *rec = &agg->dtagd_rec[1];
-	uint_t nrecs = agg->dtagd_nrecs - 1;
-	dt_pfwalk_t *pfw = arg;
-	dtrace_hdl_t *dtp = pfw->pfw_argv->pfv_dtp;
-	int i;
-
-	if (dt_printf_format(dtp, pfw->pfw_fp, pfw->pfw_argv,
-	    rec, nrecs, aggdata->dtada_data, aggdata->dtada_size,
-	    aggsdata, naggvars) == -1)
+	const dtrace_aggdata_t	*aggdata = aggsdata[0];
+	const dtrace_aggdesc_t	*agg = aggdata->dtada_desc;
+	const dtrace_recdesc_t	*rec = agg->dtagd_recs;
+	uint_t			nrecs = agg->dtagd_nrecs;
+	dt_pfwalk_t		*pfw = arg;
+	dtrace_hdl_t		*dtp = pfw->pfw_argv->pfv_dtp;
+	int			i;
+
+	if (dt_printf_format(dtp, pfw->pfw_fp, pfw->pfw_argv, rec, nrecs,
+			     aggdata->dtada_data, aggdata->dtada_size,
+			     aggsdata, naggvars) == -1)
 		return (pfw->pfw_err = dtp->dt_errno);
 
 	/*
@@ -1843,41 +1863,41 @@ dt_fprintas(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
 		((dtrace_aggdesc_t *)agg)->dtagd_flags |= DTRACE_AGD_PRINTED;
 	}
 
-	return (0);
+	return 0;
 }
-/*ARGSUSED*/
+
 int
 dtrace_fprinta(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
-    const dtrace_probedata_t *data, const dtrace_recdesc_t *recs,
-    uint_t nrecs, const void *buf, size_t len)
+	       const dtrace_probedata_t *data, const dtrace_recdesc_t *recs,
+	       uint_t nrecs, const void *buf, size_t len)
 {
-	dt_pfwalk_t pfw;
-	int i, naggvars = 0;
-	dtrace_aggvarid_t *aggvars;
+	dt_pfwalk_t	pfw;
+	int		i, naggvars = 0;
+	dtrace_aggid_t	*aggvars;
 
-	aggvars = alloca(nrecs * sizeof (dtrace_aggvarid_t));
+	aggvars = alloca(nrecs * sizeof(dtrace_aggid_t));
 
 	/*
 	 * 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.
+	 * need to scan forward through the records until we find a record that
+	 * does not belong to this printa() statement.
 	 */
 	for (i = 0; i < nrecs; i++) {
 		const dtrace_recdesc_t *nrec = &recs[i];
 
-		if (nrec->dtrd_uarg != recs->dtrd_uarg)
+		if (nrec->dtrd_arg != recs->dtrd_arg)
 			break;
 
 		if (nrec->dtrd_action != recs->dtrd_action)
-			return (dt_set_errno(dtp, EDT_BADAGG));
+			return dt_set_errno(dtp, EDT_BADAGG);
 
 		aggvars[naggvars++] =
 		    /* LINTED - alignment */
-		    *((dtrace_aggvarid_t *)((caddr_t)buf + nrec->dtrd_offset));
+		    *((dtrace_aggid_t *)((caddr_t)buf + nrec->dtrd_offset));
 	}
 
 	if (naggvars == 0)
-		return (dt_set_errno(dtp, EDT_BADAGG));
+		return dt_set_errno(dtp, EDT_BADAGG);
 
 	pfw.pfw_argv = fmtdata;
 	pfw.pfw_fp = fp;
@@ -1886,14 +1906,15 @@ dtrace_fprinta(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata,
 	if (naggvars == 1) {
 		pfw.pfw_aid = aggvars[0];
 
-		if (dtrace_aggregate_walk_sorted(dtp,
-		    dt_fprinta, &pfw) == -1 || pfw.pfw_err != 0)
-			return (-1); /* errno is set for us */
+		if (dtrace_aggregate_walk_sorted(dtp, dt_fprinta, &pfw) == -1 ||
+		    pfw.pfw_err != 0)
+			return -1; /* errno is set for us */
 	} else {
 		if (dtrace_aggregate_walk_joined(dtp, aggvars, naggvars,
-		    dt_fprintas, &pfw) == -1 || pfw.pfw_err != 0)
-			return (-1); /* errno is set for us */
+						 dt_fprintas, &pfw) == -1 ||
+		    pfw.pfw_err != 0)
+			return -1; /* errno is set for us */
 	}
 
-	return (i);
+	return i;
 }
diff --git a/libdtrace/dt_printf.h b/libdtrace/dt_printf.h
index 9fbfa30c..c274066f 100644
--- a/libdtrace/dt_printf.h
+++ b/libdtrace/dt_printf.h
@@ -24,10 +24,12 @@ struct dt_pfconv;
 struct dt_pfargv;
 struct dt_pfargd;
 
-typedef int dt_pfcheck_f(struct dt_pfargv *,
-    struct dt_pfargd *, struct dt_node *);
+typedef int dt_pfcheck_f(struct dt_pfargv *, struct dt_pfargd *,
+			 struct dt_node *);
+
 typedef int dt_pfprint_f(dtrace_hdl_t *, FILE *, const char *,
-    const struct dt_pfargd *, const void *, size_t, uint64_t);
+			 const struct dt_pfargd *, const void *, size_t,
+			 uint64_t, uint64_t);
 
 typedef struct dt_pfconv {
 	const char *pfc_name;		/* string name of input conversion */
diff --git a/libdtrace/dt_work.c b/libdtrace/dt_work.c
index 51da120e..9b5fa5aa 100644
--- a/libdtrace/dt_work.c
+++ b/libdtrace/dt_work.c
@@ -210,11 +210,9 @@ dtrace_go(dtrace_hdl_t *dtp, uint_t cflags)
 #if 0
 	if (dt_options_load(dtp) == -1)
 		return dt_set_errno(dtp, errno);
+#endif
 
 	return dt_aggregate_go(dtp);
-#else
-	return 0;
-#endif
 }
 
 int
diff --git a/libdtrace/dtrace.h b/libdtrace/dtrace.h
index 15836b9e..68213eb5 100644
--- a/libdtrace/dtrace.h
+++ b/libdtrace/dtrace.h
@@ -360,10 +360,8 @@ extern int dtrace_handle_setopt(dtrace_hdl_t *dtp,
 #define	DTRACE_AGGWALK_REMOVE		5	/* remove this element */
 
 struct dtrace_aggdata {
-	dtrace_hdl_t *dtada_handle;		/* handle to DTrace library */
+	dtrace_hdl_t *dtada_hdl;		/* handle to DTrace library */
 	dtrace_aggdesc_t *dtada_desc;		/* aggregation description */
-	dtrace_datadesc_t *dtada_ddesc;		/* probe data description */
-	dtrace_probedesc_t *dtada_pdesc;	/* probe description */
 	caddr_t dtada_data;			/* pointer to raw data */
 	uint64_t dtada_normal;			/* the normal -- 1 for denorm */
 	size_t dtada_size;			/* total size of the data */
@@ -374,7 +372,7 @@ struct dtrace_aggdata {
 
 typedef struct dtrace_print_aggdata {
 	dtrace_hdl_t *dtpa_dtp;			/* handle to DTrace library */
-	dtrace_aggvarid_t dtpa_id;		/* aggregation variable */
+	dtrace_aggid_t dtpa_id;			/* aggregation variable */
 	FILE *dtpa_fp;				/* file pointer */
 	int dtpa_allunprint;			/* print only unprinted aggs */
 } dtrace_print_aggdata_t;
@@ -394,7 +392,7 @@ extern int dtrace_aggregate_walk(dtrace_hdl_t *dtp, dtrace_aggregate_f *func,
     void *arg);
 
 extern int dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp,
-    dtrace_aggvarid_t *aggvars, int naggvars,
+    dtrace_aggid_t *aggvars, int naggvars,
     dtrace_aggregate_walk_joined_f *func, void *arg);
 
 extern int dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp,
diff --git a/test/unittest/actions/printa/err.D_PRINTA_AGGARG.fmt_int_arg.d b/test/unittest/actions/printa/err.D_PRINTA_AGGARG.fmt_int_arg.d
new file mode 100644
index 00000000..f665a96b
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PRINTA_AGGARG.fmt_int_arg.d
@@ -0,0 +1,18 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: printa() requires an aggregation argument after the format string
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	printa("%d", 1);
+	exit(0);
+}
diff --git a/test/unittest/actions/printa/err.D_PRINTA_AGGARG.fmt_mixed_args.d b/test/unittest/actions/printa/err.D_PRINTA_AGGARG.fmt_mixed_args.d
new file mode 100644
index 00000000..fa514d9b
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PRINTA_AGGARG.fmt_mixed_args.d
@@ -0,0 +1,22 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: printa() accepts only aggregation arguments after the format
+ *	string
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	@a = count();
+	@b = max(1);
+	@c = min(1);
+	printa("%@d", @a, @b, @c, 1);
+	exit(0);
+}
diff --git a/test/unittest/actions/printa/err.D_PRINTA_AGGARG.int_arg.d b/test/unittest/actions/printa/err.D_PRINTA_AGGARG.int_arg.d
new file mode 100644
index 00000000..a3e09e85
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PRINTA_AGGARG.int_arg.d
@@ -0,0 +1,18 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: printa() requires an aggregation argument
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	printa(1);
+	exit(0);
+}
diff --git a/test/unittest/actions/printa/err.D_PRINTA_AGGARG.mixed_args.d b/test/unittest/actions/printa/err.D_PRINTA_AGGARG.mixed_args.d
new file mode 100644
index 00000000..72bbd5e9
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PRINTA_AGGARG.mixed_args.d
@@ -0,0 +1,22 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: printa() accepts only aggregation arguments when there is no
+ *	format string
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	@a = count();
+	@b = max(1);
+	@c = min(1);
+	printa(@a, @b, @c, 1);
+	exit(0);
+}
diff --git a/test/unittest/actions/printa/err.D_PRINTA_AGGKEY.d b/test/unittest/actions/printa/err.D_PRINTA_AGGKEY.d
new file mode 100644
index 00000000..c6014316
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PRINTA_AGGKEY.d
@@ -0,0 +1,21 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+/* @@xfail: dtv2 */
+/*
+ * ASSERTION: printa() requires aggregation arguments to have matching key
+ *	signatures
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	@a = count();
+	@b[1] = count();
+	printa(@a, @b);
+	exit(0);
+}
diff --git a/test/unittest/actions/printa/err.D_PRINTA_AGGPROTO.d b/test/unittest/actions/printa/err.D_PRINTA_AGGPROTO.d
new file mode 100644
index 00000000..d327a9c5
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PRINTA_AGGPROTO.d
@@ -0,0 +1,21 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+/* @@xfail: dtv2 */
+/*
+ * ASSERTION: printa() requires aggregation arguments to have matching key
+ *	signatures (matching key component types)
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	@a[1, "", 2] = count();
+	@b[1, 2, ""] = count();
+	printa(@a, @b);
+	exit(0);
+}
diff --git a/test/unittest/actions/printa/err.D_PRINTA_PROTO.fmt_no_arg.d b/test/unittest/actions/printa/err.D_PRINTA_PROTO.fmt_no_arg.d
new file mode 100644
index 00000000..0f81080e
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PRINTA_PROTO.fmt_no_arg.d
@@ -0,0 +1,18 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: printa() requires an aggregation argument after the format string
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	printa("%d");
+	exit(0);
+}
diff --git a/test/unittest/actions/printa/err.D_PRINTF_ARG_PROTO.key_no_val.d b/test/unittest/actions/printa/err.D_PRINTF_ARG_PROTO.key_no_val.d
new file mode 100644
index 00000000..9f962de6
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PRINTF_ARG_PROTO.key_no_val.d
@@ -0,0 +1,20 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: printa() does not allow un-indexed aggregation arguments if a key
+ *	conversion is specified in the format string
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	@ = count();
+	printa("%d", @);
+	exit(0);
+}
diff --git a/test/unittest/actions/printa/err.D_PROTO_LEN.no_arg.d b/test/unittest/actions/printa/err.D_PROTO_LEN.no_arg.d
new file mode 100644
index 00000000..48150bc2
--- /dev/null
+++ b/test/unittest/actions/printa/err.D_PROTO_LEN.no_arg.d
@@ -0,0 +1,19 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: printa() requires an aggregation argument when there is no format
+ *	string
+ *
+ * SECTION: Output Formatting/printa()
+ */
+
+BEGIN
+{
+	printa();
+	exit(0);
+}
diff --git a/test/unittest/aggs/err.D_AGG_REDEF.idx_diff_funcs.d b/test/unittest/aggs/err.D_AGG_REDEF.idx_diff_funcs.d
new file mode 100644
index 00000000..5dafe12b
--- /dev/null
+++ b/test/unittest/aggs/err.D_AGG_REDEF.idx_diff_funcs.d
@@ -0,0 +1,19 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+/*
+ * ASSERTION: Elements in an indexed aggregation must have same definitions.
+ *
+ * SECTION: Aggregations/Aggregations
+ */
+
+BEGIN
+{
+	@[1] = count();
+	@[2] = sum(5);
+	exit(0);
+}
diff --git a/test/unittest/aggs/err.D_LLQUANT_STEPVAL.lt_factor.d b/test/unittest/aggs/err.D_LLQUANT_STEPVAL.steps_divides_factor.d
similarity index 100%
rename from test/unittest/aggs/err.D_LLQUANT_STEPVAL.lt_factor.d
rename to test/unittest/aggs/err.D_LLQUANT_STEPVAL.steps_divides_factor.d
diff --git a/test/unittest/aggs/tst.count.d b/test/unittest/aggs/tst.count.d
index 2fbba009..a0fb5c0e 100644
--- a/test/unittest/aggs/tst.count.d
+++ b/test/unittest/aggs/tst.count.d
@@ -4,14 +4,11 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
 
 /*
- * ASSERTION:
- *  Postive count() test
+ * ASSERTION: Postive count() test
  *
  * SECTION: Aggregations/Aggregations
- *
  */
 
 #pragma D option quiet
diff --git a/test/unittest/aggs/tst.lquantround.d b/test/unittest/aggs/tst.lquantround.d
index 85cc9eaa..4a3bdd00 100644
--- a/test/unittest/aggs/tst.lquantround.d
+++ b/test/unittest/aggs/tst.lquantround.d
@@ -4,7 +4,6 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
 
 #pragma D option quiet
 
diff --git a/test/unittest/printa/err.D_PRINTA_AGGARG.badagg.d b/test/unittest/printa/err.D_PRINTA_AGGARG.badagg.d
index 995191e8..75822f9c 100644
--- a/test/unittest/printa/err.D_PRINTA_AGGARG.badagg.d
+++ b/test/unittest/printa/err.D_PRINTA_AGGARG.badagg.d
@@ -4,7 +4,7 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
+
 /*
  * ASSERTION:
  *  Invokes printa() with a bad aggregation argument.
diff --git a/test/unittest/printa/err.D_PRINTA_AGGARG.badfmt.d b/test/unittest/printa/err.D_PRINTA_AGGARG.badfmt.d
index 52fe3a93..88004e3c 100644
--- a/test/unittest/printa/err.D_PRINTA_AGGARG.badfmt.d
+++ b/test/unittest/printa/err.D_PRINTA_AGGARG.badfmt.d
@@ -4,7 +4,7 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
+
 /*
  * ASSERTION:
  *  Invokes printa() with a bad format string.
diff --git a/test/unittest/printa/err.D_PRINTA_AGGARG.badval.d b/test/unittest/printa/err.D_PRINTA_AGGARG.badval.d
index e4328e84..5527da7a 100644
--- a/test/unittest/printa/err.D_PRINTA_AGGARG.badval.d
+++ b/test/unittest/printa/err.D_PRINTA_AGGARG.badval.d
@@ -4,7 +4,7 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
+
 /*
  * ASSERTION:
  *  Invokes printa() with an argument that does not match the aggregation
diff --git a/test/unittest/printa/err.D_PRINTA_PROTO.bad.d b/test/unittest/printa/err.D_PRINTA_PROTO.bad.d
index b96501f2..edf8cdd7 100644
--- a/test/unittest/printa/err.D_PRINTA_PROTO.bad.d
+++ b/test/unittest/printa/err.D_PRINTA_PROTO.bad.d
@@ -4,7 +4,7 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
+
 /*
  * ASSERTION:
  *  Invokes printa() with a bad aggregation argument.
diff --git a/test/unittest/printa/tst.def.d b/test/unittest/printa/tst.def.d
index eb60a5d6..3b564f6b 100644
--- a/test/unittest/printa/tst.def.d
+++ b/test/unittest/printa/tst.def.d
@@ -4,14 +4,11 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
 
 /*
- * ASSERTION:
- *  Test the printa() default output format.
+ * ASSERTION: Test the printa() default output format.
  *
  * SECTION: Output Formatting/printa()
- *
  */
 
 #pragma D option quiet
diff --git a/test/unittest/printa/tst.fmt.d b/test/unittest/printa/tst.fmt.d
index 17a2080c..dff80304 100644
--- a/test/unittest/printa/tst.fmt.d
+++ b/test/unittest/printa/tst.fmt.d
@@ -4,11 +4,9 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
 
 /*
- * ASSERTION:
- *  Test the printa() format string in its simplest form.
+ * ASSERTION: Test the printa() format string in its simplest form.
  *
  * SECTION: Output Formatting/printa()
  */
diff --git a/test/unittest/printa/tst.manyval.d b/test/unittest/printa/tst.manyval.d
index f450251e..17e3494e 100644
--- a/test/unittest/printa/tst.manyval.d
+++ b/test/unittest/printa/tst.manyval.d
@@ -4,14 +4,13 @@
  * Licensed under the Universal Permissive License v 1.0 as shown at
  * http://oss.oracle.com/licenses/upl.
  */
-/* @@xfail: dtv2 */
+
 
 /*
- * ASSERTION:
- *  Test the use of many aggregation results in the same format string.
+ * ASSERTION: Test the use of many aggregation results in the same format
+ *	string.
  *
  * SECTION: Output Formatting/printa()
- *
  */
 
 #pragma D option quiet
-- 
2.28.0




More information about the DTrace-devel mailing list