10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate /* 230Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include <stdlib.h> 300Sstevel@tonic-gate #include <strings.h> 310Sstevel@tonic-gate #include <errno.h> 320Sstevel@tonic-gate #include <unistd.h> 330Sstevel@tonic-gate #include <limits.h> 340Sstevel@tonic-gate #include <assert.h> 350Sstevel@tonic-gate #include <ctype.h> 360Sstevel@tonic-gate #include <alloca.h> 37457Sbmc #include <dt_impl.h> 380Sstevel@tonic-gate 39457Sbmc /* 40457Sbmc * We declare this here because (1) we need it and (2) we want to avoid a 41457Sbmc * dependency on libm in libdtrace. 42457Sbmc */ 43457Sbmc static long double 44457Sbmc dt_fabsl(long double x) 45457Sbmc { 46457Sbmc if (x < 0) 47457Sbmc return (-x); 48457Sbmc 49457Sbmc return (x); 50457Sbmc } 510Sstevel@tonic-gate 520Sstevel@tonic-gate static int 530Sstevel@tonic-gate dt_flowindent(dtrace_hdl_t *dtp, dtrace_probedata_t *data, dtrace_epid_t last, 540Sstevel@tonic-gate dtrace_bufdesc_t *buf, size_t offs) 550Sstevel@tonic-gate { 560Sstevel@tonic-gate dtrace_probedesc_t *pd = data->dtpda_pdesc, *npd; 570Sstevel@tonic-gate dtrace_eprobedesc_t *epd = data->dtpda_edesc, *nepd; 58457Sbmc char *p = pd->dtpd_provider, *n = pd->dtpd_name, *sub; 590Sstevel@tonic-gate dtrace_flowkind_t flow = DTRACEFLOW_NONE; 600Sstevel@tonic-gate const char *str = NULL; 610Sstevel@tonic-gate static const char *e_str[2] = { " -> ", " => " }; 620Sstevel@tonic-gate static const char *r_str[2] = { " <- ", " <= " }; 63457Sbmc static const char *ent = "entry", *ret = "return"; 64457Sbmc static int entlen = 0, retlen = 0; 650Sstevel@tonic-gate dtrace_epid_t next, id = epd->dtepd_epid; 660Sstevel@tonic-gate int rval; 670Sstevel@tonic-gate 68457Sbmc if (entlen == 0) { 69457Sbmc assert(retlen == 0); 70457Sbmc entlen = strlen(ent); 71457Sbmc retlen = strlen(ret); 72457Sbmc } 73457Sbmc 74457Sbmc /* 75457Sbmc * If the name of the probe is "entry" or ends with "-entry", we 76457Sbmc * treat it as an entry; if it is "return" or ends with "-return", 77457Sbmc * we treat it as a return. (This allows application-provided probes 78457Sbmc * like "method-entry" or "function-entry" to participate in flow 79457Sbmc * indentation -- without accidentally misinterpreting popular probe 80457Sbmc * names like "carpentry", "gentry" or "Coventry".) 81457Sbmc */ 82457Sbmc if ((sub = strstr(n, ent)) != NULL && sub[entlen] == '\0' && 83457Sbmc (sub == n || sub[-1] == '-')) { 840Sstevel@tonic-gate flow = DTRACEFLOW_ENTRY; 850Sstevel@tonic-gate str = e_str[strcmp(p, "syscall") == 0]; 86457Sbmc } else if ((sub = strstr(n, ret)) != NULL && sub[retlen] == '\0' && 87457Sbmc (sub == n || sub[-1] == '-')) { 880Sstevel@tonic-gate flow = DTRACEFLOW_RETURN; 890Sstevel@tonic-gate str = r_str[strcmp(p, "syscall") == 0]; 900Sstevel@tonic-gate } 910Sstevel@tonic-gate 920Sstevel@tonic-gate /* 930Sstevel@tonic-gate * If we're going to indent this, we need to check the ID of our last 940Sstevel@tonic-gate * call. If we're looking at the same probe ID but a different EPID, 950Sstevel@tonic-gate * we _don't_ want to indent. (Yes, there are some minor holes in 960Sstevel@tonic-gate * this scheme -- it's a heuristic.) 970Sstevel@tonic-gate */ 980Sstevel@tonic-gate if (flow == DTRACEFLOW_ENTRY) { 990Sstevel@tonic-gate if ((last != DTRACE_EPIDNONE && id != last && 1000Sstevel@tonic-gate pd->dtpd_id == dtp->dt_pdesc[last]->dtpd_id)) 1010Sstevel@tonic-gate flow = DTRACEFLOW_NONE; 1020Sstevel@tonic-gate } 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate /* 1050Sstevel@tonic-gate * If we're going to unindent this, it's more difficult to see if 1060Sstevel@tonic-gate * we don't actually want to unindent it -- we need to look at the 1070Sstevel@tonic-gate * _next_ EPID. 1080Sstevel@tonic-gate */ 1090Sstevel@tonic-gate if (flow == DTRACEFLOW_RETURN) { 1100Sstevel@tonic-gate offs += epd->dtepd_size; 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate do { 1130Sstevel@tonic-gate if (offs >= buf->dtbd_size) { 1140Sstevel@tonic-gate /* 1150Sstevel@tonic-gate * We're at the end -- maybe. If the oldest 1160Sstevel@tonic-gate * record is non-zero, we need to wrap. 1170Sstevel@tonic-gate */ 1180Sstevel@tonic-gate if (buf->dtbd_oldest != 0) { 1190Sstevel@tonic-gate offs = 0; 1200Sstevel@tonic-gate } else { 1210Sstevel@tonic-gate goto out; 1220Sstevel@tonic-gate } 1230Sstevel@tonic-gate } 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate next = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate if (next == DTRACE_EPIDNONE) 1280Sstevel@tonic-gate offs += sizeof (id); 1290Sstevel@tonic-gate } while (next == DTRACE_EPIDNONE); 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate if ((rval = dt_epid_lookup(dtp, next, &nepd, &npd)) != 0) 1320Sstevel@tonic-gate return (rval); 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate if (next != id && npd->dtpd_id == pd->dtpd_id) 1350Sstevel@tonic-gate flow = DTRACEFLOW_NONE; 1360Sstevel@tonic-gate } 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate out: 1390Sstevel@tonic-gate if (flow == DTRACEFLOW_ENTRY || flow == DTRACEFLOW_RETURN) { 1400Sstevel@tonic-gate data->dtpda_prefix = str; 1410Sstevel@tonic-gate } else { 1420Sstevel@tonic-gate data->dtpda_prefix = "| "; 1430Sstevel@tonic-gate } 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate if (flow == DTRACEFLOW_RETURN && data->dtpda_indent > 0) 1460Sstevel@tonic-gate data->dtpda_indent -= 2; 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate data->dtpda_flow = flow; 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate return (0); 1510Sstevel@tonic-gate } 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate static int 1540Sstevel@tonic-gate dt_nullprobe() 1550Sstevel@tonic-gate { 1560Sstevel@tonic-gate return (DTRACE_CONSUME_THIS); 1570Sstevel@tonic-gate } 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate static int 1600Sstevel@tonic-gate dt_nullrec() 1610Sstevel@tonic-gate { 1620Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 1630Sstevel@tonic-gate } 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate int 166457Sbmc dt_print_quantline(dtrace_hdl_t *dtp, FILE *fp, int64_t val, 167457Sbmc uint64_t normal, long double total, char positives, char negatives) 168457Sbmc { 169457Sbmc long double f; 170457Sbmc uint_t depth, len = 40; 171457Sbmc 172457Sbmc const char *ats = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"; 173457Sbmc const char *spaces = " "; 174457Sbmc 175457Sbmc assert(strlen(ats) == len && strlen(spaces) == len); 176457Sbmc assert(!(total == 0 && (positives || negatives))); 177457Sbmc assert(!(val < 0 && !negatives)); 178457Sbmc assert(!(val > 0 && !positives)); 179457Sbmc assert(!(val != 0 && total == 0)); 180457Sbmc 181457Sbmc if (!negatives) { 182457Sbmc if (positives) { 183457Sbmc f = (dt_fabsl((long double)val) * len) / total; 184457Sbmc depth = (uint_t)(f + 0.5); 185457Sbmc } else { 186457Sbmc depth = 0; 187457Sbmc } 188457Sbmc 189457Sbmc return (dt_printf(dtp, fp, "|%s%s %-9lld\n", ats + len - depth, 190457Sbmc spaces + depth, (long long)val / normal)); 191457Sbmc } 192457Sbmc 193457Sbmc if (!positives) { 194457Sbmc f = (dt_fabsl((long double)val) * len) / total; 195457Sbmc depth = (uint_t)(f + 0.5); 196457Sbmc 197457Sbmc return (dt_printf(dtp, fp, "%s%s| %-9lld\n", spaces + depth, 198457Sbmc ats + len - depth, (long long)val / normal)); 199457Sbmc } 200457Sbmc 201457Sbmc /* 202457Sbmc * If we're here, we have both positive and negative bucket values. 203457Sbmc * To express this graphically, we're going to generate both positive 204457Sbmc * and negative bars separated by a centerline. These bars are half 205457Sbmc * the size of normal quantize()/lquantize() bars, so we divide the 206457Sbmc * length in half before calculating the bar length. 207457Sbmc */ 208457Sbmc len /= 2; 209457Sbmc ats = &ats[len]; 210457Sbmc spaces = &spaces[len]; 211457Sbmc 212457Sbmc f = (dt_fabsl((long double)val) * len) / total; 213457Sbmc depth = (uint_t)(f + 0.5); 214457Sbmc 215457Sbmc if (val <= 0) { 216457Sbmc return (dt_printf(dtp, fp, "%s%s|%*s %-9lld\n", spaces + depth, 217457Sbmc ats + len - depth, len, "", (long long)val / normal)); 218457Sbmc } else { 219457Sbmc return (dt_printf(dtp, fp, "%20s|%s%s %-9lld\n", "", 220457Sbmc ats + len - depth, spaces + depth, 221457Sbmc (long long)val / normal)); 222457Sbmc } 223457Sbmc } 224457Sbmc 225457Sbmc int 2260Sstevel@tonic-gate dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, 2270Sstevel@tonic-gate size_t size, uint64_t normal) 2280Sstevel@tonic-gate { 229457Sbmc const int64_t *data = addr; 2300Sstevel@tonic-gate int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1; 231457Sbmc long double total = 0; 232457Sbmc char positives = 0, negatives = 0; 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) 2350Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_DMISMATCH)); 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate while (first_bin < DTRACE_QUANTIZE_NBUCKETS - 1 && data[first_bin] == 0) 2380Sstevel@tonic-gate first_bin++; 2390Sstevel@tonic-gate 240457Sbmc if (first_bin == DTRACE_QUANTIZE_NBUCKETS - 1) { 241457Sbmc /* 242457Sbmc * There isn't any data. This is possible if (and only if) 243457Sbmc * negative increment values have been used. In this case, 244457Sbmc * we'll print the buckets around 0. 245457Sbmc */ 246457Sbmc first_bin = DTRACE_QUANTIZE_ZEROBUCKET - 1; 247457Sbmc last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 1; 248457Sbmc } else { 249457Sbmc if (first_bin > 0) 250457Sbmc first_bin--; 2510Sstevel@tonic-gate 252457Sbmc while (last_bin > 0 && data[last_bin] == 0) 253457Sbmc last_bin--; 2540Sstevel@tonic-gate 255457Sbmc if (last_bin < DTRACE_QUANTIZE_NBUCKETS - 1) 256457Sbmc last_bin++; 257457Sbmc } 258457Sbmc 259457Sbmc for (i = first_bin; i <= last_bin; i++) { 260457Sbmc positives |= (data[i] > 0); 261457Sbmc negatives |= (data[i] < 0); 262457Sbmc total += dt_fabsl((long double)data[i]); 263457Sbmc } 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", 2660Sstevel@tonic-gate "------------- Distribution -------------", "count") < 0) 2670Sstevel@tonic-gate return (-1); 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate for (i = first_bin; i <= last_bin; i++) { 270457Sbmc if (dt_printf(dtp, fp, "%16lld ", 271457Sbmc (long long)DTRACE_QUANTIZE_BUCKETVAL(i)) < 0) 272457Sbmc return (-1); 2730Sstevel@tonic-gate 274457Sbmc if (dt_print_quantline(dtp, fp, data[i], normal, total, 275457Sbmc positives, negatives) < 0) 2760Sstevel@tonic-gate return (-1); 2770Sstevel@tonic-gate } 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate return (0); 2800Sstevel@tonic-gate } 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate int 2830Sstevel@tonic-gate dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr, 2840Sstevel@tonic-gate size_t size, uint64_t normal) 2850Sstevel@tonic-gate { 286457Sbmc const int64_t *data = addr; 2870Sstevel@tonic-gate int i, first_bin, last_bin, base; 288457Sbmc uint64_t arg; 289457Sbmc long double total = 0; 2900Sstevel@tonic-gate uint16_t step, levels; 291457Sbmc char positives = 0, negatives = 0; 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate if (size < sizeof (uint64_t)) 2940Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_DMISMATCH)); 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate arg = *data++; 2970Sstevel@tonic-gate size -= sizeof (uint64_t); 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate base = DTRACE_LQUANTIZE_BASE(arg); 3000Sstevel@tonic-gate step = DTRACE_LQUANTIZE_STEP(arg); 3010Sstevel@tonic-gate levels = DTRACE_LQUANTIZE_LEVELS(arg); 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate first_bin = 0; 3040Sstevel@tonic-gate last_bin = levels + 1; 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate if (size != sizeof (uint64_t) * (levels + 2)) 3070Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_DMISMATCH)); 3080Sstevel@tonic-gate 309491Sbmc while (first_bin <= levels + 1 && data[first_bin] == 0) 3100Sstevel@tonic-gate first_bin++; 3110Sstevel@tonic-gate 312491Sbmc if (first_bin > levels + 1) { 313457Sbmc first_bin = 0; 314457Sbmc last_bin = 2; 315457Sbmc } else { 316457Sbmc if (first_bin > 0) 317457Sbmc first_bin--; 3180Sstevel@tonic-gate 319457Sbmc while (last_bin > 0 && data[last_bin] == 0) 320457Sbmc last_bin--; 3210Sstevel@tonic-gate 322457Sbmc if (last_bin < levels + 1) 323457Sbmc last_bin++; 324457Sbmc } 3250Sstevel@tonic-gate 326457Sbmc for (i = first_bin; i <= last_bin; i++) { 327457Sbmc positives |= (data[i] > 0); 328457Sbmc negatives |= (data[i] < 0); 329457Sbmc total += dt_fabsl((long double)data[i]); 330457Sbmc } 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value", 3330Sstevel@tonic-gate "------------- Distribution -------------", "count") < 0) 3340Sstevel@tonic-gate return (-1); 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate for (i = first_bin; i <= last_bin; i++) { 3370Sstevel@tonic-gate char c[32]; 3380Sstevel@tonic-gate int err; 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate if (i == 0) { 3410Sstevel@tonic-gate (void) snprintf(c, sizeof (c), "< %d", 3420Sstevel@tonic-gate base / (uint32_t)normal); 3430Sstevel@tonic-gate err = dt_printf(dtp, fp, "%16s ", c); 3440Sstevel@tonic-gate } else if (i == levels + 1) { 3450Sstevel@tonic-gate (void) snprintf(c, sizeof (c), ">= %d", 3460Sstevel@tonic-gate base + (levels * step)); 3470Sstevel@tonic-gate err = dt_printf(dtp, fp, "%16s ", c); 3480Sstevel@tonic-gate } else { 3490Sstevel@tonic-gate err = dt_printf(dtp, fp, "%16d ", 3500Sstevel@tonic-gate base + (i - 1) * step); 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 353457Sbmc if (err < 0 || dt_print_quantline(dtp, fp, data[i], normal, 354457Sbmc total, positives, negatives) < 0) 3550Sstevel@tonic-gate return (-1); 3560Sstevel@tonic-gate } 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate return (0); 3590Sstevel@tonic-gate } 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate /*ARGSUSED*/ 3620Sstevel@tonic-gate static int 3630Sstevel@tonic-gate dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, 3640Sstevel@tonic-gate size_t size, uint64_t normal) 3650Sstevel@tonic-gate { 3660Sstevel@tonic-gate /* LINTED - alignment */ 3670Sstevel@tonic-gate uint64_t *data = (uint64_t *)addr; 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate return (dt_printf(dtp, fp, " %16lld", data[0] ? 3700Sstevel@tonic-gate (long long)(data[1] / normal / data[0]) : 0)); 3710Sstevel@tonic-gate } 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate /*ARGSUSED*/ 3740Sstevel@tonic-gate int 3750Sstevel@tonic-gate dt_print_bytes(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, 3760Sstevel@tonic-gate size_t nbytes, int width, int quiet) 3770Sstevel@tonic-gate { 3780Sstevel@tonic-gate /* 3790Sstevel@tonic-gate * If the byte stream is a series of printable characters, followed by 3800Sstevel@tonic-gate * a terminating byte, we print it out as a string. Otherwise, we 3810Sstevel@tonic-gate * assume that it's something else and just print the bytes. 3820Sstevel@tonic-gate */ 3830Sstevel@tonic-gate int i, j, margin = 5; 3840Sstevel@tonic-gate char *c = (char *)addr; 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate if (nbytes == 0) 3870Sstevel@tonic-gate return (0); 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate if (dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET) 3900Sstevel@tonic-gate goto raw; 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate for (i = 0; i < nbytes; i++) { 3930Sstevel@tonic-gate /* 3940Sstevel@tonic-gate * We define a "printable character" to be one for which 3950Sstevel@tonic-gate * isprint(3C) returns non-zero, isspace(3C) returns non-zero, 3960Sstevel@tonic-gate * or a character which is either backspace or the bell. 3970Sstevel@tonic-gate * Backspace and the bell are regrettably special because 3980Sstevel@tonic-gate * they fail the first two tests -- and yet they are entirely 3990Sstevel@tonic-gate * printable. These are the only two control characters that 4000Sstevel@tonic-gate * have meaning for the terminal and for which isprint(3C) and 4010Sstevel@tonic-gate * isspace(3C) return 0. 4020Sstevel@tonic-gate */ 4030Sstevel@tonic-gate if (isprint(c[i]) || isspace(c[i]) || 4040Sstevel@tonic-gate c[i] == '\b' || c[i] == '\a') 4050Sstevel@tonic-gate continue; 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate if (c[i] == '\0' && i > 0) { 4080Sstevel@tonic-gate /* 4090Sstevel@tonic-gate * This looks like it might be a string. Before we 4100Sstevel@tonic-gate * assume that it is indeed a string, check the 4110Sstevel@tonic-gate * remainder of the byte range; if it contains 4120Sstevel@tonic-gate * additional non-nul characters, we'll assume that 4130Sstevel@tonic-gate * it's a binary stream that just happens to look like 4140Sstevel@tonic-gate * a string, and we'll print out the individual bytes. 4150Sstevel@tonic-gate */ 4160Sstevel@tonic-gate for (j = i + 1; j < nbytes; j++) { 4170Sstevel@tonic-gate if (c[j] != '\0') 4180Sstevel@tonic-gate break; 4190Sstevel@tonic-gate } 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate if (j != nbytes) 4220Sstevel@tonic-gate break; 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate if (quiet) 4250Sstevel@tonic-gate return (dt_printf(dtp, fp, "%s", c)); 4260Sstevel@tonic-gate else 4270Sstevel@tonic-gate return (dt_printf(dtp, fp, " %-*s", width, c)); 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate break; 4310Sstevel@tonic-gate } 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate if (i == nbytes) { 4340Sstevel@tonic-gate /* 4350Sstevel@tonic-gate * The byte range is all printable characters, but there is 4360Sstevel@tonic-gate * no trailing nul byte. We'll assume that it's a string and 4370Sstevel@tonic-gate * print it as such. 4380Sstevel@tonic-gate */ 4390Sstevel@tonic-gate char *s = alloca(nbytes + 1); 4400Sstevel@tonic-gate bcopy(c, s, nbytes); 4410Sstevel@tonic-gate s[nbytes] = '\0'; 4420Sstevel@tonic-gate return (dt_printf(dtp, fp, " %-*s", width, s)); 4430Sstevel@tonic-gate } 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate raw: 4460Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n%*s ", margin, "") < 0) 4470Sstevel@tonic-gate return (-1); 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate for (i = 0; i < 16; i++) 4500Sstevel@tonic-gate if (dt_printf(dtp, fp, " %c", "0123456789abcdef"[i]) < 0) 4510Sstevel@tonic-gate return (-1); 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate if (dt_printf(dtp, fp, " 0123456789abcdef\n") < 0) 4540Sstevel@tonic-gate return (-1); 4550Sstevel@tonic-gate 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate for (i = 0; i < nbytes; i += 16) { 4580Sstevel@tonic-gate if (dt_printf(dtp, fp, "%*s%5x:", margin, "", i) < 0) 4590Sstevel@tonic-gate return (-1); 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate for (j = i; j < i + 16 && j < nbytes; j++) { 4620Sstevel@tonic-gate if (dt_printf(dtp, fp, " %02x", (uchar_t)c[j]) < 0) 4630Sstevel@tonic-gate return (-1); 4640Sstevel@tonic-gate } 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate while (j++ % 16) { 4670Sstevel@tonic-gate if (dt_printf(dtp, fp, " ") < 0) 4680Sstevel@tonic-gate return (-1); 4690Sstevel@tonic-gate } 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate if (dt_printf(dtp, fp, " ") < 0) 4720Sstevel@tonic-gate return (-1); 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate for (j = i; j < i + 16 && j < nbytes; j++) { 4750Sstevel@tonic-gate if (dt_printf(dtp, fp, "%c", 4760Sstevel@tonic-gate c[j] < ' ' || c[j] > '~' ? '.' : c[j]) < 0) 4770Sstevel@tonic-gate return (-1); 4780Sstevel@tonic-gate } 4790Sstevel@tonic-gate 4800Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n") < 0) 4810Sstevel@tonic-gate return (-1); 4820Sstevel@tonic-gate } 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate return (0); 4850Sstevel@tonic-gate } 4860Sstevel@tonic-gate 4870Sstevel@tonic-gate int 4880Sstevel@tonic-gate dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format, 489457Sbmc caddr_t addr, int depth, int size) 4900Sstevel@tonic-gate { 4910Sstevel@tonic-gate dtrace_syminfo_t dts; 4920Sstevel@tonic-gate GElf_Sym sym; 4930Sstevel@tonic-gate int i, indent; 4940Sstevel@tonic-gate char c[PATH_MAX * 2]; 495457Sbmc uint64_t pc; 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n") < 0) 4980Sstevel@tonic-gate return (-1); 4990Sstevel@tonic-gate 5000Sstevel@tonic-gate if (format == NULL) 5010Sstevel@tonic-gate format = "%s"; 5020Sstevel@tonic-gate 5030Sstevel@tonic-gate if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) 5040Sstevel@tonic-gate indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; 5050Sstevel@tonic-gate else 5060Sstevel@tonic-gate indent = _dtrace_stkindent; 5070Sstevel@tonic-gate 508457Sbmc for (i = 0; i < depth; i++) { 509457Sbmc switch (size) { 510457Sbmc case sizeof (uint32_t): 511457Sbmc /* LINTED - alignment */ 512457Sbmc pc = *((uint32_t *)addr); 513457Sbmc break; 514457Sbmc 515457Sbmc case sizeof (uint64_t): 516457Sbmc /* LINTED - alignment */ 517457Sbmc pc = *((uint64_t *)addr); 518457Sbmc break; 519457Sbmc 520457Sbmc default: 521457Sbmc return (dt_set_errno(dtp, EDT_BADSTACKPC)); 522457Sbmc } 523457Sbmc 524457Sbmc if (pc == NULL) 525457Sbmc break; 526457Sbmc 527457Sbmc addr += size; 528457Sbmc 5290Sstevel@tonic-gate if (dt_printf(dtp, fp, "%*s", indent, "") < 0) 5300Sstevel@tonic-gate return (-1); 5310Sstevel@tonic-gate 532457Sbmc if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { 533457Sbmc if (pc > sym.st_value) { 5340Sstevel@tonic-gate (void) snprintf(c, sizeof (c), "%s`%s+0x%llx", 5350Sstevel@tonic-gate dts.dts_object, dts.dts_name, 536457Sbmc pc - sym.st_value); 5370Sstevel@tonic-gate } else { 5380Sstevel@tonic-gate (void) snprintf(c, sizeof (c), "%s`%s", 5390Sstevel@tonic-gate dts.dts_object, dts.dts_name); 5400Sstevel@tonic-gate } 5410Sstevel@tonic-gate } else { 5420Sstevel@tonic-gate /* 5430Sstevel@tonic-gate * We'll repeat the lookup, but this time we'll specify 5440Sstevel@tonic-gate * a NULL GElf_Sym -- indicating that we're only 5450Sstevel@tonic-gate * interested in the containing module. 5460Sstevel@tonic-gate */ 547457Sbmc if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { 5480Sstevel@tonic-gate (void) snprintf(c, sizeof (c), "%s`0x%llx", 549457Sbmc dts.dts_object, pc); 5500Sstevel@tonic-gate } else { 551457Sbmc (void) snprintf(c, sizeof (c), "0x%llx", pc); 5520Sstevel@tonic-gate } 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate if (dt_printf(dtp, fp, format, c) < 0) 5560Sstevel@tonic-gate return (-1); 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n") < 0) 5590Sstevel@tonic-gate return (-1); 5600Sstevel@tonic-gate } 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate return (0); 5630Sstevel@tonic-gate } 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate int 5660Sstevel@tonic-gate dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format, 5670Sstevel@tonic-gate caddr_t addr, uint64_t arg) 5680Sstevel@tonic-gate { 569457Sbmc /* LINTED - alignment */ 570457Sbmc uint64_t *pc = (uint64_t *)addr; 5710Sstevel@tonic-gate uint32_t depth = DTRACE_USTACK_NFRAMES(arg); 5720Sstevel@tonic-gate uint32_t strsize = DTRACE_USTACK_STRSIZE(arg); 5730Sstevel@tonic-gate const char *strbase = addr + (depth + 1) * sizeof (uint64_t); 5740Sstevel@tonic-gate const char *str = strsize ? strbase : NULL; 5750Sstevel@tonic-gate int err = 0; 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2]; 5780Sstevel@tonic-gate struct ps_prochandle *P; 5790Sstevel@tonic-gate GElf_Sym sym; 5800Sstevel@tonic-gate int i, indent; 5810Sstevel@tonic-gate pid_t pid; 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate if (depth == 0) 5840Sstevel@tonic-gate return (0); 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate pid = (pid_t)*pc++; 5870Sstevel@tonic-gate 5880Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n") < 0) 5890Sstevel@tonic-gate return (-1); 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate if (format == NULL) 5920Sstevel@tonic-gate format = "%s"; 5930Sstevel@tonic-gate 5940Sstevel@tonic-gate if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET) 5950Sstevel@tonic-gate indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT]; 5960Sstevel@tonic-gate else 5970Sstevel@tonic-gate indent = _dtrace_stkindent; 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate /* 6000Sstevel@tonic-gate * Ultimately, we need to add an entry point in the library vector for 6010Sstevel@tonic-gate * determining <symbol, offset> from <pid, address>. For now, if 6020Sstevel@tonic-gate * this is a vector open, we just print the raw address or string. 6030Sstevel@tonic-gate */ 6040Sstevel@tonic-gate if (dtp->dt_vector == NULL) 6050Sstevel@tonic-gate P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); 6060Sstevel@tonic-gate else 6070Sstevel@tonic-gate P = NULL; 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate if (P != NULL) 6100Sstevel@tonic-gate dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ 6110Sstevel@tonic-gate 6120Sstevel@tonic-gate for (i = 0; i < depth && pc[i] != NULL; i++) { 613457Sbmc const prmap_t *map; 614457Sbmc 6150Sstevel@tonic-gate if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) 6160Sstevel@tonic-gate break; 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate if (P != NULL && Plookup_by_addr(P, pc[i], 6190Sstevel@tonic-gate name, sizeof (name), &sym) == 0) { 6200Sstevel@tonic-gate (void) Pobjname(P, pc[i], objname, sizeof (objname)); 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate if (pc[i] > sym.st_value) { 6230Sstevel@tonic-gate (void) snprintf(c, sizeof (c), 6240Sstevel@tonic-gate "%s`%s+0x%llx", dt_basename(objname), name, 6250Sstevel@tonic-gate (u_longlong_t)(pc[i] - sym.st_value)); 6260Sstevel@tonic-gate } else { 6270Sstevel@tonic-gate (void) snprintf(c, sizeof (c), 6280Sstevel@tonic-gate "%s`%s", dt_basename(objname), name); 6290Sstevel@tonic-gate } 630491Sbmc } else if (str != NULL && str[0] != '\0' && str[0] != '@' && 631457Sbmc (P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL || 632457Sbmc (map->pr_mflags & MA_WRITE)))) { 633457Sbmc /* 634457Sbmc * If the current string pointer in the string table 635457Sbmc * does not point to an empty string _and_ the program 636457Sbmc * counter falls in a writable region, we'll use the 637457Sbmc * string from the string table instead of the raw 638457Sbmc * address. This last condition is necessary because 639457Sbmc * some (broken) ustack helpers will return a string 640457Sbmc * even for a program counter that they can't 641457Sbmc * identify. If we have a string for a program 642457Sbmc * counter that falls in a segment that isn't 643457Sbmc * writable, we assume that we have fallen into this 644457Sbmc * case and we refuse to use the string. 645457Sbmc */ 6460Sstevel@tonic-gate (void) snprintf(c, sizeof (c), "%s", str); 6470Sstevel@tonic-gate } else { 6480Sstevel@tonic-gate if (P != NULL && Pobjname(P, pc[i], objname, 6490Sstevel@tonic-gate sizeof (objname)) != NULL) { 6500Sstevel@tonic-gate (void) snprintf(c, sizeof (c), "%s`0x%llx", 6510Sstevel@tonic-gate dt_basename(objname), (u_longlong_t)pc[i]); 6520Sstevel@tonic-gate } else { 6530Sstevel@tonic-gate (void) snprintf(c, sizeof (c), "0x%llx", 6540Sstevel@tonic-gate (u_longlong_t)pc[i]); 6550Sstevel@tonic-gate } 6560Sstevel@tonic-gate } 6570Sstevel@tonic-gate 6580Sstevel@tonic-gate if ((err = dt_printf(dtp, fp, format, c)) < 0) 6590Sstevel@tonic-gate break; 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate if ((err = dt_printf(dtp, fp, "\n")) < 0) 6620Sstevel@tonic-gate break; 6630Sstevel@tonic-gate 664491Sbmc if (str != NULL && str[0] == '@') { 665491Sbmc /* 666491Sbmc * If the first character of the string is an "at" sign, 667491Sbmc * then the string is inferred to be an annotation -- 668491Sbmc * and it is printed out beneath the frame and offset 669491Sbmc * with brackets. 670491Sbmc */ 671491Sbmc if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) 672491Sbmc break; 673491Sbmc 674491Sbmc (void) snprintf(c, sizeof (c), " [ %s ]", &str[1]); 675491Sbmc 676491Sbmc if ((err = dt_printf(dtp, fp, format, c)) < 0) 677491Sbmc break; 678491Sbmc 679491Sbmc if ((err = dt_printf(dtp, fp, "\n")) < 0) 680491Sbmc break; 681491Sbmc } 682491Sbmc 6830Sstevel@tonic-gate if (str != NULL) { 6840Sstevel@tonic-gate str += strlen(str) + 1; 6850Sstevel@tonic-gate if (str - strbase >= strsize) 6860Sstevel@tonic-gate str = NULL; 6870Sstevel@tonic-gate } 6880Sstevel@tonic-gate } 6890Sstevel@tonic-gate 6900Sstevel@tonic-gate if (P != NULL) { 6910Sstevel@tonic-gate dt_proc_unlock(dtp, P); 6920Sstevel@tonic-gate dt_proc_release(dtp, P); 6930Sstevel@tonic-gate } 6940Sstevel@tonic-gate 6950Sstevel@tonic-gate return (err); 6960Sstevel@tonic-gate } 6970Sstevel@tonic-gate 698457Sbmc static int 699457Sbmc dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act) 700457Sbmc { 701457Sbmc /* LINTED - alignment */ 702457Sbmc uint64_t pid = ((uint64_t *)addr)[0]; 703457Sbmc /* LINTED - alignment */ 704457Sbmc uint64_t pc = ((uint64_t *)addr)[1]; 705457Sbmc const char *format = " %-50s"; 706457Sbmc char *s; 707457Sbmc int n, len = 256; 708457Sbmc 709457Sbmc if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) { 710457Sbmc struct ps_prochandle *P; 711457Sbmc 712457Sbmc if ((P = dt_proc_grab(dtp, pid, 713457Sbmc PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) { 714457Sbmc GElf_Sym sym; 715457Sbmc 716457Sbmc dt_proc_lock(dtp, P); 717457Sbmc 718457Sbmc if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0) 719457Sbmc pc = sym.st_value; 720457Sbmc 721457Sbmc dt_proc_unlock(dtp, P); 722457Sbmc dt_proc_release(dtp, P); 723457Sbmc } 724457Sbmc } 725457Sbmc 726457Sbmc do { 727457Sbmc n = len; 728457Sbmc s = alloca(n); 729457Sbmc } while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) >= n); 730457Sbmc 731457Sbmc return (dt_printf(dtp, fp, format, s)); 732457Sbmc } 733457Sbmc 734457Sbmc int 735457Sbmc dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) 736457Sbmc { 737457Sbmc /* LINTED - alignment */ 738457Sbmc uint64_t pid = ((uint64_t *)addr)[0]; 739457Sbmc /* LINTED - alignment */ 740457Sbmc uint64_t pc = ((uint64_t *)addr)[1]; 741457Sbmc int err = 0; 742457Sbmc 743457Sbmc char objname[PATH_MAX], c[PATH_MAX * 2]; 744457Sbmc struct ps_prochandle *P; 745457Sbmc 746457Sbmc if (format == NULL) 747457Sbmc format = " %-50s"; 748457Sbmc 749457Sbmc /* 750457Sbmc * See the comment in dt_print_ustack() for the rationale for 751457Sbmc * printing raw addresses in the vectored case. 752457Sbmc */ 753457Sbmc if (dtp->dt_vector == NULL) 754457Sbmc P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0); 755457Sbmc else 756457Sbmc P = NULL; 757457Sbmc 758457Sbmc if (P != NULL) 759457Sbmc dt_proc_lock(dtp, P); /* lock handle while we perform lookups */ 760457Sbmc 761457Sbmc if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != NULL) { 762457Sbmc (void) snprintf(c, sizeof (c), "%s", dt_basename(objname)); 763457Sbmc } else { 764457Sbmc (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); 765457Sbmc } 766457Sbmc 767457Sbmc err = dt_printf(dtp, fp, format, c); 768457Sbmc 769457Sbmc if (P != NULL) { 770457Sbmc dt_proc_unlock(dtp, P); 771457Sbmc dt_proc_release(dtp, P); 772457Sbmc } 773457Sbmc 774457Sbmc return (err); 775457Sbmc } 776457Sbmc 777457Sbmc static int 778457Sbmc dt_print_sym(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) 779457Sbmc { 780457Sbmc /* LINTED - alignment */ 781457Sbmc uint64_t pc = *((uint64_t *)addr); 782457Sbmc dtrace_syminfo_t dts; 783457Sbmc GElf_Sym sym; 784457Sbmc char c[PATH_MAX * 2]; 785457Sbmc 786457Sbmc if (format == NULL) 787457Sbmc format = " %-50s"; 788457Sbmc 789457Sbmc if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) { 790457Sbmc (void) snprintf(c, sizeof (c), "%s`%s", 791457Sbmc dts.dts_object, dts.dts_name); 792457Sbmc } else { 793457Sbmc /* 794457Sbmc * We'll repeat the lookup, but this time we'll specify a 795457Sbmc * NULL GElf_Sym -- indicating that we're only interested in 796457Sbmc * the containing module. 797457Sbmc */ 798457Sbmc if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { 799457Sbmc (void) snprintf(c, sizeof (c), "%s`0x%llx", 800457Sbmc dts.dts_object, (u_longlong_t)pc); 801457Sbmc } else { 802457Sbmc (void) snprintf(c, sizeof (c), "0x%llx", 803457Sbmc (u_longlong_t)pc); 804457Sbmc } 805457Sbmc } 806457Sbmc 807457Sbmc if (dt_printf(dtp, fp, format, c) < 0) 808457Sbmc return (-1); 809457Sbmc 810457Sbmc return (0); 811457Sbmc } 812457Sbmc 813457Sbmc int 814457Sbmc dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr) 815457Sbmc { 816457Sbmc /* LINTED - alignment */ 817457Sbmc uint64_t pc = *((uint64_t *)addr); 818457Sbmc dtrace_syminfo_t dts; 819457Sbmc char c[PATH_MAX * 2]; 820457Sbmc 821457Sbmc if (format == NULL) 822457Sbmc format = " %-50s"; 823457Sbmc 824457Sbmc if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) { 825457Sbmc (void) snprintf(c, sizeof (c), "%s", dts.dts_object); 826457Sbmc } else { 827457Sbmc (void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc); 828457Sbmc } 829457Sbmc 830457Sbmc if (dt_printf(dtp, fp, format, c) < 0) 831457Sbmc return (-1); 832457Sbmc 833457Sbmc return (0); 834457Sbmc } 835457Sbmc 8360Sstevel@tonic-gate typedef struct dt_normal { 8370Sstevel@tonic-gate dtrace_aggvarid_t dtnd_id; 8380Sstevel@tonic-gate uint64_t dtnd_normal; 8390Sstevel@tonic-gate } dt_normal_t; 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate static int 842457Sbmc dt_normalize_agg(const dtrace_aggdata_t *aggdata, void *arg) 8430Sstevel@tonic-gate { 8440Sstevel@tonic-gate dt_normal_t *normal = arg; 8450Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 8460Sstevel@tonic-gate dtrace_aggvarid_t id = normal->dtnd_id; 8470Sstevel@tonic-gate 8480Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 8490Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 8500Sstevel@tonic-gate 851*1017Sbmc if (agg->dtagd_varid != id) 8520Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 8530Sstevel@tonic-gate 854457Sbmc ((dtrace_aggdata_t *)aggdata)->dtada_normal = normal->dtnd_normal; 8550Sstevel@tonic-gate return (DTRACE_AGGWALK_NORMALIZE); 8560Sstevel@tonic-gate } 8570Sstevel@tonic-gate 8580Sstevel@tonic-gate static int 8590Sstevel@tonic-gate dt_normalize(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) 8600Sstevel@tonic-gate { 8610Sstevel@tonic-gate dt_normal_t normal; 8620Sstevel@tonic-gate caddr_t addr; 8630Sstevel@tonic-gate 8640Sstevel@tonic-gate /* 8650Sstevel@tonic-gate * We (should) have two records: the aggregation ID followed by the 8660Sstevel@tonic-gate * normalization value. 8670Sstevel@tonic-gate */ 8680Sstevel@tonic-gate addr = base + rec->dtrd_offset; 8690Sstevel@tonic-gate 8700Sstevel@tonic-gate if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) 8710Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 8720Sstevel@tonic-gate 8730Sstevel@tonic-gate /* LINTED - alignment */ 8740Sstevel@tonic-gate normal.dtnd_id = *((dtrace_aggvarid_t *)addr); 8750Sstevel@tonic-gate rec++; 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate if (rec->dtrd_action != DTRACEACT_LIBACT) 8780Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 8790Sstevel@tonic-gate 8800Sstevel@tonic-gate if (rec->dtrd_arg != DT_ACT_NORMALIZE) 8810Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 8820Sstevel@tonic-gate 8830Sstevel@tonic-gate addr = base + rec->dtrd_offset; 8840Sstevel@tonic-gate 8850Sstevel@tonic-gate switch (rec->dtrd_size) { 8860Sstevel@tonic-gate case sizeof (uint64_t): 8870Sstevel@tonic-gate /* LINTED - alignment */ 8880Sstevel@tonic-gate normal.dtnd_normal = *((uint64_t *)addr); 8890Sstevel@tonic-gate break; 8900Sstevel@tonic-gate case sizeof (uint32_t): 8910Sstevel@tonic-gate /* LINTED - alignment */ 8920Sstevel@tonic-gate normal.dtnd_normal = *((uint32_t *)addr); 8930Sstevel@tonic-gate break; 8940Sstevel@tonic-gate case sizeof (uint16_t): 8950Sstevel@tonic-gate /* LINTED - alignment */ 8960Sstevel@tonic-gate normal.dtnd_normal = *((uint16_t *)addr); 8970Sstevel@tonic-gate break; 8980Sstevel@tonic-gate case sizeof (uint8_t): 8990Sstevel@tonic-gate normal.dtnd_normal = *((uint8_t *)addr); 9000Sstevel@tonic-gate break; 9010Sstevel@tonic-gate default: 9020Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 9030Sstevel@tonic-gate } 9040Sstevel@tonic-gate 9050Sstevel@tonic-gate (void) dtrace_aggregate_walk(dtp, dt_normalize_agg, &normal); 9060Sstevel@tonic-gate 9070Sstevel@tonic-gate return (0); 9080Sstevel@tonic-gate } 9090Sstevel@tonic-gate 9100Sstevel@tonic-gate static int 911457Sbmc dt_denormalize_agg(const dtrace_aggdata_t *aggdata, void *arg) 9120Sstevel@tonic-gate { 9130Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 9140Sstevel@tonic-gate dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 9170Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9180Sstevel@tonic-gate 919*1017Sbmc if (agg->dtagd_varid != id) 9200Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9210Sstevel@tonic-gate 9220Sstevel@tonic-gate return (DTRACE_AGGWALK_DENORMALIZE); 9230Sstevel@tonic-gate } 9240Sstevel@tonic-gate 9250Sstevel@tonic-gate static int 926457Sbmc dt_clear_agg(const dtrace_aggdata_t *aggdata, void *arg) 9270Sstevel@tonic-gate { 9280Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 9290Sstevel@tonic-gate dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); 9300Sstevel@tonic-gate 9310Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 9320Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9330Sstevel@tonic-gate 934*1017Sbmc if (agg->dtagd_varid != id) 9350Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9360Sstevel@tonic-gate 9370Sstevel@tonic-gate return (DTRACE_AGGWALK_CLEAR); 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate typedef struct dt_trunc { 9410Sstevel@tonic-gate dtrace_aggvarid_t dttd_id; 9420Sstevel@tonic-gate uint64_t dttd_remaining; 9430Sstevel@tonic-gate } dt_trunc_t; 9440Sstevel@tonic-gate 9450Sstevel@tonic-gate static int 946457Sbmc dt_trunc_agg(const dtrace_aggdata_t *aggdata, void *arg) 9470Sstevel@tonic-gate { 9480Sstevel@tonic-gate dt_trunc_t *trunc = arg; 9490Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 9500Sstevel@tonic-gate dtrace_aggvarid_t id = trunc->dttd_id; 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 9530Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9540Sstevel@tonic-gate 955*1017Sbmc if (agg->dtagd_varid != id) 9560Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9570Sstevel@tonic-gate 9580Sstevel@tonic-gate if (trunc->dttd_remaining == 0) 9590Sstevel@tonic-gate return (DTRACE_AGGWALK_REMOVE); 9600Sstevel@tonic-gate 9610Sstevel@tonic-gate trunc->dttd_remaining--; 9620Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate static int 9660Sstevel@tonic-gate dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) 9670Sstevel@tonic-gate { 9680Sstevel@tonic-gate dt_trunc_t trunc; 9690Sstevel@tonic-gate caddr_t addr; 9700Sstevel@tonic-gate int64_t remaining; 9710Sstevel@tonic-gate int (*func)(dtrace_hdl_t *, dtrace_aggregate_f *, void *); 9720Sstevel@tonic-gate 9730Sstevel@tonic-gate /* 9740Sstevel@tonic-gate * We (should) have two records: the aggregation ID followed by the 9750Sstevel@tonic-gate * number of aggregation entries after which the aggregation is to be 9760Sstevel@tonic-gate * truncated. 9770Sstevel@tonic-gate */ 9780Sstevel@tonic-gate addr = base + rec->dtrd_offset; 9790Sstevel@tonic-gate 9800Sstevel@tonic-gate if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) 9810Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADTRUNC)); 9820Sstevel@tonic-gate 9830Sstevel@tonic-gate /* LINTED - alignment */ 9840Sstevel@tonic-gate trunc.dttd_id = *((dtrace_aggvarid_t *)addr); 9850Sstevel@tonic-gate rec++; 9860Sstevel@tonic-gate 9870Sstevel@tonic-gate if (rec->dtrd_action != DTRACEACT_LIBACT) 9880Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADTRUNC)); 9890Sstevel@tonic-gate 9900Sstevel@tonic-gate if (rec->dtrd_arg != DT_ACT_TRUNC) 9910Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADTRUNC)); 9920Sstevel@tonic-gate 9930Sstevel@tonic-gate addr = base + rec->dtrd_offset; 9940Sstevel@tonic-gate 9950Sstevel@tonic-gate switch (rec->dtrd_size) { 9960Sstevel@tonic-gate case sizeof (uint64_t): 9970Sstevel@tonic-gate /* LINTED - alignment */ 9980Sstevel@tonic-gate remaining = *((int64_t *)addr); 9990Sstevel@tonic-gate break; 10000Sstevel@tonic-gate case sizeof (uint32_t): 10010Sstevel@tonic-gate /* LINTED - alignment */ 10020Sstevel@tonic-gate remaining = *((int32_t *)addr); 10030Sstevel@tonic-gate break; 10040Sstevel@tonic-gate case sizeof (uint16_t): 10050Sstevel@tonic-gate /* LINTED - alignment */ 10060Sstevel@tonic-gate remaining = *((int16_t *)addr); 10070Sstevel@tonic-gate break; 10080Sstevel@tonic-gate case sizeof (uint8_t): 10090Sstevel@tonic-gate remaining = *((int8_t *)addr); 10100Sstevel@tonic-gate break; 10110Sstevel@tonic-gate default: 10120Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 10130Sstevel@tonic-gate } 10140Sstevel@tonic-gate 10150Sstevel@tonic-gate if (remaining < 0) { 10160Sstevel@tonic-gate func = dtrace_aggregate_walk_valsorted; 10170Sstevel@tonic-gate remaining = -remaining; 10180Sstevel@tonic-gate } else { 10190Sstevel@tonic-gate func = dtrace_aggregate_walk_valrevsorted; 10200Sstevel@tonic-gate } 10210Sstevel@tonic-gate 10220Sstevel@tonic-gate assert(remaining >= 0); 10230Sstevel@tonic-gate trunc.dttd_remaining = remaining; 10240Sstevel@tonic-gate 10250Sstevel@tonic-gate (void) func(dtp, dt_trunc_agg, &trunc); 10260Sstevel@tonic-gate 10270Sstevel@tonic-gate return (0); 10280Sstevel@tonic-gate } 10290Sstevel@tonic-gate 1030*1017Sbmc static int 1031*1017Sbmc dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, 1032*1017Sbmc caddr_t addr, size_t size, uint64_t normal) 1033*1017Sbmc { 1034*1017Sbmc int err; 1035*1017Sbmc dtrace_actkind_t act = rec->dtrd_action; 1036*1017Sbmc 1037*1017Sbmc switch (act) { 1038*1017Sbmc case DTRACEACT_STACK: 1039*1017Sbmc return (dt_print_stack(dtp, fp, NULL, addr, 1040*1017Sbmc rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg)); 1041*1017Sbmc 1042*1017Sbmc case DTRACEACT_USTACK: 1043*1017Sbmc case DTRACEACT_JSTACK: 1044*1017Sbmc return (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg)); 1045*1017Sbmc 1046*1017Sbmc case DTRACEACT_USYM: 1047*1017Sbmc case DTRACEACT_UADDR: 1048*1017Sbmc return (dt_print_usym(dtp, fp, addr, act)); 1049*1017Sbmc 1050*1017Sbmc case DTRACEACT_UMOD: 1051*1017Sbmc return (dt_print_umod(dtp, fp, NULL, addr)); 1052*1017Sbmc 1053*1017Sbmc case DTRACEACT_SYM: 1054*1017Sbmc return (dt_print_sym(dtp, fp, NULL, addr)); 1055*1017Sbmc 1056*1017Sbmc case DTRACEACT_MOD: 1057*1017Sbmc return (dt_print_mod(dtp, fp, NULL, addr)); 1058*1017Sbmc 1059*1017Sbmc case DTRACEAGG_QUANTIZE: 1060*1017Sbmc return (dt_print_quantize(dtp, fp, addr, size, normal)); 1061*1017Sbmc 1062*1017Sbmc case DTRACEAGG_LQUANTIZE: 1063*1017Sbmc return (dt_print_lquantize(dtp, fp, addr, size, normal)); 1064*1017Sbmc 1065*1017Sbmc case DTRACEAGG_AVG: 1066*1017Sbmc return (dt_print_average(dtp, fp, addr, size, normal)); 1067*1017Sbmc 1068*1017Sbmc default: 1069*1017Sbmc break; 1070*1017Sbmc } 1071*1017Sbmc 1072*1017Sbmc switch (size) { 1073*1017Sbmc case sizeof (uint64_t): 1074*1017Sbmc err = dt_printf(dtp, fp, " %16lld", 1075*1017Sbmc /* LINTED - alignment */ 1076*1017Sbmc (long long)*((uint64_t *)addr) / normal); 1077*1017Sbmc break; 1078*1017Sbmc case sizeof (uint32_t): 1079*1017Sbmc /* LINTED - alignment */ 1080*1017Sbmc err = dt_printf(dtp, fp, " %8d", *((uint32_t *)addr) / 1081*1017Sbmc (uint32_t)normal); 1082*1017Sbmc break; 1083*1017Sbmc case sizeof (uint16_t): 1084*1017Sbmc /* LINTED - alignment */ 1085*1017Sbmc err = dt_printf(dtp, fp, " %5d", *((uint16_t *)addr) / 1086*1017Sbmc (uint32_t)normal); 1087*1017Sbmc break; 1088*1017Sbmc case sizeof (uint8_t): 1089*1017Sbmc err = dt_printf(dtp, fp, " %3d", *((uint8_t *)addr) / 1090*1017Sbmc (uint32_t)normal); 1091*1017Sbmc break; 1092*1017Sbmc default: 1093*1017Sbmc err = dt_print_bytes(dtp, fp, addr, size, 50, 0); 1094*1017Sbmc break; 1095*1017Sbmc } 1096*1017Sbmc 1097*1017Sbmc return (err); 1098*1017Sbmc } 1099*1017Sbmc 1100*1017Sbmc int 1101*1017Sbmc dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg) 1102*1017Sbmc { 1103*1017Sbmc int i, aggact = 0; 1104*1017Sbmc dt_print_aggdata_t *pd = arg; 1105*1017Sbmc const dtrace_aggdata_t *aggdata = aggsdata[0]; 1106*1017Sbmc dtrace_aggdesc_t *agg = aggdata->dtada_desc; 1107*1017Sbmc FILE *fp = pd->dtpa_fp; 1108*1017Sbmc dtrace_hdl_t *dtp = pd->dtpa_dtp; 1109*1017Sbmc dtrace_recdesc_t *rec; 1110*1017Sbmc dtrace_actkind_t act; 1111*1017Sbmc caddr_t addr; 1112*1017Sbmc size_t size; 1113*1017Sbmc 1114*1017Sbmc /* 1115*1017Sbmc * Iterate over each record description in the key, printing the traced 1116*1017Sbmc * data, skipping the first datum (the tuple member created by the 1117*1017Sbmc * compiler). 1118*1017Sbmc */ 1119*1017Sbmc for (i = 1; i < agg->dtagd_nrecs; i++) { 1120*1017Sbmc rec = &agg->dtagd_rec[i]; 1121*1017Sbmc act = rec->dtrd_action; 1122*1017Sbmc addr = aggdata->dtada_data + rec->dtrd_offset; 1123*1017Sbmc size = rec->dtrd_size; 1124*1017Sbmc 1125*1017Sbmc if (DTRACEACT_ISAGG(act)) { 1126*1017Sbmc aggact = i; 1127*1017Sbmc break; 1128*1017Sbmc } 1129*1017Sbmc 1130*1017Sbmc if (dt_print_datum(dtp, fp, rec, addr, size, 1) < 0) 1131*1017Sbmc return (-1); 1132*1017Sbmc 1133*1017Sbmc if (dt_buffered_flush(dtp, NULL, rec, aggdata, 1134*1017Sbmc DTRACE_BUFDATA_AGGKEY) < 0) 1135*1017Sbmc return (-1); 1136*1017Sbmc } 1137*1017Sbmc 1138*1017Sbmc assert(aggact != 0); 1139*1017Sbmc 1140*1017Sbmc for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) { 1141*1017Sbmc uint64_t normal; 1142*1017Sbmc 1143*1017Sbmc aggdata = aggsdata[i]; 1144*1017Sbmc agg = aggdata->dtada_desc; 1145*1017Sbmc rec = &agg->dtagd_rec[aggact]; 1146*1017Sbmc act = rec->dtrd_action; 1147*1017Sbmc addr = aggdata->dtada_data + rec->dtrd_offset; 1148*1017Sbmc size = rec->dtrd_size; 1149*1017Sbmc 1150*1017Sbmc assert(DTRACEACT_ISAGG(act)); 1151*1017Sbmc normal = aggdata->dtada_normal; 1152*1017Sbmc 1153*1017Sbmc if (dt_print_datum(dtp, fp, rec, addr, size, normal) < 0) 1154*1017Sbmc return (-1); 1155*1017Sbmc 1156*1017Sbmc if (dt_buffered_flush(dtp, NULL, rec, aggdata, 1157*1017Sbmc DTRACE_BUFDATA_AGGVAL) < 0) 1158*1017Sbmc return (-1); 1159*1017Sbmc 1160*1017Sbmc if (!pd->dtpa_allunprint) 1161*1017Sbmc agg->dtagd_flags |= DTRACE_AGD_PRINTED; 1162*1017Sbmc } 1163*1017Sbmc 1164*1017Sbmc if (dt_printf(dtp, fp, "\n") < 0) 1165*1017Sbmc return (-1); 1166*1017Sbmc 1167*1017Sbmc if (dt_buffered_flush(dtp, NULL, NULL, aggdata, 1168*1017Sbmc DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0) 1169*1017Sbmc return (-1); 1170*1017Sbmc 1171*1017Sbmc return (0); 1172*1017Sbmc } 1173*1017Sbmc 11740Sstevel@tonic-gate int 1175457Sbmc dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) 11760Sstevel@tonic-gate { 11770Sstevel@tonic-gate dt_print_aggdata_t *pd = arg; 11780Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 11790Sstevel@tonic-gate dtrace_aggvarid_t aggvarid = pd->dtpa_id; 11800Sstevel@tonic-gate 11810Sstevel@tonic-gate if (pd->dtpa_allunprint) { 11820Sstevel@tonic-gate if (agg->dtagd_flags & DTRACE_AGD_PRINTED) 11830Sstevel@tonic-gate return (0); 11840Sstevel@tonic-gate } else { 11850Sstevel@tonic-gate /* 11860Sstevel@tonic-gate * If we're not printing all unprinted aggregations, then the 11870Sstevel@tonic-gate * aggregation variable ID denotes a specific aggregation 11880Sstevel@tonic-gate * variable that we should print -- skip any other aggregations 11890Sstevel@tonic-gate * that we encounter. 11900Sstevel@tonic-gate */ 11910Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 11920Sstevel@tonic-gate return (0); 11930Sstevel@tonic-gate 1194*1017Sbmc if (aggvarid != agg->dtagd_varid) 11950Sstevel@tonic-gate return (0); 11960Sstevel@tonic-gate } 11970Sstevel@tonic-gate 1198*1017Sbmc return (dt_print_aggs(&aggdata, 1, arg)); 11990Sstevel@tonic-gate } 12000Sstevel@tonic-gate 1201457Sbmc int 1202457Sbmc dt_setopt(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, 1203457Sbmc const char *option, const char *value) 1204457Sbmc { 1205457Sbmc int len, rval; 1206457Sbmc char *msg; 1207457Sbmc const char *errstr; 1208457Sbmc dtrace_setoptdata_t optdata; 1209457Sbmc 1210457Sbmc bzero(&optdata, sizeof (optdata)); 1211457Sbmc (void) dtrace_getopt(dtp, option, &optdata.dtsda_oldval); 1212457Sbmc 1213457Sbmc if (dtrace_setopt(dtp, option, value) == 0) { 1214457Sbmc (void) dtrace_getopt(dtp, option, &optdata.dtsda_newval); 1215457Sbmc optdata.dtsda_probe = data; 1216457Sbmc optdata.dtsda_option = option; 1217457Sbmc optdata.dtsda_handle = dtp; 1218457Sbmc 1219457Sbmc if ((rval = dt_handle_setopt(dtp, &optdata)) != 0) 1220457Sbmc return (rval); 1221457Sbmc 1222457Sbmc return (0); 1223457Sbmc } 1224457Sbmc 1225457Sbmc errstr = dtrace_errmsg(dtp, dtrace_errno(dtp)); 1226457Sbmc len = strlen(option) + strlen(value) + strlen(errstr) + 80; 1227457Sbmc msg = alloca(len); 1228457Sbmc 1229457Sbmc (void) snprintf(msg, len, "couldn't set option \"%s\" to \"%s\": %s\n", 1230457Sbmc option, value, errstr); 1231457Sbmc 1232457Sbmc if ((rval = dt_handle_liberr(dtp, data, msg)) == 0) 1233457Sbmc return (0); 1234457Sbmc 1235457Sbmc return (rval); 1236457Sbmc } 1237457Sbmc 12380Sstevel@tonic-gate static int 12390Sstevel@tonic-gate dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, dtrace_bufdesc_t *buf, 12400Sstevel@tonic-gate dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) 12410Sstevel@tonic-gate { 12420Sstevel@tonic-gate dtrace_epid_t id; 12430Sstevel@tonic-gate size_t offs, start = buf->dtbd_oldest, end = buf->dtbd_size; 12440Sstevel@tonic-gate int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); 12450Sstevel@tonic-gate int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); 12460Sstevel@tonic-gate int rval, i, n; 12470Sstevel@tonic-gate dtrace_epid_t last = DTRACE_EPIDNONE; 12480Sstevel@tonic-gate dtrace_probedata_t data; 12490Sstevel@tonic-gate uint64_t drops; 12500Sstevel@tonic-gate caddr_t addr; 12510Sstevel@tonic-gate 12520Sstevel@tonic-gate bzero(&data, sizeof (data)); 12530Sstevel@tonic-gate data.dtpda_handle = dtp; 12540Sstevel@tonic-gate data.dtpda_cpu = cpu; 12550Sstevel@tonic-gate 12560Sstevel@tonic-gate again: 12570Sstevel@tonic-gate for (offs = start; offs < end; ) { 12580Sstevel@tonic-gate dtrace_eprobedesc_t *epd; 12590Sstevel@tonic-gate 12600Sstevel@tonic-gate /* 12610Sstevel@tonic-gate * We're guaranteed to have an ID. 12620Sstevel@tonic-gate */ 12630Sstevel@tonic-gate id = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); 12640Sstevel@tonic-gate 12650Sstevel@tonic-gate if (id == DTRACE_EPIDNONE) { 12660Sstevel@tonic-gate /* 12670Sstevel@tonic-gate * This is filler to assure proper alignment of the 12680Sstevel@tonic-gate * next record; we simply ignore it. 12690Sstevel@tonic-gate */ 12700Sstevel@tonic-gate offs += sizeof (id); 12710Sstevel@tonic-gate continue; 12720Sstevel@tonic-gate } 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc, 12750Sstevel@tonic-gate &data.dtpda_pdesc)) != 0) 12760Sstevel@tonic-gate return (rval); 12770Sstevel@tonic-gate 12780Sstevel@tonic-gate epd = data.dtpda_edesc; 12790Sstevel@tonic-gate data.dtpda_data = buf->dtbd_data + offs; 12800Sstevel@tonic-gate 12810Sstevel@tonic-gate if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) { 12820Sstevel@tonic-gate rval = dt_handle(dtp, &data); 12830Sstevel@tonic-gate 12840Sstevel@tonic-gate if (rval == DTRACE_CONSUME_NEXT) 12850Sstevel@tonic-gate goto nextepid; 12860Sstevel@tonic-gate 12870Sstevel@tonic-gate if (rval == DTRACE_CONSUME_ERROR) 12880Sstevel@tonic-gate return (-1); 12890Sstevel@tonic-gate } 12900Sstevel@tonic-gate 12910Sstevel@tonic-gate if (flow) 12920Sstevel@tonic-gate (void) dt_flowindent(dtp, &data, last, buf, offs); 12930Sstevel@tonic-gate 12940Sstevel@tonic-gate rval = (*efunc)(&data, arg); 12950Sstevel@tonic-gate 12960Sstevel@tonic-gate if (flow) { 12970Sstevel@tonic-gate if (data.dtpda_flow == DTRACEFLOW_ENTRY) 12980Sstevel@tonic-gate data.dtpda_indent += 2; 12990Sstevel@tonic-gate } 13000Sstevel@tonic-gate 13010Sstevel@tonic-gate if (rval == DTRACE_CONSUME_NEXT) 13020Sstevel@tonic-gate goto nextepid; 13030Sstevel@tonic-gate 13040Sstevel@tonic-gate if (rval == DTRACE_CONSUME_ABORT) 13050Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_DIRABORT)); 13060Sstevel@tonic-gate 13070Sstevel@tonic-gate if (rval != DTRACE_CONSUME_THIS) 13080Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADRVAL)); 13090Sstevel@tonic-gate 13100Sstevel@tonic-gate for (i = 0; i < epd->dtepd_nrecs; i++) { 13110Sstevel@tonic-gate dtrace_recdesc_t *rec = &epd->dtepd_rec[i]; 13120Sstevel@tonic-gate dtrace_actkind_t act = rec->dtrd_action; 13130Sstevel@tonic-gate 13140Sstevel@tonic-gate data.dtpda_data = buf->dtbd_data + offs + 13150Sstevel@tonic-gate rec->dtrd_offset; 13160Sstevel@tonic-gate addr = data.dtpda_data; 13170Sstevel@tonic-gate 13180Sstevel@tonic-gate if (act == DTRACEACT_LIBACT) { 1319457Sbmc uint64_t arg = rec->dtrd_arg; 1320457Sbmc dtrace_aggvarid_t id; 13210Sstevel@tonic-gate 1322457Sbmc switch (arg) { 1323457Sbmc case DT_ACT_CLEAR: 13240Sstevel@tonic-gate /* LINTED - alignment */ 13250Sstevel@tonic-gate id = *((dtrace_aggvarid_t *)addr); 13260Sstevel@tonic-gate (void) dtrace_aggregate_walk(dtp, 13270Sstevel@tonic-gate dt_clear_agg, &id); 13280Sstevel@tonic-gate continue; 13290Sstevel@tonic-gate 1330457Sbmc case DT_ACT_DENORMALIZE: 13310Sstevel@tonic-gate /* LINTED - alignment */ 13320Sstevel@tonic-gate id = *((dtrace_aggvarid_t *)addr); 13330Sstevel@tonic-gate (void) dtrace_aggregate_walk(dtp, 13340Sstevel@tonic-gate dt_denormalize_agg, &id); 13350Sstevel@tonic-gate continue; 1336457Sbmc 1337457Sbmc case DT_ACT_FTRUNCATE: 1338457Sbmc if (fp == NULL) 1339457Sbmc continue; 13400Sstevel@tonic-gate 1341457Sbmc (void) fflush(fp); 1342457Sbmc (void) ftruncate(fileno(fp), 0); 1343457Sbmc (void) fseeko(fp, 0, SEEK_SET); 1344457Sbmc continue; 1345457Sbmc 1346457Sbmc case DT_ACT_NORMALIZE: 13470Sstevel@tonic-gate if (i == epd->dtepd_nrecs - 1) 13480Sstevel@tonic-gate return (dt_set_errno(dtp, 13490Sstevel@tonic-gate EDT_BADNORMAL)); 13500Sstevel@tonic-gate 13510Sstevel@tonic-gate if (dt_normalize(dtp, 13520Sstevel@tonic-gate buf->dtbd_data + offs, rec) != 0) 13530Sstevel@tonic-gate return (-1); 13540Sstevel@tonic-gate 13550Sstevel@tonic-gate i++; 13560Sstevel@tonic-gate continue; 1357457Sbmc 1358457Sbmc case DT_ACT_SETOPT: { 1359457Sbmc uint64_t *opts = dtp->dt_options; 1360457Sbmc dtrace_recdesc_t *valrec; 1361457Sbmc uint32_t valsize; 1362457Sbmc caddr_t val; 1363457Sbmc int rv; 1364457Sbmc 1365457Sbmc if (i == epd->dtepd_nrecs - 1) { 1366457Sbmc return (dt_set_errno(dtp, 1367457Sbmc EDT_BADSETOPT)); 1368457Sbmc } 1369457Sbmc 1370457Sbmc valrec = &epd->dtepd_rec[++i]; 1371457Sbmc valsize = valrec->dtrd_size; 1372457Sbmc 1373457Sbmc if (valrec->dtrd_action != act || 1374457Sbmc valrec->dtrd_arg != arg) { 1375457Sbmc return (dt_set_errno(dtp, 1376457Sbmc EDT_BADSETOPT)); 1377457Sbmc } 1378457Sbmc 1379457Sbmc if (valsize > sizeof (uint64_t)) { 1380457Sbmc val = buf->dtbd_data + offs + 1381457Sbmc valrec->dtrd_offset; 1382457Sbmc } else { 1383457Sbmc val = "1"; 1384457Sbmc } 1385457Sbmc 1386457Sbmc rv = dt_setopt(dtp, &data, addr, val); 1387457Sbmc 1388457Sbmc if (rv != 0) 1389457Sbmc return (-1); 1390457Sbmc 1391457Sbmc flow = (opts[DTRACEOPT_FLOWINDENT] != 1392457Sbmc DTRACEOPT_UNSET); 1393457Sbmc quiet = (opts[DTRACEOPT_QUIET] != 1394457Sbmc DTRACEOPT_UNSET); 1395457Sbmc 1396457Sbmc continue; 13970Sstevel@tonic-gate } 13980Sstevel@tonic-gate 1399457Sbmc case DT_ACT_TRUNC: 14000Sstevel@tonic-gate if (i == epd->dtepd_nrecs - 1) 14010Sstevel@tonic-gate return (dt_set_errno(dtp, 14020Sstevel@tonic-gate EDT_BADTRUNC)); 14030Sstevel@tonic-gate 14040Sstevel@tonic-gate if (dt_trunc(dtp, 14050Sstevel@tonic-gate buf->dtbd_data + offs, rec) != 0) 14060Sstevel@tonic-gate return (-1); 14070Sstevel@tonic-gate 14080Sstevel@tonic-gate i++; 14090Sstevel@tonic-gate continue; 14100Sstevel@tonic-gate 1411457Sbmc default: 14120Sstevel@tonic-gate continue; 14130Sstevel@tonic-gate } 14140Sstevel@tonic-gate } 14150Sstevel@tonic-gate 14160Sstevel@tonic-gate rval = (*rfunc)(&data, rec, arg); 14170Sstevel@tonic-gate 14180Sstevel@tonic-gate if (rval == DTRACE_CONSUME_NEXT) 14190Sstevel@tonic-gate continue; 14200Sstevel@tonic-gate 14210Sstevel@tonic-gate if (rval == DTRACE_CONSUME_ABORT) 14220Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_DIRABORT)); 14230Sstevel@tonic-gate 14240Sstevel@tonic-gate if (rval != DTRACE_CONSUME_THIS) 14250Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADRVAL)); 14260Sstevel@tonic-gate 14270Sstevel@tonic-gate if (act == DTRACEACT_STACK) { 1428457Sbmc int depth = rec->dtrd_arg; 1429457Sbmc 1430457Sbmc if (dt_print_stack(dtp, fp, NULL, addr, depth, 1431457Sbmc rec->dtrd_size / depth) < 0) 14320Sstevel@tonic-gate return (-1); 14330Sstevel@tonic-gate goto nextrec; 14340Sstevel@tonic-gate } 14350Sstevel@tonic-gate 14360Sstevel@tonic-gate if (act == DTRACEACT_USTACK || 14370Sstevel@tonic-gate act == DTRACEACT_JSTACK) { 14380Sstevel@tonic-gate if (dt_print_ustack(dtp, fp, NULL, 14390Sstevel@tonic-gate addr, rec->dtrd_arg) < 0) 14400Sstevel@tonic-gate return (-1); 14410Sstevel@tonic-gate goto nextrec; 14420Sstevel@tonic-gate } 14430Sstevel@tonic-gate 1444457Sbmc if (act == DTRACEACT_SYM) { 1445457Sbmc if (dt_print_sym(dtp, fp, NULL, addr) < 0) 1446457Sbmc return (-1); 1447457Sbmc goto nextrec; 1448457Sbmc } 1449457Sbmc 1450457Sbmc if (act == DTRACEACT_MOD) { 1451457Sbmc if (dt_print_mod(dtp, fp, NULL, addr) < 0) 1452457Sbmc return (-1); 1453457Sbmc goto nextrec; 1454457Sbmc } 1455457Sbmc 1456457Sbmc if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { 1457457Sbmc if (dt_print_usym(dtp, fp, addr, act) < 0) 1458457Sbmc return (-1); 1459457Sbmc goto nextrec; 1460457Sbmc } 1461457Sbmc 1462457Sbmc if (act == DTRACEACT_UMOD) { 1463457Sbmc if (dt_print_umod(dtp, fp, NULL, addr) < 0) 1464457Sbmc return (-1); 1465457Sbmc goto nextrec; 1466457Sbmc } 1467457Sbmc 14680Sstevel@tonic-gate if (DTRACEACT_ISPRINTFLIKE(act)) { 14690Sstevel@tonic-gate void *fmtdata; 14700Sstevel@tonic-gate int (*func)(dtrace_hdl_t *, FILE *, void *, 14710Sstevel@tonic-gate const dtrace_probedata_t *, 14720Sstevel@tonic-gate const dtrace_recdesc_t *, uint_t, 14730Sstevel@tonic-gate const void *buf, size_t); 14740Sstevel@tonic-gate 14750Sstevel@tonic-gate if ((fmtdata = dt_format_lookup(dtp, 14760Sstevel@tonic-gate rec->dtrd_format)) == NULL) 14770Sstevel@tonic-gate goto nofmt; 14780Sstevel@tonic-gate 14790Sstevel@tonic-gate switch (act) { 14800Sstevel@tonic-gate case DTRACEACT_PRINTF: 14810Sstevel@tonic-gate func = dtrace_fprintf; 14820Sstevel@tonic-gate break; 14830Sstevel@tonic-gate case DTRACEACT_PRINTA: 14840Sstevel@tonic-gate func = dtrace_fprinta; 14850Sstevel@tonic-gate break; 14860Sstevel@tonic-gate case DTRACEACT_SYSTEM: 14870Sstevel@tonic-gate func = dtrace_system; 14880Sstevel@tonic-gate break; 14890Sstevel@tonic-gate case DTRACEACT_FREOPEN: 14900Sstevel@tonic-gate func = dtrace_freopen; 14910Sstevel@tonic-gate break; 14920Sstevel@tonic-gate } 14930Sstevel@tonic-gate 14940Sstevel@tonic-gate n = (*func)(dtp, fp, fmtdata, &data, 14950Sstevel@tonic-gate rec, epd->dtepd_nrecs - i, 14960Sstevel@tonic-gate (uchar_t *)buf->dtbd_data + offs, 14970Sstevel@tonic-gate buf->dtbd_size - offs); 14980Sstevel@tonic-gate 14990Sstevel@tonic-gate if (n < 0) 15000Sstevel@tonic-gate return (-1); /* errno is set for us */ 15010Sstevel@tonic-gate 15020Sstevel@tonic-gate if (n > 0) 15030Sstevel@tonic-gate i += n - 1; 15040Sstevel@tonic-gate goto nextrec; 15050Sstevel@tonic-gate } 15060Sstevel@tonic-gate 15070Sstevel@tonic-gate nofmt: 15080Sstevel@tonic-gate if (act == DTRACEACT_PRINTA) { 15090Sstevel@tonic-gate dt_print_aggdata_t pd; 1510*1017Sbmc dtrace_aggvarid_t *aggvars; 1511*1017Sbmc int j, naggvars = 0; 1512*1017Sbmc size_t size = ((epd->dtepd_nrecs - i) * 1513*1017Sbmc sizeof (dtrace_aggvarid_t)); 15140Sstevel@tonic-gate 1515*1017Sbmc if ((aggvars = dt_alloc(dtp, size)) == NULL) 1516*1017Sbmc return (-1); 1517*1017Sbmc 1518*1017Sbmc /* 1519*1017Sbmc * This might be a printa() with multiple 1520*1017Sbmc * aggregation variables. We need to scan 1521*1017Sbmc * forward through the records until we find 1522*1017Sbmc * a record from a different statement. 1523*1017Sbmc */ 1524*1017Sbmc for (j = i; j < epd->dtepd_nrecs; j++) { 1525*1017Sbmc dtrace_recdesc_t *nrec; 1526*1017Sbmc caddr_t naddr; 1527*1017Sbmc 1528*1017Sbmc nrec = &epd->dtepd_rec[j]; 1529*1017Sbmc 1530*1017Sbmc if (nrec->dtrd_uarg != rec->dtrd_uarg) 1531*1017Sbmc break; 1532*1017Sbmc 1533*1017Sbmc if (nrec->dtrd_action != act) { 1534*1017Sbmc return (dt_set_errno(dtp, 1535*1017Sbmc EDT_BADAGG)); 1536*1017Sbmc } 1537*1017Sbmc 1538*1017Sbmc naddr = buf->dtbd_data + offs + 1539*1017Sbmc nrec->dtrd_offset; 1540*1017Sbmc 1541*1017Sbmc aggvars[naggvars++] = 1542*1017Sbmc /* LINTED - alignment */ 1543*1017Sbmc *((dtrace_aggvarid_t *)naddr); 1544*1017Sbmc } 1545*1017Sbmc 1546*1017Sbmc i = j - 1; 15470Sstevel@tonic-gate bzero(&pd, sizeof (pd)); 15480Sstevel@tonic-gate pd.dtpa_dtp = dtp; 15490Sstevel@tonic-gate pd.dtpa_fp = fp; 1550*1017Sbmc 1551*1017Sbmc assert(naggvars >= 1); 1552*1017Sbmc 1553*1017Sbmc if (naggvars == 1) { 1554*1017Sbmc pd.dtpa_id = aggvars[0]; 1555*1017Sbmc dt_free(dtp, aggvars); 1556*1017Sbmc 1557*1017Sbmc if (dt_printf(dtp, fp, "\n") < 0 || 1558*1017Sbmc dtrace_aggregate_walk_sorted(dtp, 1559*1017Sbmc dt_print_agg, &pd) < 0) 1560*1017Sbmc return (-1); 1561*1017Sbmc goto nextrec; 1562*1017Sbmc } 15630Sstevel@tonic-gate 15640Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n") < 0 || 1565*1017Sbmc dtrace_aggregate_walk_joined(dtp, aggvars, 1566*1017Sbmc naggvars, dt_print_aggs, &pd) < 0) { 1567*1017Sbmc dt_free(dtp, aggvars); 15680Sstevel@tonic-gate return (-1); 1569*1017Sbmc } 15700Sstevel@tonic-gate 1571*1017Sbmc dt_free(dtp, aggvars); 15720Sstevel@tonic-gate goto nextrec; 15730Sstevel@tonic-gate } 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate switch (rec->dtrd_size) { 15760Sstevel@tonic-gate case sizeof (uint64_t): 15770Sstevel@tonic-gate n = dt_printf(dtp, fp, 15780Sstevel@tonic-gate quiet ? "%lld" : " %16lld", 15790Sstevel@tonic-gate /* LINTED - alignment */ 15800Sstevel@tonic-gate *((unsigned long long *)addr)); 15810Sstevel@tonic-gate break; 15820Sstevel@tonic-gate case sizeof (uint32_t): 15830Sstevel@tonic-gate n = dt_printf(dtp, fp, quiet ? "%d" : " %8d", 15840Sstevel@tonic-gate /* LINTED - alignment */ 15850Sstevel@tonic-gate *((uint32_t *)addr)); 15860Sstevel@tonic-gate break; 15870Sstevel@tonic-gate case sizeof (uint16_t): 15880Sstevel@tonic-gate n = dt_printf(dtp, fp, quiet ? "%d" : " %5d", 15890Sstevel@tonic-gate /* LINTED - alignment */ 15900Sstevel@tonic-gate *((uint16_t *)addr)); 15910Sstevel@tonic-gate break; 15920Sstevel@tonic-gate case sizeof (uint8_t): 15930Sstevel@tonic-gate n = dt_printf(dtp, fp, quiet ? "%d" : " %3d", 15940Sstevel@tonic-gate *((uint8_t *)addr)); 15950Sstevel@tonic-gate break; 15960Sstevel@tonic-gate default: 15970Sstevel@tonic-gate n = dt_print_bytes(dtp, fp, addr, 15980Sstevel@tonic-gate rec->dtrd_size, 33, quiet); 15990Sstevel@tonic-gate break; 16000Sstevel@tonic-gate } 16010Sstevel@tonic-gate 16020Sstevel@tonic-gate if (n < 0) 16030Sstevel@tonic-gate return (-1); /* errno is set for us */ 16040Sstevel@tonic-gate 16050Sstevel@tonic-gate nextrec: 1606*1017Sbmc if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0) 16070Sstevel@tonic-gate return (-1); /* errno is set for us */ 16080Sstevel@tonic-gate } 16090Sstevel@tonic-gate 16100Sstevel@tonic-gate /* 16110Sstevel@tonic-gate * Call the record callback with a NULL record to indicate 16120Sstevel@tonic-gate * that we're done processing this EPID. 16130Sstevel@tonic-gate */ 16140Sstevel@tonic-gate rval = (*rfunc)(&data, NULL, arg); 16150Sstevel@tonic-gate nextepid: 16160Sstevel@tonic-gate offs += epd->dtepd_size; 16170Sstevel@tonic-gate last = id; 16180Sstevel@tonic-gate } 16190Sstevel@tonic-gate 16200Sstevel@tonic-gate if (buf->dtbd_oldest != 0 && start == buf->dtbd_oldest) { 16210Sstevel@tonic-gate end = buf->dtbd_oldest; 16220Sstevel@tonic-gate start = 0; 16230Sstevel@tonic-gate goto again; 16240Sstevel@tonic-gate } 16250Sstevel@tonic-gate 16260Sstevel@tonic-gate if ((drops = buf->dtbd_drops) == 0) 16270Sstevel@tonic-gate return (0); 16280Sstevel@tonic-gate 16290Sstevel@tonic-gate /* 16300Sstevel@tonic-gate * Explicitly zero the drops to prevent us from processing them again. 16310Sstevel@tonic-gate */ 16320Sstevel@tonic-gate buf->dtbd_drops = 0; 16330Sstevel@tonic-gate 16340Sstevel@tonic-gate return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops)); 16350Sstevel@tonic-gate } 16360Sstevel@tonic-gate 16370Sstevel@tonic-gate typedef struct dt_begin { 16380Sstevel@tonic-gate dtrace_consume_probe_f *dtbgn_probefunc; 16390Sstevel@tonic-gate dtrace_consume_rec_f *dtbgn_recfunc; 16400Sstevel@tonic-gate void *dtbgn_arg; 16410Sstevel@tonic-gate dtrace_handle_err_f *dtbgn_errhdlr; 16420Sstevel@tonic-gate void *dtbgn_errarg; 16430Sstevel@tonic-gate int dtbgn_beginonly; 16440Sstevel@tonic-gate } dt_begin_t; 16450Sstevel@tonic-gate 16460Sstevel@tonic-gate static int 16470Sstevel@tonic-gate dt_consume_begin_probe(const dtrace_probedata_t *data, void *arg) 16480Sstevel@tonic-gate { 16490Sstevel@tonic-gate dt_begin_t *begin = (dt_begin_t *)arg; 16500Sstevel@tonic-gate dtrace_probedesc_t *pd = data->dtpda_pdesc; 16510Sstevel@tonic-gate 16520Sstevel@tonic-gate int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); 16530Sstevel@tonic-gate int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); 16540Sstevel@tonic-gate 16550Sstevel@tonic-gate if (begin->dtbgn_beginonly) { 16560Sstevel@tonic-gate if (!(r1 && r2)) 16570Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 16580Sstevel@tonic-gate } else { 16590Sstevel@tonic-gate if (r1 && r2) 16600Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 16610Sstevel@tonic-gate } 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate /* 16640Sstevel@tonic-gate * We have a record that we're interested in. Now call the underlying 16650Sstevel@tonic-gate * probe function... 16660Sstevel@tonic-gate */ 16670Sstevel@tonic-gate return (begin->dtbgn_probefunc(data, begin->dtbgn_arg)); 16680Sstevel@tonic-gate } 16690Sstevel@tonic-gate 16700Sstevel@tonic-gate static int 16710Sstevel@tonic-gate dt_consume_begin_record(const dtrace_probedata_t *data, 16720Sstevel@tonic-gate const dtrace_recdesc_t *rec, void *arg) 16730Sstevel@tonic-gate { 16740Sstevel@tonic-gate dt_begin_t *begin = (dt_begin_t *)arg; 16750Sstevel@tonic-gate 16760Sstevel@tonic-gate return (begin->dtbgn_recfunc(data, rec, begin->dtbgn_arg)); 16770Sstevel@tonic-gate } 16780Sstevel@tonic-gate 16790Sstevel@tonic-gate static int 1680457Sbmc dt_consume_begin_error(const dtrace_errdata_t *data, void *arg) 16810Sstevel@tonic-gate { 16820Sstevel@tonic-gate dt_begin_t *begin = (dt_begin_t *)arg; 16830Sstevel@tonic-gate dtrace_probedesc_t *pd = data->dteda_pdesc; 16840Sstevel@tonic-gate 16850Sstevel@tonic-gate int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); 16860Sstevel@tonic-gate int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); 16870Sstevel@tonic-gate 16880Sstevel@tonic-gate if (begin->dtbgn_beginonly) { 16890Sstevel@tonic-gate if (!(r1 && r2)) 16900Sstevel@tonic-gate return (DTRACE_HANDLE_OK); 16910Sstevel@tonic-gate } else { 16920Sstevel@tonic-gate if (r1 && r2) 16930Sstevel@tonic-gate return (DTRACE_HANDLE_OK); 16940Sstevel@tonic-gate } 16950Sstevel@tonic-gate 16960Sstevel@tonic-gate return (begin->dtbgn_errhdlr(data, begin->dtbgn_errarg)); 16970Sstevel@tonic-gate } 16980Sstevel@tonic-gate 16990Sstevel@tonic-gate static int 17000Sstevel@tonic-gate dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, dtrace_bufdesc_t *buf, 17010Sstevel@tonic-gate dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) 17020Sstevel@tonic-gate { 17030Sstevel@tonic-gate /* 17040Sstevel@tonic-gate * There's this idea that the BEGIN probe should be processed before 17050Sstevel@tonic-gate * everything else, and that the END probe should be processed after 17060Sstevel@tonic-gate * anything else. In the common case, this is pretty easy to deal 17070Sstevel@tonic-gate * with. However, a situation may arise where the BEGIN enabling and 17080Sstevel@tonic-gate * END enabling are on the same CPU, and some enabling in the middle 17090Sstevel@tonic-gate * occurred on a different CPU. To deal with this (blech!) we need to 17100Sstevel@tonic-gate * consume the BEGIN buffer up until the end of the BEGIN probe, and 17110Sstevel@tonic-gate * then set it aside. We will then process every other CPU, and then 17120Sstevel@tonic-gate * we'll return to the BEGIN CPU and process the rest of the data 17130Sstevel@tonic-gate * (which will inevitably include the END probe, if any). Making this 17140Sstevel@tonic-gate * even more complicated (!) is the library's ERROR enabling. Because 17150Sstevel@tonic-gate * this enabling is processed before we even get into the consume call 17160Sstevel@tonic-gate * back, any ERROR firing would result in the library's ERROR enabling 17170Sstevel@tonic-gate * being processed twice -- once in our first pass (for BEGIN probes), 17180Sstevel@tonic-gate * and again in our second pass (for everything but BEGIN probes). To 17190Sstevel@tonic-gate * deal with this, we interpose on the ERROR handler to assure that we 17200Sstevel@tonic-gate * only process ERROR enablings induced by BEGIN enablings in the 17210Sstevel@tonic-gate * first pass, and that we only process ERROR enablings _not_ induced 17220Sstevel@tonic-gate * by BEGIN enablings in the second pass. 17230Sstevel@tonic-gate */ 17240Sstevel@tonic-gate dt_begin_t begin; 17250Sstevel@tonic-gate processorid_t cpu = dtp->dt_beganon; 17260Sstevel@tonic-gate dtrace_bufdesc_t nbuf; 17270Sstevel@tonic-gate int rval, i; 17280Sstevel@tonic-gate static int max_ncpus; 17290Sstevel@tonic-gate dtrace_optval_t size; 17300Sstevel@tonic-gate 17310Sstevel@tonic-gate dtp->dt_beganon = -1; 17320Sstevel@tonic-gate 17330Sstevel@tonic-gate if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { 17340Sstevel@tonic-gate /* 17350Sstevel@tonic-gate * We really don't expect this to fail, but it is at least 17360Sstevel@tonic-gate * technically possible for this to fail with ENOENT. In this 17370Sstevel@tonic-gate * case, we just drive on... 17380Sstevel@tonic-gate */ 17390Sstevel@tonic-gate if (errno == ENOENT) 17400Sstevel@tonic-gate return (0); 17410Sstevel@tonic-gate 17420Sstevel@tonic-gate return (dt_set_errno(dtp, errno)); 17430Sstevel@tonic-gate } 17440Sstevel@tonic-gate 17450Sstevel@tonic-gate if (!dtp->dt_stopped || buf->dtbd_cpu != dtp->dt_endedon) { 17460Sstevel@tonic-gate /* 17470Sstevel@tonic-gate * This is the simple case. We're either not stopped, or if 17480Sstevel@tonic-gate * we are, we actually processed any END probes on another 17490Sstevel@tonic-gate * CPU. We can simply consume this buffer and return. 17500Sstevel@tonic-gate */ 17510Sstevel@tonic-gate return (dt_consume_cpu(dtp, fp, cpu, buf, pf, rf, arg)); 17520Sstevel@tonic-gate } 17530Sstevel@tonic-gate 17540Sstevel@tonic-gate begin.dtbgn_probefunc = pf; 17550Sstevel@tonic-gate begin.dtbgn_recfunc = rf; 17560Sstevel@tonic-gate begin.dtbgn_arg = arg; 17570Sstevel@tonic-gate begin.dtbgn_beginonly = 1; 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate /* 17600Sstevel@tonic-gate * We need to interpose on the ERROR handler to be sure that we 17610Sstevel@tonic-gate * only process ERRORs induced by BEGIN. 17620Sstevel@tonic-gate */ 17630Sstevel@tonic-gate begin.dtbgn_errhdlr = dtp->dt_errhdlr; 17640Sstevel@tonic-gate begin.dtbgn_errarg = dtp->dt_errarg; 17650Sstevel@tonic-gate dtp->dt_errhdlr = dt_consume_begin_error; 17660Sstevel@tonic-gate dtp->dt_errarg = &begin; 17670Sstevel@tonic-gate 17680Sstevel@tonic-gate rval = dt_consume_cpu(dtp, fp, cpu, buf, dt_consume_begin_probe, 17690Sstevel@tonic-gate dt_consume_begin_record, &begin); 17700Sstevel@tonic-gate 17710Sstevel@tonic-gate dtp->dt_errhdlr = begin.dtbgn_errhdlr; 17720Sstevel@tonic-gate dtp->dt_errarg = begin.dtbgn_errarg; 17730Sstevel@tonic-gate 17740Sstevel@tonic-gate if (rval != 0) 17750Sstevel@tonic-gate return (rval); 17760Sstevel@tonic-gate 17770Sstevel@tonic-gate /* 17780Sstevel@tonic-gate * Now allocate a new buffer. We'll use this to deal with every other 17790Sstevel@tonic-gate * CPU. 17800Sstevel@tonic-gate */ 17810Sstevel@tonic-gate bzero(&nbuf, sizeof (dtrace_bufdesc_t)); 17820Sstevel@tonic-gate (void) dtrace_getopt(dtp, "bufsize", &size); 17830Sstevel@tonic-gate if ((nbuf.dtbd_data = malloc(size)) == NULL) 17840Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 17850Sstevel@tonic-gate 17860Sstevel@tonic-gate if (max_ncpus == 0) 17870Sstevel@tonic-gate max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; 17880Sstevel@tonic-gate 17890Sstevel@tonic-gate for (i = 0; i < max_ncpus; i++) { 17900Sstevel@tonic-gate nbuf.dtbd_cpu = i; 17910Sstevel@tonic-gate 17920Sstevel@tonic-gate if (i == cpu) 17930Sstevel@tonic-gate continue; 17940Sstevel@tonic-gate 17950Sstevel@tonic-gate if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &nbuf) == -1) { 17960Sstevel@tonic-gate /* 17970Sstevel@tonic-gate * If we failed with ENOENT, it may be because the 17980Sstevel@tonic-gate * CPU was unconfigured -- this is okay. Any other 17990Sstevel@tonic-gate * error, however, is unexpected. 18000Sstevel@tonic-gate */ 18010Sstevel@tonic-gate if (errno == ENOENT) 18020Sstevel@tonic-gate continue; 18030Sstevel@tonic-gate 18040Sstevel@tonic-gate free(nbuf.dtbd_data); 18050Sstevel@tonic-gate 18060Sstevel@tonic-gate return (dt_set_errno(dtp, errno)); 18070Sstevel@tonic-gate } 18080Sstevel@tonic-gate 18090Sstevel@tonic-gate if ((rval = dt_consume_cpu(dtp, fp, 18100Sstevel@tonic-gate i, &nbuf, pf, rf, arg)) != 0) { 18110Sstevel@tonic-gate free(nbuf.dtbd_data); 18120Sstevel@tonic-gate return (rval); 18130Sstevel@tonic-gate } 18140Sstevel@tonic-gate } 18150Sstevel@tonic-gate 18160Sstevel@tonic-gate free(nbuf.dtbd_data); 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate /* 18190Sstevel@tonic-gate * Okay -- we're done with the other buffers. Now we want to 18200Sstevel@tonic-gate * reconsume the first buffer -- but this time we're looking for 18210Sstevel@tonic-gate * everything _but_ BEGIN. And of course, in order to only consume 18220Sstevel@tonic-gate * those ERRORs _not_ associated with BEGIN, we need to reinstall our 18230Sstevel@tonic-gate * ERROR interposition function... 18240Sstevel@tonic-gate */ 18250Sstevel@tonic-gate begin.dtbgn_beginonly = 0; 18260Sstevel@tonic-gate 18270Sstevel@tonic-gate assert(begin.dtbgn_errhdlr == dtp->dt_errhdlr); 18280Sstevel@tonic-gate assert(begin.dtbgn_errarg == dtp->dt_errarg); 18290Sstevel@tonic-gate dtp->dt_errhdlr = dt_consume_begin_error; 18300Sstevel@tonic-gate dtp->dt_errarg = &begin; 18310Sstevel@tonic-gate 18320Sstevel@tonic-gate rval = dt_consume_cpu(dtp, fp, cpu, buf, dt_consume_begin_probe, 18330Sstevel@tonic-gate dt_consume_begin_record, &begin); 18340Sstevel@tonic-gate 18350Sstevel@tonic-gate dtp->dt_errhdlr = begin.dtbgn_errhdlr; 18360Sstevel@tonic-gate dtp->dt_errarg = begin.dtbgn_errarg; 18370Sstevel@tonic-gate 18380Sstevel@tonic-gate return (rval); 18390Sstevel@tonic-gate } 18400Sstevel@tonic-gate 18410Sstevel@tonic-gate int 18420Sstevel@tonic-gate dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, 18430Sstevel@tonic-gate dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) 18440Sstevel@tonic-gate { 18450Sstevel@tonic-gate dtrace_bufdesc_t *buf = &dtp->dt_buf; 18460Sstevel@tonic-gate dtrace_optval_t size; 18470Sstevel@tonic-gate static int max_ncpus; 18480Sstevel@tonic-gate int i, rval; 18490Sstevel@tonic-gate dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_SWITCHRATE]; 18500Sstevel@tonic-gate hrtime_t now = gethrtime(); 18510Sstevel@tonic-gate 18520Sstevel@tonic-gate if (dtp->dt_lastswitch != 0) { 18530Sstevel@tonic-gate if (now - dtp->dt_lastswitch < interval) 18540Sstevel@tonic-gate return (0); 18550Sstevel@tonic-gate 18560Sstevel@tonic-gate dtp->dt_lastswitch += interval; 18570Sstevel@tonic-gate } else { 18580Sstevel@tonic-gate dtp->dt_lastswitch = now; 18590Sstevel@tonic-gate } 18600Sstevel@tonic-gate 18610Sstevel@tonic-gate if (!dtp->dt_active) 18620Sstevel@tonic-gate return (dt_set_errno(dtp, EINVAL)); 18630Sstevel@tonic-gate 18640Sstevel@tonic-gate if (max_ncpus == 0) 18650Sstevel@tonic-gate max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; 18660Sstevel@tonic-gate 18670Sstevel@tonic-gate if (pf == NULL) 18680Sstevel@tonic-gate pf = (dtrace_consume_probe_f *)dt_nullprobe; 18690Sstevel@tonic-gate 18700Sstevel@tonic-gate if (rf == NULL) 18710Sstevel@tonic-gate rf = (dtrace_consume_rec_f *)dt_nullrec; 18720Sstevel@tonic-gate 18730Sstevel@tonic-gate if (buf->dtbd_data == NULL) { 18740Sstevel@tonic-gate (void) dtrace_getopt(dtp, "bufsize", &size); 18750Sstevel@tonic-gate if ((buf->dtbd_data = malloc(size)) == NULL) 18760Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 18770Sstevel@tonic-gate 18780Sstevel@tonic-gate buf->dtbd_size = size; 18790Sstevel@tonic-gate } 18800Sstevel@tonic-gate 18810Sstevel@tonic-gate /* 18820Sstevel@tonic-gate * If we have just begun, we want to first process the CPU that 18830Sstevel@tonic-gate * executed the BEGIN probe (if any). 18840Sstevel@tonic-gate */ 18850Sstevel@tonic-gate if (dtp->dt_active && dtp->dt_beganon != -1) { 18860Sstevel@tonic-gate buf->dtbd_cpu = dtp->dt_beganon; 18870Sstevel@tonic-gate if ((rval = dt_consume_begin(dtp, fp, buf, pf, rf, arg)) != 0) 18880Sstevel@tonic-gate return (rval); 18890Sstevel@tonic-gate } 18900Sstevel@tonic-gate 18910Sstevel@tonic-gate for (i = 0; i < max_ncpus; i++) { 18920Sstevel@tonic-gate buf->dtbd_cpu = i; 18930Sstevel@tonic-gate 18940Sstevel@tonic-gate /* 18950Sstevel@tonic-gate * If we have stopped, we want to process the CPU on which the 18960Sstevel@tonic-gate * END probe was processed only _after_ we have processed 18970Sstevel@tonic-gate * everything else. 18980Sstevel@tonic-gate */ 18990Sstevel@tonic-gate if (dtp->dt_stopped && (i == dtp->dt_endedon)) 19000Sstevel@tonic-gate continue; 19010Sstevel@tonic-gate 19020Sstevel@tonic-gate if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { 19030Sstevel@tonic-gate /* 19040Sstevel@tonic-gate * If we failed with ENOENT, it may be because the 19050Sstevel@tonic-gate * CPU was unconfigured -- this is okay. Any other 19060Sstevel@tonic-gate * error, however, is unexpected. 19070Sstevel@tonic-gate */ 19080Sstevel@tonic-gate if (errno == ENOENT) 19090Sstevel@tonic-gate continue; 19100Sstevel@tonic-gate 19110Sstevel@tonic-gate return (dt_set_errno(dtp, errno)); 19120Sstevel@tonic-gate } 19130Sstevel@tonic-gate 19140Sstevel@tonic-gate if ((rval = dt_consume_cpu(dtp, fp, i, buf, pf, rf, arg)) != 0) 19150Sstevel@tonic-gate return (rval); 19160Sstevel@tonic-gate } 19170Sstevel@tonic-gate 19180Sstevel@tonic-gate if (!dtp->dt_stopped) 19190Sstevel@tonic-gate return (0); 19200Sstevel@tonic-gate 19210Sstevel@tonic-gate buf->dtbd_cpu = dtp->dt_endedon; 19220Sstevel@tonic-gate 19230Sstevel@tonic-gate if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { 19240Sstevel@tonic-gate /* 19250Sstevel@tonic-gate * This _really_ shouldn't fail, but it is strictly speaking 19260Sstevel@tonic-gate * possible for this to return ENOENT if the CPU that called 19270Sstevel@tonic-gate * the END enabling somehow managed to become unconfigured. 19280Sstevel@tonic-gate * It's unclear how the user can possibly expect anything 19290Sstevel@tonic-gate * rational to happen in this case -- the state has been thrown 19300Sstevel@tonic-gate * out along with the unconfigured CPU -- so we'll just drive 19310Sstevel@tonic-gate * on... 19320Sstevel@tonic-gate */ 19330Sstevel@tonic-gate if (errno == ENOENT) 19340Sstevel@tonic-gate return (0); 19350Sstevel@tonic-gate 19360Sstevel@tonic-gate return (dt_set_errno(dtp, errno)); 19370Sstevel@tonic-gate } 19380Sstevel@tonic-gate 19390Sstevel@tonic-gate return (dt_consume_cpu(dtp, fp, dtp->dt_endedon, buf, pf, rf, arg)); 19400Sstevel@tonic-gate } 1941