From 30ef842d708d30553d7fbc8348a381664ef62a73 Mon Sep 17 00:00:00 2001 From: bmc Date: Tue, 29 Nov 2005 12:13:13 -0800 Subject: 6340196 Probe effect when using strings in aggregations could be reduced 6350216 get_hwc_spec() runs in quadratic time 6350217 printa() refuses to use %Y/%T format specifiers 6350219 aggregation/assoc. array error messages should use "key" nomenclature 6350221 undefined behavior when lquantize() is used inconsistently 6350223 printa() should support multiple aggregations 6350224 need aggregation sorting options 6350225 need private dtrace(1M) option for buffered handler debugging --- usr/src/cmd/dtrace/dtrace.c | 149 +++++- usr/src/lib/libdtrace/common/dt_aggregate.c | 729 +++++++++++++++++++++++++--- usr/src/lib/libdtrace/common/dt_cc.c | 123 ++++- usr/src/lib/libdtrace/common/dt_consume.c | 319 +++++++----- usr/src/lib/libdtrace/common/dt_error.c | 4 + usr/src/lib/libdtrace/common/dt_errtags.h | 7 +- usr/src/lib/libdtrace/common/dt_ident.c | 18 +- usr/src/lib/libdtrace/common/dt_ident.h | 1 + usr/src/lib/libdtrace/common/dt_impl.h | 6 +- usr/src/lib/libdtrace/common/dt_map.c | 13 +- usr/src/lib/libdtrace/common/dt_open.c | 20 +- usr/src/lib/libdtrace/common/dt_options.c | 4 + usr/src/lib/libdtrace/common/dt_printf.c | 245 ++++++++-- usr/src/lib/libdtrace/common/dt_printf.h | 2 + usr/src/lib/libdtrace/common/dt_subr.c | 3 +- usr/src/lib/libdtrace/common/dtrace.h | 17 +- usr/src/uts/common/dtrace/dtrace.c | 127 ++++- usr/src/uts/common/os/modsysfile.c | 27 +- usr/src/uts/common/sys/dtrace.h | 9 +- 19 files changed, 1523 insertions(+), 300 deletions(-) (limited to 'usr/src') diff --git a/usr/src/cmd/dtrace/dtrace.c b/usr/src/cmd/dtrace/dtrace.c index 3dcfd8019d..c6c581b232 100644 --- a/usr/src/cmd/dtrace/dtrace.c +++ b/usr/src/cmd/dtrace/dtrace.c @@ -65,7 +65,7 @@ typedef struct dtrace_cmd { #define E_USAGE 2 static const char DTRACE_OPTSTR[] = - "3:6:aAb:c:CD:ef:FGHi:I:lL:m:n:o:p:P:qs:SU:vVwx:X:Z"; + "3:6:aAb:Bc:CD:ef:FGHi:I:lL:m:n:o:p:P:qs:SU:vVwx:X:Z"; static char **g_argv; static int g_argc; @@ -246,6 +246,9 @@ oprintf(const char *fmt, ...) va_list ap; int n; + if (g_ofp == NULL) + return; + va_start(ap, fmt); n = vfprintf(g_ofp, fmt, ap); va_end(ap); @@ -793,6 +796,128 @@ setopthandler(const dtrace_setoptdata_t *data, void *arg) return (DTRACE_HANDLE_OK); } +#define BUFDUMPHDR(hdr) \ + (void) printf("%s: %s%s\n", g_pname, hdr, strlen(hdr) > 0 ? ":" : ""); + +#define BUFDUMPSTR(ptr, field) \ + (void) printf("%s: %20s => ", g_pname, #field); \ + if ((ptr)->field != NULL) { \ + const char *c = (ptr)->field; \ + (void) printf("\""); \ + do { \ + if (*c == '\n') { \ + (void) printf("\\n"); \ + continue; \ + } \ + \ + (void) printf("%c", *c); \ + } while (*c++ != '\0'); \ + (void) printf("\"\n"); \ + } else { \ + (void) printf("\n"); \ + } + +#define BUFDUMPASSTR(ptr, field, str) \ + (void) printf("%s: %20s => %s\n", g_pname, #field, str); + +#define BUFDUMP(ptr, field) \ + (void) printf("%s: %20s => %lld\n", g_pname, #field, \ + (long long)(ptr)->field); + +#define BUFDUMPPTR(ptr, field) \ + (void) printf("%s: %20s => %s\n", g_pname, #field, \ + (ptr)->field != NULL ? "" : ""); + +/*ARGSUSED*/ +static int +bufhandler(const dtrace_bufdata_t *bufdata, void *arg) +{ + const dtrace_aggdata_t *agg = bufdata->dtbda_aggdata; + const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc; + const dtrace_probedesc_t *pd; + uint32_t flags = bufdata->dtbda_flags; + char buf[512], *c = buf, *end = c + sizeof (buf); + int i, printed; + + struct { + const char *name; + uint32_t value; + } flagnames[] = { + { "AGGVAL", DTRACE_BUFDATA_AGGVAL }, + { "AGGKEY", DTRACE_BUFDATA_AGGKEY }, + { "AGGFORMAT", DTRACE_BUFDATA_AGGFORMAT }, + { "AGGLAST", DTRACE_BUFDATA_AGGLAST }, + { "???", UINT32_MAX }, + { NULL } + }; + + if (bufdata->dtbda_probe != NULL) { + pd = bufdata->dtbda_probe->dtpda_pdesc; + } else if (agg != NULL) { + pd = agg->dtada_pdesc; + } else { + pd = NULL; + } + + BUFDUMPHDR(">>> Called buffer handler"); + BUFDUMPHDR(""); + + BUFDUMPHDR(" dtrace_bufdata"); + BUFDUMPSTR(bufdata, dtbda_buffered); + BUFDUMPPTR(bufdata, dtbda_probe); + BUFDUMPPTR(bufdata, dtbda_aggdata); + BUFDUMPPTR(bufdata, dtbda_recdesc); + + (void) snprintf(c, end - c, "0x%x ", bufdata->dtbda_flags); + c += strlen(c); + + for (i = 0, printed = 0; flagnames[i].name != NULL; i++) { + if (!(flags & flagnames[i].value)) + continue; + + (void) snprintf(c, end - c, + "%s%s", printed++ ? " | " : "(", flagnames[i].name); + c += strlen(c); + flags &= ~flagnames[i].value; + } + + if (printed) + (void) snprintf(c, end - c, ")"); + + BUFDUMPASSTR(bufdata, dtbda_flags, buf); + BUFDUMPHDR(""); + + if (pd != NULL) { + BUFDUMPHDR(" dtrace_probedesc"); + BUFDUMPSTR(pd, dtpd_provider); + BUFDUMPSTR(pd, dtpd_mod); + BUFDUMPSTR(pd, dtpd_func); + BUFDUMPSTR(pd, dtpd_name); + BUFDUMPHDR(""); + } + + if (rec != NULL) { + BUFDUMPHDR(" dtrace_recdesc"); + BUFDUMP(rec, dtrd_action); + BUFDUMP(rec, dtrd_size); + BUFDUMP(rec, dtrd_offset); + BUFDUMPHDR(""); + } + + if (agg != NULL) { + dtrace_aggdesc_t *desc = agg->dtada_desc; + + BUFDUMPHDR(" dtrace_aggdesc"); + BUFDUMPSTR(desc, dtagd_name); + BUFDUMP(desc, dtagd_varid); + BUFDUMP(desc, dtagd_id); + BUFDUMP(desc, dtagd_nrecs); + BUFDUMPHDR(""); + } + + return (DTRACE_HANDLE_OK); +} + /*ARGSUSED*/ static int chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) @@ -1159,6 +1284,10 @@ main(int argc, char *argv[]) dfatal("failed to set -b %s", optarg); break; + case 'B': + g_ofp = NULL; + break; + case 'C': g_cflags |= DTRACE_C_CPP; break; @@ -1281,6 +1410,18 @@ main(int argc, char *argv[]) } } + if (g_ofp == NULL && g_mode != DMODE_EXEC) { + (void) fprintf(stderr, "%s: -B not valid in combination" + " with [-AGl] options\n", g_pname); + return (E_USAGE); + } + + if (g_ofp == NULL && g_ofile != NULL) { + (void) fprintf(stderr, "%s: -B not valid in combination" + " with -o option\n", g_pname); + return (E_USAGE); + } + /* * In our third pass we handle any command-line options related to * grabbing or creating victim processes. The behavior of these calls @@ -1337,6 +1478,10 @@ main(int argc, char *argv[]) if (dtrace_handle_setopt(g_dtp, &setopthandler, NULL) == -1) dfatal("failed to establish setopt handler"); + + if (g_ofp == NULL && + dtrace_handle_buffered(g_dtp, &bufhandler, NULL) == -1) + dfatal("failed to establish buffered handler"); } (void) dtrace_getopt(g_dtp, "flowindent", &opt); @@ -1519,7 +1664,7 @@ main(int argc, char *argv[]) dfatal("processing aborted"); } - if (fflush(g_ofp) == EOF) + if (g_ofp != NULL && fflush(g_ofp) == EOF) clearerr(g_ofp); } while (!done); diff --git a/usr/src/lib/libdtrace/common/dt_aggregate.c b/usr/src/lib/libdtrace/common/dt_aggregate.c index eed4ab994d..922dec49be 100644 --- a/usr/src/lib/libdtrace/common/dt_aggregate.c +++ b/usr/src/lib/libdtrace/common/dt_aggregate.c @@ -32,9 +32,25 @@ #include #include #include +#include +#include #define DTRACE_AHASHSIZE 32779 /* big 'ol prime */ +/* + * Because qsort(3C) does not allow an argument to be passed to a comparison + * function, the variables that affect comparison must regrettably be global; + * they are protected by a global static lock, dt_qsort_lock. + */ +static pthread_mutex_t dt_qsort_lock = PTHREAD_MUTEX_INITIALIZER; + +static int dt_revsort; +static int dt_keysort; +static int dt_keypos; + +#define DT_LESSTHAN (dt_revsort == 0 ? -1 : 1) +#define DT_GREATERTHAN (dt_revsort == 0 ? 1 : -1) + static void dt_aggregate_count(int64_t *existing, int64_t *new, size_t size) { @@ -50,11 +66,11 @@ dt_aggregate_countcmp(int64_t *lhs, int64_t *rhs) int64_t lvar = *lhs; int64_t rvar = *rhs; - if (lvar > rvar) - return (1); - if (lvar < rvar) - return (-1); + return (DT_LESSTHAN); + + if (lvar > rvar) + return (DT_GREATERTHAN); return (0); } @@ -81,11 +97,11 @@ dt_aggregate_averagecmp(int64_t *lhs, int64_t *rhs) int64_t lavg = lhs[0] ? (lhs[1] / lhs[0]) : 0; int64_t ravg = rhs[0] ? (rhs[1] / rhs[0]) : 0; - if (lavg > ravg) - return (1); - if (lavg < ravg) - return (-1); + return (DT_LESSTHAN); + + if (lavg > ravg) + return (DT_GREATERTHAN); return (0); } @@ -149,11 +165,11 @@ dt_aggregate_lquantizedcmp(int64_t *lhs, int64_t *rhs) long double rsum = dt_aggregate_lquantizedsum(rhs); int64_t lzero, rzero; - if (lsum > rsum) - return (1); - if (lsum < rsum) - return (-1); + return (DT_LESSTHAN); + + if (lsum > rsum) + return (DT_GREATERTHAN); /* * If they're both equal, then we will compare based on the weights at @@ -164,11 +180,11 @@ dt_aggregate_lquantizedcmp(int64_t *lhs, int64_t *rhs) lzero = dt_aggregate_lquantizedzero(lhs); rzero = dt_aggregate_lquantizedzero(rhs); - if (lzero > rzero) - return (1); - if (lzero < rzero) - return (-1); + return (DT_LESSTHAN); + + if (lzero > rzero) + return (DT_GREATERTHAN); return (0); } @@ -192,22 +208,22 @@ dt_aggregate_quantizedcmp(int64_t *lhs, int64_t *rhs) rtotal += (long double)bucketval * (long double)rhs[i]; } - if (ltotal > rtotal) - return (1); - if (ltotal < rtotal) - return (-1); + return (DT_LESSTHAN); + + if (ltotal > rtotal) + return (DT_GREATERTHAN); /* * If they're both equal, then we will compare based on the weights at * zero. If the weights at zero are equal, then this will be judged a * tie and will be resolved based on the key comparison. */ - if (lzero > rzero) - return (1); - if (lzero < rzero) - return (-1); + return (DT_LESSTHAN); + + if (lzero > rzero) + return (DT_GREATERTHAN); return (0); } @@ -295,6 +311,28 @@ dt_aggregate_mod(dtrace_hdl_t *dtp, uint64_t *data) } } +static dtrace_aggvarid_t +dt_aggregate_aggvarid(dt_ahashent_t *ent) +{ + dtrace_aggdesc_t *agg = ent->dtahe_data.dtada_desc; + caddr_t data = ent->dtahe_data.dtada_data; + dtrace_recdesc_t *rec = agg->dtagd_rec; + + /* + * First, we'll check the variable ID in the aggdesc. If it's valid, + * we'll return it. If not, we'll use the compiler-generated ID + * present as the first record. + */ + if (agg->dtagd_varid != DTRACE_AGGVARIDNONE) + return (agg->dtagd_varid); + + agg->dtagd_varid = *((dtrace_aggvarid_t *)(uintptr_t)(data + + rec->dtrd_offset)); + + return (agg->dtagd_varid); +} + + static int dt_aggregate_snap_cpu(dtrace_hdl_t *dtp, processorid_t cpu) { @@ -479,6 +517,7 @@ hashnext: h->dtahe_hashval = hashval; h->dtahe_size = size; + (void) dt_aggregate_aggvarid(h); rec = &agg->dtagd_rec[agg->dtagd_nrecs - 1]; @@ -597,10 +636,10 @@ 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 (-1); + return (DT_LESSTHAN); if (lagg->dtagd_nrecs > ragg->dtagd_nrecs) - return (1); + return (DT_GREATERTHAN); return (0); } @@ -610,27 +649,16 @@ 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_aggdesc_t *lagg = lh->dtahe_data.dtada_desc; - dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; - caddr_t ldata = lh->dtahe_data.dtada_data; - caddr_t rdata = rh->dtahe_data.dtada_data; - dtrace_recdesc_t *lrec, *rrec; - uint64_t lid, rid; - - /* - * We know that we have a compiler-generated ID as the first record. - */ - lrec = lagg->dtagd_rec; - rrec = ragg->dtagd_rec; + dtrace_aggvarid_t lid, rid; - lid = *((uint64_t *)(uintptr_t)(ldata + lrec->dtrd_offset)); - rid = *((uint64_t *)(uintptr_t)(rdata + rrec->dtrd_offset)); + lid = dt_aggregate_aggvarid(lh); + rid = dt_aggregate_aggvarid(rh); if (lid < rid) - return (-1); + return (DT_LESSTHAN); if (lid > rid) - return (1); + return (DT_GREATERTHAN); return (0); } @@ -644,25 +672,34 @@ dt_aggregate_keycmp(const void *lhs, const void *rhs) dtrace_aggdesc_t *ragg = rh->dtahe_data.dtada_desc; dtrace_recdesc_t *lrec, *rrec; char *ldata, *rdata; - int rval, i, j; + int rval, i, j, keypos, nrecs; if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0) return (rval); - for (i = 1; i < lagg->dtagd_nrecs - 1; i++) { + nrecs = lagg->dtagd_nrecs - 1; + assert(nrecs == ragg->dtagd_nrecs - 1); + + keypos = dt_keypos + 1 >= nrecs ? 0 : dt_keypos; + + for (i = 1; i < nrecs; i++) { uint64_t lval, rval; + int ndx = i + keypos; - lrec = &lagg->dtagd_rec[i]; - rrec = &ragg->dtagd_rec[i]; + if (ndx >= nrecs) + ndx = ndx - nrecs + 1; + + lrec = &lagg->dtagd_rec[ndx]; + rrec = &ragg->dtagd_rec[ndx]; ldata = lh->dtahe_data.dtada_data + lrec->dtrd_offset; rdata = rh->dtahe_data.dtada_data + rrec->dtrd_offset; if (lrec->dtrd_size < rrec->dtrd_size) - return (-1); + return (DT_LESSTHAN); if (lrec->dtrd_size > rrec->dtrd_size) - return (1); + return (DT_GREATERTHAN); switch (lrec->dtrd_size) { case sizeof (uint64_t): @@ -697,20 +734,21 @@ dt_aggregate_keycmp(const void *lhs, const void *rhs) rval = ((uint8_t *)rdata)[j]; if (lval < rval) - return (-1); + return (DT_LESSTHAN); if (lval > rval) - return (1); + return (DT_GREATERTHAN); + } continue; } if (lval < rval) - return (-1); + return (DT_LESSTHAN); if (lval > rval) - return (1); + return (DT_GREATERTHAN); } return (0); @@ -732,27 +770,27 @@ dt_aggregate_valcmp(const void *lhs, const void *rhs) if ((rval = dt_aggregate_hashcmp(lhs, rhs)) != 0) return (rval); - if (lagg->dtagd_nrecs < ragg->dtagd_nrecs) - return (-1); - if (lagg->dtagd_nrecs > ragg->dtagd_nrecs) - return (1); + return (DT_GREATERTHAN); + + if (lagg->dtagd_nrecs < ragg->dtagd_nrecs) + return (DT_LESSTHAN); for (i = 0; i < lagg->dtagd_nrecs; i++) { lrec = &lagg->dtagd_rec[i]; rrec = &ragg->dtagd_rec[i]; if (lrec->dtrd_offset < rrec->dtrd_offset) - return (-1); + return (DT_LESSTHAN); if (lrec->dtrd_offset > rrec->dtrd_offset) - return (1); + return (DT_GREATERTHAN); if (lrec->dtrd_action < rrec->dtrd_action) - return (-1); + return (DT_LESSTHAN); if (lrec->dtrd_action > rrec->dtrd_action) - return (1); + return (DT_GREATERTHAN); } laddr = (int64_t *)(uintptr_t)(ldata + lrec->dtrd_offset); @@ -782,7 +820,15 @@ dt_aggregate_valcmp(const void *lhs, const void *rhs) assert(0); } - if (rval != 0) + return (rval); +} + +static int +dt_aggregate_valkeycmp(const void *lhs, const void *rhs) +{ + int rval; + + if ((rval = dt_aggregate_valcmp(lhs, rhs)) != 0) return (rval); /* @@ -820,7 +866,7 @@ dt_aggregate_valvarcmp(const void *lhs, const void *rhs) { int rval; - if ((rval = dt_aggregate_valcmp(lhs, rhs)) != 0) + if ((rval = dt_aggregate_valkeycmp(lhs, rhs)) != 0) return (rval); return (dt_aggregate_varcmp(lhs, rhs)); @@ -834,7 +880,7 @@ dt_aggregate_varvalcmp(const void *lhs, const void *rhs) if ((rval = dt_aggregate_varcmp(lhs, rhs)) != 0) return (rval); - return (dt_aggregate_valcmp(lhs, rhs)); + return (dt_aggregate_valkeycmp(lhs, rhs)); } static int @@ -861,6 +907,54 @@ dt_aggregate_varvalrevcmp(const void *lhs, const void *rhs) return (dt_aggregate_varvalcmp(rhs, lhs)); } +static int +dt_aggregate_bundlecmp(const void *lhs, const void *rhs) +{ + dt_ahashent_t **lh = *((dt_ahashent_t ***)lhs); + dt_ahashent_t **rh = *((dt_ahashent_t ***)rhs); + int i, rval; + + if (dt_keysort) { + /* + * If we're sorting on keys, we need to scan until we find the + * last entry -- that's the representative key. (The order of + * the bundle is values followed by key to accommodate the + * default behavior of sorting by value.) If the keys are + * equal, we'll fall into the value comparison loop, below. + */ + for (i = 0; lh[i + 1] != NULL; i++) + continue; + + assert(i != 0); + assert(rh[i + 1] == NULL); + + if ((rval = dt_aggregate_keycmp(&lh[i], &rh[i])) != 0) + return (rval); + } + + for (i = 0; ; i++) { + if (lh[i + 1] == NULL) { + /* + * All of the values are equal; if we're sorting on + * keys, then we're only here because the keys were + * found to be equal and these records are therefore + * equal. If we're not sorting on keys, we'll use the + * key comparison from the representative key as the + * tie-breaker. + */ + if (dt_keysort) + return (0); + + assert(i != 0); + assert(rh[i + 1] == NULL); + return (dt_aggregate_keycmp(&lh[i], &rh[i])); + } else { + if ((rval = dt_aggregate_valcmp(&lh[i], &rh[i])) != 0) + return (rval); + } + } +} + int dt_aggregate_go(dtrace_hdl_t *dtp) { @@ -1035,6 +1129,37 @@ dt_aggwalk_rval(dtrace_hdl_t *dtp, dt_ahashent_t *h, int rval) return (0); } +void +dt_aggregate_qsort(dtrace_hdl_t *dtp, void *base, size_t nel, size_t width, + int (*compar)(const void *, const void *)) +{ + int rev = dt_revsort, key = dt_keysort, keypos = dt_keypos; + dtrace_optval_t keyposopt = dtp->dt_options[DTRACEOPT_AGGSORTKEYPOS]; + + dt_revsort = (dtp->dt_options[DTRACEOPT_AGGSORTREV] != DTRACEOPT_UNSET); + dt_keysort = (dtp->dt_options[DTRACEOPT_AGGSORTKEY] != DTRACEOPT_UNSET); + + if (keyposopt != DTRACEOPT_UNSET && keyposopt <= INT_MAX) { + dt_keypos = (int)keyposopt; + } else { + dt_keypos = 0; + } + + if (compar == NULL) { + if (!dt_keysort) { + compar = dt_aggregate_varvalcmp; + } else { + compar = dt_aggregate_varkeycmp; + } + } + + qsort(base, nel, width, compar); + + dt_revsort = rev; + dt_keysort = key; + dt_keypos = keypos; +} + int dtrace_aggregate_walk(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) { @@ -1069,27 +1194,50 @@ dt_aggregate_walk_sorted(dtrace_hdl_t *dtp, for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) nentries++; - sorted = malloc(nentries * sizeof (dt_ahashent_t *)); + sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *)); if (sorted == NULL) - return (dt_set_errno(dtp, EDT_NOMEM)); + return (-1); for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) sorted[i++] = h; - qsort(sorted, nentries, sizeof (dt_ahashent_t *), sfunc); + (void) pthread_mutex_lock(&dt_qsort_lock); + + if (sfunc == NULL) { + dt_aggregate_qsort(dtp, sorted, nentries, + sizeof (dt_ahashent_t *), NULL); + } else { + /* + * If we've been explicitly passed a sorting function, + * we'll use that -- ignoring the values of the "aggsortrev", + * "aggsortkey" and "aggsortkeypos" options. + */ + qsort(sorted, nentries, sizeof (dt_ahashent_t *), sfunc); + } + + (void) pthread_mutex_unlock(&dt_qsort_lock); for (i = 0; i < nentries; i++) { h = sorted[i]; - if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) + if (dt_aggwalk_rval(dtp, h, func(&h->dtahe_data, arg)) == -1) { + dt_free(dtp, sorted); return (-1); + } } - free(sorted); + dt_free(dtp, sorted); return (0); } +int +dtrace_aggregate_walk_sorted(dtrace_hdl_t *dtp, + dtrace_aggregate_f *func, void *arg) +{ + return (dt_aggregate_walk_sorted(dtp, func, arg, NULL)); +} + int dtrace_aggregate_walk_keysorted(dtrace_hdl_t *dtp, dtrace_aggregate_f *func, void *arg) @@ -1154,6 +1302,449 @@ dtrace_aggregate_walk_valvarrevsorted(dtrace_hdl_t *dtp, arg, dt_aggregate_valvarrevcmp)); } +int +dtrace_aggregate_walk_joined(dtrace_hdl_t *dtp, dtrace_aggvarid_t *aggvars, + int naggvars, dtrace_aggregate_walk_joined_f *func, void *arg) +{ + dt_aggregate_t *agp = &dtp->dt_aggregate; + dt_ahashent_t *h, **sorted = NULL, ***bundle, **nbundle; + const dtrace_aggdata_t **data; + dt_ahashent_t *zaggdata = NULL; + dt_ahash_t *hash = &agp->dtat_hash; + size_t nentries = 0, nbundles = 0, start, zsize = 0, bundlesize; + dtrace_aggvarid_t max = 0, aggvar; + int rval = -1, *map, *remap = NULL; + int i, j; + dtrace_optval_t sortpos = dtp->dt_options[DTRACEOPT_AGGSORTPOS]; + + /* + * If the sorting position is greater than the number of aggregation + * variable IDs, we silently set it to 0. + */ + if (sortpos == DTRACEOPT_UNSET || sortpos >= naggvars) + sortpos = 0; + + /* + * First we need to translate the specified aggregation variable IDs + * into a linear map that will allow us to translate an aggregation + * variable ID into its position in the specified aggvars. + */ + for (i = 0; i < naggvars; i++) { + if (aggvars[i] == DTRACE_AGGVARIDNONE || aggvars[i] < 0) + return (dt_set_errno(dtp, EDT_BADAGGVAR)); + + if (aggvars[i] > max) + max = aggvars[i]; + } + + if ((map = dt_zalloc(dtp, (max + 1) * sizeof (int))) == NULL) + return (-1); + + zaggdata = dt_zalloc(dtp, naggvars * sizeof (dt_ahashent_t)); + + if (zaggdata == NULL) + goto out; + + for (i = 0; i < naggvars; i++) { + int ndx = i + sortpos; + + if (ndx >= naggvars) + ndx -= naggvars; + + aggvar = aggvars[ndx]; + assert(aggvar <= max); + + if (map[aggvar]) { + /* + * We have an aggregation variable that is present + * more than once in the array of aggregation + * variables. While it's unclear why one might want + * to do this, it's legal. To support this construct, + * we will allocate a remap that will indicate the + * position from which this aggregation variable + * should be pulled. (That is, where the remap will + * map from one position to another.) + */ + if (remap == NULL) { + remap = dt_zalloc(dtp, naggvars * sizeof (int)); + + if (remap == NULL) + goto out; + } + + /* + * Given that the variable is already present, assert + * that following through the mapping and adjusting + * for the sort position yields the same aggregation + * variable ID. + */ + assert(aggvars[(map[aggvar] - 1 + sortpos) % + naggvars] == aggvars[ndx]); + + remap[i] = map[aggvar]; + continue; + } + + map[aggvar] = i + 1; + } + + /* + * We need to take two passes over the data to size our allocation, so + * we'll use the first pass to also fill in the zero-filled data to be + * used to properly format a zero-valued aggregation. + */ + for (h = hash->dtah_all; h != NULL; h = h->dtahe_nextall) { + dtrace_aggvarid_t id; + int ndx; + + if ((id = dt_aggregate_aggvarid(h)) > max || !(ndx = map[id])) + continue; + + if (zaggdata[ndx - 1].dtahe_size == 0) { + zaggdata[ndx - 1].dtahe_size = h->dtahe_size; + zaggdata[ndx - 1].dtahe_data = h->dtahe_data; + } + + nentries++; + } + + if (nentries == 0) { + /* + * We couldn't find any entries; there is nothing else to do. + */ + rval = 0; + goto out; + } + + /* + * Before we sort the data, we're going to look for any holes in our + * zero-filled data. This will occur if an aggregation variable that + * we are being asked to print has not yet been assigned the result of + * any aggregating action for _any_ tuple. The issue becomes that we + * would like a zero value to be printed for all columns for this + * aggregation, but without any record description, we don't know the + * aggregating action that corresponds to the aggregation variable. To + * try to find a match, we're simply going to lookup aggregation IDs + * (which are guaranteed to be contiguous and to start from 1), looking + * for the specified aggregation variable ID. If we find a match, + * we'll use that. If we iterate over all aggregation IDs and don't + * find a match, then we must be an anonymous enabling. (Anonymous + * enablings can't currently derive either aggregation variable IDs or + * aggregation variable names given only an aggregation ID.) In this + * obscure case (anonymous enabling, multiple aggregation printa() with + * some aggregations not represented for any tuple), our defined + * behavior is that the zero will be printed in the format of the first + * aggregation variable that contains any non-zero value. + */ + for (i = 0; i < naggvars; i++) { + if (zaggdata[i].dtahe_size == 0) { + dtrace_aggvarid_t aggvar; + + aggvar = aggvars[(i - sortpos + naggvars) % naggvars]; + assert(zaggdata[i].dtahe_data.dtada_data == NULL); + + for (j = DTRACE_AGGIDNONE + 1; ; j++) { + dtrace_aggdesc_t *agg; + dtrace_aggdata_t *aggdata; + + if (dt_aggid_lookup(dtp, j, &agg) != 0) + break; + + if (agg->dtagd_varid != aggvar) + continue; + + /* + * We have our description -- now we need to + * cons up the zaggdata entry for it. + */ + aggdata = &zaggdata[i].dtahe_data; + aggdata->dtada_size = agg->dtagd_size; + aggdata->dtada_desc = agg; + aggdata->dtada_handle = dtp; + (void) dt_epid_lookup(dtp, agg->dtagd_epid, + &aggdata->dtada_edesc, + &aggdata->dtada_pdesc); + aggdata->dtada_normal = 1; + zaggdata[i].dtahe_hashval = 0; + zaggdata[i].dtahe_size = agg->dtagd_size; + break; + } + + if (zaggdata[i].dtahe_size == 0) { + caddr_t data; + + /* + * We couldn't find this aggregation, meaning + * that we have never seen it before for any + * tuple _and_ this is an anonymous enabling. + * That is, we're in the obscure case outlined + * above. In this case, our defined behavior + * is to format the data in the format of the + * first non-zero aggregation -- of which, of + * course, we know there to be at least one + * (or nentries would have been zero). + */ + for (j = 0; j < naggvars; j++) { + if (zaggdata[j].dtahe_size != 0) + break; + } + + assert(j < naggvars); + zaggdata[i] = zaggdata[j]; + + data = zaggdata[i].dtahe_data.dtada_data; + assert(data != NULL); + } + } + } + + /* + * Now we need to allocate our zero-filled data for use for + * aggregations that don't have a value corresponding to a given key. + */ + for (i = 0; i < naggvars; i++) { + dtrace_aggdata_t *aggdata = &zaggdata[i].dtahe_data; + dtrace_aggdesc_t *aggdesc = aggdata->dtada_desc; + dtrace_recdesc_t *rec; + uint64_t larg; + caddr_t zdata; + + zsize = zaggdata[i].dtahe_size; + assert(zsize != 0); + + if ((zdata = dt_zalloc(dtp, zsize)) == NULL) { + /* + * If we failed to allocated some zero-filled data, we + * need to zero out the remaining dtada_data pointers + * to prevent the wrong data from being freed below. + */ + for (j = i; j < naggvars; j++) + zaggdata[j].dtahe_data.dtada_data = NULL; + goto out; + } + + aggvar = aggvars[(i - sortpos + naggvars) % naggvars]; + + /* + * First, the easy bit. To maintain compatibility with + * consumers that pull the compiler-generated ID out of the + * data, we put that ID at the top of the zero-filled data. + */ + rec = &aggdesc->dtagd_rec[0]; + /* LINTED - alignment */ + *((dtrace_aggvarid_t *)(zdata + rec->dtrd_offset)) = aggvar; + + rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; + + /* + * Now for the more complicated part. If (and only if) this + * is an lquantize() aggregating action, zero-filled data is + * not equivalent to an empty record: we must also get the + * parameters for the lquantize(). + */ + if (rec->dtrd_action == DTRACEAGG_LQUANTIZE) { + if (aggdata->dtada_data != NULL) { + /* + * The easier case here is if we actually have + * some prototype data -- in which case we + * manually dig it out of the aggregation + * record. + */ + /* LINTED - alignment */ + larg = *((uint64_t *)(aggdata->dtada_data + + rec->dtrd_offset)); + } else { + /* + * We don't have any prototype data. As a + * result, we know that we _do_ have the + * compiler-generated information. (If this + * were an anonymous enabling, all of our + * zero-filled data would have prototype data + * -- either directly or indirectly.) So as + * gross as it is, we'll grovel around in the + * compiler-generated information to find the + * lquantize() parameters. + */ + dtrace_stmtdesc_t *sdp; + dt_ident_t *aid; + dt_idsig_t *isp; + + sdp = (dtrace_stmtdesc_t *)(uintptr_t) + aggdesc->dtagd_rec[0].dtrd_uarg; + aid = sdp->dtsd_aggdata; + isp = (dt_idsig_t *)aid->di_data; + assert(isp->dis_auxinfo != 0); + larg = isp->dis_auxinfo; + } + + /* LINTED - alignment */ + *((uint64_t *)(zdata + rec->dtrd_offset)) = larg; + } + + aggdata->dtada_data = zdata; + } + + /* + * Now that we've dealt with setting up our zero-filled data, we can + * allocate our sorted array, and take another pass over the data to + * fill it. + */ + sorted = dt_alloc(dtp, nentries * sizeof (dt_ahashent_t *)); + + if (sorted == NULL) + goto out; + + for (h = hash->dtah_all, i = 0; h != NULL; h = h->dtahe_nextall) { + dtrace_aggvarid_t id; + + if ((id = dt_aggregate_aggvarid(h)) > max || !map[id]) + continue; + + sorted[i++] = h; + } + + assert(i == nentries); + + /* + * We've loaded our array; now we need to sort by value to allow us + * to create bundles of like value. We're going to acquire the + * dt_qsort_lock here, and hold it across all of our subsequent + * comparison and sorting. + */ + (void) pthread_mutex_lock(&dt_qsort_lock); + + qsort(sorted, nentries, sizeof (dt_ahashent_t *), + dt_aggregate_keyvarcmp); + + /* + * Now we need to go through and create bundles. Because the number + * of bundles is bounded by the size of the sorted array, we're going + * to reuse the underlying storage. And note that "bundle" is an + * array of pointers to arrays of pointers to dt_ahashent_t -- making + * its type (regrettably) "dt_ahashent_t ***". (Regrettable because + * '*' -- like '_' and 'X' -- should never appear in triplicate in + * an ideal world.) + */ + bundle = (dt_ahashent_t ***)sorted; + + for (i = 1, start = 0; i <= nentries; i++) { + if (i < nentries && + dt_aggregate_keycmp(&sorted[i], &sorted[i - 1]) == 0) + continue; + + /* + * We have a bundle boundary. Everything from start to + * (i - 1) belongs in one bundle. + */ + assert(i - start <= naggvars); + bundlesize = (naggvars + 2) * sizeof (dt_ahashent_t *); + + if ((nbundle = dt_zalloc(dtp, bundlesize)) == NULL) { + (void) pthread_mutex_unlock(&dt_qsort_lock); + goto out; + } + + for (j = start; j < i; j++) { + dtrace_aggvarid_t id = dt_aggregate_aggvarid(sorted[j]); + + assert(id <= max); + assert(map[id] != 0); + assert(map[id] - 1 < naggvars); + assert(nbundle[map[id] - 1] == NULL); + nbundle[map[id] - 1] = sorted[j]; + + if (nbundle[naggvars] == NULL) + nbundle[naggvars] = sorted[j]; + } + + for (j = 0; j < naggvars; j++) { + if (nbundle[j] != NULL) + continue; + + /* + * Before we assume that this aggregation variable + * isn't present (and fall back to using the + * zero-filled data allocated earlier), check the + * remap. If we have a remapping, we'll drop it in + * here. Note that we might be remapping an + * aggregation variable that isn't present for this + * key; in this case, the aggregation data that we + * copy will point to the zeroed data. + */ + if (remap != NULL && remap[j]) { + assert(remap[j] - 1 < j); + assert(nbundle[remap[j] - 1] != NULL); + nbundle[j] = nbundle[remap[j] - 1]; + } else { + nbundle[j] = &zaggdata[j]; + } + } + + bundle[nbundles++] = nbundle; + start = i; + } + + /* + * Now we need to re-sort based on the first value. + */ + dt_aggregate_qsort(dtp, bundle, nbundles, sizeof (dt_ahashent_t **), + dt_aggregate_bundlecmp); + + (void) pthread_mutex_unlock(&dt_qsort_lock); + + /* + * We're done! Now we just need to go back over the sorted bundles, + * calling the function. + */ + data = alloca((naggvars + 1) * sizeof (dtrace_aggdata_t *)); + + for (i = 0; i < nbundles; i++) { + for (j = 0; j < naggvars; j++) + data[j + 1] = NULL; + + for (j = 0; j < naggvars; j++) { + int ndx = j - sortpos; + + if (ndx < 0) + ndx += naggvars; + + assert(bundle[i][ndx] != NULL); + data[j + 1] = &bundle[i][ndx]->dtahe_data; + } + + for (j = 0; j < naggvars; j++) + assert(data[j + 1] != NULL); + + /* + * The representative key is the last element in the bundle. + * Assert that we have one, and then set it to be the first + * element of data. + */ + assert(bundle[i][j] != NULL); + data[0] = &bundle[i][j]->dtahe_data; + + if ((rval = func(data, naggvars + 1, arg)) == -1) + goto out; + } + + rval = 0; +out: + for (i = 0; i < nbundles; i++) + dt_free(dtp, bundle[i]); + + if (zaggdata != NULL) { + for (i = 0; i < naggvars; i++) + dt_free(dtp, zaggdata[i].dtahe_data.dtada_data); + } + + dt_free(dtp, zaggdata); + dt_free(dtp, sorted); + dt_free(dtp, remap); + dt_free(dtp, map); + + return (rval); +} + int dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp, dtrace_aggregate_walk_f *func) @@ -1165,7 +1756,7 @@ dtrace_aggregate_print(dtrace_hdl_t *dtp, FILE *fp, pd.dtpa_allunprint = 1; if (func == NULL) - func = dtrace_aggregate_walk_valsorted; + func = dtrace_aggregate_walk_sorted; if ((*func)(dtp, dt_print_agg, &pd) == -1) return (dt_set_errno(dtp, dtp->dt_errno)); diff --git a/usr/src/lib/libdtrace/common/dt_cc.c b/usr/src/lib/libdtrace/common/dt_cc.c index bd7204101a..e4272187dd 100644 --- a/usr/src/lib/libdtrace/common/dt_cc.c +++ b/usr/src/lib/libdtrace/common/dt_cc.c @@ -490,7 +490,7 @@ dt_action_printa(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) dt_ident_t *aid, *fid; dtrace_actdesc_t *ap; const char *format; - dt_node_t *anp; + dt_node_t *anp, *proto = NULL; char n[DT_TYPE_NAMELEN]; int argc = 0, argr = 0; @@ -515,39 +515,59 @@ dt_action_printa(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) argr = 1; } - if (argc != argr) { + if (argc < argr) { dnerror(dnp, D_PRINTA_PROTO, "%s( ) prototype mismatch: %d args passed, %d expected\n", dnp->dn_ident->di_name, argc, argr); } - 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))); - } + assert(anp != NULL); - aid = anp->dn_ident; - fid = aid->di_iarg; + 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))); + } - if (aid->di_gen == dtp->dt_gen && !(aid->di_flags & DT_IDFLG_MOD)) { - dnerror(dnp, D_PRINTA_AGGBAD, - "undefined aggregation: @%s\n", aid->di_name); - } + aid = anp->dn_ident; + fid = aid->di_iarg; - if (format != NULL) { - yylineno = dnp->dn_line; + if (aid->di_gen == dtp->dt_gen && + !(aid->di_flags & DT_IDFLG_MOD)) { + dnerror(dnp, D_PRINTA_AGGBAD, + "undefined aggregation: @%s\n", aid->di_name); + } - sdp->dtsd_fmtdata = dt_printf_create(yypcb->pcb_hdl, format); - dt_printf_validate(sdp->dtsd_fmtdata, - DT_PRINTF_AGGREGATION, dnp->dn_ident, 1, - fid->di_id, ((dt_idsig_t *)aid->di_data)->dis_args); - } + /* + * If we have multiple aggregations, we must be sure that + * their key signatures match. + */ + if (proto != NULL) { + dt_printa_validate(proto, anp); + } else { + proto = anp; + } - ap = dt_stmt_action(dtp, sdp); - dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_PRINTA); + if (format != NULL) { + yylineno = dnp->dn_line; + + sdp->dtsd_fmtdata = + dt_printf_create(yypcb->pcb_hdl, format); + dt_printf_validate(sdp->dtsd_fmtdata, + DT_PRINTF_AGGREGATION, dnp->dn_ident, 1, + fid->di_id, ((dt_idsig_t *)aid->di_data)->dis_args); + format = NULL; + } + + ap = dt_stmt_action(dtp, sdp); + dt_action_difconst(ap, anp->dn_ident->di_id, DTRACEACT_PRINTA); + + anp = anp->dn_list; + argr++; + } } static void @@ -1148,7 +1168,8 @@ dt_compile_agg(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) dt_node_t *arg1 = dnp->dn_aggfun->dn_args->dn_list; dt_node_t *arg2 = arg1->dn_list; dt_node_t *arg3 = arg2->dn_list; - uint64_t nlevels, step = 1; + dt_idsig_t *isp; + uint64_t nlevels, step = 1, oarg; int64_t baseval, limitval; if (arg1->dn_kind != DT_NODE_INT) { @@ -1213,6 +1234,58 @@ dt_compile_agg(dtrace_hdl_t *dtp, dt_node_t *dnp, dtrace_stmtdesc_t *sdp) ((baseval << DTRACE_LQUANTIZE_BASESHIFT) & DTRACE_LQUANTIZE_BASEMASK); + assert(arg != 0); + + isp = (dt_idsig_t *)aid->di_data; + + if (isp->dis_auxinfo == 0) { + /* + * This is the first time we've seen an lquantize() + * for this aggregation; we'll store our argument + * as the auxiliary signature information. + */ + isp->dis_auxinfo = arg; + } else if ((oarg = isp->dis_auxinfo) != arg) { + /* + * If we have seen this lquantize() before and the + * argument doesn't match the original argument, pick + * the original argument apart to concisely report the + * mismatch. + */ + int obaseval = DTRACE_LQUANTIZE_BASE(oarg); + int onlevels = DTRACE_LQUANTIZE_LEVELS(oarg); + int ostep = DTRACE_LQUANTIZE_STEP(oarg); + + if (obaseval != baseval) { + dnerror(dnp, D_LQUANT_MATCHBASE, "lquantize( ) " + "base (argument #1) doesn't match previous " + "declaration: expected %d, found %d\n", + obaseval, (int)baseval); + } + + if (onlevels * ostep != nlevels * step) { + dnerror(dnp, D_LQUANT_MATCHLIM, "lquantize( ) " + "limit (argument #2) doesn't match previous" + " declaration: expected %d, found %d\n", + obaseval + onlevels * ostep, + (int)baseval + (int)nlevels * (int)step); + } + + if (ostep != step) { + dnerror(dnp, D_LQUANT_MATCHSTEP, "lquantize( ) " + "step (argument #3) doesn't match previous " + "declaration: expected %d, found %d\n", + ostep, (int)step); + } + + /* + * We shouldn't be able to get here -- one of the + * parameters must be mismatched if the arguments + * didn't match. + */ + assert(0); + } + incr = arg3 != NULL ? arg3->dn_list : NULL; argmax = 5; } diff --git a/usr/src/lib/libdtrace/common/dt_consume.c b/usr/src/lib/libdtrace/common/dt_consume.c index 8a788db6c7..f86f78f610 100644 --- a/usr/src/lib/libdtrace/common/dt_consume.c +++ b/usr/src/lib/libdtrace/common/dt_consume.c @@ -844,12 +844,11 @@ 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; - uintptr_t data = (uintptr_t)aggdata->dtada_data; if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); - if (id != *(dtrace_aggvarid_t *)(data + agg->dtagd_rec[0].dtrd_offset)) + if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); ((dtrace_aggdata_t *)aggdata)->dtada_normal = normal->dtnd_normal; @@ -913,12 +912,11 @@ 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); - uintptr_t data = (uintptr_t)aggdata->dtada_data; if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); - if (id != *(dtrace_aggvarid_t *)(data + agg->dtagd_rec[0].dtrd_offset)) + if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); return (DTRACE_AGGWALK_DENORMALIZE); @@ -929,12 +927,11 @@ 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); - uintptr_t data = (uintptr_t)aggdata->dtada_data; if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); - if (id != *(dtrace_aggvarid_t *)(data + agg->dtagd_rec[0].dtrd_offset)) + if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); return (DTRACE_AGGWALK_CLEAR); @@ -951,12 +948,11 @@ 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; - uintptr_t data = (uintptr_t)aggdata->dtada_data; if (agg->dtagd_nrecs == 0) return (DTRACE_AGGWALK_NEXT); - if (id != *(dtrace_aggvarid_t *)(data + agg->dtagd_rec[0].dtrd_offset)) + if (agg->dtagd_varid != id) return (DTRACE_AGGWALK_NEXT); if (trunc->dttd_remaining == 0) @@ -1031,137 +1027,176 @@ dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) return (0); } -int -dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) +static int +dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, + caddr_t addr, size_t size, uint64_t normal) { - int i, err = 0; - dt_print_aggdata_t *pd = arg; - dtrace_aggdesc_t *agg = aggdata->dtada_desc; - FILE *fp = pd->dtpa_fp; - dtrace_hdl_t *dtp = pd->dtpa_dtp; - dtrace_aggvarid_t aggvarid = pd->dtpa_id; - uintptr_t data = (uintptr_t)aggdata->dtada_data; + int err; + dtrace_actkind_t act = rec->dtrd_action; - if (pd->dtpa_allunprint) { - if (agg->dtagd_flags & DTRACE_AGD_PRINTED) - return (0); - } else { - /* - * If we're not printing all unprinted aggregations, then the - * aggregation variable ID denotes a specific aggregation - * variable that we should print -- skip any other aggregations - * that we encounter. - */ - if (agg->dtagd_nrecs == 0) - return (0); + switch (act) { + case DTRACEACT_STACK: + return (dt_print_stack(dtp, fp, NULL, addr, + rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg)); - if (aggvarid != *(dtrace_aggvarid_t *)(data + - agg->dtagd_rec[0].dtrd_offset)) - return (0); - } + case DTRACEACT_USTACK: + case DTRACEACT_JSTACK: + return (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg)); - /* - * Iterate over each record description, printing the traced data, - * skipping the first datum (the tuple member created by the compiler). - */ - for (i = 1; err >= 0 && i < agg->dtagd_nrecs; i++) { - dtrace_recdesc_t *rec = &agg->dtagd_rec[i]; - dtrace_actkind_t act = rec->dtrd_action; - caddr_t addr = aggdata->dtada_data + rec->dtrd_offset; - size_t size = rec->dtrd_size; - uint64_t normal; + case DTRACEACT_USYM: + case DTRACEACT_UADDR: + return (dt_print_usym(dtp, fp, addr, act)); - normal = DTRACEACT_ISAGG(act) ? aggdata->dtada_normal : 1; + case DTRACEACT_UMOD: + return (dt_print_umod(dtp, fp, NULL, addr)); - if (act == DTRACEACT_STACK) { - err = dt_print_stack(dtp, fp, NULL, addr, - rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg); - goto nextrec; - } + case DTRACEACT_SYM: + return (dt_print_sym(dtp, fp, NULL, addr)); - if (act == DTRACEACT_USTACK || act == DTRACEACT_JSTACK) { - err = dt_print_ustack(dtp, fp, NULL, addr, - rec->dtrd_arg); - goto nextrec; - } + case DTRACEACT_MOD: + return (dt_print_mod(dtp, fp, NULL, addr)); - if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { - err = dt_print_usym(dtp, fp, addr, act); - goto nextrec; - } + case DTRACEAGG_QUANTIZE: + return (dt_print_quantize(dtp, fp, addr, size, normal)); - if (act == DTRACEACT_UMOD) { - err = dt_print_umod(dtp, fp, NULL, addr); - goto nextrec; - } + case DTRACEAGG_LQUANTIZE: + return (dt_print_lquantize(dtp, fp, addr, size, normal)); - if (act == DTRACEACT_SYM) { - err = dt_print_sym(dtp, fp, NULL, addr); - goto nextrec; - } + case DTRACEAGG_AVG: + return (dt_print_average(dtp, fp, addr, size, normal)); - if (act == DTRACEACT_MOD) { - err = dt_print_mod(dtp, fp, NULL, addr); - goto nextrec; - } + default: + break; + } - if (act == DTRACEAGG_QUANTIZE) { - err = dt_print_quantize(dtp, fp, addr, size, normal); - goto nextrec; - } + switch (size) { + case sizeof (uint64_t): + err = dt_printf(dtp, fp, " %16lld", + /* LINTED - alignment */ + (long long)*((uint64_t *)addr) / normal); + break; + case sizeof (uint32_t): + /* LINTED - alignment */ + err = dt_printf(dtp, fp, " %8d", *((uint32_t *)addr) / + (uint32_t)normal); + break; + case sizeof (uint16_t): + /* LINTED - alignment */ + err = dt_printf(dtp, fp, " %5d", *((uint16_t *)addr) / + (uint32_t)normal); + break; + case sizeof (uint8_t): + err = dt_printf(dtp, fp, " %3d", *((uint8_t *)addr) / + (uint32_t)normal); + break; + default: + err = dt_print_bytes(dtp, fp, addr, size, 50, 0); + break; + } - if (act == DTRACEAGG_LQUANTIZE) { - err = dt_print_lquantize(dtp, fp, addr, size, normal); - goto nextrec; - } + return (err); +} - if (act == DTRACEAGG_AVG) { - err = dt_print_average(dtp, fp, addr, size, normal); - goto nextrec; - } +int +dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) +{ + int i, aggact = 0; + dt_print_aggdata_t *pd = arg; + const dtrace_aggdata_t *aggdata = aggsdata[0]; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + FILE *fp = pd->dtpa_fp; + dtrace_hdl_t *dtp = pd->dtpa_dtp; + dtrace_recdesc_t *rec; + dtrace_actkind_t act; + caddr_t addr; + size_t size; - switch (size) { - case sizeof (uint64_t): - err = dt_printf(dtp, fp, " %16lld", - /* LINTED - alignment */ - (long long)*((uint64_t *)addr) / normal); - break; - case sizeof (uint32_t): - /* LINTED - alignment */ - err = dt_printf(dtp, fp, " %8d", *((uint32_t *)addr) / - (uint32_t)normal); - break; - case sizeof (uint16_t): - /* LINTED - alignment */ - err = dt_printf(dtp, fp, " %5d", *((uint16_t *)addr) / - (uint32_t)normal); - break; - case sizeof (uint8_t): - err = dt_printf(dtp, fp, " %3d", *((uint8_t *)addr) / - (uint32_t)normal); - break; - default: - err = dt_print_bytes(dtp, fp, addr, size, 50, 0); + /* + * Iterate over each record description in the key, printing the traced + * data, skipping the first datum (the tuple member created by the + * compiler). + */ + for (i = 1; i < agg->dtagd_nrecs; i++) { + rec = &agg->dtagd_rec[i]; + act = rec->dtrd_action; + addr = aggdata->dtada_data + rec->dtrd_offset; + size = rec->dtrd_size; + + if (DTRACEACT_ISAGG(act)) { + aggact = i; break; } -nextrec: - if (dt_buffered_flush(dtp, NULL, rec, aggdata) < 0) + if (dt_print_datum(dtp, fp, rec, addr, size, 1) < 0) + return (-1); + + if (dt_buffered_flush(dtp, NULL, rec, aggdata, + DTRACE_BUFDATA_AGGKEY) < 0) return (-1); } - if (err >= 0) - err = dt_printf(dtp, fp, "\n"); + assert(aggact != 0); + + for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) { + uint64_t normal; + + aggdata = aggsdata[i]; + agg = aggdata->dtada_desc; + rec = &agg->dtagd_rec[aggact]; + act = rec->dtrd_action; + addr = aggdata->dtada_data + rec->dtrd_offset; + size = rec->dtrd_size; + + assert(DTRACEACT_ISAGG(act)); + normal = aggdata->dtada_normal; + + if (dt_print_datum(dtp, fp, rec, addr, size, normal) < 0) + return (-1); + + if (dt_buffered_flush(dtp, NULL, rec, aggdata, + DTRACE_BUFDATA_AGGVAL) < 0) + return (-1); - if (dt_buffered_flush(dtp, NULL, NULL, aggdata) < 0) + if (!pd->dtpa_allunprint) + agg->dtagd_flags |= DTRACE_AGD_PRINTED; + } + + if (dt_printf(dtp, fp, "\n") < 0) return (-1); - if (!pd->dtpa_allunprint) - agg->dtagd_flags |= DTRACE_AGD_PRINTED; + if (dt_buffered_flush(dtp, NULL, NULL, aggdata, + DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) + return (-1); - return (err < 0 ? -1 : 0); + return (0); } +int +dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) +{ + dt_print_aggdata_t *pd = arg; + dtrace_aggdesc_t *agg = aggdata->dtada_desc; + dtrace_aggvarid_t aggvarid = pd->dtpa_id; + + if (pd->dtpa_allunprint) { + if (agg->dtagd_flags & DTRACE_AGD_PRINTED) + return (0); + } else { + /* + * If we're not printing all unprinted aggregations, then the + * aggregation variable ID denotes a specific aggregation + * variable that we should print -- skip any other aggregations + * that we encounter. + */ + if (agg->dtagd_nrecs == 0) + return (0); + + if (aggvarid != agg->dtagd_varid) + return (0); + } + + return (dt_print_aggs(&aggdata, 1, arg)); +} int dt_setopt(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, @@ -1472,18 +1507,68 @@ again: nofmt: if (act == DTRACEACT_PRINTA) { dt_print_aggdata_t pd; + dtrace_aggvarid_t *aggvars; + int j, naggvars = 0; + size_t size = ((epd->dtepd_nrecs - i) * + sizeof (dtrace_aggvarid_t)); + + if ((aggvars = dt_alloc(dtp, size)) == NULL) + return (-1); + + /* + * This might be a printa() with multiple + * aggregation variables. We need to scan + * forward through the records until we find + * a record from a different statement. + */ + for (j = i; j < epd->dtepd_nrecs; j++) { + dtrace_recdesc_t *nrec; + caddr_t naddr; + + nrec = &epd->dtepd_rec[j]; + + if (nrec->dtrd_uarg != rec->dtrd_uarg) + break; + + if (nrec->dtrd_action != act) { + return (dt_set_errno(dtp, + EDT_BADAGG)); + } + + naddr = buf->dtbd_data + offs + + nrec->dtrd_offset; + aggvars[naggvars++] = + /* LINTED - alignment */ + *((dtrace_aggvarid_t *)naddr); + } + + i = j - 1; bzero(&pd, sizeof (pd)); pd.dtpa_dtp = dtp; pd.dtpa_fp = fp; - /* LINTED - alignment */ - pd.dtpa_id = *((dtrace_aggvarid_t *)addr); + + assert(naggvars >= 1); + + if (naggvars == 1) { + pd.dtpa_id = aggvars[0]; + dt_free(dtp, aggvars); + + if (dt_printf(dtp, fp, "\n") < 0 || + dtrace_aggregate_walk_sorted(dtp, + dt_print_agg, &pd) < 0) + return (-1); + goto nextrec; + } if (dt_printf(dtp, fp, "\n") < 0 || - dtrace_aggregate_walk_valsorted(dtp, - dt_print_agg, &pd) < 0) + dtrace_aggregate_walk_joined(dtp, aggvars, + naggvars, dt_print_aggs, &pd) < 0) { + dt_free(dtp, aggvars); return (-1); + } + dt_free(dtp, aggvars); goto nextrec; } @@ -1518,7 +1603,7 @@ nofmt: return (-1); /* errno is set for us */ nextrec: - if (dt_buffered_flush(dtp, &data, rec, NULL) < 0) + if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) return (-1); /* errno is set for us */ } diff --git a/usr/src/lib/libdtrace/common/dt_error.c b/usr/src/lib/libdtrace/common/dt_error.c index 7da10b7fc8..18ebd4887a 100644 --- a/usr/src/lib/libdtrace/common/dt_error.c +++ b/usr/src/lib/libdtrace/common/dt_error.c @@ -104,6 +104,10 @@ static const struct { { EDT_ELFVERSION, "libelf is out-of-date with respect to libdtrace" }, { EDT_NOBUFFERED, "Attempt to buffer output without handler" }, { EDT_UNSTABLE, "Description matched an unstable set of probes" }, + { EDT_BADSETOPT, "Invalid setopt() library action" }, + { EDT_BADSTACKPC, "Invalid stack program counter size" }, + { EDT_BADAGGVAR, "Invalid aggregation variable identifier" }, + { EDT_OVERSION, "Client requested deprecated version of library" } }; static const int _dt_nerr = sizeof (_dt_errlist) / sizeof (_dt_errlist[0]); diff --git a/usr/src/lib/libdtrace/common/dt_errtags.h b/usr/src/lib/libdtrace/common/dt_errtags.h index 23e790831b..9e32dfdf24 100644 --- a/usr/src/lib/libdtrace/common/dt_errtags.h +++ b/usr/src/lib/libdtrace/common/dt_errtags.h @@ -183,6 +183,8 @@ typedef enum { D_PRINTA_PROTO, /* printa() prototype mismatch */ D_PRINTA_AGGARG, /* aggregation arg type mismatch */ D_PRINTA_AGGBAD, /* printa() aggregation not defined */ + D_PRINTA_AGGKEY, /* printa() aggregation key mismatch */ + D_PRINTA_AGGPROTO, /* printa() aggregation mismatch */ D_TRACE_VOID, /* trace() argument has void type */ D_TRACE_DYN, /* trace() argument has dynamic type */ D_TRACEMEM_ADDR, /* tracemem() address bad type */ @@ -230,7 +232,10 @@ typedef enum { D_PROV_PRDUP, /* duplicate probe declaration */ D_PROV_PRARGLEN, /* probe argument list too long */ D_PROV_PRXLATOR, /* probe argument translator missing */ - D_FREOPEN_INVALID /* frename() filename is invalid */ + D_FREOPEN_INVALID, /* frename() filename is invalid */ + D_LQUANT_MATCHBASE, /* lquantize() mismatch on base */ + D_LQUANT_MATCHLIM, /* lquantize() mismatch on limit */ + D_LQUANT_MATCHSTEP /* lquantize() mismatch on step */ } dt_errtag_t; extern const char *dt_errtag(dt_errtag_t); diff --git a/usr/src/lib/libdtrace/common/dt_ident.c b/usr/src/lib/libdtrace/common/dt_ident.c index e2be36a9c8..58b767d343 100644 --- a/usr/src/lib/libdtrace/common/dt_ident.c +++ b/usr/src/lib/libdtrace/common/dt_ident.c @@ -53,11 +53,13 @@ dt_idcook_sign(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args, const char *prefix, const char *suffix) { dt_idsig_t *isp = idp->di_data; - int i, compat, mismatch, arglimit; + int i, compat, mismatch, arglimit, iskey; char n1[DT_TYPE_NAMELEN]; char n2[DT_TYPE_NAMELEN]; + iskey = idp->di_kind == DT_IDENT_ARRAY || idp->di_kind == DT_IDENT_AGG; + if (isp->dis_varargs >= 0) { mismatch = argc < isp->dis_varargs; arglimit = isp->dis_varargs; @@ -70,9 +72,9 @@ dt_idcook_sign(dt_node_t *dnp, dt_ident_t *idp, } if (mismatch) { - xyerror(D_PROTO_LEN, "%s%s%s prototype mismatch: %d arg%s" + xyerror(D_PROTO_LEN, "%s%s%s prototype mismatch: %d %s%s" "passed, %s%d expected\n", prefix, idp->di_name, suffix, - argc, argc == 1 ? " " : "s ", + argc, iskey ? "key" : "arg", argc == 1 ? " " : "s ", isp->dis_optargs >= 0 ? "at least " : "", isp->dis_optargs >= 0 ? isp->dis_optargs : arglimit); } @@ -85,11 +87,13 @@ dt_idcook_sign(dt_node_t *dnp, dt_ident_t *idp, if (!compat) { xyerror(D_PROTO_ARG, - "%s%s%s argument #%d is incompatible with " - "prototype:\n\tprototype: %s\n\t argument: %s\n", - prefix, idp->di_name, suffix, i + 1, + "%s%s%s %s #%d is incompatible with " + "prototype:\n\tprototype: %s\n\t%9s: %s\n", + prefix, idp->di_name, suffix, + iskey ? "key" : "argument", i + 1, dt_node_type_name(&isp->dis_args[i], n1, sizeof (n1)), + iskey ? "key" : "argument", dt_node_type_name(args, n2, sizeof (n2))); } } @@ -117,6 +121,7 @@ dt_idcook_assc(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) isp->dis_optargs = -1; isp->dis_argc = argc; isp->dis_args = NULL; + isp->dis_auxinfo = 0; if (argc != 0 && (isp->dis_args = calloc(argc, sizeof (dt_node_t))) == NULL) { @@ -212,6 +217,7 @@ dt_idcook_func(dt_node_t *dnp, dt_ident_t *idp, int argc, dt_node_t *args) isp->dis_optargs = -1; isp->dis_argc = i; isp->dis_args = NULL; + isp->dis_auxinfo = 0; if (i != 0 && (isp->dis_args = calloc(i, sizeof (dt_node_t))) == NULL) { diff --git a/usr/src/lib/libdtrace/common/dt_ident.h b/usr/src/lib/libdtrace/common/dt_ident.h index c1f784dccd..cc80d6e98b 100644 --- a/usr/src/lib/libdtrace/common/dt_ident.h +++ b/usr/src/lib/libdtrace/common/dt_ident.h @@ -49,6 +49,7 @@ typedef struct dt_idsig { int dis_optargs; /* argument index of start of optargs (or -1) */ int dis_argc; /* number of types in this signature */ struct dt_node *dis_args; /* array of nodes representing formal types */ + uint64_t dis_auxinfo; /* auxiliary signature information, if any */ } dt_idsig_t; typedef struct dt_idnode { diff --git a/usr/src/lib/libdtrace/common/dt_impl.h b/usr/src/lib/libdtrace/common/dt_impl.h index 773509f4d7..9771276dde 100644 --- a/usr/src/lib/libdtrace/common/dt_impl.h +++ b/usr/src/lib/libdtrace/common/dt_impl.h @@ -484,7 +484,9 @@ enum { EDT_NOBUFFERED, /* attempt to buffer output without handler */ EDT_UNSTABLE, /* description matched unstable set of probes */ EDT_BADSETOPT, /* invalid setopt library action */ - EDT_BADSTACKPC /* invalid stack program counter size */ + EDT_BADSTACKPC, /* invalid stack program counter size */ + EDT_BADAGGVAR, /* invalid aggregation variable identifier */ + EDT_OVERSION /* client is requesting deprecated version */ }; /* @@ -550,7 +552,7 @@ extern ulong_t dt_popcb(const ulong_t *, ulong_t); extern int dt_buffered_enable(dtrace_hdl_t *); extern int dt_buffered_flush(dtrace_hdl_t *, dtrace_probedata_t *, - const dtrace_recdesc_t *, const dtrace_aggdata_t *); + const dtrace_recdesc_t *, const dtrace_aggdata_t *, uint32_t flags); extern void dt_buffered_disable(dtrace_hdl_t *); extern void dt_buffered_destroy(dtrace_hdl_t *); diff --git a/usr/src/lib/libdtrace/common/dt_map.c b/usr/src/lib/libdtrace/common/dt_map.c index d07a966be8..f39f734b17 100644 --- a/usr/src/lib/libdtrace/common/dt_map.c +++ b/usr/src/lib/libdtrace/common/dt_map.c @@ -351,7 +351,15 @@ dt_aggid_add(dtrace_hdl_t *dtp, dtrace_aggid_t id) } } - if (agg->dtagd_rec[0].dtrd_uarg) { + /* + * 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) { dtrace_stmtdesc_t *sdp; dt_ident_t *aid; @@ -359,6 +367,9 @@ dt_aggid_add(dtrace_hdl_t *dtp, dtrace_aggid_t id) 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; } if ((epid = agg->dtagd_epid) >= dtp->dt_maxprobe || diff --git a/usr/src/lib/libdtrace/common/dt_open.c b/usr/src/lib/libdtrace/common/dt_open.c index f2c7713d85..6d094033fe 100644 --- a/usr/src/lib/libdtrace/common/dt_open.c +++ b/usr/src/lib/libdtrace/common/dt_open.c @@ -344,7 +344,7 @@ static const dt_ident_t _dtrace_globals[] = { &dt_idops_type, "uint64_t" }, { "walltimestamp", DT_IDENT_SCALAR, 0, DIF_VAR_WALLTIMESTAMP, DT_ATTR_STABCMN, DT_VERS_1_0, - &dt_idops_type, "uint64_t" }, + &dt_idops_type, "int64_t" }, { "zonename", DT_IDENT_SCALAR, 0, DIF_VAR_ZONENAME, DT_ATTR_STABCMN, DT_VERS_1_0, &dt_idops_type, "string" }, { NULL, 0, 0, 0, { 0, 0, 0 }, 0, NULL, NULL } @@ -758,6 +758,21 @@ dt_vopen(int version, int flags, int *errp, if (version > DTRACE_VERSION) return (set_open_errno(dtp, errp, EDT_VERSION)); + if (version < DTRACE_VERSION) { + /* + * Currently, increasing the library version number is used to + * denote a binary incompatible change. That is, a consumer + * of the library cannot run on a version of the library with + * a higher DTRACE_VERSION number than the consumer compiled + * against. Once the library API has been committed to, + * backwards binary compatibility will be required; at that + * time, this check should change to return EDT_OVERSION only + * if the specified version number is less than the version + * number at the time of interface commitment. + */ + return (set_open_errno(dtp, errp, EDT_OVERSION)); + } + if (flags & ~DTRACE_O_MASK) return (set_open_errno(dtp, errp, EINVAL)); @@ -948,7 +963,8 @@ alloc: bcopy(_dtrace_ints_64, dtp->dt_ints, sizeof (_dtrace_ints_64)); dtp->dt_macros = dt_idhash_create("macro", NULL, 0, UINT_MAX); - dtp->dt_aggs = dt_idhash_create("aggregation", NULL, 0, UINT_MAX); + dtp->dt_aggs = dt_idhash_create("aggregation", NULL, + DTRACE_AGGVARIDNONE + 1, UINT_MAX); dtp->dt_globals = dt_idhash_create("global", _dtrace_globals, DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX); diff --git a/usr/src/lib/libdtrace/common/dt_options.c b/usr/src/lib/libdtrace/common/dt_options.c index 4f80e0966d..2c333be434 100644 --- a/usr/src/lib/libdtrace/common/dt_options.c +++ b/usr/src/lib/libdtrace/common/dt_options.c @@ -910,6 +910,10 @@ static const dt_option_t _dtrace_rtoptions[] = { */ static const dt_option_t _dtrace_drtoptions[] = { { "aggrate", dt_opt_rate, DTRACEOPT_AGGRATE }, + { "aggsortkey", dt_opt_runtime, DTRACEOPT_AGGSORTKEY }, + { "aggsortkeypos", dt_opt_runtime, DTRACEOPT_AGGSORTKEYPOS }, + { "aggsortpos", dt_opt_runtime, DTRACEOPT_AGGSORTPOS }, + { "aggsortrev", dt_opt_runtime, DTRACEOPT_AGGSORTREV }, { "flowindent", dt_opt_runtime, DTRACEOPT_FLOWINDENT }, { "quiet", dt_opt_runtime, DTRACEOPT_QUIET }, { "rawbytes", dt_opt_runtime, DTRACEOPT_RAWBYTES }, diff --git a/usr/src/lib/libdtrace/common/dt_printf.c b/usr/src/lib/libdtrace/common/dt_printf.c index c3224d11e3..cea2d081b7 100644 --- a/usr/src/lib/libdtrace/common/dt_printf.c +++ b/usr/src/lib/libdtrace/common/dt_printf.c @@ -77,6 +77,14 @@ pfcheck_stack(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) return (dt_node_is_stack(dnp)); } +/*ARGSUSED*/ +static int +pfcheck_time(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) +{ + return (dt_node_is_integer(dnp) && + dt_node_type_size(dnp) == sizeof (uint64_t)); +} + /*ARGSUSED*/ static int pfcheck_str(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp) @@ -613,13 +621,13 @@ static const dt_pfconv_t _dtrace_conversions[] = { { "p", "x", pfproto_addr, pfcheck_addr, pfprint_uint }, { "s", "s", "char [] or string (or use stringof)", pfcheck_str, pfprint_cstr }, { "S", "s", pfproto_cstr, pfcheck_str, pfprint_estr }, -{ "T", "s", "uint64_t", pfcheck_type, pfprint_time822 }, +{ "T", "s", "int64_t", pfcheck_time, pfprint_time822 }, { "u", "u", pfproto_xint, pfcheck_xint, pfprint_uint }, { "wc", "wc", "int", pfcheck_type, pfprint_sint }, /* a.k.a. wchar_t */ { "ws", "ws", pfproto_wstr, pfcheck_wstr, pfprint_wstr }, { "x", "x", pfproto_xint, pfcheck_xint, pfprint_uint }, { "X", "X", pfproto_xint, pfcheck_xint, pfprint_uint }, -{ "Y", "s", "uint64_t", pfcheck_type, pfprint_time }, +{ "Y", "s", "int64_t", pfcheck_time, pfprint_time }, { "%", "%", "void", pfcheck_type, pfprint_pct }, { NULL, NULL, NULL, NULL, NULL } }; @@ -1107,6 +1115,63 @@ dt_printf_validate(dt_pfargv_t *pfv, uint_t flags, } } +void +dt_printa_validate(dt_node_t *lhs, dt_node_t *rhs) +{ + dt_ident_t *lid, *rid; + dt_node_t *lproto, *rproto; + int largc, rargc, argn; + char n1[DT_TYPE_NAMELEN]; + char n2[DT_TYPE_NAMELEN]; + + assert(lhs->dn_kind == DT_NODE_AGG); + assert(rhs->dn_kind == DT_NODE_AGG); + + lid = lhs->dn_ident; + rid = rhs->dn_ident; + + lproto = ((dt_idsig_t *)lid->di_data)->dis_args; + rproto = ((dt_idsig_t *)rid->di_data)->dis_args; + + /* + * First, get an argument count on each side. These must match. + */ + for (largc = 0; lproto != NULL; lproto = lproto->dn_list) + largc++; + + for (rargc = 0; rproto != NULL; rproto = rproto->dn_list) + rargc++; + + if (largc != rargc) { + xyerror(D_PRINTA_AGGKEY, "printa( ): @%s and @%s do not have " + "matching key signatures: @%s has %d key%s, @%s has %d " + "key%s", lid->di_name, rid->di_name, + lid->di_name, largc, largc == 1 ? "" : "s", + rid->di_name, rargc, rargc == 1 ? "" : "s"); + } + + /* + * Now iterate over the keys to verify that each type matches. + */ + lproto = ((dt_idsig_t *)lid->di_data)->dis_args; + rproto = ((dt_idsig_t *)rid->di_data)->dis_args; + + for (argn = 1; lproto != NULL; argn++, lproto = lproto->dn_list, + rproto = rproto->dn_list) { + assert(rproto != NULL); + + if (dt_node_is_argcompat(lproto, rproto)) + continue; + + xyerror(D_PRINTA_AGGPROTO, "printa( ): @%s[ ] key #%d is " + "incompatible with @%s:\n%9s key #%d: %s\n" + "%9s key #%d: %s\n", + rid->di_name, argn, lid->di_name, lid->di_name, argn, + dt_node_type_name(lproto, n1, sizeof (n1)), rid->di_name, + argn, dt_node_type_name(rproto, n2, sizeof (n2))); + } +} + static int dt_printf_getint(dtrace_hdl_t *dtp, const dtrace_recdesc_t *recp, uint_t nrecs, const void *buf, size_t len, int *ip) @@ -1177,27 +1242,36 @@ pfprint_lquantize(dtrace_hdl_t *dtp, FILE *fp, const char *format, static int dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, const dtrace_recdesc_t *recs, uint_t nrecs, const void *buf, - size_t len, const dtrace_aggdata_t *adp) + size_t len, const dtrace_aggdata_t **aggsdata, int naggvars) { dt_pfargd_t *pfd = pfv->pfv_argv; const dtrace_recdesc_t *recp = recs; - const dtrace_recdesc_t *aggr = NULL; - uchar_t *lim = (uchar_t *)buf + len; + const dtrace_aggdata_t *aggdata; + dtrace_aggdesc_t *agg; + caddr_t lim = (caddr_t)buf + len, limit; char format[64] = "%"; - uint64_t normal = adp ? adp->dtada_normal : 1; - int i; + int i, aggrec, curagg = -1; + uint64_t normal; /* - * If we are formatting an aggregation, set 'aggr' to the final record - * description (the aggregation result) so we can use this record with - * any conversion where DT_PFCONV_AGG is set. We then decrement nrecs - * to prevent this record from being used with any other conversion. + * If we are formatting an aggregation, set 'aggrec' to the index of + * the final record description (the aggregation result) so we can use + * this record index with any conversion where DT_PFCONV_AGG is set. + * (The actual aggregation used will vary as we increment through the + * aggregation variables that we have been passed.) Finally, we + * decrement nrecs to prevent this record from being used with any + * other conversion. */ if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) { - assert(adp != NULL); + assert(aggsdata != NULL); + assert(naggvars > 0); + if (nrecs == 0) return (dt_set_errno(dtp, EDT_DMISMATCH)); - aggr = recp + nrecs - 1; + + curagg = naggvars > 1 ? 1 : 0; + aggdata = aggsdata[0]; + aggrec = aggdata->dtada_desc->dtagd_nrecs - 1; nrecs--; } @@ -1210,8 +1284,9 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, char *f = format + 1; /* skip initial '%' */ const dtrace_recdesc_t *rec; dt_pfprint_f *func; - uchar_t *addr; + caddr_t addr; size_t size; + uint32_t flags; if (pfd->pfd_preflen != 0) { char *tmp = alloca(pfd->pfd_preflen + 1); @@ -1225,12 +1300,16 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) { /* * For printa(), we flush the buffer after each - * prefix, setting the record to NULL to - * indicate that this does not correspond to - * a particular tuple element, but is rather - * part of the format string. + * prefix, setting the flags to indicate that + * this is part of the printa() format string. */ - if (dt_buffered_flush(dtp, NULL, NULL, adp) < 0) + flags = DTRACE_BUFDATA_AGGFORMAT; + + if (pfc == NULL && i == pfv->pfv_argc - 1) + flags |= DTRACE_BUFDATA_AGGLAST; + + if (dt_buffered_flush(dtp, NULL, NULL, + aggdata, flags) < 0) return (-1); } } @@ -1265,20 +1344,48 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, return (-1); /* errno is set for us */ if (pfd->pfd_flags & DT_PFCONV_AGG) { - if (aggr == NULL) + /* + * This should be impossible -- the compiler shouldn't + * create a DT_PFCONV_AGG conversion without an + * aggregation present. Still, we'd rather fail + * gracefully than blow up... + */ + if (aggsdata == NULL) return (dt_set_errno(dtp, EDT_DMISMATCH)); - rec = aggr; + + aggdata = aggsdata[curagg]; + agg = aggdata->dtada_desc; + + /* + * We increment the current aggregation variable, but + * not beyond the number of aggregation variables that + * we're printing. This has the (desired) effect that + * DT_PFCONV_AGG conversions beyond the number of + * aggregation variables (re-)convert the aggregation + * value of the last aggregation variable. + */ + if (curagg < naggvars - 1) + curagg++; + + rec = &agg->dtagd_rec[aggrec]; + addr = aggdata->dtada_data + rec->dtrd_offset; + limit = addr + aggdata->dtada_size; + normal = aggdata->dtada_normal; + flags = DTRACE_BUFDATA_AGGVAL; } else { if (nrecs == 0) return (dt_set_errno(dtp, EDT_DMISMATCH)); rec = recp++; nrecs--; + addr = (caddr_t)buf + rec->dtrd_offset; + limit = lim; + normal = 1; + flags = DTRACE_BUFDATA_AGGKEY; } - addr = (uchar_t *)buf + rec->dtrd_offset; size = rec->dtrd_size; - if (addr + size > lim) { + if (addr + size > limit) { dt_dprintf("bad size: addr=%p size=0x%x lim=%p\n", (void *)addr, rec->dtrd_size, (void *)lim); return (dt_set_errno(dtp, EDT_DOFFSET)); @@ -1343,16 +1450,20 @@ 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, - rec == aggr ? normal : 1) < 0) + if (func(dtp, fp, format, pfd, addr, size, normal) < 0) return (-1); /* errno is set for us */ if (pfv->pfv_flags & DT_PRINTF_AGGREGATION) { /* * For printa(), we flush the buffer after each tuple - * element. + * element, inidicating that this is the last record + * as appropriate. */ - if (dt_buffered_flush(dtp, NULL, rec, adp) < 0) + if (i == pfv->pfv_argc - 1) + flags |= DTRACE_BUFDATA_AGGLAST; + + if (dt_buffered_flush(dtp, NULL, + rec, aggdata, flags) < 0) return (-1); } } @@ -1379,7 +1490,8 @@ dtrace_sprintf(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata, bzero(dtp->dt_sprintf_buf, size); dtp->dt_sprintf_buflen = size; - rval = dt_printf_format(dtp, fp, fmtdata, recp, nrecs, buf, len, NULL); + rval = dt_printf_format(dtp, fp, fmtdata, recp, nrecs, buf, len, + NULL, 0); dtp->dt_sprintf_buflen = 0; if (rval == -1) @@ -1519,7 +1631,7 @@ dtrace_fprintf(dtrace_hdl_t *dtp, FILE *fp, void *fmtdata, uint_t nrecs, const void *buf, size_t len) { return (dt_printf_format(dtp, fp, fmtdata, - recp, nrecs, buf, len, NULL)); + recp, nrecs, buf, len, NULL, 0)); } void * @@ -1670,7 +1782,7 @@ dt_fprinta(const dtrace_aggdata_t *adp, void *arg) 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) + recp, nrecs, adp->dtada_data, adp->dtada_size, &adp, 1) == -1) return (pfw->pfw_err = dtp->dt_errno); /* @@ -1682,27 +1794,82 @@ dt_fprinta(const dtrace_aggdata_t *adp, void *arg) 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) + return (pfw->pfw_err = dtp->dt_errno); + + /* + * For each aggregation, indicate that it has been printed, casting + * away the const as necessary. + */ + for (i = 1; i < naggvars; i++) { + agg = aggsdata[i]->dtada_desc; + ((dtrace_aggdesc_t *)agg)->dtagd_flags |= DTRACE_AGD_PRINTED; + } + + 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_recdesc_t *recp = recs; dt_pfwalk_t pfw; - int id; + int i, naggvars = 0; + dtrace_aggvarid_t *aggvars; + + aggvars = alloca(nrecs * sizeof (dtrace_aggvarid_t)); - if (dt_printf_getint(dtp, recp++, nrecs--, buf, len, &id) == -1) - return (-1); /* errno is set for us */ + /* + * This might be a printa() with multiple aggregation variables. We + * need to scan forward through the records until we find a record from + * a different statement. + */ + for (i = 0; i < nrecs; i++) { + const dtrace_recdesc_t *nrec = &recs[i]; + + if (nrec->dtrd_uarg != recs->dtrd_uarg) + break; + + if (nrec->dtrd_action != recs->dtrd_action) + return (dt_set_errno(dtp, EDT_BADAGG)); + + aggvars[naggvars++] = + /* LINTED - alignment */ + *((dtrace_aggvarid_t *)((caddr_t)buf + nrec->dtrd_offset)); + } + + if (naggvars == 0) + return (dt_set_errno(dtp, EDT_BADAGG)); pfw.pfw_argv = fmtdata; - pfw.pfw_aid = id; pfw.pfw_fp = fp; pfw.pfw_err = 0; - if (dtrace_aggregate_walk_valsorted(dtp, dt_fprinta, &pfw) == -1 || - pfw.pfw_err != 0) - return (-1); /* errno is set for us */ + if (naggvars == 1) { + pfw.pfw_aid = aggvars[0]; - return ((int)(recp - recs)); + 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 */ + } + + return (i); } diff --git a/usr/src/lib/libdtrace/common/dt_printf.h b/usr/src/lib/libdtrace/common/dt_printf.h index 70677a67b3..b3b5b8b94b 100644 --- a/usr/src/lib/libdtrace/common/dt_printf.h +++ b/usr/src/lib/libdtrace/common/dt_printf.h @@ -119,6 +119,8 @@ extern void dt_printf_destroy(dt_pfargv_t *); extern void dt_printf_validate(dt_pfargv_t *, uint_t, struct dt_ident *, int, dtrace_actkind_t, struct dt_node *); +extern void dt_printa_validate(struct dt_node *, struct dt_node *); + extern int dt_print_stack(dtrace_hdl_t *, FILE *, const char *, caddr_t, int, int); extern int dt_print_ustack(dtrace_hdl_t *, FILE *, diff --git a/usr/src/lib/libdtrace/common/dt_subr.c b/usr/src/lib/libdtrace/common/dt_subr.c index 47a161df07..b2163e69e9 100644 --- a/usr/src/lib/libdtrace/common/dt_subr.c +++ b/usr/src/lib/libdtrace/common/dt_subr.c @@ -656,7 +656,7 @@ dt_printf(dtrace_hdl_t *dtp, FILE *fp, const char *format, ...) int dt_buffered_flush(dtrace_hdl_t *dtp, dtrace_probedata_t *pdata, - const dtrace_recdesc_t *rec, const dtrace_aggdata_t *agg) + const dtrace_recdesc_t *rec, const dtrace_aggdata_t *agg, uint32_t flags) { dtrace_bufdata_t data; @@ -668,6 +668,7 @@ dt_buffered_flush(dtrace_hdl_t *dtp, dtrace_probedata_t *pdata, data.dtbda_probe = pdata; data.dtbda_recdesc = rec; data.dtbda_aggdata = agg; + data.dtbda_flags = flags; if ((*dtp->dt_bufhdlr)(&data, dtp->dt_bufarg) == DTRACE_HANDLE_ABORT) return (dt_set_errno(dtp, EDT_DIRABORT)); diff --git a/usr/src/lib/libdtrace/common/dtrace.h b/usr/src/lib/libdtrace/common/dtrace.h index 6d8fa80dc9..3d5998cb2f 100644 --- a/usr/src/lib/libdtrace/common/dtrace.h +++ b/usr/src/lib/libdtrace/common/dtrace.h @@ -49,14 +49,13 @@ extern "C" { * Please refer to the "Solaris Dynamic Tracing Guide" for more information. */ -#define DTRACE_VERSION 1 /* library ABI interface version */ +#define DTRACE_VERSION 2 /* library ABI interface version */ struct ps_prochandle; typedef struct dtrace_hdl dtrace_hdl_t; typedef struct dtrace_prog dtrace_prog_t; typedef struct dtrace_vector dtrace_vector_t; typedef struct dtrace_aggdata dtrace_aggdata_t; -typedef int64_t dtrace_aggvarid_t; #define DTRACE_O_NODEV 0x01 /* do not open dtrace(7D) device */ #define DTRACE_O_NOSYS 0x02 /* do not load /system/object modules */ @@ -298,12 +297,18 @@ extern int dtrace_handle_drop(dtrace_hdl_t *, dtrace_handle_drop_f *, void *); typedef void dtrace_handle_proc_f(struct ps_prochandle *, void *); extern int dtrace_handle_proc(dtrace_hdl_t *, dtrace_handle_proc_f *, void *); +#define DTRACE_BUFDATA_AGGKEY 0x0001 /* aggregation key */ +#define DTRACE_BUFDATA_AGGVAL 0x0002 /* aggregation value */ +#define DTRACE_BUFDATA_AGGFORMAT 0x0004 /* aggregation format data */ +#define DTRACE_BUFDATA_AGGLAST 0x0008 /* last for this key/val */ + typedef struct dtrace_bufdata { dtrace_hdl_t *dtbda_handle; /* handle to DTrace library */ const char *dtbda_buffered; /* buffered output */ dtrace_probedata_t *dtbda_probe; /* probe data */ const dtrace_recdesc_t *dtbda_recdesc; /* record description */ const dtrace_aggdata_t *dtbda_aggdata; /* aggregation data, if agg. */ + uint32_t dtbda_flags; /* flags; see above */ } dtrace_bufdata_t; typedef int dtrace_handle_buffered_f(const dtrace_bufdata_t *, void *); @@ -354,6 +359,8 @@ struct dtrace_aggdata { typedef int dtrace_aggregate_f(const dtrace_aggdata_t *, void *); typedef int dtrace_aggregate_walk_f(dtrace_hdl_t *, dtrace_aggregate_f *, void *); +typedef int dtrace_aggregate_walk_joined_f(const dtrace_aggdata_t **, + const int, void *); extern void dtrace_aggregate_clear(dtrace_hdl_t *); extern int dtrace_aggregate_snap(dtrace_hdl_t *); @@ -362,6 +369,12 @@ extern int dtrace_aggregate_print(dtrace_hdl_t *, FILE *, extern int dtrace_aggregate_walk(dtrace_hdl_t *, dtrace_aggregate_f *, void *); +extern int dtrace_aggregate_walk_joined(dtrace_hdl_t *, + dtrace_aggvarid_t *, int, dtrace_aggregate_walk_joined_f *, void *); + +extern int dtrace_aggregate_walk_sorted(dtrace_hdl_t *, + dtrace_aggregate_f *, void *); + extern int dtrace_aggregate_walk_keysorted(dtrace_hdl_t *, dtrace_aggregate_f *, void *); diff --git a/usr/src/uts/common/dtrace/dtrace.c b/usr/src/uts/common/dtrace/dtrace.c index 5974bf5486..ebc91a7319 100644 --- a/usr/src/uts/common/dtrace/dtrace.c +++ b/usr/src/uts/common/dtrace/dtrace.c @@ -407,6 +407,10 @@ dtrace_load##bits(uintptr_t addr) \ ((flags) & CPU_DTRACE_NOSCRATCH) ? DTRACEFLT_NOSCRATCH : \ DTRACEFLT_UNKNOWN) +#define DTRACEACT_ISSTRING(act) \ + ((act)->dta_kind == DTRACEACT_DIFEXPR && \ + (act)->dta_difo->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) + static dtrace_probe_t *dtrace_probe_lookup_id(dtrace_id_t id); static void dtrace_enabling_provide(dtrace_provider_t *); static int dtrace_enabling_match(dtrace_enabling_t *, int *); @@ -1490,9 +1494,10 @@ dtrace_aggregate(dtrace_aggregation_t *agg, dtrace_buffer_t *dbuf, uint32_t align = sizeof (uint64_t) - 1; dtrace_aggbuffer_t *agb; dtrace_aggkey_t *key; - uint32_t hashval = 0; + uint32_t hashval = 0, limit, isstr; caddr_t tomax, data, kdata; dtrace_actkind_t action; + dtrace_action_t *act; uintptr_t offs; if (buf == NULL) @@ -1563,6 +1568,9 @@ dtrace_aggregate(dtrace_aggregation_t *agg, dtrace_buffer_t *dbuf, agb->dtagb_hash[i] = NULL; } + ASSERT(agg->dtag_first != NULL); + ASSERT(agg->dtag_first->dta_intuple); + /* * Calculate the hash value based on the key. Note that we _don't_ * include the aggid in the hashing (but we will store it as part of @@ -1572,10 +1580,20 @@ dtrace_aggregate(dtrace_aggregation_t *agg, dtrace_buffer_t *dbuf, * algorithm (and a comparison with other algorithms) may be found by * running the ::dtrace_aggstat MDB dcmd. */ - for (i = sizeof (dtrace_aggid_t); i < size; i++) { - hashval += data[i]; - hashval += (hashval << 10); - hashval ^= (hashval >> 6); + for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) { + i = act->dta_rec.dtrd_offset - agg->dtag_base; + limit = i + act->dta_rec.dtrd_size; + ASSERT(limit <= size); + isstr = DTRACEACT_ISSTRING(act); + + for (; i < limit; i++) { + hashval += data[i]; + hashval += (hashval << 10); + hashval ^= (hashval >> 6); + + if (isstr && data[i] == '\0') + break; + } } hashval += (hashval << 3); @@ -1583,8 +1601,9 @@ dtrace_aggregate(dtrace_aggregation_t *agg, dtrace_buffer_t *dbuf, hashval += (hashval << 15); /* - * Yes, the divide here is expensive. If the cycle count here becomes - * prohibitive, we can do tricks to eliminate it. + * Yes, the divide here is expensive -- but it's generally the least + * of the performance issues given the amount of data that we iterate + * over to compute hash values, compare data, etc. */ ndx = hashval % agb->dtagb_hashsize; @@ -1598,9 +1617,20 @@ dtrace_aggregate(dtrace_aggregation_t *agg, dtrace_buffer_t *dbuf, kdata = key->dtak_data; ASSERT(kdata >= tomax && kdata < tomax + buf->dtb_size); - for (i = sizeof (dtrace_aggid_t); i < size; i++) { - if (kdata[i] != data[i]) - goto next; + for (act = agg->dtag_first; act->dta_intuple; + act = act->dta_next) { + i = act->dta_rec.dtrd_offset - agg->dtag_base; + limit = i + act->dta_rec.dtrd_size; + ASSERT(limit <= size); + isstr = DTRACEACT_ISSTRING(act); + + for (; i < limit; i++) { + if (kdata[i] != data[i]) + goto next; + + if (isstr && data[i] == '\0') + break; + } } if (action != key->dtak_action) { @@ -1661,6 +1691,34 @@ next: for (i = sizeof (dtrace_aggid_t); i < size; i++) kdata[i] = data[i]; + /* + * Because strings are not zeroed out by default, we need to iterate + * looking for actions that store strings, and we need to explicitly + * pad these strings out with zeroes. + */ + for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) { + int nul; + + if (!DTRACEACT_ISSTRING(act)) + continue; + + i = act->dta_rec.dtrd_offset - agg->dtag_base; + limit = i + act->dta_rec.dtrd_size; + ASSERT(limit <= size); + + for (nul = 0; i < limit; i++) { + if (nul) { + kdata[i] = '\0'; + continue; + } + + if (data[i] != '\0') + continue; + + nul = 1; + } + } + for (i = size; i < fsize; i++) kdata[i] = 0; @@ -4988,6 +5046,7 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, if (dp->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) { char c = '\0' + 1; + int intuple = act->dta_intuple; size_t s; for (s = 0; s < size; s++) { @@ -4996,6 +5055,9 @@ dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, DTRACE_STORE(uint8_t, tomax, valoffs++, c); + + if (c == '\0' && intuple) + break; } continue; @@ -8591,16 +8653,7 @@ dtrace_ecb_action_add(dtrace_ecb_t *ecb, dtrace_actdesc_t *desc) rec->dtrd_action = action->dta_kind; rec->dtrd_arg = arg; - - if (ecb->dte_state == dtrace_anon.dta_state) { - /* - * If this is an anonymous enabling, explicitly clear the uarg. - */ - rec->dtrd_uarg = 0; - } else { - rec->dtrd_uarg = desc->dtad_uarg; - } - + rec->dtrd_uarg = desc->dtad_uarg; rec->dtrd_alignment = (uint16_t)align; rec->dtrd_format = format; @@ -11245,6 +11298,15 @@ dtrace_state_go(dtrace_state_t *state, processorid_t *cpu) state->dts_anon = dtrace_anon_grab(); ASSERT(state->dts_anon != NULL); + state = state->dts_anon; + + /* + * We want "grabanon" to be set in the grabbed state, so we'll + * copy that option value from the grabbing state into the + * grabbed state. + */ + state->dts_options[DTRACEOPT_GRABANON] = + opt[DTRACEOPT_GRABANON]; *cpu = dtrace_anon.dta_beganon; @@ -11254,8 +11316,6 @@ dtrace_state_go(dtrace_state_t *state, processorid_t *cpu) * we don't allow any further option processing -- but we * don't return failure. */ - state = state->dts_anon; - if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) goto out; } @@ -13378,6 +13438,20 @@ dtrace_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv) for (act = agg->dtag_first; ; act = act->dta_next) { ASSERT(act->dta_intuple || DTRACEACT_ISAGG(act->dta_kind)); + + /* + * If this action has a record size of zero, it + * denotes an argument to the aggregating action. + * Because the presence of this record doesn't (or + * shouldn't) affect the way the data is interpreted, + * we don't copy it out to save user-level the + * confusion of dealing with a zero-length record. + */ + if (act->dta_rec.dtrd_size == 0) { + ASSERT(agg->dtag_hasarg); + continue; + } + aggdesc.dtagd_nrecs++; if (act == &agg->dtag_action) @@ -13402,6 +13476,15 @@ dtrace_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv) for (act = agg->dtag_first; ; act = act->dta_next) { dtrace_recdesc_t rec = act->dta_rec; + /* + * See the comment in the above loop for why we pass + * over zero-length records. + */ + if (rec.dtrd_size == 0) { + ASSERT(agg->dtag_hasarg); + continue; + } + if (nrecs-- == 0) break; diff --git a/usr/src/uts/common/os/modsysfile.c b/usr/src/uts/common/os/modsysfile.c index 1c1210f0f2..7ffcf66d10 100644 --- a/usr/src/uts/common/os/modsysfile.c +++ b/usr/src/uts/common/os/modsysfile.c @@ -1568,8 +1568,9 @@ struct val_list { } val; }; -static void -add_val(struct val_list **val_listp, int val_type, caddr_t val) +static struct val_list * +add_val(struct val_list **val_listp, struct val_list *tail, + int val_type, caddr_t val) { struct val_list *new_val, *listp = *val_listp; @@ -1583,14 +1584,17 @@ add_val(struct val_list **val_listp, int val_type, caddr_t val) new_val->val.integer = (int)(uintptr_t)val; } - if (listp) { - while (listp->val_next) { - listp = listp->val_next; - } - listp->val_next = new_val; + ASSERT((listp == NULL && tail == NULL) || + (listp != NULL && tail != NULL)); + + if (tail != NULL) { + ASSERT(tail->val_next == NULL); + tail->val_next = new_val; } else { *val_listp = new_val; } + + return (new_val); } /* @@ -1750,7 +1754,7 @@ get_hwc_spec(struct _buf *file, char *tokbuf, size_t linesize) token_t token; struct hwc_spec *hwcp; struct dev_info *devi; - struct val_list *val_list; + struct val_list *val_list, *tail; hwc_state_t state; u_longlong_t ival; @@ -1761,6 +1765,7 @@ get_hwc_spec(struct _buf *file, char *tokbuf, size_t linesize) token = NAME; prop_name = NULL; val_list = NULL; + tail = NULL; string = NULL; do { #ifdef DEBUG @@ -1776,6 +1781,7 @@ get_hwc_spec(struct _buf *file, char *tokbuf, size_t linesize) prop_name, val_list); prop_name = NULL; val_list = NULL; + tail = NULL; /*FALLTHROUGH*/ case hwc_begin: if (strcmp(tokbuf, "PARENT") == 0 || @@ -1855,7 +1861,7 @@ get_hwc_spec(struct _buf *file, char *tokbuf, size_t linesize) /*FALLTHROUGH*/ case prop_equals: case prop_equals_string_comma: - add_val(&val_list, 0, string); + tail = add_val(&val_list, tail, 0, string); string = NULL; state = prop_equals_string; break; @@ -1869,7 +1875,8 @@ get_hwc_spec(struct _buf *file, char *tokbuf, size_t linesize) case prop_equals: case prop_equals_integer_comma: (void) kobj_getvalue(tokbuf, &ival); - add_val(&val_list, 1, (caddr_t)(uintptr_t)ival); + tail = add_val(&val_list, tail, + 1, (caddr_t)(uintptr_t)ival); state = prop_equals_integer; break; default: diff --git a/usr/src/uts/common/sys/dtrace.h b/usr/src/uts/common/sys/dtrace.h index 1076d065e3..07b58f9411 100644 --- a/usr/src/uts/common/sys/dtrace.h +++ b/usr/src/uts/common/sys/dtrace.h @@ -61,6 +61,7 @@ extern "C" { #define DTRACE_IDNONE 0 /* invalid probe identifier */ #define DTRACE_EPIDNONE 0 /* invalid enabled probe identifier */ #define DTRACE_AGGIDNONE 0 /* invalid aggregation identifier */ +#define DTRACE_AGGVARIDNONE 0 /* invalid aggregation variable ID */ #define DTRACE_CACHEIDNONE 0 /* invalid predicate cache */ #define DTRACE_PROVNONE 0 /* invalid provider identifier */ #define DTRACE_METAPROVNONE 0 /* invalid meta-provider identifier */ @@ -77,6 +78,7 @@ extern "C" { typedef uint32_t dtrace_id_t; /* probe identifier */ typedef uint32_t dtrace_epid_t; /* enabled probe identifier */ typedef uint32_t dtrace_aggid_t; /* aggregation identifier */ +typedef int64_t dtrace_aggvarid_t; /* aggregation variable identifier */ typedef uint16_t dtrace_actkind_t; /* action kind */ typedef int64_t dtrace_optval_t; /* option value */ typedef uint32_t dtrace_cacheid_t; /* predicate cache identifier */ @@ -885,6 +887,7 @@ typedef struct dtrace_eprobedesc { typedef struct dtrace_aggdesc { DTRACE_PTR(char, dtagd_name); /* not filled in by kernel */ + dtrace_aggvarid_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 */ @@ -944,7 +947,11 @@ typedef struct dtrace_fmtdesc { #define DTRACEOPT_RAWBYTES 20 /* always print bytes in raw form */ #define DTRACEOPT_JSTACKFRAMES 21 /* number of jstack() frames */ #define DTRACEOPT_JSTACKSTRSIZE 22 /* size of jstack() string table */ -#define DTRACEOPT_MAX 23 /* number of options */ +#define DTRACEOPT_AGGSORTKEY 23 /* sort aggregations by key */ +#define DTRACEOPT_AGGSORTREV 24 /* reverse-sort aggregations */ +#define DTRACEOPT_AGGSORTPOS 25 /* agg. position to sort on */ +#define DTRACEOPT_AGGSORTKEYPOS 26 /* agg. key position to sort on */ +#define DTRACEOPT_MAX 27 /* number of options */ #define DTRACEOPT_UNSET (dtrace_optval_t)-2 /* unset option */ -- cgit v1.2.3