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 309*491Sbmc while (first_bin <= levels + 1 && data[first_bin] == 0) 3100Sstevel@tonic-gate first_bin++; 3110Sstevel@tonic-gate 312*491Sbmc 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 } 630*491Sbmc } 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 664*491Sbmc if (str != NULL && str[0] == '@') { 665*491Sbmc /* 666*491Sbmc * If the first character of the string is an "at" sign, 667*491Sbmc * then the string is inferred to be an annotation -- 668*491Sbmc * and it is printed out beneath the frame and offset 669*491Sbmc * with brackets. 670*491Sbmc */ 671*491Sbmc if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0) 672*491Sbmc break; 673*491Sbmc 674*491Sbmc (void) snprintf(c, sizeof (c), " [ %s ]", &str[1]); 675*491Sbmc 676*491Sbmc if ((err = dt_printf(dtp, fp, format, c)) < 0) 677*491Sbmc break; 678*491Sbmc 679*491Sbmc if ((err = dt_printf(dtp, fp, "\n")) < 0) 680*491Sbmc break; 681*491Sbmc } 682*491Sbmc 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 uintptr_t data = (uintptr_t)aggdata->dtada_data; 8480Sstevel@tonic-gate 8490Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 8500Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate if (id != *(dtrace_aggvarid_t *)(data + agg->dtagd_rec[0].dtrd_offset)) 8530Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 8540Sstevel@tonic-gate 855457Sbmc ((dtrace_aggdata_t *)aggdata)->dtada_normal = normal->dtnd_normal; 8560Sstevel@tonic-gate return (DTRACE_AGGWALK_NORMALIZE); 8570Sstevel@tonic-gate } 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate static int 8600Sstevel@tonic-gate dt_normalize(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) 8610Sstevel@tonic-gate { 8620Sstevel@tonic-gate dt_normal_t normal; 8630Sstevel@tonic-gate caddr_t addr; 8640Sstevel@tonic-gate 8650Sstevel@tonic-gate /* 8660Sstevel@tonic-gate * We (should) have two records: the aggregation ID followed by the 8670Sstevel@tonic-gate * normalization value. 8680Sstevel@tonic-gate */ 8690Sstevel@tonic-gate addr = base + rec->dtrd_offset; 8700Sstevel@tonic-gate 8710Sstevel@tonic-gate if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) 8720Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 8730Sstevel@tonic-gate 8740Sstevel@tonic-gate /* LINTED - alignment */ 8750Sstevel@tonic-gate normal.dtnd_id = *((dtrace_aggvarid_t *)addr); 8760Sstevel@tonic-gate rec++; 8770Sstevel@tonic-gate 8780Sstevel@tonic-gate if (rec->dtrd_action != DTRACEACT_LIBACT) 8790Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate if (rec->dtrd_arg != DT_ACT_NORMALIZE) 8820Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate addr = base + rec->dtrd_offset; 8850Sstevel@tonic-gate 8860Sstevel@tonic-gate switch (rec->dtrd_size) { 8870Sstevel@tonic-gate case sizeof (uint64_t): 8880Sstevel@tonic-gate /* LINTED - alignment */ 8890Sstevel@tonic-gate normal.dtnd_normal = *((uint64_t *)addr); 8900Sstevel@tonic-gate break; 8910Sstevel@tonic-gate case sizeof (uint32_t): 8920Sstevel@tonic-gate /* LINTED - alignment */ 8930Sstevel@tonic-gate normal.dtnd_normal = *((uint32_t *)addr); 8940Sstevel@tonic-gate break; 8950Sstevel@tonic-gate case sizeof (uint16_t): 8960Sstevel@tonic-gate /* LINTED - alignment */ 8970Sstevel@tonic-gate normal.dtnd_normal = *((uint16_t *)addr); 8980Sstevel@tonic-gate break; 8990Sstevel@tonic-gate case sizeof (uint8_t): 9000Sstevel@tonic-gate normal.dtnd_normal = *((uint8_t *)addr); 9010Sstevel@tonic-gate break; 9020Sstevel@tonic-gate default: 9030Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 9040Sstevel@tonic-gate } 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate (void) dtrace_aggregate_walk(dtp, dt_normalize_agg, &normal); 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate return (0); 9090Sstevel@tonic-gate } 9100Sstevel@tonic-gate 9110Sstevel@tonic-gate static int 912457Sbmc dt_denormalize_agg(const dtrace_aggdata_t *aggdata, void *arg) 9130Sstevel@tonic-gate { 9140Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 9150Sstevel@tonic-gate dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); 9160Sstevel@tonic-gate uintptr_t data = (uintptr_t)aggdata->dtada_data; 9170Sstevel@tonic-gate 9180Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 9190Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9200Sstevel@tonic-gate 9210Sstevel@tonic-gate if (id != *(dtrace_aggvarid_t *)(data + agg->dtagd_rec[0].dtrd_offset)) 9220Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9230Sstevel@tonic-gate 9240Sstevel@tonic-gate return (DTRACE_AGGWALK_DENORMALIZE); 9250Sstevel@tonic-gate } 9260Sstevel@tonic-gate 9270Sstevel@tonic-gate static int 928457Sbmc dt_clear_agg(const dtrace_aggdata_t *aggdata, void *arg) 9290Sstevel@tonic-gate { 9300Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 9310Sstevel@tonic-gate dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg); 9320Sstevel@tonic-gate uintptr_t data = (uintptr_t)aggdata->dtada_data; 9330Sstevel@tonic-gate 9340Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 9350Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9360Sstevel@tonic-gate 9370Sstevel@tonic-gate if (id != *(dtrace_aggvarid_t *)(data + agg->dtagd_rec[0].dtrd_offset)) 9380Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate return (DTRACE_AGGWALK_CLEAR); 9410Sstevel@tonic-gate } 9420Sstevel@tonic-gate 9430Sstevel@tonic-gate typedef struct dt_trunc { 9440Sstevel@tonic-gate dtrace_aggvarid_t dttd_id; 9450Sstevel@tonic-gate uint64_t dttd_remaining; 9460Sstevel@tonic-gate } dt_trunc_t; 9470Sstevel@tonic-gate 9480Sstevel@tonic-gate static int 949457Sbmc dt_trunc_agg(const dtrace_aggdata_t *aggdata, void *arg) 9500Sstevel@tonic-gate { 9510Sstevel@tonic-gate dt_trunc_t *trunc = arg; 9520Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 9530Sstevel@tonic-gate dtrace_aggvarid_t id = trunc->dttd_id; 9540Sstevel@tonic-gate uintptr_t data = (uintptr_t)aggdata->dtada_data; 9550Sstevel@tonic-gate 9560Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 9570Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9580Sstevel@tonic-gate 9590Sstevel@tonic-gate if (id != *(dtrace_aggvarid_t *)(data + agg->dtagd_rec[0].dtrd_offset)) 9600Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9610Sstevel@tonic-gate 9620Sstevel@tonic-gate if (trunc->dttd_remaining == 0) 9630Sstevel@tonic-gate return (DTRACE_AGGWALK_REMOVE); 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate trunc->dttd_remaining--; 9660Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9670Sstevel@tonic-gate } 9680Sstevel@tonic-gate 9690Sstevel@tonic-gate static int 9700Sstevel@tonic-gate dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec) 9710Sstevel@tonic-gate { 9720Sstevel@tonic-gate dt_trunc_t trunc; 9730Sstevel@tonic-gate caddr_t addr; 9740Sstevel@tonic-gate int64_t remaining; 9750Sstevel@tonic-gate int (*func)(dtrace_hdl_t *, dtrace_aggregate_f *, void *); 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate /* 9780Sstevel@tonic-gate * We (should) have two records: the aggregation ID followed by the 9790Sstevel@tonic-gate * number of aggregation entries after which the aggregation is to be 9800Sstevel@tonic-gate * truncated. 9810Sstevel@tonic-gate */ 9820Sstevel@tonic-gate addr = base + rec->dtrd_offset; 9830Sstevel@tonic-gate 9840Sstevel@tonic-gate if (rec->dtrd_size != sizeof (dtrace_aggvarid_t)) 9850Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADTRUNC)); 9860Sstevel@tonic-gate 9870Sstevel@tonic-gate /* LINTED - alignment */ 9880Sstevel@tonic-gate trunc.dttd_id = *((dtrace_aggvarid_t *)addr); 9890Sstevel@tonic-gate rec++; 9900Sstevel@tonic-gate 9910Sstevel@tonic-gate if (rec->dtrd_action != DTRACEACT_LIBACT) 9920Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADTRUNC)); 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate if (rec->dtrd_arg != DT_ACT_TRUNC) 9950Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADTRUNC)); 9960Sstevel@tonic-gate 9970Sstevel@tonic-gate addr = base + rec->dtrd_offset; 9980Sstevel@tonic-gate 9990Sstevel@tonic-gate switch (rec->dtrd_size) { 10000Sstevel@tonic-gate case sizeof (uint64_t): 10010Sstevel@tonic-gate /* LINTED - alignment */ 10020Sstevel@tonic-gate remaining = *((int64_t *)addr); 10030Sstevel@tonic-gate break; 10040Sstevel@tonic-gate case sizeof (uint32_t): 10050Sstevel@tonic-gate /* LINTED - alignment */ 10060Sstevel@tonic-gate remaining = *((int32_t *)addr); 10070Sstevel@tonic-gate break; 10080Sstevel@tonic-gate case sizeof (uint16_t): 10090Sstevel@tonic-gate /* LINTED - alignment */ 10100Sstevel@tonic-gate remaining = *((int16_t *)addr); 10110Sstevel@tonic-gate break; 10120Sstevel@tonic-gate case sizeof (uint8_t): 10130Sstevel@tonic-gate remaining = *((int8_t *)addr); 10140Sstevel@tonic-gate break; 10150Sstevel@tonic-gate default: 10160Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADNORMAL)); 10170Sstevel@tonic-gate } 10180Sstevel@tonic-gate 10190Sstevel@tonic-gate if (remaining < 0) { 10200Sstevel@tonic-gate func = dtrace_aggregate_walk_valsorted; 10210Sstevel@tonic-gate remaining = -remaining; 10220Sstevel@tonic-gate } else { 10230Sstevel@tonic-gate func = dtrace_aggregate_walk_valrevsorted; 10240Sstevel@tonic-gate } 10250Sstevel@tonic-gate 10260Sstevel@tonic-gate assert(remaining >= 0); 10270Sstevel@tonic-gate trunc.dttd_remaining = remaining; 10280Sstevel@tonic-gate 10290Sstevel@tonic-gate (void) func(dtp, dt_trunc_agg, &trunc); 10300Sstevel@tonic-gate 10310Sstevel@tonic-gate return (0); 10320Sstevel@tonic-gate } 10330Sstevel@tonic-gate 10340Sstevel@tonic-gate int 1035457Sbmc dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg) 10360Sstevel@tonic-gate { 10370Sstevel@tonic-gate int i, err = 0; 10380Sstevel@tonic-gate dt_print_aggdata_t *pd = arg; 10390Sstevel@tonic-gate dtrace_aggdesc_t *agg = aggdata->dtada_desc; 10400Sstevel@tonic-gate FILE *fp = pd->dtpa_fp; 10410Sstevel@tonic-gate dtrace_hdl_t *dtp = pd->dtpa_dtp; 10420Sstevel@tonic-gate dtrace_aggvarid_t aggvarid = pd->dtpa_id; 10430Sstevel@tonic-gate uintptr_t data = (uintptr_t)aggdata->dtada_data; 10440Sstevel@tonic-gate 10450Sstevel@tonic-gate if (pd->dtpa_allunprint) { 10460Sstevel@tonic-gate if (agg->dtagd_flags & DTRACE_AGD_PRINTED) 10470Sstevel@tonic-gate return (0); 10480Sstevel@tonic-gate } else { 10490Sstevel@tonic-gate /* 10500Sstevel@tonic-gate * If we're not printing all unprinted aggregations, then the 10510Sstevel@tonic-gate * aggregation variable ID denotes a specific aggregation 10520Sstevel@tonic-gate * variable that we should print -- skip any other aggregations 10530Sstevel@tonic-gate * that we encounter. 10540Sstevel@tonic-gate */ 10550Sstevel@tonic-gate if (agg->dtagd_nrecs == 0) 10560Sstevel@tonic-gate return (0); 10570Sstevel@tonic-gate 10580Sstevel@tonic-gate if (aggvarid != *(dtrace_aggvarid_t *)(data + 10590Sstevel@tonic-gate agg->dtagd_rec[0].dtrd_offset)) 10600Sstevel@tonic-gate return (0); 10610Sstevel@tonic-gate } 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate /* 10640Sstevel@tonic-gate * Iterate over each record description, printing the traced data, 10650Sstevel@tonic-gate * skipping the first datum (the tuple member created by the compiler). 10660Sstevel@tonic-gate */ 10670Sstevel@tonic-gate for (i = 1; err >= 0 && i < agg->dtagd_nrecs; i++) { 10680Sstevel@tonic-gate dtrace_recdesc_t *rec = &agg->dtagd_rec[i]; 10690Sstevel@tonic-gate dtrace_actkind_t act = rec->dtrd_action; 10700Sstevel@tonic-gate caddr_t addr = aggdata->dtada_data + rec->dtrd_offset; 10710Sstevel@tonic-gate size_t size = rec->dtrd_size; 10720Sstevel@tonic-gate uint64_t normal; 10730Sstevel@tonic-gate 10740Sstevel@tonic-gate normal = DTRACEACT_ISAGG(act) ? aggdata->dtada_normal : 1; 10750Sstevel@tonic-gate 10760Sstevel@tonic-gate if (act == DTRACEACT_STACK) { 1077457Sbmc err = dt_print_stack(dtp, fp, NULL, addr, 1078457Sbmc rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg); 10790Sstevel@tonic-gate goto nextrec; 10800Sstevel@tonic-gate } 10810Sstevel@tonic-gate 10820Sstevel@tonic-gate if (act == DTRACEACT_USTACK || act == DTRACEACT_JSTACK) { 10830Sstevel@tonic-gate err = dt_print_ustack(dtp, fp, NULL, addr, 10840Sstevel@tonic-gate rec->dtrd_arg); 10850Sstevel@tonic-gate goto nextrec; 10860Sstevel@tonic-gate } 10870Sstevel@tonic-gate 1088457Sbmc if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { 1089457Sbmc err = dt_print_usym(dtp, fp, addr, act); 1090457Sbmc goto nextrec; 1091457Sbmc } 1092457Sbmc 1093457Sbmc if (act == DTRACEACT_UMOD) { 1094457Sbmc err = dt_print_umod(dtp, fp, NULL, addr); 1095457Sbmc goto nextrec; 1096457Sbmc } 1097457Sbmc 1098457Sbmc if (act == DTRACEACT_SYM) { 1099457Sbmc err = dt_print_sym(dtp, fp, NULL, addr); 1100457Sbmc goto nextrec; 1101457Sbmc } 1102457Sbmc 1103457Sbmc if (act == DTRACEACT_MOD) { 1104457Sbmc err = dt_print_mod(dtp, fp, NULL, addr); 1105457Sbmc goto nextrec; 1106457Sbmc } 1107457Sbmc 11080Sstevel@tonic-gate if (act == DTRACEAGG_QUANTIZE) { 11090Sstevel@tonic-gate err = dt_print_quantize(dtp, fp, addr, size, normal); 11100Sstevel@tonic-gate goto nextrec; 11110Sstevel@tonic-gate } 11120Sstevel@tonic-gate 11130Sstevel@tonic-gate if (act == DTRACEAGG_LQUANTIZE) { 11140Sstevel@tonic-gate err = dt_print_lquantize(dtp, fp, addr, size, normal); 11150Sstevel@tonic-gate goto nextrec; 11160Sstevel@tonic-gate } 11170Sstevel@tonic-gate 11180Sstevel@tonic-gate if (act == DTRACEAGG_AVG) { 11190Sstevel@tonic-gate err = dt_print_average(dtp, fp, addr, size, normal); 11200Sstevel@tonic-gate goto nextrec; 11210Sstevel@tonic-gate } 11220Sstevel@tonic-gate 11230Sstevel@tonic-gate switch (size) { 11240Sstevel@tonic-gate case sizeof (uint64_t): 11250Sstevel@tonic-gate err = dt_printf(dtp, fp, " %16lld", 11260Sstevel@tonic-gate /* LINTED - alignment */ 11270Sstevel@tonic-gate (long long)*((uint64_t *)addr) / normal); 11280Sstevel@tonic-gate break; 11290Sstevel@tonic-gate case sizeof (uint32_t): 11300Sstevel@tonic-gate /* LINTED - alignment */ 11310Sstevel@tonic-gate err = dt_printf(dtp, fp, " %8d", *((uint32_t *)addr) / 11320Sstevel@tonic-gate (uint32_t)normal); 11330Sstevel@tonic-gate break; 11340Sstevel@tonic-gate case sizeof (uint16_t): 11350Sstevel@tonic-gate /* LINTED - alignment */ 11360Sstevel@tonic-gate err = dt_printf(dtp, fp, " %5d", *((uint16_t *)addr) / 11370Sstevel@tonic-gate (uint32_t)normal); 11380Sstevel@tonic-gate break; 11390Sstevel@tonic-gate case sizeof (uint8_t): 11400Sstevel@tonic-gate err = dt_printf(dtp, fp, " %3d", *((uint8_t *)addr) / 11410Sstevel@tonic-gate (uint32_t)normal); 11420Sstevel@tonic-gate break; 11430Sstevel@tonic-gate default: 11440Sstevel@tonic-gate err = dt_print_bytes(dtp, fp, addr, size, 50, 0); 11450Sstevel@tonic-gate break; 11460Sstevel@tonic-gate } 11470Sstevel@tonic-gate 11480Sstevel@tonic-gate nextrec: 11490Sstevel@tonic-gate if (dt_buffered_flush(dtp, NULL, rec, aggdata) < 0) 11500Sstevel@tonic-gate return (-1); 11510Sstevel@tonic-gate } 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate if (err >= 0) 11540Sstevel@tonic-gate err = dt_printf(dtp, fp, "\n"); 11550Sstevel@tonic-gate 11560Sstevel@tonic-gate if (dt_buffered_flush(dtp, NULL, NULL, aggdata) < 0) 11570Sstevel@tonic-gate return (-1); 11580Sstevel@tonic-gate 11590Sstevel@tonic-gate if (!pd->dtpa_allunprint) 11600Sstevel@tonic-gate agg->dtagd_flags |= DTRACE_AGD_PRINTED; 11610Sstevel@tonic-gate 11620Sstevel@tonic-gate return (err < 0 ? -1 : 0); 11630Sstevel@tonic-gate } 11640Sstevel@tonic-gate 1165457Sbmc 1166457Sbmc int 1167457Sbmc dt_setopt(dtrace_hdl_t *dtp, const dtrace_probedata_t *data, 1168457Sbmc const char *option, const char *value) 1169457Sbmc { 1170457Sbmc int len, rval; 1171457Sbmc char *msg; 1172457Sbmc const char *errstr; 1173457Sbmc dtrace_setoptdata_t optdata; 1174457Sbmc 1175457Sbmc bzero(&optdata, sizeof (optdata)); 1176457Sbmc (void) dtrace_getopt(dtp, option, &optdata.dtsda_oldval); 1177457Sbmc 1178457Sbmc if (dtrace_setopt(dtp, option, value) == 0) { 1179457Sbmc (void) dtrace_getopt(dtp, option, &optdata.dtsda_newval); 1180457Sbmc optdata.dtsda_probe = data; 1181457Sbmc optdata.dtsda_option = option; 1182457Sbmc optdata.dtsda_handle = dtp; 1183457Sbmc 1184457Sbmc if ((rval = dt_handle_setopt(dtp, &optdata)) != 0) 1185457Sbmc return (rval); 1186457Sbmc 1187457Sbmc return (0); 1188457Sbmc } 1189457Sbmc 1190457Sbmc errstr = dtrace_errmsg(dtp, dtrace_errno(dtp)); 1191457Sbmc len = strlen(option) + strlen(value) + strlen(errstr) + 80; 1192457Sbmc msg = alloca(len); 1193457Sbmc 1194457Sbmc (void) snprintf(msg, len, "couldn't set option \"%s\" to \"%s\": %s\n", 1195457Sbmc option, value, errstr); 1196457Sbmc 1197457Sbmc if ((rval = dt_handle_liberr(dtp, data, msg)) == 0) 1198457Sbmc return (0); 1199457Sbmc 1200457Sbmc return (rval); 1201457Sbmc } 1202457Sbmc 12030Sstevel@tonic-gate static int 12040Sstevel@tonic-gate dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, dtrace_bufdesc_t *buf, 12050Sstevel@tonic-gate dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg) 12060Sstevel@tonic-gate { 12070Sstevel@tonic-gate dtrace_epid_t id; 12080Sstevel@tonic-gate size_t offs, start = buf->dtbd_oldest, end = buf->dtbd_size; 12090Sstevel@tonic-gate int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET); 12100Sstevel@tonic-gate int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET); 12110Sstevel@tonic-gate int rval, i, n; 12120Sstevel@tonic-gate dtrace_epid_t last = DTRACE_EPIDNONE; 12130Sstevel@tonic-gate dtrace_probedata_t data; 12140Sstevel@tonic-gate uint64_t drops; 12150Sstevel@tonic-gate caddr_t addr; 12160Sstevel@tonic-gate 12170Sstevel@tonic-gate bzero(&data, sizeof (data)); 12180Sstevel@tonic-gate data.dtpda_handle = dtp; 12190Sstevel@tonic-gate data.dtpda_cpu = cpu; 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate again: 12220Sstevel@tonic-gate for (offs = start; offs < end; ) { 12230Sstevel@tonic-gate dtrace_eprobedesc_t *epd; 12240Sstevel@tonic-gate 12250Sstevel@tonic-gate /* 12260Sstevel@tonic-gate * We're guaranteed to have an ID. 12270Sstevel@tonic-gate */ 12280Sstevel@tonic-gate id = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs); 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate if (id == DTRACE_EPIDNONE) { 12310Sstevel@tonic-gate /* 12320Sstevel@tonic-gate * This is filler to assure proper alignment of the 12330Sstevel@tonic-gate * next record; we simply ignore it. 12340Sstevel@tonic-gate */ 12350Sstevel@tonic-gate offs += sizeof (id); 12360Sstevel@tonic-gate continue; 12370Sstevel@tonic-gate } 12380Sstevel@tonic-gate 12390Sstevel@tonic-gate if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc, 12400Sstevel@tonic-gate &data.dtpda_pdesc)) != 0) 12410Sstevel@tonic-gate return (rval); 12420Sstevel@tonic-gate 12430Sstevel@tonic-gate epd = data.dtpda_edesc; 12440Sstevel@tonic-gate data.dtpda_data = buf->dtbd_data + offs; 12450Sstevel@tonic-gate 12460Sstevel@tonic-gate if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) { 12470Sstevel@tonic-gate rval = dt_handle(dtp, &data); 12480Sstevel@tonic-gate 12490Sstevel@tonic-gate if (rval == DTRACE_CONSUME_NEXT) 12500Sstevel@tonic-gate goto nextepid; 12510Sstevel@tonic-gate 12520Sstevel@tonic-gate if (rval == DTRACE_CONSUME_ERROR) 12530Sstevel@tonic-gate return (-1); 12540Sstevel@tonic-gate } 12550Sstevel@tonic-gate 12560Sstevel@tonic-gate if (flow) 12570Sstevel@tonic-gate (void) dt_flowindent(dtp, &data, last, buf, offs); 12580Sstevel@tonic-gate 12590Sstevel@tonic-gate rval = (*efunc)(&data, arg); 12600Sstevel@tonic-gate 12610Sstevel@tonic-gate if (flow) { 12620Sstevel@tonic-gate if (data.dtpda_flow == DTRACEFLOW_ENTRY) 12630Sstevel@tonic-gate data.dtpda_indent += 2; 12640Sstevel@tonic-gate } 12650Sstevel@tonic-gate 12660Sstevel@tonic-gate if (rval == DTRACE_CONSUME_NEXT) 12670Sstevel@tonic-gate goto nextepid; 12680Sstevel@tonic-gate 12690Sstevel@tonic-gate if (rval == DTRACE_CONSUME_ABORT) 12700Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_DIRABORT)); 12710Sstevel@tonic-gate 12720Sstevel@tonic-gate if (rval != DTRACE_CONSUME_THIS) 12730Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADRVAL)); 12740Sstevel@tonic-gate 12750Sstevel@tonic-gate for (i = 0; i < epd->dtepd_nrecs; i++) { 12760Sstevel@tonic-gate dtrace_recdesc_t *rec = &epd->dtepd_rec[i]; 12770Sstevel@tonic-gate dtrace_actkind_t act = rec->dtrd_action; 12780Sstevel@tonic-gate 12790Sstevel@tonic-gate data.dtpda_data = buf->dtbd_data + offs + 12800Sstevel@tonic-gate rec->dtrd_offset; 12810Sstevel@tonic-gate addr = data.dtpda_data; 12820Sstevel@tonic-gate 12830Sstevel@tonic-gate if (act == DTRACEACT_LIBACT) { 1284457Sbmc uint64_t arg = rec->dtrd_arg; 1285457Sbmc dtrace_aggvarid_t id; 12860Sstevel@tonic-gate 1287457Sbmc switch (arg) { 1288457Sbmc case DT_ACT_CLEAR: 12890Sstevel@tonic-gate /* LINTED - alignment */ 12900Sstevel@tonic-gate id = *((dtrace_aggvarid_t *)addr); 12910Sstevel@tonic-gate (void) dtrace_aggregate_walk(dtp, 12920Sstevel@tonic-gate dt_clear_agg, &id); 12930Sstevel@tonic-gate continue; 12940Sstevel@tonic-gate 1295457Sbmc case DT_ACT_DENORMALIZE: 12960Sstevel@tonic-gate /* LINTED - alignment */ 12970Sstevel@tonic-gate id = *((dtrace_aggvarid_t *)addr); 12980Sstevel@tonic-gate (void) dtrace_aggregate_walk(dtp, 12990Sstevel@tonic-gate dt_denormalize_agg, &id); 13000Sstevel@tonic-gate continue; 1301457Sbmc 1302457Sbmc case DT_ACT_FTRUNCATE: 1303457Sbmc if (fp == NULL) 1304457Sbmc continue; 13050Sstevel@tonic-gate 1306457Sbmc (void) fflush(fp); 1307457Sbmc (void) ftruncate(fileno(fp), 0); 1308457Sbmc (void) fseeko(fp, 0, SEEK_SET); 1309457Sbmc continue; 1310457Sbmc 1311457Sbmc case DT_ACT_NORMALIZE: 13120Sstevel@tonic-gate if (i == epd->dtepd_nrecs - 1) 13130Sstevel@tonic-gate return (dt_set_errno(dtp, 13140Sstevel@tonic-gate EDT_BADNORMAL)); 13150Sstevel@tonic-gate 13160Sstevel@tonic-gate if (dt_normalize(dtp, 13170Sstevel@tonic-gate buf->dtbd_data + offs, rec) != 0) 13180Sstevel@tonic-gate return (-1); 13190Sstevel@tonic-gate 13200Sstevel@tonic-gate i++; 13210Sstevel@tonic-gate continue; 1322457Sbmc 1323457Sbmc case DT_ACT_SETOPT: { 1324457Sbmc uint64_t *opts = dtp->dt_options; 1325457Sbmc dtrace_recdesc_t *valrec; 1326457Sbmc uint32_t valsize; 1327457Sbmc caddr_t val; 1328457Sbmc int rv; 1329457Sbmc 1330457Sbmc if (i == epd->dtepd_nrecs - 1) { 1331457Sbmc return (dt_set_errno(dtp, 1332457Sbmc EDT_BADSETOPT)); 1333457Sbmc } 1334457Sbmc 1335457Sbmc valrec = &epd->dtepd_rec[++i]; 1336457Sbmc valsize = valrec->dtrd_size; 1337457Sbmc 1338457Sbmc if (valrec->dtrd_action != act || 1339457Sbmc valrec->dtrd_arg != arg) { 1340457Sbmc return (dt_set_errno(dtp, 1341457Sbmc EDT_BADSETOPT)); 1342457Sbmc } 1343457Sbmc 1344457Sbmc if (valsize > sizeof (uint64_t)) { 1345457Sbmc val = buf->dtbd_data + offs + 1346457Sbmc valrec->dtrd_offset; 1347457Sbmc } else { 1348457Sbmc val = "1"; 1349457Sbmc } 1350457Sbmc 1351457Sbmc rv = dt_setopt(dtp, &data, addr, val); 1352457Sbmc 1353457Sbmc if (rv != 0) 1354457Sbmc return (-1); 1355457Sbmc 1356457Sbmc flow = (opts[DTRACEOPT_FLOWINDENT] != 1357457Sbmc DTRACEOPT_UNSET); 1358457Sbmc quiet = (opts[DTRACEOPT_QUIET] != 1359457Sbmc DTRACEOPT_UNSET); 1360457Sbmc 1361457Sbmc continue; 13620Sstevel@tonic-gate } 13630Sstevel@tonic-gate 1364457Sbmc case DT_ACT_TRUNC: 13650Sstevel@tonic-gate if (i == epd->dtepd_nrecs - 1) 13660Sstevel@tonic-gate return (dt_set_errno(dtp, 13670Sstevel@tonic-gate EDT_BADTRUNC)); 13680Sstevel@tonic-gate 13690Sstevel@tonic-gate if (dt_trunc(dtp, 13700Sstevel@tonic-gate buf->dtbd_data + offs, rec) != 0) 13710Sstevel@tonic-gate return (-1); 13720Sstevel@tonic-gate 13730Sstevel@tonic-gate i++; 13740Sstevel@tonic-gate continue; 13750Sstevel@tonic-gate 1376457Sbmc default: 13770Sstevel@tonic-gate continue; 13780Sstevel@tonic-gate } 13790Sstevel@tonic-gate } 13800Sstevel@tonic-gate 13810Sstevel@tonic-gate rval = (*rfunc)(&data, rec, arg); 13820Sstevel@tonic-gate 13830Sstevel@tonic-gate if (rval == DTRACE_CONSUME_NEXT) 13840Sstevel@tonic-gate continue; 13850Sstevel@tonic-gate 13860Sstevel@tonic-gate if (rval == DTRACE_CONSUME_ABORT) 13870Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_DIRABORT)); 13880Sstevel@tonic-gate 13890Sstevel@tonic-gate if (rval != DTRACE_CONSUME_THIS) 13900Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_BADRVAL)); 13910Sstevel@tonic-gate 13920Sstevel@tonic-gate if (act == DTRACEACT_STACK) { 1393457Sbmc int depth = rec->dtrd_arg; 1394457Sbmc 1395457Sbmc if (dt_print_stack(dtp, fp, NULL, addr, depth, 1396457Sbmc rec->dtrd_size / depth) < 0) 13970Sstevel@tonic-gate return (-1); 13980Sstevel@tonic-gate goto nextrec; 13990Sstevel@tonic-gate } 14000Sstevel@tonic-gate 14010Sstevel@tonic-gate if (act == DTRACEACT_USTACK || 14020Sstevel@tonic-gate act == DTRACEACT_JSTACK) { 14030Sstevel@tonic-gate if (dt_print_ustack(dtp, fp, NULL, 14040Sstevel@tonic-gate addr, rec->dtrd_arg) < 0) 14050Sstevel@tonic-gate return (-1); 14060Sstevel@tonic-gate goto nextrec; 14070Sstevel@tonic-gate } 14080Sstevel@tonic-gate 1409457Sbmc if (act == DTRACEACT_SYM) { 1410457Sbmc if (dt_print_sym(dtp, fp, NULL, addr) < 0) 1411457Sbmc return (-1); 1412457Sbmc goto nextrec; 1413457Sbmc } 1414457Sbmc 1415457Sbmc if (act == DTRACEACT_MOD) { 1416457Sbmc if (dt_print_mod(dtp, fp, NULL, addr) < 0) 1417457Sbmc return (-1); 1418457Sbmc goto nextrec; 1419457Sbmc } 1420457Sbmc 1421457Sbmc if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) { 1422457Sbmc if (dt_print_usym(dtp, fp, addr, act) < 0) 1423457Sbmc return (-1); 1424457Sbmc goto nextrec; 1425457Sbmc } 1426457Sbmc 1427457Sbmc if (act == DTRACEACT_UMOD) { 1428457Sbmc if (dt_print_umod(dtp, fp, NULL, addr) < 0) 1429457Sbmc return (-1); 1430457Sbmc goto nextrec; 1431457Sbmc } 1432457Sbmc 14330Sstevel@tonic-gate if (DTRACEACT_ISPRINTFLIKE(act)) { 14340Sstevel@tonic-gate void *fmtdata; 14350Sstevel@tonic-gate int (*func)(dtrace_hdl_t *, FILE *, void *, 14360Sstevel@tonic-gate const dtrace_probedata_t *, 14370Sstevel@tonic-gate const dtrace_recdesc_t *, uint_t, 14380Sstevel@tonic-gate const void *buf, size_t); 14390Sstevel@tonic-gate 14400Sstevel@tonic-gate if ((fmtdata = dt_format_lookup(dtp, 14410Sstevel@tonic-gate rec->dtrd_format)) == NULL) 14420Sstevel@tonic-gate goto nofmt; 14430Sstevel@tonic-gate 14440Sstevel@tonic-gate switch (act) { 14450Sstevel@tonic-gate case DTRACEACT_PRINTF: 14460Sstevel@tonic-gate func = dtrace_fprintf; 14470Sstevel@tonic-gate break; 14480Sstevel@tonic-gate case DTRACEACT_PRINTA: 14490Sstevel@tonic-gate func = dtrace_fprinta; 14500Sstevel@tonic-gate break; 14510Sstevel@tonic-gate case DTRACEACT_SYSTEM: 14520Sstevel@tonic-gate func = dtrace_system; 14530Sstevel@tonic-gate break; 14540Sstevel@tonic-gate case DTRACEACT_FREOPEN: 14550Sstevel@tonic-gate func = dtrace_freopen; 14560Sstevel@tonic-gate break; 14570Sstevel@tonic-gate } 14580Sstevel@tonic-gate 14590Sstevel@tonic-gate n = (*func)(dtp, fp, fmtdata, &data, 14600Sstevel@tonic-gate rec, epd->dtepd_nrecs - i, 14610Sstevel@tonic-gate (uchar_t *)buf->dtbd_data + offs, 14620Sstevel@tonic-gate buf->dtbd_size - offs); 14630Sstevel@tonic-gate 14640Sstevel@tonic-gate if (n < 0) 14650Sstevel@tonic-gate return (-1); /* errno is set for us */ 14660Sstevel@tonic-gate 14670Sstevel@tonic-gate if (n > 0) 14680Sstevel@tonic-gate i += n - 1; 14690Sstevel@tonic-gate goto nextrec; 14700Sstevel@tonic-gate } 14710Sstevel@tonic-gate 14720Sstevel@tonic-gate nofmt: 14730Sstevel@tonic-gate if (act == DTRACEACT_PRINTA) { 14740Sstevel@tonic-gate dt_print_aggdata_t pd; 14750Sstevel@tonic-gate 14760Sstevel@tonic-gate bzero(&pd, sizeof (pd)); 14770Sstevel@tonic-gate pd.dtpa_dtp = dtp; 14780Sstevel@tonic-gate pd.dtpa_fp = fp; 14790Sstevel@tonic-gate /* LINTED - alignment */ 14800Sstevel@tonic-gate pd.dtpa_id = *((dtrace_aggvarid_t *)addr); 14810Sstevel@tonic-gate 14820Sstevel@tonic-gate if (dt_printf(dtp, fp, "\n") < 0 || 14830Sstevel@tonic-gate dtrace_aggregate_walk_valsorted(dtp, 14840Sstevel@tonic-gate dt_print_agg, &pd) < 0) 14850Sstevel@tonic-gate return (-1); 14860Sstevel@tonic-gate 14870Sstevel@tonic-gate goto nextrec; 14880Sstevel@tonic-gate } 14890Sstevel@tonic-gate 14900Sstevel@tonic-gate switch (rec->dtrd_size) { 14910Sstevel@tonic-gate case sizeof (uint64_t): 14920Sstevel@tonic-gate n = dt_printf(dtp, fp, 14930Sstevel@tonic-gate quiet ? "%lld" : " %16lld", 14940Sstevel@tonic-gate /* LINTED - alignment */ 14950Sstevel@tonic-gate *((unsigned long long *)addr)); 14960Sstevel@tonic-gate break; 14970Sstevel@tonic-gate case sizeof (uint32_t): 14980Sstevel@tonic-gate n = dt_printf(dtp, fp, quiet ? "%d" : " %8d", 14990Sstevel@tonic-gate /* LINTED - alignment */ 15000Sstevel@tonic-gate *((uint32_t *)addr)); 15010Sstevel@tonic-gate break; 15020Sstevel@tonic-gate case sizeof (uint16_t): 15030Sstevel@tonic-gate n = dt_printf(dtp, fp, quiet ? "%d" : " %5d", 15040Sstevel@tonic-gate /* LINTED - alignment */ 15050Sstevel@tonic-gate *((uint16_t *)addr)); 15060Sstevel@tonic-gate break; 15070Sstevel@tonic-gate case sizeof (uint8_t): 15080Sstevel@tonic-gate n = dt_printf(dtp, fp, quiet ? "%d" : " %3d", 15090Sstevel@tonic-gate *((uint8_t *)addr)); 15100Sstevel@tonic-gate break; 15110Sstevel@tonic-gate default: 15120Sstevel@tonic-gate n = dt_print_bytes(dtp, fp, addr, 15130Sstevel@tonic-gate rec->dtrd_size, 33, quiet); 15140Sstevel@tonic-gate break; 15150Sstevel@tonic-gate } 15160Sstevel@tonic-gate 15170Sstevel@tonic-gate if (n < 0) 15180Sstevel@tonic-gate return (-1); /* errno is set for us */ 15190Sstevel@tonic-gate 15200Sstevel@tonic-gate nextrec: 15210Sstevel@tonic-gate if (dt_buffered_flush(dtp, &data, rec, NULL) < 0) 15220Sstevel@tonic-gate return (-1); /* errno is set for us */ 15230Sstevel@tonic-gate } 15240Sstevel@tonic-gate 15250Sstevel@tonic-gate /* 15260Sstevel@tonic-gate * Call the record callback with a NULL record to indicate 15270Sstevel@tonic-gate * that we're done processing this EPID. 15280Sstevel@tonic-gate */ 15290Sstevel@tonic-gate rval = (*rfunc)(&data, NULL, arg); 15300Sstevel@tonic-gate nextepid: 15310Sstevel@tonic-gate offs += epd->dtepd_size; 15320Sstevel@tonic-gate last = id; 15330Sstevel@tonic-gate } 15340Sstevel@tonic-gate 15350Sstevel@tonic-gate if (buf->dtbd_oldest != 0 && start == buf->dtbd_oldest) { 15360Sstevel@tonic-gate end = buf->dtbd_oldest; 15370Sstevel@tonic-gate start = 0; 15380Sstevel@tonic-gate goto again; 15390Sstevel@tonic-gate } 15400Sstevel@tonic-gate 15410Sstevel@tonic-gate if ((drops = buf->dtbd_drops) == 0) 15420Sstevel@tonic-gate return (0); 15430Sstevel@tonic-gate 15440Sstevel@tonic-gate /* 15450Sstevel@tonic-gate * Explicitly zero the drops to prevent us from processing them again. 15460Sstevel@tonic-gate */ 15470Sstevel@tonic-gate buf->dtbd_drops = 0; 15480Sstevel@tonic-gate 15490Sstevel@tonic-gate return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops)); 15500Sstevel@tonic-gate } 15510Sstevel@tonic-gate 15520Sstevel@tonic-gate typedef struct dt_begin { 15530Sstevel@tonic-gate dtrace_consume_probe_f *dtbgn_probefunc; 15540Sstevel@tonic-gate dtrace_consume_rec_f *dtbgn_recfunc; 15550Sstevel@tonic-gate void *dtbgn_arg; 15560Sstevel@tonic-gate dtrace_handle_err_f *dtbgn_errhdlr; 15570Sstevel@tonic-gate void *dtbgn_errarg; 15580Sstevel@tonic-gate int dtbgn_beginonly; 15590Sstevel@tonic-gate } dt_begin_t; 15600Sstevel@tonic-gate 15610Sstevel@tonic-gate static int 15620Sstevel@tonic-gate dt_consume_begin_probe(const dtrace_probedata_t *data, void *arg) 15630Sstevel@tonic-gate { 15640Sstevel@tonic-gate dt_begin_t *begin = (dt_begin_t *)arg; 15650Sstevel@tonic-gate dtrace_probedesc_t *pd = data->dtpda_pdesc; 15660Sstevel@tonic-gate 15670Sstevel@tonic-gate int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); 15680Sstevel@tonic-gate int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); 15690Sstevel@tonic-gate 15700Sstevel@tonic-gate if (begin->dtbgn_beginonly) { 15710Sstevel@tonic-gate if (!(r1 && r2)) 15720Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 15730Sstevel@tonic-gate } else { 15740Sstevel@tonic-gate if (r1 && r2) 15750Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 15760Sstevel@tonic-gate } 15770Sstevel@tonic-gate 15780Sstevel@tonic-gate /* 15790Sstevel@tonic-gate * We have a record that we're interested in. Now call the underlying 15800Sstevel@tonic-gate * probe function... 15810Sstevel@tonic-gate */ 15820Sstevel@tonic-gate return (begin->dtbgn_probefunc(data, begin->dtbgn_arg)); 15830Sstevel@tonic-gate } 15840Sstevel@tonic-gate 15850Sstevel@tonic-gate static int 15860Sstevel@tonic-gate dt_consume_begin_record(const dtrace_probedata_t *data, 15870Sstevel@tonic-gate const dtrace_recdesc_t *rec, void *arg) 15880Sstevel@tonic-gate { 15890Sstevel@tonic-gate dt_begin_t *begin = (dt_begin_t *)arg; 15900Sstevel@tonic-gate 15910Sstevel@tonic-gate return (begin->dtbgn_recfunc(data, rec, begin->dtbgn_arg)); 15920Sstevel@tonic-gate } 15930Sstevel@tonic-gate 15940Sstevel@tonic-gate static int 1595457Sbmc dt_consume_begin_error(const dtrace_errdata_t *data, void *arg) 15960Sstevel@tonic-gate { 15970Sstevel@tonic-gate dt_begin_t *begin = (dt_begin_t *)arg; 15980Sstevel@tonic-gate dtrace_probedesc_t *pd = data->dteda_pdesc; 15990Sstevel@tonic-gate 16000Sstevel@tonic-gate int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0); 16010Sstevel@tonic-gate int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0); 16020Sstevel@tonic-gate 16030Sstevel@tonic-gate if (begin->dtbgn_beginonly) { 16040Sstevel@tonic-gate if (!(r1 && r2)) 16050Sstevel@tonic-gate return (DTRACE_HANDLE_OK); 16060Sstevel@tonic-gate } else { 16070Sstevel@tonic-gate if (r1 && r2) 16080Sstevel@tonic-gate return (DTRACE_HANDLE_OK); 16090Sstevel@tonic-gate } 16100Sstevel@tonic-gate 16110Sstevel@tonic-gate return (begin->dtbgn_errhdlr(data, begin->dtbgn_errarg)); 16120Sstevel@tonic-gate } 16130Sstevel@tonic-gate 16140Sstevel@tonic-gate static int 16150Sstevel@tonic-gate dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, dtrace_bufdesc_t *buf, 16160Sstevel@tonic-gate dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) 16170Sstevel@tonic-gate { 16180Sstevel@tonic-gate /* 16190Sstevel@tonic-gate * There's this idea that the BEGIN probe should be processed before 16200Sstevel@tonic-gate * everything else, and that the END probe should be processed after 16210Sstevel@tonic-gate * anything else. In the common case, this is pretty easy to deal 16220Sstevel@tonic-gate * with. However, a situation may arise where the BEGIN enabling and 16230Sstevel@tonic-gate * END enabling are on the same CPU, and some enabling in the middle 16240Sstevel@tonic-gate * occurred on a different CPU. To deal with this (blech!) we need to 16250Sstevel@tonic-gate * consume the BEGIN buffer up until the end of the BEGIN probe, and 16260Sstevel@tonic-gate * then set it aside. We will then process every other CPU, and then 16270Sstevel@tonic-gate * we'll return to the BEGIN CPU and process the rest of the data 16280Sstevel@tonic-gate * (which will inevitably include the END probe, if any). Making this 16290Sstevel@tonic-gate * even more complicated (!) is the library's ERROR enabling. Because 16300Sstevel@tonic-gate * this enabling is processed before we even get into the consume call 16310Sstevel@tonic-gate * back, any ERROR firing would result in the library's ERROR enabling 16320Sstevel@tonic-gate * being processed twice -- once in our first pass (for BEGIN probes), 16330Sstevel@tonic-gate * and again in our second pass (for everything but BEGIN probes). To 16340Sstevel@tonic-gate * deal with this, we interpose on the ERROR handler to assure that we 16350Sstevel@tonic-gate * only process ERROR enablings induced by BEGIN enablings in the 16360Sstevel@tonic-gate * first pass, and that we only process ERROR enablings _not_ induced 16370Sstevel@tonic-gate * by BEGIN enablings in the second pass. 16380Sstevel@tonic-gate */ 16390Sstevel@tonic-gate dt_begin_t begin; 16400Sstevel@tonic-gate processorid_t cpu = dtp->dt_beganon; 16410Sstevel@tonic-gate dtrace_bufdesc_t nbuf; 16420Sstevel@tonic-gate int rval, i; 16430Sstevel@tonic-gate static int max_ncpus; 16440Sstevel@tonic-gate dtrace_optval_t size; 16450Sstevel@tonic-gate 16460Sstevel@tonic-gate dtp->dt_beganon = -1; 16470Sstevel@tonic-gate 16480Sstevel@tonic-gate if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { 16490Sstevel@tonic-gate /* 16500Sstevel@tonic-gate * We really don't expect this to fail, but it is at least 16510Sstevel@tonic-gate * technically possible for this to fail with ENOENT. In this 16520Sstevel@tonic-gate * case, we just drive on... 16530Sstevel@tonic-gate */ 16540Sstevel@tonic-gate if (errno == ENOENT) 16550Sstevel@tonic-gate return (0); 16560Sstevel@tonic-gate 16570Sstevel@tonic-gate return (dt_set_errno(dtp, errno)); 16580Sstevel@tonic-gate } 16590Sstevel@tonic-gate 16600Sstevel@tonic-gate if (!dtp->dt_stopped || buf->dtbd_cpu != dtp->dt_endedon) { 16610Sstevel@tonic-gate /* 16620Sstevel@tonic-gate * This is the simple case. We're either not stopped, or if 16630Sstevel@tonic-gate * we are, we actually processed any END probes on another 16640Sstevel@tonic-gate * CPU. We can simply consume this buffer and return. 16650Sstevel@tonic-gate */ 16660Sstevel@tonic-gate return (dt_consume_cpu(dtp, fp, cpu, buf, pf, rf, arg)); 16670Sstevel@tonic-gate } 16680Sstevel@tonic-gate 16690Sstevel@tonic-gate begin.dtbgn_probefunc = pf; 16700Sstevel@tonic-gate begin.dtbgn_recfunc = rf; 16710Sstevel@tonic-gate begin.dtbgn_arg = arg; 16720Sstevel@tonic-gate begin.dtbgn_beginonly = 1; 16730Sstevel@tonic-gate 16740Sstevel@tonic-gate /* 16750Sstevel@tonic-gate * We need to interpose on the ERROR handler to be sure that we 16760Sstevel@tonic-gate * only process ERRORs induced by BEGIN. 16770Sstevel@tonic-gate */ 16780Sstevel@tonic-gate begin.dtbgn_errhdlr = dtp->dt_errhdlr; 16790Sstevel@tonic-gate begin.dtbgn_errarg = dtp->dt_errarg; 16800Sstevel@tonic-gate dtp->dt_errhdlr = dt_consume_begin_error; 16810Sstevel@tonic-gate dtp->dt_errarg = &begin; 16820Sstevel@tonic-gate 16830Sstevel@tonic-gate rval = dt_consume_cpu(dtp, fp, cpu, buf, dt_consume_begin_probe, 16840Sstevel@tonic-gate dt_consume_begin_record, &begin); 16850Sstevel@tonic-gate 16860Sstevel@tonic-gate dtp->dt_errhdlr = begin.dtbgn_errhdlr; 16870Sstevel@tonic-gate dtp->dt_errarg = begin.dtbgn_errarg; 16880Sstevel@tonic-gate 16890Sstevel@tonic-gate if (rval != 0) 16900Sstevel@tonic-gate return (rval); 16910Sstevel@tonic-gate 16920Sstevel@tonic-gate /* 16930Sstevel@tonic-gate * Now allocate a new buffer. We'll use this to deal with every other 16940Sstevel@tonic-gate * CPU. 16950Sstevel@tonic-gate */ 16960Sstevel@tonic-gate bzero(&nbuf, sizeof (dtrace_bufdesc_t)); 16970Sstevel@tonic-gate (void) dtrace_getopt(dtp, "bufsize", &size); 16980Sstevel@tonic-gate if ((nbuf.dtbd_data = malloc(size)) == NULL) 16990Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 17000Sstevel@tonic-gate 17010Sstevel@tonic-gate if (max_ncpus == 0) 17020Sstevel@tonic-gate max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; 17030Sstevel@tonic-gate 17040Sstevel@tonic-gate for (i = 0; i < max_ncpus; i++) { 17050Sstevel@tonic-gate nbuf.dtbd_cpu = i; 17060Sstevel@tonic-gate 17070Sstevel@tonic-gate if (i == cpu) 17080Sstevel@tonic-gate continue; 17090Sstevel@tonic-gate 17100Sstevel@tonic-gate if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &nbuf) == -1) { 17110Sstevel@tonic-gate /* 17120Sstevel@tonic-gate * If we failed with ENOENT, it may be because the 17130Sstevel@tonic-gate * CPU was unconfigured -- this is okay. Any other 17140Sstevel@tonic-gate * error, however, is unexpected. 17150Sstevel@tonic-gate */ 17160Sstevel@tonic-gate if (errno == ENOENT) 17170Sstevel@tonic-gate continue; 17180Sstevel@tonic-gate 17190Sstevel@tonic-gate free(nbuf.dtbd_data); 17200Sstevel@tonic-gate 17210Sstevel@tonic-gate return (dt_set_errno(dtp, errno)); 17220Sstevel@tonic-gate } 17230Sstevel@tonic-gate 17240Sstevel@tonic-gate if ((rval = dt_consume_cpu(dtp, fp, 17250Sstevel@tonic-gate i, &nbuf, pf, rf, arg)) != 0) { 17260Sstevel@tonic-gate free(nbuf.dtbd_data); 17270Sstevel@tonic-gate return (rval); 17280Sstevel@tonic-gate } 17290Sstevel@tonic-gate } 17300Sstevel@tonic-gate 17310Sstevel@tonic-gate free(nbuf.dtbd_data); 17320Sstevel@tonic-gate 17330Sstevel@tonic-gate /* 17340Sstevel@tonic-gate * Okay -- we're done with the other buffers. Now we want to 17350Sstevel@tonic-gate * reconsume the first buffer -- but this time we're looking for 17360Sstevel@tonic-gate * everything _but_ BEGIN. And of course, in order to only consume 17370Sstevel@tonic-gate * those ERRORs _not_ associated with BEGIN, we need to reinstall our 17380Sstevel@tonic-gate * ERROR interposition function... 17390Sstevel@tonic-gate */ 17400Sstevel@tonic-gate begin.dtbgn_beginonly = 0; 17410Sstevel@tonic-gate 17420Sstevel@tonic-gate assert(begin.dtbgn_errhdlr == dtp->dt_errhdlr); 17430Sstevel@tonic-gate assert(begin.dtbgn_errarg == dtp->dt_errarg); 17440Sstevel@tonic-gate dtp->dt_errhdlr = dt_consume_begin_error; 17450Sstevel@tonic-gate dtp->dt_errarg = &begin; 17460Sstevel@tonic-gate 17470Sstevel@tonic-gate rval = dt_consume_cpu(dtp, fp, cpu, buf, dt_consume_begin_probe, 17480Sstevel@tonic-gate dt_consume_begin_record, &begin); 17490Sstevel@tonic-gate 17500Sstevel@tonic-gate dtp->dt_errhdlr = begin.dtbgn_errhdlr; 17510Sstevel@tonic-gate dtp->dt_errarg = begin.dtbgn_errarg; 17520Sstevel@tonic-gate 17530Sstevel@tonic-gate return (rval); 17540Sstevel@tonic-gate } 17550Sstevel@tonic-gate 17560Sstevel@tonic-gate int 17570Sstevel@tonic-gate dtrace_consume(dtrace_hdl_t *dtp, FILE *fp, 17580Sstevel@tonic-gate dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg) 17590Sstevel@tonic-gate { 17600Sstevel@tonic-gate dtrace_bufdesc_t *buf = &dtp->dt_buf; 17610Sstevel@tonic-gate dtrace_optval_t size; 17620Sstevel@tonic-gate static int max_ncpus; 17630Sstevel@tonic-gate int i, rval; 17640Sstevel@tonic-gate dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_SWITCHRATE]; 17650Sstevel@tonic-gate hrtime_t now = gethrtime(); 17660Sstevel@tonic-gate 17670Sstevel@tonic-gate if (dtp->dt_lastswitch != 0) { 17680Sstevel@tonic-gate if (now - dtp->dt_lastswitch < interval) 17690Sstevel@tonic-gate return (0); 17700Sstevel@tonic-gate 17710Sstevel@tonic-gate dtp->dt_lastswitch += interval; 17720Sstevel@tonic-gate } else { 17730Sstevel@tonic-gate dtp->dt_lastswitch = now; 17740Sstevel@tonic-gate } 17750Sstevel@tonic-gate 17760Sstevel@tonic-gate if (!dtp->dt_active) 17770Sstevel@tonic-gate return (dt_set_errno(dtp, EINVAL)); 17780Sstevel@tonic-gate 17790Sstevel@tonic-gate if (max_ncpus == 0) 17800Sstevel@tonic-gate max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1; 17810Sstevel@tonic-gate 17820Sstevel@tonic-gate if (pf == NULL) 17830Sstevel@tonic-gate pf = (dtrace_consume_probe_f *)dt_nullprobe; 17840Sstevel@tonic-gate 17850Sstevel@tonic-gate if (rf == NULL) 17860Sstevel@tonic-gate rf = (dtrace_consume_rec_f *)dt_nullrec; 17870Sstevel@tonic-gate 17880Sstevel@tonic-gate if (buf->dtbd_data == NULL) { 17890Sstevel@tonic-gate (void) dtrace_getopt(dtp, "bufsize", &size); 17900Sstevel@tonic-gate if ((buf->dtbd_data = malloc(size)) == NULL) 17910Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 17920Sstevel@tonic-gate 17930Sstevel@tonic-gate buf->dtbd_size = size; 17940Sstevel@tonic-gate } 17950Sstevel@tonic-gate 17960Sstevel@tonic-gate /* 17970Sstevel@tonic-gate * If we have just begun, we want to first process the CPU that 17980Sstevel@tonic-gate * executed the BEGIN probe (if any). 17990Sstevel@tonic-gate */ 18000Sstevel@tonic-gate if (dtp->dt_active && dtp->dt_beganon != -1) { 18010Sstevel@tonic-gate buf->dtbd_cpu = dtp->dt_beganon; 18020Sstevel@tonic-gate if ((rval = dt_consume_begin(dtp, fp, buf, pf, rf, arg)) != 0) 18030Sstevel@tonic-gate return (rval); 18040Sstevel@tonic-gate } 18050Sstevel@tonic-gate 18060Sstevel@tonic-gate for (i = 0; i < max_ncpus; i++) { 18070Sstevel@tonic-gate buf->dtbd_cpu = i; 18080Sstevel@tonic-gate 18090Sstevel@tonic-gate /* 18100Sstevel@tonic-gate * If we have stopped, we want to process the CPU on which the 18110Sstevel@tonic-gate * END probe was processed only _after_ we have processed 18120Sstevel@tonic-gate * everything else. 18130Sstevel@tonic-gate */ 18140Sstevel@tonic-gate if (dtp->dt_stopped && (i == dtp->dt_endedon)) 18150Sstevel@tonic-gate continue; 18160Sstevel@tonic-gate 18170Sstevel@tonic-gate if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { 18180Sstevel@tonic-gate /* 18190Sstevel@tonic-gate * If we failed with ENOENT, it may be because the 18200Sstevel@tonic-gate * CPU was unconfigured -- this is okay. Any other 18210Sstevel@tonic-gate * error, however, is unexpected. 18220Sstevel@tonic-gate */ 18230Sstevel@tonic-gate if (errno == ENOENT) 18240Sstevel@tonic-gate continue; 18250Sstevel@tonic-gate 18260Sstevel@tonic-gate return (dt_set_errno(dtp, errno)); 18270Sstevel@tonic-gate } 18280Sstevel@tonic-gate 18290Sstevel@tonic-gate if ((rval = dt_consume_cpu(dtp, fp, i, buf, pf, rf, arg)) != 0) 18300Sstevel@tonic-gate return (rval); 18310Sstevel@tonic-gate } 18320Sstevel@tonic-gate 18330Sstevel@tonic-gate if (!dtp->dt_stopped) 18340Sstevel@tonic-gate return (0); 18350Sstevel@tonic-gate 18360Sstevel@tonic-gate buf->dtbd_cpu = dtp->dt_endedon; 18370Sstevel@tonic-gate 18380Sstevel@tonic-gate if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) { 18390Sstevel@tonic-gate /* 18400Sstevel@tonic-gate * This _really_ shouldn't fail, but it is strictly speaking 18410Sstevel@tonic-gate * possible for this to return ENOENT if the CPU that called 18420Sstevel@tonic-gate * the END enabling somehow managed to become unconfigured. 18430Sstevel@tonic-gate * It's unclear how the user can possibly expect anything 18440Sstevel@tonic-gate * rational to happen in this case -- the state has been thrown 18450Sstevel@tonic-gate * out along with the unconfigured CPU -- so we'll just drive 18460Sstevel@tonic-gate * on... 18470Sstevel@tonic-gate */ 18480Sstevel@tonic-gate if (errno == ENOENT) 18490Sstevel@tonic-gate return (0); 18500Sstevel@tonic-gate 18510Sstevel@tonic-gate return (dt_set_errno(dtp, errno)); 18520Sstevel@tonic-gate } 18530Sstevel@tonic-gate 18540Sstevel@tonic-gate return (dt_consume_cpu(dtp, fp, dtp->dt_endedon, buf, pf, rf, arg)); 18550Sstevel@tonic-gate } 1856