xref: /onnv-gate/usr/src/lib/libdtrace/common/dt_consume.c (revision 10791:944abfb5b345)
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
55984Sjhaslam  * Common Development and Distribution License (the "License").
65984Sjhaslam  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*10791SJonathan.Haslam@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <stdlib.h>
270Sstevel@tonic-gate #include <strings.h>
280Sstevel@tonic-gate #include <errno.h>
290Sstevel@tonic-gate #include <unistd.h>
300Sstevel@tonic-gate #include <limits.h>
310Sstevel@tonic-gate #include <assert.h>
320Sstevel@tonic-gate #include <ctype.h>
330Sstevel@tonic-gate #include <alloca.h>
34457Sbmc #include <dt_impl.h>
350Sstevel@tonic-gate 
365984Sjhaslam #define	DT_MASK_LO 0x00000000FFFFFFFFULL
375984Sjhaslam 
38457Sbmc /*
39457Sbmc  * We declare this here because (1) we need it and (2) we want to avoid a
40457Sbmc  * dependency on libm in libdtrace.
41457Sbmc  */
42457Sbmc static long double
dt_fabsl(long double x)43457Sbmc dt_fabsl(long double x)
44457Sbmc {
45457Sbmc 	if (x < 0)
46457Sbmc 		return (-x);
47457Sbmc 
48457Sbmc 	return (x);
49457Sbmc }
500Sstevel@tonic-gate 
515984Sjhaslam /*
525984Sjhaslam  * 128-bit arithmetic functions needed to support the stddev() aggregating
535984Sjhaslam  * action.
545984Sjhaslam  */
555984Sjhaslam static int
dt_gt_128(uint64_t * a,uint64_t * b)565984Sjhaslam dt_gt_128(uint64_t *a, uint64_t *b)
575984Sjhaslam {
585984Sjhaslam 	return (a[1] > b[1] || (a[1] == b[1] && a[0] > b[0]));
595984Sjhaslam }
605984Sjhaslam 
615984Sjhaslam static int
dt_ge_128(uint64_t * a,uint64_t * b)625984Sjhaslam dt_ge_128(uint64_t *a, uint64_t *b)
635984Sjhaslam {
645984Sjhaslam 	return (a[1] > b[1] || (a[1] == b[1] && a[0] >= b[0]));
655984Sjhaslam }
665984Sjhaslam 
675984Sjhaslam static int
dt_le_128(uint64_t * a,uint64_t * b)685984Sjhaslam dt_le_128(uint64_t *a, uint64_t *b)
695984Sjhaslam {
705984Sjhaslam 	return (a[1] < b[1] || (a[1] == b[1] && a[0] <= b[0]));
715984Sjhaslam }
725984Sjhaslam 
735984Sjhaslam /*
745984Sjhaslam  * Shift the 128-bit value in a by b. If b is positive, shift left.
755984Sjhaslam  * If b is negative, shift right.
765984Sjhaslam  */
775984Sjhaslam static void
dt_shift_128(uint64_t * a,int b)785984Sjhaslam dt_shift_128(uint64_t *a, int b)
795984Sjhaslam {
805984Sjhaslam 	uint64_t mask;
815984Sjhaslam 
825984Sjhaslam 	if (b == 0)
835984Sjhaslam 		return;
845984Sjhaslam 
855984Sjhaslam 	if (b < 0) {
865984Sjhaslam 		b = -b;
875984Sjhaslam 		if (b >= 64) {
885984Sjhaslam 			a[0] = a[1] >> (b - 64);
895984Sjhaslam 			a[1] = 0;
905984Sjhaslam 		} else {
915984Sjhaslam 			a[0] >>= b;
925984Sjhaslam 			mask = 1LL << (64 - b);
935984Sjhaslam 			mask -= 1;
945984Sjhaslam 			a[0] |= ((a[1] & mask) << (64 - b));
955984Sjhaslam 			a[1] >>= b;
965984Sjhaslam 		}
975984Sjhaslam 	} else {
985984Sjhaslam 		if (b >= 64) {
995984Sjhaslam 			a[1] = a[0] << (b - 64);
1005984Sjhaslam 			a[0] = 0;
1015984Sjhaslam 		} else {
1025984Sjhaslam 			a[1] <<= b;
1035984Sjhaslam 			mask = a[0] >> (64 - b);
1045984Sjhaslam 			a[1] |= mask;
1055984Sjhaslam 			a[0] <<= b;
1065984Sjhaslam 		}
1075984Sjhaslam 	}
1085984Sjhaslam }
1095984Sjhaslam 
1105984Sjhaslam static int
dt_nbits_128(uint64_t * a)1115984Sjhaslam dt_nbits_128(uint64_t *a)
1125984Sjhaslam {
1135984Sjhaslam 	int nbits = 0;
1145984Sjhaslam 	uint64_t tmp[2];
1155984Sjhaslam 	uint64_t zero[2] = { 0, 0 };
1165984Sjhaslam 
1175984Sjhaslam 	tmp[0] = a[0];
1185984Sjhaslam 	tmp[1] = a[1];
1195984Sjhaslam 
1205984Sjhaslam 	dt_shift_128(tmp, -1);
1215984Sjhaslam 	while (dt_gt_128(tmp, zero)) {
1225984Sjhaslam 		dt_shift_128(tmp, -1);
1235984Sjhaslam 		nbits++;
1245984Sjhaslam 	}
1255984Sjhaslam 
1265984Sjhaslam 	return (nbits);
1275984Sjhaslam }
1285984Sjhaslam 
1295984Sjhaslam static void
dt_subtract_128(uint64_t * minuend,uint64_t * subtrahend,uint64_t * difference)1305984Sjhaslam dt_subtract_128(uint64_t *minuend, uint64_t *subtrahend, uint64_t *difference)
1315984Sjhaslam {
1325984Sjhaslam 	uint64_t result[2];
1335984Sjhaslam 
1345984Sjhaslam 	result[0] = minuend[0] - subtrahend[0];
1355984Sjhaslam 	result[1] = minuend[1] - subtrahend[1] -
1365984Sjhaslam 	    (minuend[0] < subtrahend[0] ? 1 : 0);
1375984Sjhaslam 
1385984Sjhaslam 	difference[0] = result[0];
1395984Sjhaslam 	difference[1] = result[1];
1405984Sjhaslam }
1415984Sjhaslam 
1425984Sjhaslam static void
dt_add_128(uint64_t * addend1,uint64_t * addend2,uint64_t * sum)1435984Sjhaslam dt_add_128(uint64_t *addend1, uint64_t *addend2, uint64_t *sum)
1445984Sjhaslam {
1455984Sjhaslam 	uint64_t result[2];
1465984Sjhaslam 
1475984Sjhaslam 	result[0] = addend1[0] + addend2[0];
1485984Sjhaslam 	result[1] = addend1[1] + addend2[1] +
1495984Sjhaslam 	    (result[0] < addend1[0] || result[0] < addend2[0] ? 1 : 0);
1505984Sjhaslam 
1515984Sjhaslam 	sum[0] = result[0];
1525984Sjhaslam 	sum[1] = result[1];
1535984Sjhaslam }
1545984Sjhaslam 
1555984Sjhaslam /*
1565984Sjhaslam  * The basic idea is to break the 2 64-bit values into 4 32-bit values,
1575984Sjhaslam  * use native multiplication on those, and then re-combine into the
1585984Sjhaslam  * resulting 128-bit value.
1595984Sjhaslam  *
1605984Sjhaslam  * (hi1 << 32 + lo1) * (hi2 << 32 + lo2) =
1615984Sjhaslam  *     hi1 * hi2 << 64 +
1625984Sjhaslam  *     hi1 * lo2 << 32 +
1635984Sjhaslam  *     hi2 * lo1 << 32 +
1645984Sjhaslam  *     lo1 * lo2
1655984Sjhaslam  */
1665984Sjhaslam static void
dt_multiply_128(uint64_t factor1,uint64_t factor2,uint64_t * product)1675984Sjhaslam dt_multiply_128(uint64_t factor1, uint64_t factor2, uint64_t *product)
1685984Sjhaslam {
1695984Sjhaslam 	uint64_t hi1, hi2, lo1, lo2;
1705984Sjhaslam 	uint64_t tmp[2];
1715984Sjhaslam 
1725984Sjhaslam 	hi1 = factor1 >> 32;
1735984Sjhaslam 	hi2 = factor2 >> 32;
1745984Sjhaslam 
1755984Sjhaslam 	lo1 = factor1 & DT_MASK_LO;
1765984Sjhaslam 	lo2 = factor2 & DT_MASK_LO;
1775984Sjhaslam 
1785984Sjhaslam 	product[0] = lo1 * lo2;
1795984Sjhaslam 	product[1] = hi1 * hi2;
1805984Sjhaslam 
1815984Sjhaslam 	tmp[0] = hi1 * lo2;
1825984Sjhaslam 	tmp[1] = 0;
1835984Sjhaslam 	dt_shift_128(tmp, 32);
1845984Sjhaslam 	dt_add_128(product, tmp, product);
1855984Sjhaslam 
1865984Sjhaslam 	tmp[0] = hi2 * lo1;
1875984Sjhaslam 	tmp[1] = 0;
1885984Sjhaslam 	dt_shift_128(tmp, 32);
1895984Sjhaslam 	dt_add_128(product, tmp, product);
1905984Sjhaslam }
1915984Sjhaslam 
1925984Sjhaslam /*
1935984Sjhaslam  * This is long-hand division.
1945984Sjhaslam  *
1955984Sjhaslam  * We initialize subtrahend by shifting divisor left as far as possible. We
1965984Sjhaslam  * loop, comparing subtrahend to dividend:  if subtrahend is smaller, we
1975984Sjhaslam  * subtract and set the appropriate bit in the result.  We then shift
1985984Sjhaslam  * subtrahend right by one bit for the next comparison.
1995984Sjhaslam  */
2005984Sjhaslam static void
dt_divide_128(uint64_t * dividend,uint64_t divisor,uint64_t * quotient)2015984Sjhaslam dt_divide_128(uint64_t *dividend, uint64_t divisor, uint64_t *quotient)
2025984Sjhaslam {
2035984Sjhaslam 	uint64_t result[2] = { 0, 0 };
2045984Sjhaslam 	uint64_t remainder[2];
2055984Sjhaslam 	uint64_t subtrahend[2];
2065984Sjhaslam 	uint64_t divisor_128[2];
2075984Sjhaslam 	uint64_t mask[2] = { 1, 0 };
2085984Sjhaslam 	int log = 0;
2095984Sjhaslam 
2105984Sjhaslam 	assert(divisor != 0);
2115984Sjhaslam 
2125984Sjhaslam 	divisor_128[0] = divisor;
2135984Sjhaslam 	divisor_128[1] = 0;
2145984Sjhaslam 
2155984Sjhaslam 	remainder[0] = dividend[0];
2165984Sjhaslam 	remainder[1] = dividend[1];
2175984Sjhaslam 
2185984Sjhaslam 	subtrahend[0] = divisor;
2195984Sjhaslam 	subtrahend[1] = 0;
2205984Sjhaslam 
2215984Sjhaslam 	while (divisor > 0) {
2225984Sjhaslam 		log++;
2235984Sjhaslam 		divisor >>= 1;
2245984Sjhaslam 	}
2255984Sjhaslam 
2265984Sjhaslam 	dt_shift_128(subtrahend, 128 - log);
2275984Sjhaslam 	dt_shift_128(mask, 128 - log);
2285984Sjhaslam 
2295984Sjhaslam 	while (dt_ge_128(remainder, divisor_128)) {
2305984Sjhaslam 		if (dt_ge_128(remainder, subtrahend)) {
2315984Sjhaslam 			dt_subtract_128(remainder, subtrahend, remainder);
2325984Sjhaslam 			result[0] |= mask[0];
2335984Sjhaslam 			result[1] |= mask[1];
2345984Sjhaslam 		}
2355984Sjhaslam 
2365984Sjhaslam 		dt_shift_128(subtrahend, -1);
2375984Sjhaslam 		dt_shift_128(mask, -1);
2385984Sjhaslam 	}
2395984Sjhaslam 
2405984Sjhaslam 	quotient[0] = result[0];
2415984Sjhaslam 	quotient[1] = result[1];
2425984Sjhaslam }
2435984Sjhaslam 
2445984Sjhaslam /*
2455984Sjhaslam  * This is the long-hand method of calculating a square root.
2465984Sjhaslam  * The algorithm is as follows:
2475984Sjhaslam  *
2485984Sjhaslam  * 1. Group the digits by 2 from the right.
2495984Sjhaslam  * 2. Over the leftmost group, find the largest single-digit number
2505984Sjhaslam  *    whose square is less than that group.
2515984Sjhaslam  * 3. Subtract the result of the previous step (2 or 4, depending) and
2525984Sjhaslam  *    bring down the next two-digit group.
2535984Sjhaslam  * 4. For the result R we have so far, find the largest single-digit number
2545984Sjhaslam  *    x such that 2 * R * 10 * x + x^2 is less than the result from step 3.
2555984Sjhaslam  *    (Note that this is doubling R and performing a decimal left-shift by 1
2565984Sjhaslam  *    and searching for the appropriate decimal to fill the one's place.)
2575984Sjhaslam  *    The value x is the next digit in the square root.
2585984Sjhaslam  * Repeat steps 3 and 4 until the desired precision is reached.  (We're
2595984Sjhaslam  * dealing with integers, so the above is sufficient.)
2605984Sjhaslam  *
2615984Sjhaslam  * In decimal, the square root of 582,734 would be calculated as so:
2625984Sjhaslam  *
2635984Sjhaslam  *     __7__6__3
2645984Sjhaslam  *    | 58 27 34
2655984Sjhaslam  *     -49       (7^2 == 49 => 7 is the first digit in the square root)
2665984Sjhaslam  *      --
2675984Sjhaslam  *       9 27    (Subtract and bring down the next group.)
2685984Sjhaslam  * 146   8 76    (2 * 7 * 10 * 6 + 6^2 == 876 => 6 is the next digit in
2695984Sjhaslam  *      -----     the square root)
2705984Sjhaslam  *         51 34 (Subtract and bring down the next group.)
2715984Sjhaslam  * 1523    45 69 (2 * 76 * 10 * 3 + 3^2 == 4569 => 3 is the next digit in
2725984Sjhaslam  *         -----  the square root)
2735984Sjhaslam  *          5 65 (remainder)
2745984Sjhaslam  *
2755984Sjhaslam  * The above algorithm applies similarly in binary, but note that the
2765984Sjhaslam  * only possible non-zero value for x in step 4 is 1, so step 4 becomes a
2775984Sjhaslam  * simple decision: is 2 * R * 2 * 1 + 1^2 (aka R << 2 + 1) less than the
2785984Sjhaslam  * preceding difference?
2795984Sjhaslam  *
2805984Sjhaslam  * In binary, the square root of 11011011 would be calculated as so:
2815984Sjhaslam  *
2825984Sjhaslam  *     __1__1__1__0
2835984Sjhaslam  *    | 11 01 10 11
2845984Sjhaslam  *      01          (0 << 2 + 1 == 1 < 11 => this bit is 1)
2855984Sjhaslam  *      --
2865984Sjhaslam  *      10 01 10 11
2875984Sjhaslam  * 101   1 01       (1 << 2 + 1 == 101 < 1001 => next bit is 1)
2885984Sjhaslam  *      -----
2895984Sjhaslam  *       1 00 10 11
2905984Sjhaslam  * 1101    11 01    (11 << 2 + 1 == 1101 < 10010 => next bit is 1)
2915984Sjhaslam  *       -------
2925984Sjhaslam  *          1 01 11
2935984Sjhaslam  * 11101    1 11 01 (111 << 2 + 1 == 11101 > 10111 => last bit is 0)
2945984Sjhaslam  *
2955984Sjhaslam  */
2965984Sjhaslam static uint64_t
dt_sqrt_128(uint64_t * square)2975984Sjhaslam dt_sqrt_128(uint64_t *square)
2985984Sjhaslam {
2995984Sjhaslam 	uint64_t result[2] = { 0, 0 };
3005984Sjhaslam 	uint64_t diff[2] = { 0, 0 };
3015984Sjhaslam 	uint64_t one[2] = { 1, 0 };
3025984Sjhaslam 	uint64_t next_pair[2];
3035984Sjhaslam 	uint64_t next_try[2];
3045984Sjhaslam 	uint64_t bit_pairs, pair_shift;
3055984Sjhaslam 	int i;
3065984Sjhaslam 
3075984Sjhaslam 	bit_pairs = dt_nbits_128(square) / 2;
3085984Sjhaslam 	pair_shift = bit_pairs * 2;
3095984Sjhaslam 
3105984Sjhaslam 	for (i = 0; i <= bit_pairs; i++) {
3115984Sjhaslam 		/*
3125984Sjhaslam 		 * Bring down the next pair of bits.
3135984Sjhaslam 		 */
3145984Sjhaslam 		next_pair[0] = square[0];
3155984Sjhaslam 		next_pair[1] = square[1];
3165984Sjhaslam 		dt_shift_128(next_pair, -pair_shift);
3175984Sjhaslam 		next_pair[0] &= 0x3;
3185984Sjhaslam 		next_pair[1] = 0;
3195984Sjhaslam 
3205984Sjhaslam 		dt_shift_128(diff, 2);
3215984Sjhaslam 		dt_add_128(diff, next_pair, diff);
3225984Sjhaslam 
3235984Sjhaslam 		/*
3245984Sjhaslam 		 * next_try = R << 2 + 1
3255984Sjhaslam 		 */
3265984Sjhaslam 		next_try[0] = result[0];
3275984Sjhaslam 		next_try[1] = result[1];
3285984Sjhaslam 		dt_shift_128(next_try, 2);
3295984Sjhaslam 		dt_add_128(next_try, one, next_try);
3305984Sjhaslam 
3315984Sjhaslam 		if (dt_le_128(next_try, diff)) {
3325984Sjhaslam 			dt_subtract_128(diff, next_try, diff);
3335984Sjhaslam 			dt_shift_128(result, 1);
3345984Sjhaslam 			dt_add_128(result, one, result);
3355984Sjhaslam 		} else {
3365984Sjhaslam 			dt_shift_128(result, 1);
3375984Sjhaslam 		}
3385984Sjhaslam 
3395984Sjhaslam 		pair_shift -= 2;
3405984Sjhaslam 	}
3415984Sjhaslam 
3425984Sjhaslam 	assert(result[1] == 0);
3435984Sjhaslam 
3445984Sjhaslam 	return (result[0]);
3455984Sjhaslam }
3465984Sjhaslam 
3475984Sjhaslam uint64_t
dt_stddev(uint64_t * data,uint64_t normal)3485984Sjhaslam dt_stddev(uint64_t *data, uint64_t normal)
3495984Sjhaslam {
3505984Sjhaslam 	uint64_t avg_of_squares[2];
3515984Sjhaslam 	uint64_t square_of_avg[2];
3525984Sjhaslam 	int64_t norm_avg;
3535984Sjhaslam 	uint64_t diff[2];
3545984Sjhaslam 
3555984Sjhaslam 	/*
3565984Sjhaslam 	 * The standard approximation for standard deviation is
3575984Sjhaslam 	 * sqrt(average(x**2) - average(x)**2), i.e. the square root
3585984Sjhaslam 	 * of the average of the squares minus the square of the average.
3595984Sjhaslam 	 */
3605984Sjhaslam 	dt_divide_128(data + 2, normal, avg_of_squares);
3615984Sjhaslam 	dt_divide_128(avg_of_squares, data[0], avg_of_squares);
3625984Sjhaslam 
3635984Sjhaslam 	norm_avg = (int64_t)data[1] / (int64_t)normal / (int64_t)data[0];
3645984Sjhaslam 
3655984Sjhaslam 	if (norm_avg < 0)
3665984Sjhaslam 		norm_avg = -norm_avg;
3675984Sjhaslam 
3685984Sjhaslam 	dt_multiply_128((uint64_t)norm_avg, (uint64_t)norm_avg, square_of_avg);
3695984Sjhaslam 
3705984Sjhaslam 	dt_subtract_128(avg_of_squares, square_of_avg, diff);
3715984Sjhaslam 
3725984Sjhaslam 	return (dt_sqrt_128(diff));
3735984Sjhaslam }
3745984Sjhaslam 
3750Sstevel@tonic-gate static int
dt_flowindent(dtrace_hdl_t * dtp,dtrace_probedata_t * data,dtrace_epid_t last,dtrace_bufdesc_t * buf,size_t offs)3760Sstevel@tonic-gate dt_flowindent(dtrace_hdl_t *dtp, dtrace_probedata_t *data, dtrace_epid_t last,
3770Sstevel@tonic-gate     dtrace_bufdesc_t *buf, size_t offs)
3780Sstevel@tonic-gate {
3790Sstevel@tonic-gate 	dtrace_probedesc_t *pd = data->dtpda_pdesc, *npd;
3800Sstevel@tonic-gate 	dtrace_eprobedesc_t *epd = data->dtpda_edesc, *nepd;
381457Sbmc 	char *p = pd->dtpd_provider, *n = pd->dtpd_name, *sub;
3820Sstevel@tonic-gate 	dtrace_flowkind_t flow = DTRACEFLOW_NONE;
3830Sstevel@tonic-gate 	const char *str = NULL;
3840Sstevel@tonic-gate 	static const char *e_str[2] = { " -> ", " => " };
3850Sstevel@tonic-gate 	static const char *r_str[2] = { " <- ", " <= " };
386457Sbmc 	static const char *ent = "entry", *ret = "return";
387457Sbmc 	static int entlen = 0, retlen = 0;
3880Sstevel@tonic-gate 	dtrace_epid_t next, id = epd->dtepd_epid;
3890Sstevel@tonic-gate 	int rval;
3900Sstevel@tonic-gate 
391457Sbmc 	if (entlen == 0) {
392457Sbmc 		assert(retlen == 0);
393457Sbmc 		entlen = strlen(ent);
394457Sbmc 		retlen = strlen(ret);
395457Sbmc 	}
396457Sbmc 
397457Sbmc 	/*
398457Sbmc 	 * If the name of the probe is "entry" or ends with "-entry", we
399457Sbmc 	 * treat it as an entry; if it is "return" or ends with "-return",
400457Sbmc 	 * we treat it as a return.  (This allows application-provided probes
401457Sbmc 	 * like "method-entry" or "function-entry" to participate in flow
402457Sbmc 	 * indentation -- without accidentally misinterpreting popular probe
403457Sbmc 	 * names like "carpentry", "gentry" or "Coventry".)
404457Sbmc 	 */
405457Sbmc 	if ((sub = strstr(n, ent)) != NULL && sub[entlen] == '\0' &&
406457Sbmc 	    (sub == n || sub[-1] == '-')) {
4070Sstevel@tonic-gate 		flow = DTRACEFLOW_ENTRY;
4080Sstevel@tonic-gate 		str = e_str[strcmp(p, "syscall") == 0];
409457Sbmc 	} else if ((sub = strstr(n, ret)) != NULL && sub[retlen] == '\0' &&
410457Sbmc 	    (sub == n || sub[-1] == '-')) {
4110Sstevel@tonic-gate 		flow = DTRACEFLOW_RETURN;
4120Sstevel@tonic-gate 		str = r_str[strcmp(p, "syscall") == 0];
4130Sstevel@tonic-gate 	}
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	/*
4160Sstevel@tonic-gate 	 * If we're going to indent this, we need to check the ID of our last
4170Sstevel@tonic-gate 	 * call.  If we're looking at the same probe ID but a different EPID,
4180Sstevel@tonic-gate 	 * we _don't_ want to indent.  (Yes, there are some minor holes in
4190Sstevel@tonic-gate 	 * this scheme -- it's a heuristic.)
4200Sstevel@tonic-gate 	 */
4210Sstevel@tonic-gate 	if (flow == DTRACEFLOW_ENTRY) {
4220Sstevel@tonic-gate 		if ((last != DTRACE_EPIDNONE && id != last &&
4230Sstevel@tonic-gate 		    pd->dtpd_id == dtp->dt_pdesc[last]->dtpd_id))
4240Sstevel@tonic-gate 			flow = DTRACEFLOW_NONE;
4250Sstevel@tonic-gate 	}
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	/*
4280Sstevel@tonic-gate 	 * If we're going to unindent this, it's more difficult to see if
4290Sstevel@tonic-gate 	 * we don't actually want to unindent it -- we need to look at the
4300Sstevel@tonic-gate 	 * _next_ EPID.
4310Sstevel@tonic-gate 	 */
4320Sstevel@tonic-gate 	if (flow == DTRACEFLOW_RETURN) {
4330Sstevel@tonic-gate 		offs += epd->dtepd_size;
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 		do {
4360Sstevel@tonic-gate 			if (offs >= buf->dtbd_size) {
4370Sstevel@tonic-gate 				/*
4380Sstevel@tonic-gate 				 * We're at the end -- maybe.  If the oldest
4390Sstevel@tonic-gate 				 * record is non-zero, we need to wrap.
4400Sstevel@tonic-gate 				 */
4410Sstevel@tonic-gate 				if (buf->dtbd_oldest != 0) {
4420Sstevel@tonic-gate 					offs = 0;
4430Sstevel@tonic-gate 				} else {
4440Sstevel@tonic-gate 					goto out;
4450Sstevel@tonic-gate 				}
4460Sstevel@tonic-gate 			}
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 			next = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs);
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 			if (next == DTRACE_EPIDNONE)
4510Sstevel@tonic-gate 				offs += sizeof (id);
4520Sstevel@tonic-gate 		} while (next == DTRACE_EPIDNONE);
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 		if ((rval = dt_epid_lookup(dtp, next, &nepd, &npd)) != 0)
4550Sstevel@tonic-gate 			return (rval);
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 		if (next != id && npd->dtpd_id == pd->dtpd_id)
4580Sstevel@tonic-gate 			flow = DTRACEFLOW_NONE;
4590Sstevel@tonic-gate 	}
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate out:
4620Sstevel@tonic-gate 	if (flow == DTRACEFLOW_ENTRY || flow == DTRACEFLOW_RETURN) {
4630Sstevel@tonic-gate 		data->dtpda_prefix = str;
4640Sstevel@tonic-gate 	} else {
4650Sstevel@tonic-gate 		data->dtpda_prefix = "| ";
4660Sstevel@tonic-gate 	}
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	if (flow == DTRACEFLOW_RETURN && data->dtpda_indent > 0)
4690Sstevel@tonic-gate 		data->dtpda_indent -= 2;
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	data->dtpda_flow = flow;
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	return (0);
4740Sstevel@tonic-gate }
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate static int
dt_nullprobe()4770Sstevel@tonic-gate dt_nullprobe()
4780Sstevel@tonic-gate {
4790Sstevel@tonic-gate 	return (DTRACE_CONSUME_THIS);
4800Sstevel@tonic-gate }
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate static int
dt_nullrec()4830Sstevel@tonic-gate dt_nullrec()
4840Sstevel@tonic-gate {
4850Sstevel@tonic-gate 	return (DTRACE_CONSUME_NEXT);
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate int
dt_print_quantline(dtrace_hdl_t * dtp,FILE * fp,int64_t val,uint64_t normal,long double total,char positives,char negatives)489457Sbmc dt_print_quantline(dtrace_hdl_t *dtp, FILE *fp, int64_t val,
490457Sbmc     uint64_t normal, long double total, char positives, char negatives)
491457Sbmc {
492457Sbmc 	long double f;
493457Sbmc 	uint_t depth, len = 40;
494457Sbmc 
495457Sbmc 	const char *ats = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
496457Sbmc 	const char *spaces = "                                        ";
497457Sbmc 
498457Sbmc 	assert(strlen(ats) == len && strlen(spaces) == len);
499457Sbmc 	assert(!(total == 0 && (positives || negatives)));
500457Sbmc 	assert(!(val < 0 && !negatives));
501457Sbmc 	assert(!(val > 0 && !positives));
502457Sbmc 	assert(!(val != 0 && total == 0));
503457Sbmc 
504457Sbmc 	if (!negatives) {
505457Sbmc 		if (positives) {
506457Sbmc 			f = (dt_fabsl((long double)val) * len) / total;
507457Sbmc 			depth = (uint_t)(f + 0.5);
508457Sbmc 		} else {
509457Sbmc 			depth = 0;
510457Sbmc 		}
511457Sbmc 
512457Sbmc 		return (dt_printf(dtp, fp, "|%s%s %-9lld\n", ats + len - depth,
513457Sbmc 		    spaces + depth, (long long)val / normal));
514457Sbmc 	}
515457Sbmc 
516457Sbmc 	if (!positives) {
517457Sbmc 		f = (dt_fabsl((long double)val) * len) / total;
518457Sbmc 		depth = (uint_t)(f + 0.5);
519457Sbmc 
520457Sbmc 		return (dt_printf(dtp, fp, "%s%s| %-9lld\n", spaces + depth,
521457Sbmc 		    ats + len - depth, (long long)val / normal));
522457Sbmc 	}
523457Sbmc 
524457Sbmc 	/*
525457Sbmc 	 * If we're here, we have both positive and negative bucket values.
526457Sbmc 	 * To express this graphically, we're going to generate both positive
527457Sbmc 	 * and negative bars separated by a centerline.  These bars are half
528457Sbmc 	 * the size of normal quantize()/lquantize() bars, so we divide the
529457Sbmc 	 * length in half before calculating the bar length.
530457Sbmc 	 */
531457Sbmc 	len /= 2;
532457Sbmc 	ats = &ats[len];
533457Sbmc 	spaces = &spaces[len];
534457Sbmc 
535457Sbmc 	f = (dt_fabsl((long double)val) * len) / total;
536457Sbmc 	depth = (uint_t)(f + 0.5);
537457Sbmc 
538457Sbmc 	if (val <= 0) {
539457Sbmc 		return (dt_printf(dtp, fp, "%s%s|%*s %-9lld\n", spaces + depth,
540457Sbmc 		    ats + len - depth, len, "", (long long)val / normal));
541457Sbmc 	} else {
542457Sbmc 		return (dt_printf(dtp, fp, "%20s|%s%s %-9lld\n", "",
543457Sbmc 		    ats + len - depth, spaces + depth,
544457Sbmc 		    (long long)val / normal));
545457Sbmc 	}
546457Sbmc }
547457Sbmc 
548457Sbmc int
dt_print_quantize(dtrace_hdl_t * dtp,FILE * fp,const void * addr,size_t size,uint64_t normal)5490Sstevel@tonic-gate dt_print_quantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
5500Sstevel@tonic-gate     size_t size, uint64_t normal)
5510Sstevel@tonic-gate {
552457Sbmc 	const int64_t *data = addr;
5530Sstevel@tonic-gate 	int i, first_bin = 0, last_bin = DTRACE_QUANTIZE_NBUCKETS - 1;
554457Sbmc 	long double total = 0;
555457Sbmc 	char positives = 0, negatives = 0;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	if (size != DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t))
5580Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_DMISMATCH));
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	while (first_bin < DTRACE_QUANTIZE_NBUCKETS - 1 && data[first_bin] == 0)
5610Sstevel@tonic-gate 		first_bin++;
5620Sstevel@tonic-gate 
563457Sbmc 	if (first_bin == DTRACE_QUANTIZE_NBUCKETS - 1) {
564457Sbmc 		/*
565457Sbmc 		 * There isn't any data.  This is possible if (and only if)
566457Sbmc 		 * negative increment values have been used.  In this case,
567457Sbmc 		 * we'll print the buckets around 0.
568457Sbmc 		 */
569457Sbmc 		first_bin = DTRACE_QUANTIZE_ZEROBUCKET - 1;
570457Sbmc 		last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 1;
571457Sbmc 	} else {
572457Sbmc 		if (first_bin > 0)
573457Sbmc 			first_bin--;
5740Sstevel@tonic-gate 
575457Sbmc 		while (last_bin > 0 && data[last_bin] == 0)
576457Sbmc 			last_bin--;
5770Sstevel@tonic-gate 
578457Sbmc 		if (last_bin < DTRACE_QUANTIZE_NBUCKETS - 1)
579457Sbmc 			last_bin++;
580457Sbmc 	}
581457Sbmc 
582457Sbmc 	for (i = first_bin; i <= last_bin; i++) {
583457Sbmc 		positives |= (data[i] > 0);
584457Sbmc 		negatives |= (data[i] < 0);
585457Sbmc 		total += dt_fabsl((long double)data[i]);
586457Sbmc 	}
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 	if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value",
5890Sstevel@tonic-gate 	    "------------- Distribution -------------", "count") < 0)
5900Sstevel@tonic-gate 		return (-1);
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 	for (i = first_bin; i <= last_bin; i++) {
593457Sbmc 		if (dt_printf(dtp, fp, "%16lld ",
594457Sbmc 		    (long long)DTRACE_QUANTIZE_BUCKETVAL(i)) < 0)
595457Sbmc 			return (-1);
5960Sstevel@tonic-gate 
597457Sbmc 		if (dt_print_quantline(dtp, fp, data[i], normal, total,
598457Sbmc 		    positives, negatives) < 0)
5990Sstevel@tonic-gate 			return (-1);
6000Sstevel@tonic-gate 	}
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	return (0);
6030Sstevel@tonic-gate }
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate int
dt_print_lquantize(dtrace_hdl_t * dtp,FILE * fp,const void * addr,size_t size,uint64_t normal)6060Sstevel@tonic-gate dt_print_lquantize(dtrace_hdl_t *dtp, FILE *fp, const void *addr,
6070Sstevel@tonic-gate     size_t size, uint64_t normal)
6080Sstevel@tonic-gate {
609457Sbmc 	const int64_t *data = addr;
6100Sstevel@tonic-gate 	int i, first_bin, last_bin, base;
611457Sbmc 	uint64_t arg;
612457Sbmc 	long double total = 0;
6130Sstevel@tonic-gate 	uint16_t step, levels;
614457Sbmc 	char positives = 0, negatives = 0;
6150Sstevel@tonic-gate 
6160Sstevel@tonic-gate 	if (size < sizeof (uint64_t))
6170Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_DMISMATCH));
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	arg = *data++;
6200Sstevel@tonic-gate 	size -= sizeof (uint64_t);
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	base = DTRACE_LQUANTIZE_BASE(arg);
6230Sstevel@tonic-gate 	step = DTRACE_LQUANTIZE_STEP(arg);
6240Sstevel@tonic-gate 	levels = DTRACE_LQUANTIZE_LEVELS(arg);
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	first_bin = 0;
6270Sstevel@tonic-gate 	last_bin = levels + 1;
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	if (size != sizeof (uint64_t) * (levels + 2))
6300Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_DMISMATCH));
6310Sstevel@tonic-gate 
632491Sbmc 	while (first_bin <= levels + 1 && data[first_bin] == 0)
6330Sstevel@tonic-gate 		first_bin++;
6340Sstevel@tonic-gate 
635491Sbmc 	if (first_bin > levels + 1) {
636457Sbmc 		first_bin = 0;
637457Sbmc 		last_bin = 2;
638457Sbmc 	} else {
639457Sbmc 		if (first_bin > 0)
640457Sbmc 			first_bin--;
6410Sstevel@tonic-gate 
642457Sbmc 		while (last_bin > 0 && data[last_bin] == 0)
643457Sbmc 			last_bin--;
6440Sstevel@tonic-gate 
645457Sbmc 		if (last_bin < levels + 1)
646457Sbmc 			last_bin++;
647457Sbmc 	}
6480Sstevel@tonic-gate 
649457Sbmc 	for (i = first_bin; i <= last_bin; i++) {
650457Sbmc 		positives |= (data[i] > 0);
651457Sbmc 		negatives |= (data[i] < 0);
652457Sbmc 		total += dt_fabsl((long double)data[i]);
653457Sbmc 	}
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate 	if (dt_printf(dtp, fp, "\n%16s %41s %-9s\n", "value",
6560Sstevel@tonic-gate 	    "------------- Distribution -------------", "count") < 0)
6570Sstevel@tonic-gate 		return (-1);
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	for (i = first_bin; i <= last_bin; i++) {
6600Sstevel@tonic-gate 		char c[32];
6610Sstevel@tonic-gate 		int err;
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 		if (i == 0) {
6640Sstevel@tonic-gate 			(void) snprintf(c, sizeof (c), "< %d",
6650Sstevel@tonic-gate 			    base / (uint32_t)normal);
6660Sstevel@tonic-gate 			err = dt_printf(dtp, fp, "%16s ", c);
6670Sstevel@tonic-gate 		} else if (i == levels + 1) {
6680Sstevel@tonic-gate 			(void) snprintf(c, sizeof (c), ">= %d",
6690Sstevel@tonic-gate 			    base + (levels * step));
6700Sstevel@tonic-gate 			err = dt_printf(dtp, fp, "%16s ", c);
6710Sstevel@tonic-gate 		} else {
6720Sstevel@tonic-gate 			err = dt_printf(dtp, fp, "%16d ",
6730Sstevel@tonic-gate 			    base + (i - 1) * step);
6740Sstevel@tonic-gate 		}
6750Sstevel@tonic-gate 
676457Sbmc 		if (err < 0 || dt_print_quantline(dtp, fp, data[i], normal,
677457Sbmc 		    total, positives, negatives) < 0)
6780Sstevel@tonic-gate 			return (-1);
6790Sstevel@tonic-gate 	}
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	return (0);
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate /*ARGSUSED*/
6850Sstevel@tonic-gate static int
dt_print_average(dtrace_hdl_t * dtp,FILE * fp,caddr_t addr,size_t size,uint64_t normal)6860Sstevel@tonic-gate dt_print_average(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr,
6870Sstevel@tonic-gate     size_t size, uint64_t normal)
6880Sstevel@tonic-gate {
6890Sstevel@tonic-gate 	/* LINTED - alignment */
6905984Sjhaslam 	int64_t *data = (int64_t *)addr;
6915984Sjhaslam 
6925984Sjhaslam 	return (dt_printf(dtp, fp, " %16lld", data[0] ?
6935984Sjhaslam 	    (long long)(data[1] / (int64_t)normal / data[0]) : 0));
6945984Sjhaslam }
6955984Sjhaslam 
6965984Sjhaslam /*ARGSUSED*/
6975984Sjhaslam static int
dt_print_stddev(dtrace_hdl_t * dtp,FILE * fp,caddr_t addr,size_t size,uint64_t normal)6985984Sjhaslam dt_print_stddev(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr,
6995984Sjhaslam     size_t size, uint64_t normal)
7005984Sjhaslam {
7015984Sjhaslam 	/* LINTED - alignment */
7020Sstevel@tonic-gate 	uint64_t *data = (uint64_t *)addr;
7030Sstevel@tonic-gate 
7045984Sjhaslam 	return (dt_printf(dtp, fp, " %16llu", data[0] ?
7055984Sjhaslam 	    (unsigned long long) dt_stddev(data, normal) : 0));
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate /*ARGSUSED*/
7090Sstevel@tonic-gate int
dt_print_bytes(dtrace_hdl_t * dtp,FILE * fp,caddr_t addr,size_t nbytes,int width,int quiet)7100Sstevel@tonic-gate dt_print_bytes(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr,
7110Sstevel@tonic-gate     size_t nbytes, int width, int quiet)
7120Sstevel@tonic-gate {
7130Sstevel@tonic-gate 	/*
7140Sstevel@tonic-gate 	 * If the byte stream is a series of printable characters, followed by
7150Sstevel@tonic-gate 	 * a terminating byte, we print it out as a string.  Otherwise, we
7160Sstevel@tonic-gate 	 * assume that it's something else and just print the bytes.
7170Sstevel@tonic-gate 	 */
7180Sstevel@tonic-gate 	int i, j, margin = 5;
7190Sstevel@tonic-gate 	char *c = (char *)addr;
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 	if (nbytes == 0)
7220Sstevel@tonic-gate 		return (0);
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	if (dtp->dt_options[DTRACEOPT_RAWBYTES] != DTRACEOPT_UNSET)
7250Sstevel@tonic-gate 		goto raw;
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	for (i = 0; i < nbytes; i++) {
7280Sstevel@tonic-gate 		/*
7290Sstevel@tonic-gate 		 * We define a "printable character" to be one for which
7300Sstevel@tonic-gate 		 * isprint(3C) returns non-zero, isspace(3C) returns non-zero,
7310Sstevel@tonic-gate 		 * or a character which is either backspace or the bell.
7320Sstevel@tonic-gate 		 * Backspace and the bell are regrettably special because
7330Sstevel@tonic-gate 		 * they fail the first two tests -- and yet they are entirely
7340Sstevel@tonic-gate 		 * printable.  These are the only two control characters that
7350Sstevel@tonic-gate 		 * have meaning for the terminal and for which isprint(3C) and
7360Sstevel@tonic-gate 		 * isspace(3C) return 0.
7370Sstevel@tonic-gate 		 */
7380Sstevel@tonic-gate 		if (isprint(c[i]) || isspace(c[i]) ||
7390Sstevel@tonic-gate 		    c[i] == '\b' || c[i] == '\a')
7400Sstevel@tonic-gate 			continue;
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 		if (c[i] == '\0' && i > 0) {
7430Sstevel@tonic-gate 			/*
7440Sstevel@tonic-gate 			 * This looks like it might be a string.  Before we
7450Sstevel@tonic-gate 			 * assume that it is indeed a string, check the
7460Sstevel@tonic-gate 			 * remainder of the byte range; if it contains
7470Sstevel@tonic-gate 			 * additional non-nul characters, we'll assume that
7480Sstevel@tonic-gate 			 * it's a binary stream that just happens to look like
7490Sstevel@tonic-gate 			 * a string, and we'll print out the individual bytes.
7500Sstevel@tonic-gate 			 */
7510Sstevel@tonic-gate 			for (j = i + 1; j < nbytes; j++) {
7520Sstevel@tonic-gate 				if (c[j] != '\0')
7530Sstevel@tonic-gate 					break;
7540Sstevel@tonic-gate 			}
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 			if (j != nbytes)
7570Sstevel@tonic-gate 				break;
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 			if (quiet)
7600Sstevel@tonic-gate 				return (dt_printf(dtp, fp, "%s", c));
7610Sstevel@tonic-gate 			else
7620Sstevel@tonic-gate 				return (dt_printf(dtp, fp, "  %-*s", width, c));
7630Sstevel@tonic-gate 		}
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 		break;
7660Sstevel@tonic-gate 	}
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 	if (i == nbytes) {
7690Sstevel@tonic-gate 		/*
7700Sstevel@tonic-gate 		 * The byte range is all printable characters, but there is
7710Sstevel@tonic-gate 		 * no trailing nul byte.  We'll assume that it's a string and
7720Sstevel@tonic-gate 		 * print it as such.
7730Sstevel@tonic-gate 		 */
7740Sstevel@tonic-gate 		char *s = alloca(nbytes + 1);
7750Sstevel@tonic-gate 		bcopy(c, s, nbytes);
7760Sstevel@tonic-gate 		s[nbytes] = '\0';
7770Sstevel@tonic-gate 		return (dt_printf(dtp, fp, "  %-*s", width, s));
7780Sstevel@tonic-gate 	}
7790Sstevel@tonic-gate 
7800Sstevel@tonic-gate raw:
7810Sstevel@tonic-gate 	if (dt_printf(dtp, fp, "\n%*s      ", margin, "") < 0)
7820Sstevel@tonic-gate 		return (-1);
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate 	for (i = 0; i < 16; i++)
7850Sstevel@tonic-gate 		if (dt_printf(dtp, fp, "  %c", "0123456789abcdef"[i]) < 0)
7860Sstevel@tonic-gate 			return (-1);
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 	if (dt_printf(dtp, fp, "  0123456789abcdef\n") < 0)
7890Sstevel@tonic-gate 		return (-1);
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	for (i = 0; i < nbytes; i += 16) {
7930Sstevel@tonic-gate 		if (dt_printf(dtp, fp, "%*s%5x:", margin, "", i) < 0)
7940Sstevel@tonic-gate 			return (-1);
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate 		for (j = i; j < i + 16 && j < nbytes; j++) {
7970Sstevel@tonic-gate 			if (dt_printf(dtp, fp, " %02x", (uchar_t)c[j]) < 0)
7980Sstevel@tonic-gate 				return (-1);
7990Sstevel@tonic-gate 		}
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 		while (j++ % 16) {
8020Sstevel@tonic-gate 			if (dt_printf(dtp, fp, "   ") < 0)
8030Sstevel@tonic-gate 				return (-1);
8040Sstevel@tonic-gate 		}
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 		if (dt_printf(dtp, fp, "  ") < 0)
8070Sstevel@tonic-gate 			return (-1);
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 		for (j = i; j < i + 16 && j < nbytes; j++) {
8100Sstevel@tonic-gate 			if (dt_printf(dtp, fp, "%c",
8110Sstevel@tonic-gate 			    c[j] < ' ' || c[j] > '~' ? '.' : c[j]) < 0)
8120Sstevel@tonic-gate 				return (-1);
8130Sstevel@tonic-gate 		}
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 		if (dt_printf(dtp, fp, "\n") < 0)
8160Sstevel@tonic-gate 			return (-1);
8170Sstevel@tonic-gate 	}
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	return (0);
8200Sstevel@tonic-gate }
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate int
dt_print_stack(dtrace_hdl_t * dtp,FILE * fp,const char * format,caddr_t addr,int depth,int size)8230Sstevel@tonic-gate dt_print_stack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
824457Sbmc     caddr_t addr, int depth, int size)
8250Sstevel@tonic-gate {
8260Sstevel@tonic-gate 	dtrace_syminfo_t dts;
8270Sstevel@tonic-gate 	GElf_Sym sym;
8280Sstevel@tonic-gate 	int i, indent;
8290Sstevel@tonic-gate 	char c[PATH_MAX * 2];
830457Sbmc 	uint64_t pc;
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	if (dt_printf(dtp, fp, "\n") < 0)
8330Sstevel@tonic-gate 		return (-1);
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate 	if (format == NULL)
8360Sstevel@tonic-gate 		format = "%s";
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate 	if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET)
8390Sstevel@tonic-gate 		indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT];
8400Sstevel@tonic-gate 	else
8410Sstevel@tonic-gate 		indent = _dtrace_stkindent;
8420Sstevel@tonic-gate 
843457Sbmc 	for (i = 0; i < depth; i++) {
844457Sbmc 		switch (size) {
845457Sbmc 		case sizeof (uint32_t):
846457Sbmc 			/* LINTED - alignment */
847457Sbmc 			pc = *((uint32_t *)addr);
848457Sbmc 			break;
849457Sbmc 
850457Sbmc 		case sizeof (uint64_t):
851457Sbmc 			/* LINTED - alignment */
852457Sbmc 			pc = *((uint64_t *)addr);
853457Sbmc 			break;
854457Sbmc 
855457Sbmc 		default:
856457Sbmc 			return (dt_set_errno(dtp, EDT_BADSTACKPC));
857457Sbmc 		}
858457Sbmc 
859457Sbmc 		if (pc == NULL)
860457Sbmc 			break;
861457Sbmc 
862457Sbmc 		addr += size;
863457Sbmc 
8640Sstevel@tonic-gate 		if (dt_printf(dtp, fp, "%*s", indent, "") < 0)
8650Sstevel@tonic-gate 			return (-1);
8660Sstevel@tonic-gate 
867457Sbmc 		if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) {
868457Sbmc 			if (pc > sym.st_value) {
8690Sstevel@tonic-gate 				(void) snprintf(c, sizeof (c), "%s`%s+0x%llx",
8700Sstevel@tonic-gate 				    dts.dts_object, dts.dts_name,
871457Sbmc 				    pc - sym.st_value);
8720Sstevel@tonic-gate 			} else {
8730Sstevel@tonic-gate 				(void) snprintf(c, sizeof (c), "%s`%s",
8740Sstevel@tonic-gate 				    dts.dts_object, dts.dts_name);
8750Sstevel@tonic-gate 			}
8760Sstevel@tonic-gate 		} else {
8770Sstevel@tonic-gate 			/*
8780Sstevel@tonic-gate 			 * We'll repeat the lookup, but this time we'll specify
8790Sstevel@tonic-gate 			 * a NULL GElf_Sym -- indicating that we're only
8800Sstevel@tonic-gate 			 * interested in the containing module.
8810Sstevel@tonic-gate 			 */
882457Sbmc 			if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) {
8830Sstevel@tonic-gate 				(void) snprintf(c, sizeof (c), "%s`0x%llx",
884457Sbmc 				    dts.dts_object, pc);
8850Sstevel@tonic-gate 			} else {
886457Sbmc 				(void) snprintf(c, sizeof (c), "0x%llx", pc);
8870Sstevel@tonic-gate 			}
8880Sstevel@tonic-gate 		}
8890Sstevel@tonic-gate 
8900Sstevel@tonic-gate 		if (dt_printf(dtp, fp, format, c) < 0)
8910Sstevel@tonic-gate 			return (-1);
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 		if (dt_printf(dtp, fp, "\n") < 0)
8940Sstevel@tonic-gate 			return (-1);
8950Sstevel@tonic-gate 	}
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	return (0);
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate int
dt_print_ustack(dtrace_hdl_t * dtp,FILE * fp,const char * format,caddr_t addr,uint64_t arg)9010Sstevel@tonic-gate dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
9020Sstevel@tonic-gate     caddr_t addr, uint64_t arg)
9030Sstevel@tonic-gate {
904457Sbmc 	/* LINTED - alignment */
905457Sbmc 	uint64_t *pc = (uint64_t *)addr;
9060Sstevel@tonic-gate 	uint32_t depth = DTRACE_USTACK_NFRAMES(arg);
9070Sstevel@tonic-gate 	uint32_t strsize = DTRACE_USTACK_STRSIZE(arg);
9080Sstevel@tonic-gate 	const char *strbase = addr + (depth + 1) * sizeof (uint64_t);
9090Sstevel@tonic-gate 	const char *str = strsize ? strbase : NULL;
9100Sstevel@tonic-gate 	int err = 0;
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	char name[PATH_MAX], objname[PATH_MAX], c[PATH_MAX * 2];
9130Sstevel@tonic-gate 	struct ps_prochandle *P;
9140Sstevel@tonic-gate 	GElf_Sym sym;
9150Sstevel@tonic-gate 	int i, indent;
9160Sstevel@tonic-gate 	pid_t pid;
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 	if (depth == 0)
9190Sstevel@tonic-gate 		return (0);
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	pid = (pid_t)*pc++;
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 	if (dt_printf(dtp, fp, "\n") < 0)
9240Sstevel@tonic-gate 		return (-1);
9250Sstevel@tonic-gate 
9260Sstevel@tonic-gate 	if (format == NULL)
9270Sstevel@tonic-gate 		format = "%s";
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 	if (dtp->dt_options[DTRACEOPT_STACKINDENT] != DTRACEOPT_UNSET)
9300Sstevel@tonic-gate 		indent = (int)dtp->dt_options[DTRACEOPT_STACKINDENT];
9310Sstevel@tonic-gate 	else
9320Sstevel@tonic-gate 		indent = _dtrace_stkindent;
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 	/*
9350Sstevel@tonic-gate 	 * Ultimately, we need to add an entry point in the library vector for
9360Sstevel@tonic-gate 	 * determining <symbol, offset> from <pid, address>.  For now, if
9370Sstevel@tonic-gate 	 * this is a vector open, we just print the raw address or string.
9380Sstevel@tonic-gate 	 */
9390Sstevel@tonic-gate 	if (dtp->dt_vector == NULL)
9400Sstevel@tonic-gate 		P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0);
9410Sstevel@tonic-gate 	else
9420Sstevel@tonic-gate 		P = NULL;
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 	if (P != NULL)
9450Sstevel@tonic-gate 		dt_proc_lock(dtp, P); /* lock handle while we perform lookups */
9460Sstevel@tonic-gate 
9470Sstevel@tonic-gate 	for (i = 0; i < depth && pc[i] != NULL; i++) {
948457Sbmc 		const prmap_t *map;
949457Sbmc 
9500Sstevel@tonic-gate 		if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0)
9510Sstevel@tonic-gate 			break;
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 		if (P != NULL && Plookup_by_addr(P, pc[i],
9540Sstevel@tonic-gate 		    name, sizeof (name), &sym) == 0) {
9550Sstevel@tonic-gate 			(void) Pobjname(P, pc[i], objname, sizeof (objname));
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 			if (pc[i] > sym.st_value) {
9580Sstevel@tonic-gate 				(void) snprintf(c, sizeof (c),
9590Sstevel@tonic-gate 				    "%s`%s+0x%llx", dt_basename(objname), name,
9600Sstevel@tonic-gate 				    (u_longlong_t)(pc[i] - sym.st_value));
9610Sstevel@tonic-gate 			} else {
9620Sstevel@tonic-gate 				(void) snprintf(c, sizeof (c),
9630Sstevel@tonic-gate 				    "%s`%s", dt_basename(objname), name);
9640Sstevel@tonic-gate 			}
965491Sbmc 		} else if (str != NULL && str[0] != '\0' && str[0] != '@' &&
966457Sbmc 		    (P != NULL && ((map = Paddr_to_map(P, pc[i])) == NULL ||
967457Sbmc 		    (map->pr_mflags & MA_WRITE)))) {
968457Sbmc 			/*
969457Sbmc 			 * If the current string pointer in the string table
970457Sbmc 			 * does not point to an empty string _and_ the program
971457Sbmc 			 * counter falls in a writable region, we'll use the
972457Sbmc 			 * string from the string table instead of the raw
973457Sbmc 			 * address.  This last condition is necessary because
974457Sbmc 			 * some (broken) ustack helpers will return a string
975457Sbmc 			 * even for a program counter that they can't
976457Sbmc 			 * identify.  If we have a string for a program
977457Sbmc 			 * counter that falls in a segment that isn't
978457Sbmc 			 * writable, we assume that we have fallen into this
979457Sbmc 			 * case and we refuse to use the string.
980457Sbmc 			 */
9810Sstevel@tonic-gate 			(void) snprintf(c, sizeof (c), "%s", str);
9820Sstevel@tonic-gate 		} else {
9830Sstevel@tonic-gate 			if (P != NULL && Pobjname(P, pc[i], objname,
9840Sstevel@tonic-gate 			    sizeof (objname)) != NULL) {
9850Sstevel@tonic-gate 				(void) snprintf(c, sizeof (c), "%s`0x%llx",
9860Sstevel@tonic-gate 				    dt_basename(objname), (u_longlong_t)pc[i]);
9870Sstevel@tonic-gate 			} else {
9880Sstevel@tonic-gate 				(void) snprintf(c, sizeof (c), "0x%llx",
9890Sstevel@tonic-gate 				    (u_longlong_t)pc[i]);
9900Sstevel@tonic-gate 			}
9910Sstevel@tonic-gate 		}
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 		if ((err = dt_printf(dtp, fp, format, c)) < 0)
9940Sstevel@tonic-gate 			break;
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 		if ((err = dt_printf(dtp, fp, "\n")) < 0)
9970Sstevel@tonic-gate 			break;
9980Sstevel@tonic-gate 
999491Sbmc 		if (str != NULL && str[0] == '@') {
1000491Sbmc 			/*
1001491Sbmc 			 * If the first character of the string is an "at" sign,
1002491Sbmc 			 * then the string is inferred to be an annotation --
1003491Sbmc 			 * and it is printed out beneath the frame and offset
1004491Sbmc 			 * with brackets.
1005491Sbmc 			 */
1006491Sbmc 			if ((err = dt_printf(dtp, fp, "%*s", indent, "")) < 0)
1007491Sbmc 				break;
1008491Sbmc 
1009491Sbmc 			(void) snprintf(c, sizeof (c), "  [ %s ]", &str[1]);
1010491Sbmc 
1011491Sbmc 			if ((err = dt_printf(dtp, fp, format, c)) < 0)
1012491Sbmc 				break;
1013491Sbmc 
1014491Sbmc 			if ((err = dt_printf(dtp, fp, "\n")) < 0)
1015491Sbmc 				break;
1016491Sbmc 		}
1017491Sbmc 
10180Sstevel@tonic-gate 		if (str != NULL) {
10190Sstevel@tonic-gate 			str += strlen(str) + 1;
10200Sstevel@tonic-gate 			if (str - strbase >= strsize)
10210Sstevel@tonic-gate 				str = NULL;
10220Sstevel@tonic-gate 		}
10230Sstevel@tonic-gate 	}
10240Sstevel@tonic-gate 
10250Sstevel@tonic-gate 	if (P != NULL) {
10260Sstevel@tonic-gate 		dt_proc_unlock(dtp, P);
10270Sstevel@tonic-gate 		dt_proc_release(dtp, P);
10280Sstevel@tonic-gate 	}
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 	return (err);
10310Sstevel@tonic-gate }
10320Sstevel@tonic-gate 
1033457Sbmc static int
dt_print_usym(dtrace_hdl_t * dtp,FILE * fp,caddr_t addr,dtrace_actkind_t act)1034457Sbmc dt_print_usym(dtrace_hdl_t *dtp, FILE *fp, caddr_t addr, dtrace_actkind_t act)
1035457Sbmc {
1036457Sbmc 	/* LINTED - alignment */
1037457Sbmc 	uint64_t pid = ((uint64_t *)addr)[0];
1038457Sbmc 	/* LINTED - alignment */
1039457Sbmc 	uint64_t pc = ((uint64_t *)addr)[1];
1040457Sbmc 	const char *format = "  %-50s";
1041457Sbmc 	char *s;
1042457Sbmc 	int n, len = 256;
1043457Sbmc 
1044457Sbmc 	if (act == DTRACEACT_USYM && dtp->dt_vector == NULL) {
1045457Sbmc 		struct ps_prochandle *P;
1046457Sbmc 
1047457Sbmc 		if ((P = dt_proc_grab(dtp, pid,
1048457Sbmc 		    PGRAB_RDONLY | PGRAB_FORCE, 0)) != NULL) {
1049457Sbmc 			GElf_Sym sym;
1050457Sbmc 
1051457Sbmc 			dt_proc_lock(dtp, P);
1052457Sbmc 
1053457Sbmc 			if (Plookup_by_addr(P, pc, NULL, 0, &sym) == 0)
1054457Sbmc 				pc = sym.st_value;
1055457Sbmc 
1056457Sbmc 			dt_proc_unlock(dtp, P);
1057457Sbmc 			dt_proc_release(dtp, P);
1058457Sbmc 		}
1059457Sbmc 	}
1060457Sbmc 
1061457Sbmc 	do {
1062457Sbmc 		n = len;
1063457Sbmc 		s = alloca(n);
1064*10791SJonathan.Haslam@Sun.COM 	} while ((len = dtrace_uaddr2str(dtp, pid, pc, s, n)) > n);
1065457Sbmc 
1066457Sbmc 	return (dt_printf(dtp, fp, format, s));
1067457Sbmc }
1068457Sbmc 
1069457Sbmc int
dt_print_umod(dtrace_hdl_t * dtp,FILE * fp,const char * format,caddr_t addr)1070457Sbmc dt_print_umod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr)
1071457Sbmc {
1072457Sbmc 	/* LINTED - alignment */
1073457Sbmc 	uint64_t pid = ((uint64_t *)addr)[0];
1074457Sbmc 	/* LINTED - alignment */
1075457Sbmc 	uint64_t pc = ((uint64_t *)addr)[1];
1076457Sbmc 	int err = 0;
1077457Sbmc 
1078457Sbmc 	char objname[PATH_MAX], c[PATH_MAX * 2];
1079457Sbmc 	struct ps_prochandle *P;
1080457Sbmc 
1081457Sbmc 	if (format == NULL)
1082457Sbmc 		format = "  %-50s";
1083457Sbmc 
1084457Sbmc 	/*
1085457Sbmc 	 * See the comment in dt_print_ustack() for the rationale for
1086457Sbmc 	 * printing raw addresses in the vectored case.
1087457Sbmc 	 */
1088457Sbmc 	if (dtp->dt_vector == NULL)
1089457Sbmc 		P = dt_proc_grab(dtp, pid, PGRAB_RDONLY | PGRAB_FORCE, 0);
1090457Sbmc 	else
1091457Sbmc 		P = NULL;
1092457Sbmc 
1093457Sbmc 	if (P != NULL)
1094457Sbmc 		dt_proc_lock(dtp, P); /* lock handle while we perform lookups */
1095457Sbmc 
1096457Sbmc 	if (P != NULL && Pobjname(P, pc, objname, sizeof (objname)) != NULL) {
1097457Sbmc 		(void) snprintf(c, sizeof (c), "%s", dt_basename(objname));
1098457Sbmc 	} else {
1099457Sbmc 		(void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc);
1100457Sbmc 	}
1101457Sbmc 
1102457Sbmc 	err = dt_printf(dtp, fp, format, c);
1103457Sbmc 
1104457Sbmc 	if (P != NULL) {
1105457Sbmc 		dt_proc_unlock(dtp, P);
1106457Sbmc 		dt_proc_release(dtp, P);
1107457Sbmc 	}
1108457Sbmc 
1109457Sbmc 	return (err);
1110457Sbmc }
1111457Sbmc 
1112457Sbmc static int
dt_print_sym(dtrace_hdl_t * dtp,FILE * fp,const char * format,caddr_t addr)1113457Sbmc dt_print_sym(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr)
1114457Sbmc {
1115457Sbmc 	/* LINTED - alignment */
1116457Sbmc 	uint64_t pc = *((uint64_t *)addr);
1117457Sbmc 	dtrace_syminfo_t dts;
1118457Sbmc 	GElf_Sym sym;
1119457Sbmc 	char c[PATH_MAX * 2];
1120457Sbmc 
1121457Sbmc 	if (format == NULL)
1122457Sbmc 		format = "  %-50s";
1123457Sbmc 
1124457Sbmc 	if (dtrace_lookup_by_addr(dtp, pc, &sym, &dts) == 0) {
1125457Sbmc 		(void) snprintf(c, sizeof (c), "%s`%s",
1126457Sbmc 		    dts.dts_object, dts.dts_name);
1127457Sbmc 	} else {
1128457Sbmc 		/*
1129457Sbmc 		 * We'll repeat the lookup, but this time we'll specify a
1130457Sbmc 		 * NULL GElf_Sym -- indicating that we're only interested in
1131457Sbmc 		 * the containing module.
1132457Sbmc 		 */
1133457Sbmc 		if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) {
1134457Sbmc 			(void) snprintf(c, sizeof (c), "%s`0x%llx",
1135457Sbmc 			    dts.dts_object, (u_longlong_t)pc);
1136457Sbmc 		} else {
1137457Sbmc 			(void) snprintf(c, sizeof (c), "0x%llx",
1138457Sbmc 			    (u_longlong_t)pc);
1139457Sbmc 		}
1140457Sbmc 	}
1141457Sbmc 
1142457Sbmc 	if (dt_printf(dtp, fp, format, c) < 0)
1143457Sbmc 		return (-1);
1144457Sbmc 
1145457Sbmc 	return (0);
1146457Sbmc }
1147457Sbmc 
1148457Sbmc int
dt_print_mod(dtrace_hdl_t * dtp,FILE * fp,const char * format,caddr_t addr)1149457Sbmc dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr)
1150457Sbmc {
1151457Sbmc 	/* LINTED - alignment */
1152457Sbmc 	uint64_t pc = *((uint64_t *)addr);
1153457Sbmc 	dtrace_syminfo_t dts;
1154457Sbmc 	char c[PATH_MAX * 2];
1155457Sbmc 
1156457Sbmc 	if (format == NULL)
1157457Sbmc 		format = "  %-50s";
1158457Sbmc 
1159457Sbmc 	if (dtrace_lookup_by_addr(dtp, pc, NULL, &dts) == 0) {
1160457Sbmc 		(void) snprintf(c, sizeof (c), "%s", dts.dts_object);
1161457Sbmc 	} else {
1162457Sbmc 		(void) snprintf(c, sizeof (c), "0x%llx", (u_longlong_t)pc);
1163457Sbmc 	}
1164457Sbmc 
1165457Sbmc 	if (dt_printf(dtp, fp, format, c) < 0)
1166457Sbmc 		return (-1);
1167457Sbmc 
1168457Sbmc 	return (0);
1169457Sbmc }
1170457Sbmc 
11710Sstevel@tonic-gate typedef struct dt_normal {
11720Sstevel@tonic-gate 	dtrace_aggvarid_t dtnd_id;
11730Sstevel@tonic-gate 	uint64_t dtnd_normal;
11740Sstevel@tonic-gate } dt_normal_t;
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate static int
dt_normalize_agg(const dtrace_aggdata_t * aggdata,void * arg)1177457Sbmc dt_normalize_agg(const dtrace_aggdata_t *aggdata, void *arg)
11780Sstevel@tonic-gate {
11790Sstevel@tonic-gate 	dt_normal_t *normal = arg;
11800Sstevel@tonic-gate 	dtrace_aggdesc_t *agg = aggdata->dtada_desc;
11810Sstevel@tonic-gate 	dtrace_aggvarid_t id = normal->dtnd_id;
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 	if (agg->dtagd_nrecs == 0)
11840Sstevel@tonic-gate 		return (DTRACE_AGGWALK_NEXT);
11850Sstevel@tonic-gate 
11861017Sbmc 	if (agg->dtagd_varid != id)
11870Sstevel@tonic-gate 		return (DTRACE_AGGWALK_NEXT);
11880Sstevel@tonic-gate 
1189457Sbmc 	((dtrace_aggdata_t *)aggdata)->dtada_normal = normal->dtnd_normal;
11900Sstevel@tonic-gate 	return (DTRACE_AGGWALK_NORMALIZE);
11910Sstevel@tonic-gate }
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate static int
dt_normalize(dtrace_hdl_t * dtp,caddr_t base,dtrace_recdesc_t * rec)11940Sstevel@tonic-gate dt_normalize(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec)
11950Sstevel@tonic-gate {
11960Sstevel@tonic-gate 	dt_normal_t normal;
11970Sstevel@tonic-gate 	caddr_t addr;
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 	/*
12000Sstevel@tonic-gate 	 * We (should) have two records:  the aggregation ID followed by the
12010Sstevel@tonic-gate 	 * normalization value.
12020Sstevel@tonic-gate 	 */
12030Sstevel@tonic-gate 	addr = base + rec->dtrd_offset;
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 	if (rec->dtrd_size != sizeof (dtrace_aggvarid_t))
12060Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_BADNORMAL));
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 	/* LINTED - alignment */
12090Sstevel@tonic-gate 	normal.dtnd_id = *((dtrace_aggvarid_t *)addr);
12100Sstevel@tonic-gate 	rec++;
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate 	if (rec->dtrd_action != DTRACEACT_LIBACT)
12130Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_BADNORMAL));
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 	if (rec->dtrd_arg != DT_ACT_NORMALIZE)
12160Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_BADNORMAL));
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate 	addr = base + rec->dtrd_offset;
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 	switch (rec->dtrd_size) {
12210Sstevel@tonic-gate 	case sizeof (uint64_t):
12220Sstevel@tonic-gate 		/* LINTED - alignment */
12230Sstevel@tonic-gate 		normal.dtnd_normal = *((uint64_t *)addr);
12240Sstevel@tonic-gate 		break;
12250Sstevel@tonic-gate 	case sizeof (uint32_t):
12260Sstevel@tonic-gate 		/* LINTED - alignment */
12270Sstevel@tonic-gate 		normal.dtnd_normal = *((uint32_t *)addr);
12280Sstevel@tonic-gate 		break;
12290Sstevel@tonic-gate 	case sizeof (uint16_t):
12300Sstevel@tonic-gate 		/* LINTED - alignment */
12310Sstevel@tonic-gate 		normal.dtnd_normal = *((uint16_t *)addr);
12320Sstevel@tonic-gate 		break;
12330Sstevel@tonic-gate 	case sizeof (uint8_t):
12340Sstevel@tonic-gate 		normal.dtnd_normal = *((uint8_t *)addr);
12350Sstevel@tonic-gate 		break;
12360Sstevel@tonic-gate 	default:
12370Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_BADNORMAL));
12380Sstevel@tonic-gate 	}
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 	(void) dtrace_aggregate_walk(dtp, dt_normalize_agg, &normal);
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 	return (0);
12430Sstevel@tonic-gate }
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate static int
dt_denormalize_agg(const dtrace_aggdata_t * aggdata,void * arg)1246457Sbmc dt_denormalize_agg(const dtrace_aggdata_t *aggdata, void *arg)
12470Sstevel@tonic-gate {
12480Sstevel@tonic-gate 	dtrace_aggdesc_t *agg = aggdata->dtada_desc;
12490Sstevel@tonic-gate 	dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg);
12500Sstevel@tonic-gate 
12510Sstevel@tonic-gate 	if (agg->dtagd_nrecs == 0)
12520Sstevel@tonic-gate 		return (DTRACE_AGGWALK_NEXT);
12530Sstevel@tonic-gate 
12541017Sbmc 	if (agg->dtagd_varid != id)
12550Sstevel@tonic-gate 		return (DTRACE_AGGWALK_NEXT);
12560Sstevel@tonic-gate 
12570Sstevel@tonic-gate 	return (DTRACE_AGGWALK_DENORMALIZE);
12580Sstevel@tonic-gate }
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate static int
dt_clear_agg(const dtrace_aggdata_t * aggdata,void * arg)1261457Sbmc dt_clear_agg(const dtrace_aggdata_t *aggdata, void *arg)
12620Sstevel@tonic-gate {
12630Sstevel@tonic-gate 	dtrace_aggdesc_t *agg = aggdata->dtada_desc;
12640Sstevel@tonic-gate 	dtrace_aggvarid_t id = *((dtrace_aggvarid_t *)arg);
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate 	if (agg->dtagd_nrecs == 0)
12670Sstevel@tonic-gate 		return (DTRACE_AGGWALK_NEXT);
12680Sstevel@tonic-gate 
12691017Sbmc 	if (agg->dtagd_varid != id)
12700Sstevel@tonic-gate 		return (DTRACE_AGGWALK_NEXT);
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	return (DTRACE_AGGWALK_CLEAR);
12730Sstevel@tonic-gate }
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate typedef struct dt_trunc {
12760Sstevel@tonic-gate 	dtrace_aggvarid_t dttd_id;
12770Sstevel@tonic-gate 	uint64_t dttd_remaining;
12780Sstevel@tonic-gate } dt_trunc_t;
12790Sstevel@tonic-gate 
12800Sstevel@tonic-gate static int
dt_trunc_agg(const dtrace_aggdata_t * aggdata,void * arg)1281457Sbmc dt_trunc_agg(const dtrace_aggdata_t *aggdata, void *arg)
12820Sstevel@tonic-gate {
12830Sstevel@tonic-gate 	dt_trunc_t *trunc = arg;
12840Sstevel@tonic-gate 	dtrace_aggdesc_t *agg = aggdata->dtada_desc;
12850Sstevel@tonic-gate 	dtrace_aggvarid_t id = trunc->dttd_id;
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate 	if (agg->dtagd_nrecs == 0)
12880Sstevel@tonic-gate 		return (DTRACE_AGGWALK_NEXT);
12890Sstevel@tonic-gate 
12901017Sbmc 	if (agg->dtagd_varid != id)
12910Sstevel@tonic-gate 		return (DTRACE_AGGWALK_NEXT);
12920Sstevel@tonic-gate 
12930Sstevel@tonic-gate 	if (trunc->dttd_remaining == 0)
12940Sstevel@tonic-gate 		return (DTRACE_AGGWALK_REMOVE);
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate 	trunc->dttd_remaining--;
12970Sstevel@tonic-gate 	return (DTRACE_AGGWALK_NEXT);
12980Sstevel@tonic-gate }
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate static int
dt_trunc(dtrace_hdl_t * dtp,caddr_t base,dtrace_recdesc_t * rec)13010Sstevel@tonic-gate dt_trunc(dtrace_hdl_t *dtp, caddr_t base, dtrace_recdesc_t *rec)
13020Sstevel@tonic-gate {
13030Sstevel@tonic-gate 	dt_trunc_t trunc;
13040Sstevel@tonic-gate 	caddr_t addr;
13050Sstevel@tonic-gate 	int64_t remaining;
13060Sstevel@tonic-gate 	int (*func)(dtrace_hdl_t *, dtrace_aggregate_f *, void *);
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate 	/*
13090Sstevel@tonic-gate 	 * We (should) have two records:  the aggregation ID followed by the
13100Sstevel@tonic-gate 	 * number of aggregation entries after which the aggregation is to be
13110Sstevel@tonic-gate 	 * truncated.
13120Sstevel@tonic-gate 	 */
13130Sstevel@tonic-gate 	addr = base + rec->dtrd_offset;
13140Sstevel@tonic-gate 
13150Sstevel@tonic-gate 	if (rec->dtrd_size != sizeof (dtrace_aggvarid_t))
13160Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_BADTRUNC));
13170Sstevel@tonic-gate 
13180Sstevel@tonic-gate 	/* LINTED - alignment */
13190Sstevel@tonic-gate 	trunc.dttd_id = *((dtrace_aggvarid_t *)addr);
13200Sstevel@tonic-gate 	rec++;
13210Sstevel@tonic-gate 
13220Sstevel@tonic-gate 	if (rec->dtrd_action != DTRACEACT_LIBACT)
13230Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_BADTRUNC));
13240Sstevel@tonic-gate 
13250Sstevel@tonic-gate 	if (rec->dtrd_arg != DT_ACT_TRUNC)
13260Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_BADTRUNC));
13270Sstevel@tonic-gate 
13280Sstevel@tonic-gate 	addr = base + rec->dtrd_offset;
13290Sstevel@tonic-gate 
13300Sstevel@tonic-gate 	switch (rec->dtrd_size) {
13310Sstevel@tonic-gate 	case sizeof (uint64_t):
13320Sstevel@tonic-gate 		/* LINTED - alignment */
13330Sstevel@tonic-gate 		remaining = *((int64_t *)addr);
13340Sstevel@tonic-gate 		break;
13350Sstevel@tonic-gate 	case sizeof (uint32_t):
13360Sstevel@tonic-gate 		/* LINTED - alignment */
13370Sstevel@tonic-gate 		remaining = *((int32_t *)addr);
13380Sstevel@tonic-gate 		break;
13390Sstevel@tonic-gate 	case sizeof (uint16_t):
13400Sstevel@tonic-gate 		/* LINTED - alignment */
13410Sstevel@tonic-gate 		remaining = *((int16_t *)addr);
13420Sstevel@tonic-gate 		break;
13430Sstevel@tonic-gate 	case sizeof (uint8_t):
13440Sstevel@tonic-gate 		remaining = *((int8_t *)addr);
13450Sstevel@tonic-gate 		break;
13460Sstevel@tonic-gate 	default:
13470Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_BADNORMAL));
13480Sstevel@tonic-gate 	}
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 	if (remaining < 0) {
13510Sstevel@tonic-gate 		func = dtrace_aggregate_walk_valsorted;
13520Sstevel@tonic-gate 		remaining = -remaining;
13530Sstevel@tonic-gate 	} else {
13540Sstevel@tonic-gate 		func = dtrace_aggregate_walk_valrevsorted;
13550Sstevel@tonic-gate 	}
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate 	assert(remaining >= 0);
13580Sstevel@tonic-gate 	trunc.dttd_remaining = remaining;
13590Sstevel@tonic-gate 
13600Sstevel@tonic-gate 	(void) func(dtp, dt_trunc_agg, &trunc);
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate 	return (0);
13630Sstevel@tonic-gate }
13640Sstevel@tonic-gate 
13651017Sbmc static int
dt_print_datum(dtrace_hdl_t * dtp,FILE * fp,dtrace_recdesc_t * rec,caddr_t addr,size_t size,uint64_t normal)13661017Sbmc dt_print_datum(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
13671017Sbmc     caddr_t addr, size_t size, uint64_t normal)
13681017Sbmc {
13691017Sbmc 	int err;
13701017Sbmc 	dtrace_actkind_t act = rec->dtrd_action;
13711017Sbmc 
13721017Sbmc 	switch (act) {
13731017Sbmc 	case DTRACEACT_STACK:
13741017Sbmc 		return (dt_print_stack(dtp, fp, NULL, addr,
13751017Sbmc 		    rec->dtrd_arg, rec->dtrd_size / rec->dtrd_arg));
13761017Sbmc 
13771017Sbmc 	case DTRACEACT_USTACK:
13781017Sbmc 	case DTRACEACT_JSTACK:
13791017Sbmc 		return (dt_print_ustack(dtp, fp, NULL, addr, rec->dtrd_arg));
13801017Sbmc 
13811017Sbmc 	case DTRACEACT_USYM:
13821017Sbmc 	case DTRACEACT_UADDR:
13831017Sbmc 		return (dt_print_usym(dtp, fp, addr, act));
13841017Sbmc 
13851017Sbmc 	case DTRACEACT_UMOD:
13861017Sbmc 		return (dt_print_umod(dtp, fp, NULL, addr));
13871017Sbmc 
13881017Sbmc 	case DTRACEACT_SYM:
13891017Sbmc 		return (dt_print_sym(dtp, fp, NULL, addr));
13901017Sbmc 
13911017Sbmc 	case DTRACEACT_MOD:
13921017Sbmc 		return (dt_print_mod(dtp, fp, NULL, addr));
13931017Sbmc 
13941017Sbmc 	case DTRACEAGG_QUANTIZE:
13951017Sbmc 		return (dt_print_quantize(dtp, fp, addr, size, normal));
13961017Sbmc 
13971017Sbmc 	case DTRACEAGG_LQUANTIZE:
13981017Sbmc 		return (dt_print_lquantize(dtp, fp, addr, size, normal));
13991017Sbmc 
14001017Sbmc 	case DTRACEAGG_AVG:
14011017Sbmc 		return (dt_print_average(dtp, fp, addr, size, normal));
14021017Sbmc 
14035984Sjhaslam 	case DTRACEAGG_STDDEV:
14045984Sjhaslam 		return (dt_print_stddev(dtp, fp, addr, size, normal));
14055984Sjhaslam 
14061017Sbmc 	default:
14071017Sbmc 		break;
14081017Sbmc 	}
14091017Sbmc 
14101017Sbmc 	switch (size) {
14111017Sbmc 	case sizeof (uint64_t):
14121017Sbmc 		err = dt_printf(dtp, fp, " %16lld",
14131017Sbmc 		    /* LINTED - alignment */
14141017Sbmc 		    (long long)*((uint64_t *)addr) / normal);
14151017Sbmc 		break;
14161017Sbmc 	case sizeof (uint32_t):
14171017Sbmc 		/* LINTED - alignment */
14181017Sbmc 		err = dt_printf(dtp, fp, " %8d", *((uint32_t *)addr) /
14191017Sbmc 		    (uint32_t)normal);
14201017Sbmc 		break;
14211017Sbmc 	case sizeof (uint16_t):
14221017Sbmc 		/* LINTED - alignment */
14231017Sbmc 		err = dt_printf(dtp, fp, " %5d", *((uint16_t *)addr) /
14241017Sbmc 		    (uint32_t)normal);
14251017Sbmc 		break;
14261017Sbmc 	case sizeof (uint8_t):
14271017Sbmc 		err = dt_printf(dtp, fp, " %3d", *((uint8_t *)addr) /
14281017Sbmc 		    (uint32_t)normal);
14291017Sbmc 		break;
14301017Sbmc 	default:
14311017Sbmc 		err = dt_print_bytes(dtp, fp, addr, size, 50, 0);
14321017Sbmc 		break;
14331017Sbmc 	}
14341017Sbmc 
14351017Sbmc 	return (err);
14361017Sbmc }
14371017Sbmc 
14381017Sbmc int
dt_print_aggs(const dtrace_aggdata_t ** aggsdata,int naggvars,void * arg)14391017Sbmc dt_print_aggs(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
14401017Sbmc {
14411017Sbmc 	int i, aggact = 0;
14421017Sbmc 	dt_print_aggdata_t *pd = arg;
14431017Sbmc 	const dtrace_aggdata_t *aggdata = aggsdata[0];
14441017Sbmc 	dtrace_aggdesc_t *agg = aggdata->dtada_desc;
14451017Sbmc 	FILE *fp = pd->dtpa_fp;
14461017Sbmc 	dtrace_hdl_t *dtp = pd->dtpa_dtp;
14471017Sbmc 	dtrace_recdesc_t *rec;
14481017Sbmc 	dtrace_actkind_t act;
14491017Sbmc 	caddr_t addr;
14501017Sbmc 	size_t size;
14511017Sbmc 
14521017Sbmc 	/*
14531017Sbmc 	 * Iterate over each record description in the key, printing the traced
14541017Sbmc 	 * data, skipping the first datum (the tuple member created by the
14551017Sbmc 	 * compiler).
14561017Sbmc 	 */
14571017Sbmc 	for (i = 1; i < agg->dtagd_nrecs; i++) {
14581017Sbmc 		rec = &agg->dtagd_rec[i];
14591017Sbmc 		act = rec->dtrd_action;
14601017Sbmc 		addr = aggdata->dtada_data + rec->dtrd_offset;
14611017Sbmc 		size = rec->dtrd_size;
14621017Sbmc 
14631017Sbmc 		if (DTRACEACT_ISAGG(act)) {
14641017Sbmc 			aggact = i;
14651017Sbmc 			break;
14661017Sbmc 		}
14671017Sbmc 
14681017Sbmc 		if (dt_print_datum(dtp, fp, rec, addr, size, 1) < 0)
14691017Sbmc 			return (-1);
14701017Sbmc 
14711017Sbmc 		if (dt_buffered_flush(dtp, NULL, rec, aggdata,
14721017Sbmc 		    DTRACE_BUFDATA_AGGKEY) < 0)
14731017Sbmc 			return (-1);
14741017Sbmc 	}
14751017Sbmc 
14761017Sbmc 	assert(aggact != 0);
14771017Sbmc 
14781017Sbmc 	for (i = (naggvars == 1 ? 0 : 1); i < naggvars; i++) {
14791017Sbmc 		uint64_t normal;
14801017Sbmc 
14811017Sbmc 		aggdata = aggsdata[i];
14821017Sbmc 		agg = aggdata->dtada_desc;
14831017Sbmc 		rec = &agg->dtagd_rec[aggact];
14841017Sbmc 		act = rec->dtrd_action;
14851017Sbmc 		addr = aggdata->dtada_data + rec->dtrd_offset;
14861017Sbmc 		size = rec->dtrd_size;
14871017Sbmc 
14881017Sbmc 		assert(DTRACEACT_ISAGG(act));
14891017Sbmc 		normal = aggdata->dtada_normal;
14901017Sbmc 
14911017Sbmc 		if (dt_print_datum(dtp, fp, rec, addr, size, normal) < 0)
14921017Sbmc 			return (-1);
14931017Sbmc 
14941017Sbmc 		if (dt_buffered_flush(dtp, NULL, rec, aggdata,
14951017Sbmc 		    DTRACE_BUFDATA_AGGVAL) < 0)
14961017Sbmc 			return (-1);
14971017Sbmc 
14981017Sbmc 		if (!pd->dtpa_allunprint)
14991017Sbmc 			agg->dtagd_flags |= DTRACE_AGD_PRINTED;
15001017Sbmc 	}
15011017Sbmc 
15021017Sbmc 	if (dt_printf(dtp, fp, "\n") < 0)
15031017Sbmc 		return (-1);
15041017Sbmc 
15051017Sbmc 	if (dt_buffered_flush(dtp, NULL, NULL, aggdata,
15061017Sbmc 	    DTRACE_BUFDATA_AGGFORMAT | DTRACE_BUFDATA_AGGLAST) < 0)
15071017Sbmc 		return (-1);
15081017Sbmc 
15091017Sbmc 	return (0);
15101017Sbmc }
15111017Sbmc 
15120Sstevel@tonic-gate int
dt_print_agg(const dtrace_aggdata_t * aggdata,void * arg)1513457Sbmc dt_print_agg(const dtrace_aggdata_t *aggdata, void *arg)
15140Sstevel@tonic-gate {
15150Sstevel@tonic-gate 	dt_print_aggdata_t *pd = arg;
15160Sstevel@tonic-gate 	dtrace_aggdesc_t *agg = aggdata->dtada_desc;
15170Sstevel@tonic-gate 	dtrace_aggvarid_t aggvarid = pd->dtpa_id;
15180Sstevel@tonic-gate 
15190Sstevel@tonic-gate 	if (pd->dtpa_allunprint) {
15200Sstevel@tonic-gate 		if (agg->dtagd_flags & DTRACE_AGD_PRINTED)
15210Sstevel@tonic-gate 			return (0);
15220Sstevel@tonic-gate 	} else {
15230Sstevel@tonic-gate 		/*
15240Sstevel@tonic-gate 		 * If we're not printing all unprinted aggregations, then the
15250Sstevel@tonic-gate 		 * aggregation variable ID denotes a specific aggregation
15260Sstevel@tonic-gate 		 * variable that we should print -- skip any other aggregations
15270Sstevel@tonic-gate 		 * that we encounter.
15280Sstevel@tonic-gate 		 */
15290Sstevel@tonic-gate 		if (agg->dtagd_nrecs == 0)
15300Sstevel@tonic-gate 			return (0);
15310Sstevel@tonic-gate 
15321017Sbmc 		if (aggvarid != agg->dtagd_varid)
15330Sstevel@tonic-gate 			return (0);
15340Sstevel@tonic-gate 	}
15350Sstevel@tonic-gate 
15361017Sbmc 	return (dt_print_aggs(&aggdata, 1, arg));
15370Sstevel@tonic-gate }
15380Sstevel@tonic-gate 
1539457Sbmc int
dt_setopt(dtrace_hdl_t * dtp,const dtrace_probedata_t * data,const char * option,const char * value)1540457Sbmc dt_setopt(dtrace_hdl_t *dtp, const dtrace_probedata_t *data,
1541457Sbmc     const char *option, const char *value)
1542457Sbmc {
1543457Sbmc 	int len, rval;
1544457Sbmc 	char *msg;
1545457Sbmc 	const char *errstr;
1546457Sbmc 	dtrace_setoptdata_t optdata;
1547457Sbmc 
1548457Sbmc 	bzero(&optdata, sizeof (optdata));
1549457Sbmc 	(void) dtrace_getopt(dtp, option, &optdata.dtsda_oldval);
1550457Sbmc 
1551457Sbmc 	if (dtrace_setopt(dtp, option, value) == 0) {
1552457Sbmc 		(void) dtrace_getopt(dtp, option, &optdata.dtsda_newval);
1553457Sbmc 		optdata.dtsda_probe = data;
1554457Sbmc 		optdata.dtsda_option = option;
1555457Sbmc 		optdata.dtsda_handle = dtp;
1556457Sbmc 
1557457Sbmc 		if ((rval = dt_handle_setopt(dtp, &optdata)) != 0)
1558457Sbmc 			return (rval);
1559457Sbmc 
1560457Sbmc 		return (0);
1561457Sbmc 	}
1562457Sbmc 
1563457Sbmc 	errstr = dtrace_errmsg(dtp, dtrace_errno(dtp));
1564457Sbmc 	len = strlen(option) + strlen(value) + strlen(errstr) + 80;
1565457Sbmc 	msg = alloca(len);
1566457Sbmc 
1567457Sbmc 	(void) snprintf(msg, len, "couldn't set option \"%s\" to \"%s\": %s\n",
1568457Sbmc 	    option, value, errstr);
1569457Sbmc 
1570457Sbmc 	if ((rval = dt_handle_liberr(dtp, data, msg)) == 0)
1571457Sbmc 		return (0);
1572457Sbmc 
1573457Sbmc 	return (rval);
1574457Sbmc }
1575457Sbmc 
15760Sstevel@tonic-gate static int
dt_consume_cpu(dtrace_hdl_t * dtp,FILE * fp,int cpu,dtrace_bufdesc_t * buf,dtrace_consume_probe_f * efunc,dtrace_consume_rec_f * rfunc,void * arg)15770Sstevel@tonic-gate dt_consume_cpu(dtrace_hdl_t *dtp, FILE *fp, int cpu, dtrace_bufdesc_t *buf,
15780Sstevel@tonic-gate     dtrace_consume_probe_f *efunc, dtrace_consume_rec_f *rfunc, void *arg)
15790Sstevel@tonic-gate {
15800Sstevel@tonic-gate 	dtrace_epid_t id;
15810Sstevel@tonic-gate 	size_t offs, start = buf->dtbd_oldest, end = buf->dtbd_size;
15820Sstevel@tonic-gate 	int flow = (dtp->dt_options[DTRACEOPT_FLOWINDENT] != DTRACEOPT_UNSET);
15830Sstevel@tonic-gate 	int quiet = (dtp->dt_options[DTRACEOPT_QUIET] != DTRACEOPT_UNSET);
15840Sstevel@tonic-gate 	int rval, i, n;
15850Sstevel@tonic-gate 	dtrace_epid_t last = DTRACE_EPIDNONE;
15860Sstevel@tonic-gate 	dtrace_probedata_t data;
15870Sstevel@tonic-gate 	uint64_t drops;
15880Sstevel@tonic-gate 	caddr_t addr;
15890Sstevel@tonic-gate 
15900Sstevel@tonic-gate 	bzero(&data, sizeof (data));
15910Sstevel@tonic-gate 	data.dtpda_handle = dtp;
15920Sstevel@tonic-gate 	data.dtpda_cpu = cpu;
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate again:
15950Sstevel@tonic-gate 	for (offs = start; offs < end; ) {
15960Sstevel@tonic-gate 		dtrace_eprobedesc_t *epd;
15970Sstevel@tonic-gate 
15980Sstevel@tonic-gate 		/*
15990Sstevel@tonic-gate 		 * We're guaranteed to have an ID.
16000Sstevel@tonic-gate 		 */
16010Sstevel@tonic-gate 		id = *(uint32_t *)((uintptr_t)buf->dtbd_data + offs);
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 		if (id == DTRACE_EPIDNONE) {
16040Sstevel@tonic-gate 			/*
16050Sstevel@tonic-gate 			 * This is filler to assure proper alignment of the
16060Sstevel@tonic-gate 			 * next record; we simply ignore it.
16070Sstevel@tonic-gate 			 */
16080Sstevel@tonic-gate 			offs += sizeof (id);
16090Sstevel@tonic-gate 			continue;
16100Sstevel@tonic-gate 		}
16110Sstevel@tonic-gate 
16120Sstevel@tonic-gate 		if ((rval = dt_epid_lookup(dtp, id, &data.dtpda_edesc,
16130Sstevel@tonic-gate 		    &data.dtpda_pdesc)) != 0)
16140Sstevel@tonic-gate 			return (rval);
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 		epd = data.dtpda_edesc;
16170Sstevel@tonic-gate 		data.dtpda_data = buf->dtbd_data + offs;
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 		if (data.dtpda_edesc->dtepd_uarg != DT_ECB_DEFAULT) {
16200Sstevel@tonic-gate 			rval = dt_handle(dtp, &data);
16210Sstevel@tonic-gate 
16220Sstevel@tonic-gate 			if (rval == DTRACE_CONSUME_NEXT)
16230Sstevel@tonic-gate 				goto nextepid;
16240Sstevel@tonic-gate 
16250Sstevel@tonic-gate 			if (rval == DTRACE_CONSUME_ERROR)
16260Sstevel@tonic-gate 				return (-1);
16270Sstevel@tonic-gate 		}
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 		if (flow)
16300Sstevel@tonic-gate 			(void) dt_flowindent(dtp, &data, last, buf, offs);
16310Sstevel@tonic-gate 
16320Sstevel@tonic-gate 		rval = (*efunc)(&data, arg);
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 		if (flow) {
16350Sstevel@tonic-gate 			if (data.dtpda_flow == DTRACEFLOW_ENTRY)
16360Sstevel@tonic-gate 				data.dtpda_indent += 2;
16370Sstevel@tonic-gate 		}
16380Sstevel@tonic-gate 
16390Sstevel@tonic-gate 		if (rval == DTRACE_CONSUME_NEXT)
16400Sstevel@tonic-gate 			goto nextepid;
16410Sstevel@tonic-gate 
16420Sstevel@tonic-gate 		if (rval == DTRACE_CONSUME_ABORT)
16430Sstevel@tonic-gate 			return (dt_set_errno(dtp, EDT_DIRABORT));
16440Sstevel@tonic-gate 
16450Sstevel@tonic-gate 		if (rval != DTRACE_CONSUME_THIS)
16460Sstevel@tonic-gate 			return (dt_set_errno(dtp, EDT_BADRVAL));
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 		for (i = 0; i < epd->dtepd_nrecs; i++) {
16490Sstevel@tonic-gate 			dtrace_recdesc_t *rec = &epd->dtepd_rec[i];
16500Sstevel@tonic-gate 			dtrace_actkind_t act = rec->dtrd_action;
16510Sstevel@tonic-gate 
16520Sstevel@tonic-gate 			data.dtpda_data = buf->dtbd_data + offs +
16530Sstevel@tonic-gate 			    rec->dtrd_offset;
16540Sstevel@tonic-gate 			addr = data.dtpda_data;
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate 			if (act == DTRACEACT_LIBACT) {
1657457Sbmc 				uint64_t arg = rec->dtrd_arg;
1658457Sbmc 				dtrace_aggvarid_t id;
16590Sstevel@tonic-gate 
1660457Sbmc 				switch (arg) {
1661457Sbmc 				case DT_ACT_CLEAR:
16620Sstevel@tonic-gate 					/* LINTED - alignment */
16630Sstevel@tonic-gate 					id = *((dtrace_aggvarid_t *)addr);
16640Sstevel@tonic-gate 					(void) dtrace_aggregate_walk(dtp,
16650Sstevel@tonic-gate 					    dt_clear_agg, &id);
16660Sstevel@tonic-gate 					continue;
16670Sstevel@tonic-gate 
1668457Sbmc 				case DT_ACT_DENORMALIZE:
16690Sstevel@tonic-gate 					/* LINTED - alignment */
16700Sstevel@tonic-gate 					id = *((dtrace_aggvarid_t *)addr);
16710Sstevel@tonic-gate 					(void) dtrace_aggregate_walk(dtp,
16720Sstevel@tonic-gate 					    dt_denormalize_agg, &id);
16730Sstevel@tonic-gate 					continue;
1674457Sbmc 
1675457Sbmc 				case DT_ACT_FTRUNCATE:
1676457Sbmc 					if (fp == NULL)
1677457Sbmc 						continue;
16780Sstevel@tonic-gate 
1679457Sbmc 					(void) fflush(fp);
1680457Sbmc 					(void) ftruncate(fileno(fp), 0);
1681457Sbmc 					(void) fseeko(fp, 0, SEEK_SET);
1682457Sbmc 					continue;
1683457Sbmc 
1684457Sbmc 				case DT_ACT_NORMALIZE:
16850Sstevel@tonic-gate 					if (i == epd->dtepd_nrecs - 1)
16860Sstevel@tonic-gate 						return (dt_set_errno(dtp,
16870Sstevel@tonic-gate 						    EDT_BADNORMAL));
16880Sstevel@tonic-gate 
16890Sstevel@tonic-gate 					if (dt_normalize(dtp,
16900Sstevel@tonic-gate 					    buf->dtbd_data + offs, rec) != 0)
16910Sstevel@tonic-gate 						return (-1);
16920Sstevel@tonic-gate 
16930Sstevel@tonic-gate 					i++;
16940Sstevel@tonic-gate 					continue;
1695457Sbmc 
1696457Sbmc 				case DT_ACT_SETOPT: {
1697457Sbmc 					uint64_t *opts = dtp->dt_options;
1698457Sbmc 					dtrace_recdesc_t *valrec;
1699457Sbmc 					uint32_t valsize;
1700457Sbmc 					caddr_t val;
1701457Sbmc 					int rv;
1702457Sbmc 
1703457Sbmc 					if (i == epd->dtepd_nrecs - 1) {
1704457Sbmc 						return (dt_set_errno(dtp,
1705457Sbmc 						    EDT_BADSETOPT));
1706457Sbmc 					}
1707457Sbmc 
1708457Sbmc 					valrec = &epd->dtepd_rec[++i];
1709457Sbmc 					valsize = valrec->dtrd_size;
1710457Sbmc 
1711457Sbmc 					if (valrec->dtrd_action != act ||
1712457Sbmc 					    valrec->dtrd_arg != arg) {
1713457Sbmc 						return (dt_set_errno(dtp,
1714457Sbmc 						    EDT_BADSETOPT));
1715457Sbmc 					}
1716457Sbmc 
1717457Sbmc 					if (valsize > sizeof (uint64_t)) {
1718457Sbmc 						val = buf->dtbd_data + offs +
1719457Sbmc 						    valrec->dtrd_offset;
1720457Sbmc 					} else {
1721457Sbmc 						val = "1";
1722457Sbmc 					}
1723457Sbmc 
1724457Sbmc 					rv = dt_setopt(dtp, &data, addr, val);
1725457Sbmc 
1726457Sbmc 					if (rv != 0)
1727457Sbmc 						return (-1);
1728457Sbmc 
1729457Sbmc 					flow = (opts[DTRACEOPT_FLOWINDENT] !=
1730457Sbmc 					    DTRACEOPT_UNSET);
1731457Sbmc 					quiet = (opts[DTRACEOPT_QUIET] !=
1732457Sbmc 					    DTRACEOPT_UNSET);
1733457Sbmc 
1734457Sbmc 					continue;
17350Sstevel@tonic-gate 				}
17360Sstevel@tonic-gate 
1737457Sbmc 				case DT_ACT_TRUNC:
17380Sstevel@tonic-gate 					if (i == epd->dtepd_nrecs - 1)
17390Sstevel@tonic-gate 						return (dt_set_errno(dtp,
17400Sstevel@tonic-gate 						    EDT_BADTRUNC));
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 					if (dt_trunc(dtp,
17430Sstevel@tonic-gate 					    buf->dtbd_data + offs, rec) != 0)
17440Sstevel@tonic-gate 						return (-1);
17450Sstevel@tonic-gate 
17460Sstevel@tonic-gate 					i++;
17470Sstevel@tonic-gate 					continue;
17480Sstevel@tonic-gate 
1749457Sbmc 				default:
17500Sstevel@tonic-gate 					continue;
17510Sstevel@tonic-gate 				}
17520Sstevel@tonic-gate 			}
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 			rval = (*rfunc)(&data, rec, arg);
17550Sstevel@tonic-gate 
17560Sstevel@tonic-gate 			if (rval == DTRACE_CONSUME_NEXT)
17570Sstevel@tonic-gate 				continue;
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 			if (rval == DTRACE_CONSUME_ABORT)
17600Sstevel@tonic-gate 				return (dt_set_errno(dtp, EDT_DIRABORT));
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 			if (rval != DTRACE_CONSUME_THIS)
17630Sstevel@tonic-gate 				return (dt_set_errno(dtp, EDT_BADRVAL));
17640Sstevel@tonic-gate 
17650Sstevel@tonic-gate 			if (act == DTRACEACT_STACK) {
1766457Sbmc 				int depth = rec->dtrd_arg;
1767457Sbmc 
1768457Sbmc 				if (dt_print_stack(dtp, fp, NULL, addr, depth,
1769457Sbmc 				    rec->dtrd_size / depth) < 0)
17700Sstevel@tonic-gate 					return (-1);
17710Sstevel@tonic-gate 				goto nextrec;
17720Sstevel@tonic-gate 			}
17730Sstevel@tonic-gate 
17740Sstevel@tonic-gate 			if (act == DTRACEACT_USTACK ||
17750Sstevel@tonic-gate 			    act == DTRACEACT_JSTACK) {
17760Sstevel@tonic-gate 				if (dt_print_ustack(dtp, fp, NULL,
17770Sstevel@tonic-gate 				    addr, rec->dtrd_arg) < 0)
17780Sstevel@tonic-gate 					return (-1);
17790Sstevel@tonic-gate 				goto nextrec;
17800Sstevel@tonic-gate 			}
17810Sstevel@tonic-gate 
1782457Sbmc 			if (act == DTRACEACT_SYM) {
1783457Sbmc 				if (dt_print_sym(dtp, fp, NULL, addr) < 0)
1784457Sbmc 					return (-1);
1785457Sbmc 				goto nextrec;
1786457Sbmc 			}
1787457Sbmc 
1788457Sbmc 			if (act == DTRACEACT_MOD) {
1789457Sbmc 				if (dt_print_mod(dtp, fp, NULL, addr) < 0)
1790457Sbmc 					return (-1);
1791457Sbmc 				goto nextrec;
1792457Sbmc 			}
1793457Sbmc 
1794457Sbmc 			if (act == DTRACEACT_USYM || act == DTRACEACT_UADDR) {
1795457Sbmc 				if (dt_print_usym(dtp, fp, addr, act) < 0)
1796457Sbmc 					return (-1);
1797457Sbmc 				goto nextrec;
1798457Sbmc 			}
1799457Sbmc 
1800457Sbmc 			if (act == DTRACEACT_UMOD) {
1801457Sbmc 				if (dt_print_umod(dtp, fp, NULL, addr) < 0)
1802457Sbmc 					return (-1);
1803457Sbmc 				goto nextrec;
1804457Sbmc 			}
1805457Sbmc 
18060Sstevel@tonic-gate 			if (DTRACEACT_ISPRINTFLIKE(act)) {
18070Sstevel@tonic-gate 				void *fmtdata;
18080Sstevel@tonic-gate 				int (*func)(dtrace_hdl_t *, FILE *, void *,
18090Sstevel@tonic-gate 				    const dtrace_probedata_t *,
18100Sstevel@tonic-gate 				    const dtrace_recdesc_t *, uint_t,
18110Sstevel@tonic-gate 				    const void *buf, size_t);
18120Sstevel@tonic-gate 
18130Sstevel@tonic-gate 				if ((fmtdata = dt_format_lookup(dtp,
18140Sstevel@tonic-gate 				    rec->dtrd_format)) == NULL)
18150Sstevel@tonic-gate 					goto nofmt;
18160Sstevel@tonic-gate 
18170Sstevel@tonic-gate 				switch (act) {
18180Sstevel@tonic-gate 				case DTRACEACT_PRINTF:
18190Sstevel@tonic-gate 					func = dtrace_fprintf;
18200Sstevel@tonic-gate 					break;
18210Sstevel@tonic-gate 				case DTRACEACT_PRINTA:
18220Sstevel@tonic-gate 					func = dtrace_fprinta;
18230Sstevel@tonic-gate 					break;
18240Sstevel@tonic-gate 				case DTRACEACT_SYSTEM:
18250Sstevel@tonic-gate 					func = dtrace_system;
18260Sstevel@tonic-gate 					break;
18270Sstevel@tonic-gate 				case DTRACEACT_FREOPEN:
18280Sstevel@tonic-gate 					func = dtrace_freopen;
18290Sstevel@tonic-gate 					break;
18300Sstevel@tonic-gate 				}
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate 				n = (*func)(dtp, fp, fmtdata, &data,
18330Sstevel@tonic-gate 				    rec, epd->dtepd_nrecs - i,
18340Sstevel@tonic-gate 				    (uchar_t *)buf->dtbd_data + offs,
18350Sstevel@tonic-gate 				    buf->dtbd_size - offs);
18360Sstevel@tonic-gate 
18370Sstevel@tonic-gate 				if (n < 0)
18380Sstevel@tonic-gate 					return (-1); /* errno is set for us */
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 				if (n > 0)
18410Sstevel@tonic-gate 					i += n - 1;
18420Sstevel@tonic-gate 				goto nextrec;
18430Sstevel@tonic-gate 			}
18440Sstevel@tonic-gate 
18450Sstevel@tonic-gate nofmt:
18460Sstevel@tonic-gate 			if (act == DTRACEACT_PRINTA) {
18470Sstevel@tonic-gate 				dt_print_aggdata_t pd;
18481017Sbmc 				dtrace_aggvarid_t *aggvars;
18491017Sbmc 				int j, naggvars = 0;
18501017Sbmc 				size_t size = ((epd->dtepd_nrecs - i) *
18511017Sbmc 				    sizeof (dtrace_aggvarid_t));
18520Sstevel@tonic-gate 
18531017Sbmc 				if ((aggvars = dt_alloc(dtp, size)) == NULL)
18541017Sbmc 					return (-1);
18551017Sbmc 
18561017Sbmc 				/*
18571017Sbmc 				 * This might be a printa() with multiple
18581017Sbmc 				 * aggregation variables.  We need to scan
18591017Sbmc 				 * forward through the records until we find
18601017Sbmc 				 * a record from a different statement.
18611017Sbmc 				 */
18621017Sbmc 				for (j = i; j < epd->dtepd_nrecs; j++) {
18631017Sbmc 					dtrace_recdesc_t *nrec;
18641017Sbmc 					caddr_t naddr;
18651017Sbmc 
18661017Sbmc 					nrec = &epd->dtepd_rec[j];
18671017Sbmc 
18681017Sbmc 					if (nrec->dtrd_uarg != rec->dtrd_uarg)
18691017Sbmc 						break;
18701017Sbmc 
18711017Sbmc 					if (nrec->dtrd_action != act) {
18721017Sbmc 						return (dt_set_errno(dtp,
18731017Sbmc 						    EDT_BADAGG));
18741017Sbmc 					}
18751017Sbmc 
18761017Sbmc 					naddr = buf->dtbd_data + offs +
18771017Sbmc 					    nrec->dtrd_offset;
18781017Sbmc 
18791017Sbmc 					aggvars[naggvars++] =
18801017Sbmc 					    /* LINTED - alignment */
18811017Sbmc 					    *((dtrace_aggvarid_t *)naddr);
18821017Sbmc 				}
18831017Sbmc 
18841017Sbmc 				i = j - 1;
18850Sstevel@tonic-gate 				bzero(&pd, sizeof (pd));
18860Sstevel@tonic-gate 				pd.dtpa_dtp = dtp;
18870Sstevel@tonic-gate 				pd.dtpa_fp = fp;
18881017Sbmc 
18891017Sbmc 				assert(naggvars >= 1);
18901017Sbmc 
18911017Sbmc 				if (naggvars == 1) {
18921017Sbmc 					pd.dtpa_id = aggvars[0];
18931017Sbmc 					dt_free(dtp, aggvars);
18941017Sbmc 
18951017Sbmc 					if (dt_printf(dtp, fp, "\n") < 0 ||
18961017Sbmc 					    dtrace_aggregate_walk_sorted(dtp,
18971017Sbmc 					    dt_print_agg, &pd) < 0)
18981017Sbmc 						return (-1);
18991017Sbmc 					goto nextrec;
19001017Sbmc 				}
19010Sstevel@tonic-gate 
19020Sstevel@tonic-gate 				if (dt_printf(dtp, fp, "\n") < 0 ||
19031017Sbmc 				    dtrace_aggregate_walk_joined(dtp, aggvars,
19041017Sbmc 				    naggvars, dt_print_aggs, &pd) < 0) {
19051017Sbmc 					dt_free(dtp, aggvars);
19060Sstevel@tonic-gate 					return (-1);
19071017Sbmc 				}
19080Sstevel@tonic-gate 
19091017Sbmc 				dt_free(dtp, aggvars);
19100Sstevel@tonic-gate 				goto nextrec;
19110Sstevel@tonic-gate 			}
19120Sstevel@tonic-gate 
19130Sstevel@tonic-gate 			switch (rec->dtrd_size) {
19140Sstevel@tonic-gate 			case sizeof (uint64_t):
19150Sstevel@tonic-gate 				n = dt_printf(dtp, fp,
19160Sstevel@tonic-gate 				    quiet ? "%lld" : " %16lld",
19170Sstevel@tonic-gate 				    /* LINTED - alignment */
19180Sstevel@tonic-gate 				    *((unsigned long long *)addr));
19190Sstevel@tonic-gate 				break;
19200Sstevel@tonic-gate 			case sizeof (uint32_t):
19210Sstevel@tonic-gate 				n = dt_printf(dtp, fp, quiet ? "%d" : " %8d",
19220Sstevel@tonic-gate 				    /* LINTED - alignment */
19230Sstevel@tonic-gate 				    *((uint32_t *)addr));
19240Sstevel@tonic-gate 				break;
19250Sstevel@tonic-gate 			case sizeof (uint16_t):
19260Sstevel@tonic-gate 				n = dt_printf(dtp, fp, quiet ? "%d" : " %5d",
19270Sstevel@tonic-gate 				    /* LINTED - alignment */
19280Sstevel@tonic-gate 				    *((uint16_t *)addr));
19290Sstevel@tonic-gate 				break;
19300Sstevel@tonic-gate 			case sizeof (uint8_t):
19310Sstevel@tonic-gate 				n = dt_printf(dtp, fp, quiet ? "%d" : " %3d",
19320Sstevel@tonic-gate 				    *((uint8_t *)addr));
19330Sstevel@tonic-gate 				break;
19340Sstevel@tonic-gate 			default:
19350Sstevel@tonic-gate 				n = dt_print_bytes(dtp, fp, addr,
19360Sstevel@tonic-gate 				    rec->dtrd_size, 33, quiet);
19370Sstevel@tonic-gate 				break;
19380Sstevel@tonic-gate 			}
19390Sstevel@tonic-gate 
19400Sstevel@tonic-gate 			if (n < 0)
19410Sstevel@tonic-gate 				return (-1); /* errno is set for us */
19420Sstevel@tonic-gate 
19430Sstevel@tonic-gate nextrec:
19441017Sbmc 			if (dt_buffered_flush(dtp, &data, rec, NULL, 0) < 0)
19450Sstevel@tonic-gate 				return (-1); /* errno is set for us */
19460Sstevel@tonic-gate 		}
19470Sstevel@tonic-gate 
19480Sstevel@tonic-gate 		/*
19490Sstevel@tonic-gate 		 * Call the record callback with a NULL record to indicate
19500Sstevel@tonic-gate 		 * that we're done processing this EPID.
19510Sstevel@tonic-gate 		 */
19520Sstevel@tonic-gate 		rval = (*rfunc)(&data, NULL, arg);
19530Sstevel@tonic-gate nextepid:
19540Sstevel@tonic-gate 		offs += epd->dtepd_size;
19550Sstevel@tonic-gate 		last = id;
19560Sstevel@tonic-gate 	}
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	if (buf->dtbd_oldest != 0 && start == buf->dtbd_oldest) {
19590Sstevel@tonic-gate 		end = buf->dtbd_oldest;
19600Sstevel@tonic-gate 		start = 0;
19610Sstevel@tonic-gate 		goto again;
19620Sstevel@tonic-gate 	}
19630Sstevel@tonic-gate 
19640Sstevel@tonic-gate 	if ((drops = buf->dtbd_drops) == 0)
19650Sstevel@tonic-gate 		return (0);
19660Sstevel@tonic-gate 
19670Sstevel@tonic-gate 	/*
19680Sstevel@tonic-gate 	 * Explicitly zero the drops to prevent us from processing them again.
19690Sstevel@tonic-gate 	 */
19700Sstevel@tonic-gate 	buf->dtbd_drops = 0;
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	return (dt_handle_cpudrop(dtp, cpu, DTRACEDROP_PRINCIPAL, drops));
19730Sstevel@tonic-gate }
19740Sstevel@tonic-gate 
19750Sstevel@tonic-gate typedef struct dt_begin {
19760Sstevel@tonic-gate 	dtrace_consume_probe_f *dtbgn_probefunc;
19770Sstevel@tonic-gate 	dtrace_consume_rec_f *dtbgn_recfunc;
19780Sstevel@tonic-gate 	void *dtbgn_arg;
19790Sstevel@tonic-gate 	dtrace_handle_err_f *dtbgn_errhdlr;
19800Sstevel@tonic-gate 	void *dtbgn_errarg;
19810Sstevel@tonic-gate 	int dtbgn_beginonly;
19820Sstevel@tonic-gate } dt_begin_t;
19830Sstevel@tonic-gate 
19840Sstevel@tonic-gate static int
dt_consume_begin_probe(const dtrace_probedata_t * data,void * arg)19850Sstevel@tonic-gate dt_consume_begin_probe(const dtrace_probedata_t *data, void *arg)
19860Sstevel@tonic-gate {
19870Sstevel@tonic-gate 	dt_begin_t *begin = (dt_begin_t *)arg;
19880Sstevel@tonic-gate 	dtrace_probedesc_t *pd = data->dtpda_pdesc;
19890Sstevel@tonic-gate 
19900Sstevel@tonic-gate 	int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0);
19910Sstevel@tonic-gate 	int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0);
19920Sstevel@tonic-gate 
19930Sstevel@tonic-gate 	if (begin->dtbgn_beginonly) {
19940Sstevel@tonic-gate 		if (!(r1 && r2))
19950Sstevel@tonic-gate 			return (DTRACE_CONSUME_NEXT);
19960Sstevel@tonic-gate 	} else {
19970Sstevel@tonic-gate 		if (r1 && r2)
19980Sstevel@tonic-gate 			return (DTRACE_CONSUME_NEXT);
19990Sstevel@tonic-gate 	}
20000Sstevel@tonic-gate 
20010Sstevel@tonic-gate 	/*
20020Sstevel@tonic-gate 	 * We have a record that we're interested in.  Now call the underlying
20030Sstevel@tonic-gate 	 * probe function...
20040Sstevel@tonic-gate 	 */
20050Sstevel@tonic-gate 	return (begin->dtbgn_probefunc(data, begin->dtbgn_arg));
20060Sstevel@tonic-gate }
20070Sstevel@tonic-gate 
20080Sstevel@tonic-gate static int
dt_consume_begin_record(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,void * arg)20090Sstevel@tonic-gate dt_consume_begin_record(const dtrace_probedata_t *data,
20100Sstevel@tonic-gate     const dtrace_recdesc_t *rec, void *arg)
20110Sstevel@tonic-gate {
20120Sstevel@tonic-gate 	dt_begin_t *begin = (dt_begin_t *)arg;
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate 	return (begin->dtbgn_recfunc(data, rec, begin->dtbgn_arg));
20150Sstevel@tonic-gate }
20160Sstevel@tonic-gate 
20170Sstevel@tonic-gate static int
dt_consume_begin_error(const dtrace_errdata_t * data,void * arg)2018457Sbmc dt_consume_begin_error(const dtrace_errdata_t *data, void *arg)
20190Sstevel@tonic-gate {
20200Sstevel@tonic-gate 	dt_begin_t *begin = (dt_begin_t *)arg;
20210Sstevel@tonic-gate 	dtrace_probedesc_t *pd = data->dteda_pdesc;
20220Sstevel@tonic-gate 
20230Sstevel@tonic-gate 	int r1 = (strcmp(pd->dtpd_provider, "dtrace") == 0);
20240Sstevel@tonic-gate 	int r2 = (strcmp(pd->dtpd_name, "BEGIN") == 0);
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate 	if (begin->dtbgn_beginonly) {
20270Sstevel@tonic-gate 		if (!(r1 && r2))
20280Sstevel@tonic-gate 			return (DTRACE_HANDLE_OK);
20290Sstevel@tonic-gate 	} else {
20300Sstevel@tonic-gate 		if (r1 && r2)
20310Sstevel@tonic-gate 			return (DTRACE_HANDLE_OK);
20320Sstevel@tonic-gate 	}
20330Sstevel@tonic-gate 
20340Sstevel@tonic-gate 	return (begin->dtbgn_errhdlr(data, begin->dtbgn_errarg));
20350Sstevel@tonic-gate }
20360Sstevel@tonic-gate 
20370Sstevel@tonic-gate static int
dt_consume_begin(dtrace_hdl_t * dtp,FILE * fp,dtrace_bufdesc_t * buf,dtrace_consume_probe_f * pf,dtrace_consume_rec_f * rf,void * arg)20380Sstevel@tonic-gate dt_consume_begin(dtrace_hdl_t *dtp, FILE *fp, dtrace_bufdesc_t *buf,
20390Sstevel@tonic-gate     dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg)
20400Sstevel@tonic-gate {
20410Sstevel@tonic-gate 	/*
20420Sstevel@tonic-gate 	 * There's this idea that the BEGIN probe should be processed before
20430Sstevel@tonic-gate 	 * everything else, and that the END probe should be processed after
20440Sstevel@tonic-gate 	 * anything else.  In the common case, this is pretty easy to deal
20450Sstevel@tonic-gate 	 * with.  However, a situation may arise where the BEGIN enabling and
20460Sstevel@tonic-gate 	 * END enabling are on the same CPU, and some enabling in the middle
20470Sstevel@tonic-gate 	 * occurred on a different CPU.  To deal with this (blech!) we need to
20480Sstevel@tonic-gate 	 * consume the BEGIN buffer up until the end of the BEGIN probe, and
20490Sstevel@tonic-gate 	 * then set it aside.  We will then process every other CPU, and then
20500Sstevel@tonic-gate 	 * we'll return to the BEGIN CPU and process the rest of the data
20510Sstevel@tonic-gate 	 * (which will inevitably include the END probe, if any).  Making this
20520Sstevel@tonic-gate 	 * even more complicated (!) is the library's ERROR enabling.  Because
20530Sstevel@tonic-gate 	 * this enabling is processed before we even get into the consume call
20540Sstevel@tonic-gate 	 * back, any ERROR firing would result in the library's ERROR enabling
20550Sstevel@tonic-gate 	 * being processed twice -- once in our first pass (for BEGIN probes),
20560Sstevel@tonic-gate 	 * and again in our second pass (for everything but BEGIN probes).  To
20570Sstevel@tonic-gate 	 * deal with this, we interpose on the ERROR handler to assure that we
20580Sstevel@tonic-gate 	 * only process ERROR enablings induced by BEGIN enablings in the
20590Sstevel@tonic-gate 	 * first pass, and that we only process ERROR enablings _not_ induced
20600Sstevel@tonic-gate 	 * by BEGIN enablings in the second pass.
20610Sstevel@tonic-gate 	 */
20620Sstevel@tonic-gate 	dt_begin_t begin;
20630Sstevel@tonic-gate 	processorid_t cpu = dtp->dt_beganon;
20640Sstevel@tonic-gate 	dtrace_bufdesc_t nbuf;
20650Sstevel@tonic-gate 	int rval, i;
20660Sstevel@tonic-gate 	static int max_ncpus;
20670Sstevel@tonic-gate 	dtrace_optval_t size;
20680Sstevel@tonic-gate 
20690Sstevel@tonic-gate 	dtp->dt_beganon = -1;
20700Sstevel@tonic-gate 
20710Sstevel@tonic-gate 	if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) {
20720Sstevel@tonic-gate 		/*
20730Sstevel@tonic-gate 		 * We really don't expect this to fail, but it is at least
20740Sstevel@tonic-gate 		 * technically possible for this to fail with ENOENT.  In this
20750Sstevel@tonic-gate 		 * case, we just drive on...
20760Sstevel@tonic-gate 		 */
20770Sstevel@tonic-gate 		if (errno == ENOENT)
20780Sstevel@tonic-gate 			return (0);
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 		return (dt_set_errno(dtp, errno));
20810Sstevel@tonic-gate 	}
20820Sstevel@tonic-gate 
20830Sstevel@tonic-gate 	if (!dtp->dt_stopped || buf->dtbd_cpu != dtp->dt_endedon) {
20840Sstevel@tonic-gate 		/*
20850Sstevel@tonic-gate 		 * This is the simple case.  We're either not stopped, or if
20860Sstevel@tonic-gate 		 * we are, we actually processed any END probes on another
20870Sstevel@tonic-gate 		 * CPU.  We can simply consume this buffer and return.
20880Sstevel@tonic-gate 		 */
20890Sstevel@tonic-gate 		return (dt_consume_cpu(dtp, fp, cpu, buf, pf, rf, arg));
20900Sstevel@tonic-gate 	}
20910Sstevel@tonic-gate 
20920Sstevel@tonic-gate 	begin.dtbgn_probefunc = pf;
20930Sstevel@tonic-gate 	begin.dtbgn_recfunc = rf;
20940Sstevel@tonic-gate 	begin.dtbgn_arg = arg;
20950Sstevel@tonic-gate 	begin.dtbgn_beginonly = 1;
20960Sstevel@tonic-gate 
20970Sstevel@tonic-gate 	/*
20980Sstevel@tonic-gate 	 * We need to interpose on the ERROR handler to be sure that we
20990Sstevel@tonic-gate 	 * only process ERRORs induced by BEGIN.
21000Sstevel@tonic-gate 	 */
21010Sstevel@tonic-gate 	begin.dtbgn_errhdlr = dtp->dt_errhdlr;
21020Sstevel@tonic-gate 	begin.dtbgn_errarg = dtp->dt_errarg;
21030Sstevel@tonic-gate 	dtp->dt_errhdlr = dt_consume_begin_error;
21040Sstevel@tonic-gate 	dtp->dt_errarg = &begin;
21050Sstevel@tonic-gate 
21060Sstevel@tonic-gate 	rval = dt_consume_cpu(dtp, fp, cpu, buf, dt_consume_begin_probe,
21070Sstevel@tonic-gate 	    dt_consume_begin_record, &begin);
21080Sstevel@tonic-gate 
21090Sstevel@tonic-gate 	dtp->dt_errhdlr = begin.dtbgn_errhdlr;
21100Sstevel@tonic-gate 	dtp->dt_errarg = begin.dtbgn_errarg;
21110Sstevel@tonic-gate 
21120Sstevel@tonic-gate 	if (rval != 0)
21130Sstevel@tonic-gate 		return (rval);
21140Sstevel@tonic-gate 
21150Sstevel@tonic-gate 	/*
21160Sstevel@tonic-gate 	 * Now allocate a new buffer.  We'll use this to deal with every other
21170Sstevel@tonic-gate 	 * CPU.
21180Sstevel@tonic-gate 	 */
21190Sstevel@tonic-gate 	bzero(&nbuf, sizeof (dtrace_bufdesc_t));
21200Sstevel@tonic-gate 	(void) dtrace_getopt(dtp, "bufsize", &size);
21210Sstevel@tonic-gate 	if ((nbuf.dtbd_data = malloc(size)) == NULL)
21220Sstevel@tonic-gate 		return (dt_set_errno(dtp, EDT_NOMEM));
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 	if (max_ncpus == 0)
21250Sstevel@tonic-gate 		max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1;
21260Sstevel@tonic-gate 
21270Sstevel@tonic-gate 	for (i = 0; i < max_ncpus; i++) {
21280Sstevel@tonic-gate 		nbuf.dtbd_cpu = i;
21290Sstevel@tonic-gate 
21300Sstevel@tonic-gate 		if (i == cpu)
21310Sstevel@tonic-gate 			continue;
21320Sstevel@tonic-gate 
21330Sstevel@tonic-gate 		if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, &nbuf) == -1) {
21340Sstevel@tonic-gate 			/*
21350Sstevel@tonic-gate 			 * If we failed with ENOENT, it may be because the
21360Sstevel@tonic-gate 			 * CPU was unconfigured -- this is okay.  Any other
21370Sstevel@tonic-gate 			 * error, however, is unexpected.
21380Sstevel@tonic-gate 			 */
21390Sstevel@tonic-gate 			if (errno == ENOENT)
21400Sstevel@tonic-gate 				continue;
21410Sstevel@tonic-gate 
21420Sstevel@tonic-gate 			free(nbuf.dtbd_data);
21430Sstevel@tonic-gate 
21440Sstevel@tonic-gate 			return (dt_set_errno(dtp, errno));
21450Sstevel@tonic-gate 		}
21460Sstevel@tonic-gate 
21470Sstevel@tonic-gate 		if ((rval = dt_consume_cpu(dtp, fp,
21480Sstevel@tonic-gate 		    i, &nbuf, pf, rf, arg)) != 0) {
21490Sstevel@tonic-gate 			free(nbuf.dtbd_data);
21500Sstevel@tonic-gate 			return (rval);
21510Sstevel@tonic-gate 		}
21520Sstevel@tonic-gate 	}
21530Sstevel@tonic-gate 
21540Sstevel@tonic-gate 	free(nbuf.dtbd_data);
21550Sstevel@tonic-gate 
21560Sstevel@tonic-gate 	/*
21570Sstevel@tonic-gate 	 * Okay -- we're done with the other buffers.  Now we want to
21580Sstevel@tonic-gate 	 * reconsume the first buffer -- but this time we're looking for
21590Sstevel@tonic-gate 	 * everything _but_ BEGIN.  And of course, in order to only consume
21600Sstevel@tonic-gate 	 * those ERRORs _not_ associated with BEGIN, we need to reinstall our
21610Sstevel@tonic-gate 	 * ERROR interposition function...
21620Sstevel@tonic-gate 	 */
21630Sstevel@tonic-gate 	begin.dtbgn_beginonly = 0;
21640Sstevel@tonic-gate 
21650Sstevel@tonic-gate 	assert(begin.dtbgn_errhdlr == dtp->dt_errhdlr);
21660Sstevel@tonic-gate 	assert(begin.dtbgn_errarg == dtp->dt_errarg);
21670Sstevel@tonic-gate 	dtp->dt_errhdlr = dt_consume_begin_error;
21680Sstevel@tonic-gate 	dtp->dt_errarg = &begin;
21690Sstevel@tonic-gate 
21700Sstevel@tonic-gate 	rval = dt_consume_cpu(dtp, fp, cpu, buf, dt_consume_begin_probe,
21710Sstevel@tonic-gate 	    dt_consume_begin_record, &begin);
21720Sstevel@tonic-gate 
21730Sstevel@tonic-gate 	dtp->dt_errhdlr = begin.dtbgn_errhdlr;
21740Sstevel@tonic-gate 	dtp->dt_errarg = begin.dtbgn_errarg;
21750Sstevel@tonic-gate 
21760Sstevel@tonic-gate 	return (rval);
21770Sstevel@tonic-gate }
21780Sstevel@tonic-gate 
21790Sstevel@tonic-gate int
dtrace_consume(dtrace_hdl_t * dtp,FILE * fp,dtrace_consume_probe_f * pf,dtrace_consume_rec_f * rf,void * arg)21800Sstevel@tonic-gate dtrace_consume(dtrace_hdl_t *dtp, FILE *fp,
21810Sstevel@tonic-gate     dtrace_consume_probe_f *pf, dtrace_consume_rec_f *rf, void *arg)
21820Sstevel@tonic-gate {
21830Sstevel@tonic-gate 	dtrace_bufdesc_t *buf = &dtp->dt_buf;
21840Sstevel@tonic-gate 	dtrace_optval_t size;
21850Sstevel@tonic-gate 	static int max_ncpus;
21860Sstevel@tonic-gate 	int i, rval;
21870Sstevel@tonic-gate 	dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_SWITCHRATE];
21880Sstevel@tonic-gate 	hrtime_t now = gethrtime();
21890Sstevel@tonic-gate 
21900Sstevel@tonic-gate 	if (dtp->dt_lastswitch != 0) {
21910Sstevel@tonic-gate 		if (now - dtp->dt_lastswitch < interval)
21920Sstevel@tonic-gate 			return (0);
21930Sstevel@tonic-gate 
21940Sstevel@tonic-gate 		dtp->dt_lastswitch += interval;
21950Sstevel@tonic-gate 	} else {
21960Sstevel@tonic-gate 		dtp->dt_lastswitch = now;
21970Sstevel@tonic-gate 	}
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate 	if (!dtp->dt_active)
22000Sstevel@tonic-gate 		return (dt_set_errno(dtp, EINVAL));
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 	if (max_ncpus == 0)
22030Sstevel@tonic-gate 		max_ncpus = dt_sysconf(dtp, _SC_CPUID_MAX) + 1;
22040Sstevel@tonic-gate 
22050Sstevel@tonic-gate 	if (pf == NULL)
22060Sstevel@tonic-gate 		pf = (dtrace_consume_probe_f *)dt_nullprobe;
22070Sstevel@tonic-gate 
22080Sstevel@tonic-gate 	if (rf == NULL)
22090Sstevel@tonic-gate 		rf = (dtrace_consume_rec_f *)dt_nullrec;
22100Sstevel@tonic-gate 
22110Sstevel@tonic-gate 	if (buf->dtbd_data == NULL) {
22120Sstevel@tonic-gate 		(void) dtrace_getopt(dtp, "bufsize", &size);
22130Sstevel@tonic-gate 		if ((buf->dtbd_data = malloc(size)) == NULL)
22140Sstevel@tonic-gate 			return (dt_set_errno(dtp, EDT_NOMEM));
22150Sstevel@tonic-gate 
22160Sstevel@tonic-gate 		buf->dtbd_size = size;
22170Sstevel@tonic-gate 	}
22180Sstevel@tonic-gate 
22190Sstevel@tonic-gate 	/*
22200Sstevel@tonic-gate 	 * If we have just begun, we want to first process the CPU that
22210Sstevel@tonic-gate 	 * executed the BEGIN probe (if any).
22220Sstevel@tonic-gate 	 */
22230Sstevel@tonic-gate 	if (dtp->dt_active && dtp->dt_beganon != -1) {
22240Sstevel@tonic-gate 		buf->dtbd_cpu = dtp->dt_beganon;
22250Sstevel@tonic-gate 		if ((rval = dt_consume_begin(dtp, fp, buf, pf, rf, arg)) != 0)
22260Sstevel@tonic-gate 			return (rval);
22270Sstevel@tonic-gate 	}
22280Sstevel@tonic-gate 
22290Sstevel@tonic-gate 	for (i = 0; i < max_ncpus; i++) {
22300Sstevel@tonic-gate 		buf->dtbd_cpu = i;
22310Sstevel@tonic-gate 
22320Sstevel@tonic-gate 		/*
22330Sstevel@tonic-gate 		 * If we have stopped, we want to process the CPU on which the
22340Sstevel@tonic-gate 		 * END probe was processed only _after_ we have processed
22350Sstevel@tonic-gate 		 * everything else.
22360Sstevel@tonic-gate 		 */
22370Sstevel@tonic-gate 		if (dtp->dt_stopped && (i == dtp->dt_endedon))
22380Sstevel@tonic-gate 			continue;
22390Sstevel@tonic-gate 
22400Sstevel@tonic-gate 		if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) {
22410Sstevel@tonic-gate 			/*
22420Sstevel@tonic-gate 			 * If we failed with ENOENT, it may be because the
22430Sstevel@tonic-gate 			 * CPU was unconfigured -- this is okay.  Any other
22440Sstevel@tonic-gate 			 * error, however, is unexpected.
22450Sstevel@tonic-gate 			 */
22460Sstevel@tonic-gate 			if (errno == ENOENT)
22470Sstevel@tonic-gate 				continue;
22480Sstevel@tonic-gate 
22490Sstevel@tonic-gate 			return (dt_set_errno(dtp, errno));
22500Sstevel@tonic-gate 		}
22510Sstevel@tonic-gate 
22520Sstevel@tonic-gate 		if ((rval = dt_consume_cpu(dtp, fp, i, buf, pf, rf, arg)) != 0)
22530Sstevel@tonic-gate 			return (rval);
22540Sstevel@tonic-gate 	}
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate 	if (!dtp->dt_stopped)
22570Sstevel@tonic-gate 		return (0);
22580Sstevel@tonic-gate 
22590Sstevel@tonic-gate 	buf->dtbd_cpu = dtp->dt_endedon;
22600Sstevel@tonic-gate 
22610Sstevel@tonic-gate 	if (dt_ioctl(dtp, DTRACEIOC_BUFSNAP, buf) == -1) {
22620Sstevel@tonic-gate 		/*
22630Sstevel@tonic-gate 		 * This _really_ shouldn't fail, but it is strictly speaking
22640Sstevel@tonic-gate 		 * possible for this to return ENOENT if the CPU that called
22650Sstevel@tonic-gate 		 * the END enabling somehow managed to become unconfigured.
22660Sstevel@tonic-gate 		 * It's unclear how the user can possibly expect anything
22670Sstevel@tonic-gate 		 * rational to happen in this case -- the state has been thrown
22680Sstevel@tonic-gate 		 * out along with the unconfigured CPU -- so we'll just drive
22690Sstevel@tonic-gate 		 * on...
22700Sstevel@tonic-gate 		 */
22710Sstevel@tonic-gate 		if (errno == ENOENT)
22720Sstevel@tonic-gate 			return (0);
22730Sstevel@tonic-gate 
22740Sstevel@tonic-gate 		return (dt_set_errno(dtp, errno));
22750Sstevel@tonic-gate 	}
22760Sstevel@tonic-gate 
22770Sstevel@tonic-gate 	return (dt_consume_cpu(dtp, fp, dtp->dt_endedon, buf, pf, rf, arg));
22780Sstevel@tonic-gate }
2279