xref: /onnv-gate/usr/src/cmd/plockstat/plockstat.c (revision 2915:f68850bdbf52)
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
51464Sahl  * Common Development and Distribution License (the "License").
61464Sahl  * 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  */
211464Sahl 
220Sstevel@tonic-gate /*
231464Sahl  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <assert.h>
300Sstevel@tonic-gate #include <dtrace.h>
310Sstevel@tonic-gate #include <limits.h>
320Sstevel@tonic-gate #include <link.h>
330Sstevel@tonic-gate #include <priv.h>
340Sstevel@tonic-gate #include <signal.h>
350Sstevel@tonic-gate #include <stdlib.h>
360Sstevel@tonic-gate #include <stdarg.h>
370Sstevel@tonic-gate #include <stdio.h>
380Sstevel@tonic-gate #include <string.h>
390Sstevel@tonic-gate #include <strings.h>
400Sstevel@tonic-gate #include <errno.h>
410Sstevel@tonic-gate #include <sys/wait.h>
421464Sahl #include <libgen.h>
430Sstevel@tonic-gate #include <libproc.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate static char *g_pname;
460Sstevel@tonic-gate static dtrace_hdl_t *g_dtp;
470Sstevel@tonic-gate struct ps_prochandle *g_pr;
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #define	E_SUCCESS	0
500Sstevel@tonic-gate #define	E_ERROR		1
510Sstevel@tonic-gate #define	E_USAGE		2
520Sstevel@tonic-gate 
530Sstevel@tonic-gate /*
540Sstevel@tonic-gate  * For hold times we use a global associative array since for mutexes, in
550Sstevel@tonic-gate  * user-land, it's not invalid to release a sychonization primitive that
560Sstevel@tonic-gate  * another thread acquired; rwlocks require a thread-local associative array
570Sstevel@tonic-gate  * since multiple thread can hold the same lock for reading. Note that we
580Sstevel@tonic-gate  * ignore recursive mutex acquisitions and releases as they don't truly
590Sstevel@tonic-gate  * affect lock contention.
600Sstevel@tonic-gate  */
610Sstevel@tonic-gate static const char *g_hold_init =
620Sstevel@tonic-gate "plockstat$target:::rw-acquire\n"
630Sstevel@tonic-gate "{\n"
640Sstevel@tonic-gate "	self->rwhold[arg0] = timestamp;\n"
650Sstevel@tonic-gate "}\n"
660Sstevel@tonic-gate "plockstat$target:::mutex-acquire\n"
670Sstevel@tonic-gate "/arg1 == 0/\n"
680Sstevel@tonic-gate "{\n"
690Sstevel@tonic-gate "	mtxhold[arg0] = timestamp;\n"
700Sstevel@tonic-gate "}\n";
710Sstevel@tonic-gate 
720Sstevel@tonic-gate static const char *g_hold_histogram =
730Sstevel@tonic-gate "plockstat$target:::rw-release\n"
740Sstevel@tonic-gate "/self->rwhold[arg0] && arg1 == 1/\n"
750Sstevel@tonic-gate "{\n"
760Sstevel@tonic-gate "	@rw_w_hold[arg0, ustack()] =\n"
770Sstevel@tonic-gate "	    quantize(timestamp - self->rwhold[arg0]);\n"
780Sstevel@tonic-gate "	self->rwhold[arg0] = 0;\n"
791464Sahl "	rw_w_hold_found = 1;\n"
800Sstevel@tonic-gate "}\n"
810Sstevel@tonic-gate "plockstat$target:::rw-release\n"
820Sstevel@tonic-gate "/self->rwhold[arg0]/\n"
830Sstevel@tonic-gate "{\n"
840Sstevel@tonic-gate "	@rw_r_hold[arg0, ustack()] =\n"
850Sstevel@tonic-gate "	    quantize(timestamp - self->rwhold[arg0]);\n"
860Sstevel@tonic-gate "	self->rwhold[arg0] = 0;\n"
871464Sahl "	rw_r_hold_found = 1;\n"
880Sstevel@tonic-gate "}\n"
890Sstevel@tonic-gate "plockstat$target:::mutex-release\n"
900Sstevel@tonic-gate "/mtxhold[arg0] && arg1 == 0/\n"
910Sstevel@tonic-gate "{\n"
920Sstevel@tonic-gate "	@mtx_hold[arg0, ustack()] = quantize(timestamp - mtxhold[arg0]);\n"
930Sstevel@tonic-gate "	mtxhold[arg0] = 0;\n"
941464Sahl "	mtx_hold_found = 1;\n"
951464Sahl "}\n"
961464Sahl "\n"
971464Sahl "END\n"
981464Sahl "/mtx_hold_found/\n"
991464Sahl "{\n"
1001464Sahl "	trace(\"Mutex hold\");\n"
1011464Sahl "	printa(@mtx_hold);\n"
1021464Sahl "}\n"
1031464Sahl "END\n"
1041464Sahl "/rw_r_hold_found/\n"
1051464Sahl "{\n"
1061464Sahl "	trace(\"R/W reader hold\");\n"
1071464Sahl "	printa(@rw_r_hold);\n"
1081464Sahl "}\n"
1091464Sahl "END\n"
1101464Sahl "/rw_w_hold_found/\n"
1111464Sahl "{\n"
1121464Sahl "	trace(\"R/W writer hold\");\n"
1131464Sahl "	printa(@rw_w_hold);\n"
1140Sstevel@tonic-gate "}\n";
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate static const char *g_hold_times =
1170Sstevel@tonic-gate "plockstat$target:::rw-release\n"
1180Sstevel@tonic-gate "/self->rwhold[arg0] && arg1 == 1/\n"
1190Sstevel@tonic-gate "{\n"
1201464Sahl "	@rw_w_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"
1211464Sahl "	@rw_w_hold_count[arg0, ustack(5)] = count();\n"
1220Sstevel@tonic-gate "	self->rwhold[arg0] = 0;\n"
1231464Sahl "	rw_w_hold_found = 1;\n"
1240Sstevel@tonic-gate "}\n"
1250Sstevel@tonic-gate "plockstat$target:::rw-release\n"
1260Sstevel@tonic-gate "/self->rwhold[arg0]/\n"
1270Sstevel@tonic-gate "{\n"
1281464Sahl "	@rw_r_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"
1291464Sahl "	@rw_r_hold_count[arg0, ustack(5)] = count();\n"
1300Sstevel@tonic-gate "	self->rwhold[arg0] = 0;\n"
1311464Sahl "	rw_r_hold_found = 1;\n"
1320Sstevel@tonic-gate "}\n"
1330Sstevel@tonic-gate "plockstat$target:::mutex-release\n"
1340Sstevel@tonic-gate "/mtxhold[arg0] && arg1 == 0/\n"
1350Sstevel@tonic-gate "{\n"
1361464Sahl "	@mtx_hold[arg0, ustack(5)] = sum(timestamp - mtxhold[arg0]);\n"
1371464Sahl "	@mtx_hold_count[arg0, ustack(5)] = count();\n"
1380Sstevel@tonic-gate "	mtxhold[arg0] = 0;\n"
1391464Sahl "	mtx_hold_found = 1;\n"
1401464Sahl "}\n"
1411464Sahl "\n"
1421464Sahl "END\n"
1431464Sahl "/mtx_hold_found/\n"
1441464Sahl "{\n"
1451464Sahl "	trace(\"Mutex hold\");\n"
1461464Sahl "	printa(@mtx_hold, @mtx_hold_count);\n"
1471464Sahl "}\n"
1481464Sahl "END\n"
1491464Sahl "/rw_r_hold_found/\n"
1501464Sahl "{\n"
1511464Sahl "	trace(\"R/W reader hold\");\n"
1521464Sahl "	printa(@rw_r_hold, @rw_r_hold_count);\n"
1531464Sahl "}\n"
1541464Sahl "END\n"
1551464Sahl "/rw_w_hold_found/\n"
1561464Sahl "{\n"
1571464Sahl "	trace(\"R/W writer hold\");\n"
1581464Sahl "	printa(@rw_w_hold, @rw_w_hold_count);\n"
1590Sstevel@tonic-gate "}\n";
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate /*
1630Sstevel@tonic-gate  * For contention, we use thread-local associative arrays since we're tracing
1640Sstevel@tonic-gate  * a single thread's activity in libc and multiple threads can be blocking or
1650Sstevel@tonic-gate  * spinning on the same sychonization primitive.
1660Sstevel@tonic-gate  */
1670Sstevel@tonic-gate static const char *g_ctnd_init =
1680Sstevel@tonic-gate "plockstat$target:::rw-block\n"
1690Sstevel@tonic-gate "{\n"
1700Sstevel@tonic-gate "	self->rwblock[arg0] = timestamp;\n"
1710Sstevel@tonic-gate "}\n"
1720Sstevel@tonic-gate "plockstat$target:::mutex-block\n"
1730Sstevel@tonic-gate "{\n"
1740Sstevel@tonic-gate "	self->mtxblock[arg0] = timestamp;\n"
1750Sstevel@tonic-gate "}\n"
1760Sstevel@tonic-gate "plockstat$target:::mutex-spin\n"
1770Sstevel@tonic-gate "{\n"
1780Sstevel@tonic-gate "	self->mtxspin[arg0] = timestamp;\n"
1790Sstevel@tonic-gate "}\n";
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate static const char *g_ctnd_histogram =
1820Sstevel@tonic-gate "plockstat$target:::rw-blocked\n"
1830Sstevel@tonic-gate "/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
1840Sstevel@tonic-gate "{\n"
1850Sstevel@tonic-gate "	@rw_w_block[arg0, ustack()] =\n"
1860Sstevel@tonic-gate "	    quantize(timestamp - self->rwblock[arg0]);\n"
1870Sstevel@tonic-gate "	self->rwblock[arg0] = 0;\n"
1881464Sahl "	rw_w_block_found = 1;\n"
1890Sstevel@tonic-gate "}\n"
1900Sstevel@tonic-gate "plockstat$target:::rw-blocked\n"
1910Sstevel@tonic-gate "/self->rwblock[arg0] && arg2 != 0/\n"
1920Sstevel@tonic-gate "{\n"
1930Sstevel@tonic-gate "	@rw_r_block[arg0, ustack()] =\n"
1940Sstevel@tonic-gate "	    quantize(timestamp - self->rwblock[arg0]);\n"
1950Sstevel@tonic-gate "	self->rwblock[arg0] = 0;\n"
1961464Sahl "	rw_r_block_found = 1;\n"
1970Sstevel@tonic-gate "}\n"
1980Sstevel@tonic-gate "plockstat$target:::rw-blocked\n"
1990Sstevel@tonic-gate "/self->rwblock[arg0]/\n"
2000Sstevel@tonic-gate "{\n"
2010Sstevel@tonic-gate "	self->rwblock[arg0] = 0;\n"
2020Sstevel@tonic-gate "}\n"
2030Sstevel@tonic-gate "plockstat$target:::mutex-spun\n"
2040Sstevel@tonic-gate "/self->mtxspin[arg0] && arg1 != 0/\n"
2050Sstevel@tonic-gate "{\n"
2060Sstevel@tonic-gate "	@mtx_spin[arg0, ustack()] =\n"
2070Sstevel@tonic-gate "	    quantize(timestamp - self->mtxspin[arg0]);\n"
2080Sstevel@tonic-gate "	self->mtxspin[arg0] = 0;\n"
2091464Sahl "	mtx_spin_found = 1;\n"
2100Sstevel@tonic-gate "}\n"
2110Sstevel@tonic-gate "plockstat$target:::mutex-spun\n"
2120Sstevel@tonic-gate "/self->mtxspin[arg0]/\n"
2130Sstevel@tonic-gate "{\n"
2140Sstevel@tonic-gate "	@mtx_vain_spin[arg0, ustack()] =\n"
2150Sstevel@tonic-gate "	    quantize(timestamp - self->mtxspin[arg0]);\n"
2160Sstevel@tonic-gate "	self->mtxspin[arg0] = 0;\n"
2171464Sahl "	mtx_vain_spin_found = 1;\n"
2180Sstevel@tonic-gate "}\n"
2190Sstevel@tonic-gate "plockstat$target:::mutex-blocked\n"
2200Sstevel@tonic-gate "/self->mtxblock[arg0] && arg1 != 0/\n"
2210Sstevel@tonic-gate "{\n"
2220Sstevel@tonic-gate "	@mtx_block[arg0, ustack()] =\n"
2230Sstevel@tonic-gate "	    quantize(timestamp - self->mtxblock[arg0]);\n"
2240Sstevel@tonic-gate "	self->mtxblock[arg0] = 0;\n"
2251464Sahl "	mtx_block_found = 1;\n"
2260Sstevel@tonic-gate "}\n"
2270Sstevel@tonic-gate "plockstat$target:::mutex-blocked\n"
2280Sstevel@tonic-gate "/self->mtxblock[arg0]/\n"
2290Sstevel@tonic-gate "{\n"
2300Sstevel@tonic-gate "	self->mtxblock[arg0] = 0;\n"
2311464Sahl "}\n"
2321464Sahl "\n"
2331464Sahl "END\n"
2341464Sahl "/mtx_block_found/\n"
2351464Sahl "{\n"
2361464Sahl "	trace(\"Mutex block\");\n"
2371464Sahl "	printa(@mtx_block);\n"
2381464Sahl "}\n"
2391464Sahl "END\n"
2401464Sahl "/mtx_spin_found/\n"
2411464Sahl "{\n"
2421464Sahl "	trace(\"Mutex spin\");\n"
2431464Sahl "	printa(@mtx_spin);\n"
2441464Sahl "}\n"
2451464Sahl "END\n"
2461464Sahl "/mtx_vain_spin_found/\n"
2471464Sahl "{\n"
2481464Sahl "	trace(\"Mutex unsuccessful spin\");\n"
2491464Sahl "	printa(@mtx_vain_spin);\n"
2501464Sahl "}\n"
2511464Sahl "END\n"
2521464Sahl "/rw_r_block_found/\n"
2531464Sahl "{\n"
2541464Sahl "	trace(\"R/W reader block\");\n"
2551464Sahl "	printa(@rw_r_block);\n"
2561464Sahl "}\n"
2571464Sahl "END\n"
2581464Sahl "/rw_w_block_found/\n"
2591464Sahl "{\n"
2601464Sahl "	trace(\"R/W writer block\");\n"
2611464Sahl "	printa(@rw_w_block);\n"
2620Sstevel@tonic-gate "}\n";
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate static const char *g_ctnd_times =
2660Sstevel@tonic-gate "plockstat$target:::rw-blocked\n"
2670Sstevel@tonic-gate "/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
2680Sstevel@tonic-gate "{\n"
2690Sstevel@tonic-gate "	@rw_w_block[arg0, ustack(5)] =\n"
2701464Sahl "	    sum(timestamp - self->rwblock[arg0]);\n"
2711464Sahl "	@rw_w_block_count[arg0, ustack(5)] = count();\n"
2720Sstevel@tonic-gate "	self->rwblock[arg0] = 0;\n"
2731464Sahl "	rw_w_block_found = 1;\n"
2740Sstevel@tonic-gate "}\n"
2750Sstevel@tonic-gate "plockstat$target:::rw-blocked\n"
2760Sstevel@tonic-gate "/self->rwblock[arg0] && arg2 != 0/\n"
2770Sstevel@tonic-gate "{\n"
2780Sstevel@tonic-gate "	@rw_r_block[arg0, ustack(5)] =\n"
2791464Sahl "	    sum(timestamp - self->rwblock[arg0]);\n"
2801464Sahl "	@rw_r_block_count[arg0, ustack(5)] = count();\n"
2810Sstevel@tonic-gate "	self->rwblock[arg0] = 0;\n"
2821464Sahl "	rw_r_block_found = 1;\n"
2830Sstevel@tonic-gate "}\n"
2840Sstevel@tonic-gate "plockstat$target:::rw-blocked\n"
2850Sstevel@tonic-gate "/self->rwblock[arg0]/\n"
2860Sstevel@tonic-gate "{\n"
2870Sstevel@tonic-gate "	self->rwblock[arg0] = 0;\n"
2880Sstevel@tonic-gate "}\n"
2890Sstevel@tonic-gate "plockstat$target:::mutex-spun\n"
2900Sstevel@tonic-gate "/self->mtxspin[arg0] && arg1 != 0/\n"
2910Sstevel@tonic-gate "{\n"
2920Sstevel@tonic-gate "	@mtx_spin[arg0, ustack(5)] =\n"
2931464Sahl "	    sum(timestamp - self->mtxspin[arg0]);\n"
2941464Sahl "	@mtx_spin_count[arg0, ustack(5)] = count();\n"
2950Sstevel@tonic-gate "	self->mtxspin[arg0] = 0;\n"
2961464Sahl "	mtx_spin_found = 1;\n"
2970Sstevel@tonic-gate "}\n"
2980Sstevel@tonic-gate "plockstat$target:::mutex-spun\n"
2990Sstevel@tonic-gate "/self->mtxspin[arg0]/\n"
3000Sstevel@tonic-gate "{\n"
3010Sstevel@tonic-gate "	@mtx_vain_spin[arg0, ustack(5)] =\n"
3021464Sahl "	    sum(timestamp - self->mtxspin[arg0]);\n"
3031464Sahl "	@mtx_vain_spin_count[arg0, ustack(5)] = count();\n"
3040Sstevel@tonic-gate "	self->mtxspin[arg0] = 0;\n"
3051464Sahl "	mtx_vain_spin_found = 1;\n"
3060Sstevel@tonic-gate "}\n"
3070Sstevel@tonic-gate "plockstat$target:::mutex-blocked\n"
3080Sstevel@tonic-gate "/self->mtxblock[arg0] && arg1 != 0/\n"
3090Sstevel@tonic-gate "{\n"
3100Sstevel@tonic-gate "	@mtx_block[arg0, ustack(5)] =\n"
3111464Sahl "	    sum(timestamp - self->mtxblock[arg0]);\n"
3121464Sahl "	@mtx_block_count[arg0, ustack(5)] = count();\n"
3130Sstevel@tonic-gate "	self->mtxblock[arg0] = 0;\n"
3141464Sahl "	mtx_block_found = 1;\n"
3150Sstevel@tonic-gate "}\n"
3160Sstevel@tonic-gate "plockstat$target:::mutex-blocked\n"
3170Sstevel@tonic-gate "/self->mtxblock[arg0]/\n"
3180Sstevel@tonic-gate "{\n"
3190Sstevel@tonic-gate "	self->mtxblock[arg0] = 0;\n"
3201464Sahl "}\n"
3211464Sahl "\n"
3221464Sahl "END\n"
3231464Sahl "/mtx_block_found/\n"
3241464Sahl "{\n"
3251464Sahl "	trace(\"Mutex block\");\n"
3261464Sahl "	printa(@mtx_block, @mtx_block_count);\n"
3271464Sahl "}\n"
3281464Sahl "END\n"
3291464Sahl "/mtx_spin_found/\n"
3301464Sahl "{\n"
3311464Sahl "	trace(\"Mutex spin\");\n"
3321464Sahl "	printa(@mtx_spin, @mtx_spin_count);\n"
3331464Sahl "}\n"
3341464Sahl "END\n"
3351464Sahl "/mtx_vain_spin_found/\n"
3361464Sahl "{\n"
3371464Sahl "	trace(\"Mutex unsuccessful spin\");\n"
3381464Sahl "	printa(@mtx_vain_spin, @mtx_vain_spin_count);\n"
3391464Sahl "}\n"
3401464Sahl "END\n"
3411464Sahl "/rw_r_block_found/\n"
3421464Sahl "{\n"
3431464Sahl "	trace(\"R/W reader block\");\n"
3441464Sahl "	printa(@rw_r_block, @rw_r_block_count);\n"
3451464Sahl "}\n"
3461464Sahl "END\n"
3471464Sahl "/rw_w_block_found/\n"
3481464Sahl "{\n"
3491464Sahl "	trace(\"R/W writer block\");\n"
3501464Sahl "	printa(@rw_w_block, @rw_w_block_count);\n"
3510Sstevel@tonic-gate "}\n";
3520Sstevel@tonic-gate 
3531464Sahl static char g_prog[4096];
3540Sstevel@tonic-gate static size_t g_proglen;
3550Sstevel@tonic-gate static int g_opt_V, g_opt_s;
3560Sstevel@tonic-gate static int g_intr;
3571464Sahl static int g_exited;
3580Sstevel@tonic-gate static dtrace_optval_t g_nframes;
3590Sstevel@tonic-gate static ulong_t g_nent = ULONG_MAX;
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate #define	PLOCKSTAT_OPTSTR	"n:ps:e:vx:ACHV"
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate static void
usage(void)3640Sstevel@tonic-gate usage(void)
3650Sstevel@tonic-gate {
3660Sstevel@tonic-gate 	(void) fprintf(stderr, "Usage:\n"
3670Sstevel@tonic-gate 	    "\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
3680Sstevel@tonic-gate 	    "\t    command [arg...]\n"
3690Sstevel@tonic-gate 	    "\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
3700Sstevel@tonic-gate 	    "\t    -p pid\n", g_pname, g_pname);
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	exit(E_USAGE);
3730Sstevel@tonic-gate }
3740Sstevel@tonic-gate 
3750Sstevel@tonic-gate static void
verror(const char * fmt,va_list ap)3760Sstevel@tonic-gate verror(const char *fmt, va_list ap)
3770Sstevel@tonic-gate {
3780Sstevel@tonic-gate 	int error = errno;
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", g_pname);
3810Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, ap);
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 	if (fmt[strlen(fmt) - 1] != '\n')
3840Sstevel@tonic-gate 		(void) fprintf(stderr, ": %s\n", strerror(error));
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate /*PRINTFLIKE1*/
3880Sstevel@tonic-gate static void
fatal(const char * fmt,...)3890Sstevel@tonic-gate fatal(const char *fmt, ...)
3900Sstevel@tonic-gate {
3910Sstevel@tonic-gate 	va_list ap;
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	va_start(ap, fmt);
3940Sstevel@tonic-gate 	verror(fmt, ap);
3950Sstevel@tonic-gate 	va_end(ap);
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	if (g_pr != NULL && g_dtp != NULL)
3980Sstevel@tonic-gate 		dtrace_proc_release(g_dtp, g_pr);
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	exit(E_ERROR);
4010Sstevel@tonic-gate }
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate /*PRINTFLIKE1*/
4040Sstevel@tonic-gate static void
dfatal(const char * fmt,...)4050Sstevel@tonic-gate dfatal(const char *fmt, ...)
4060Sstevel@tonic-gate {
4070Sstevel@tonic-gate 	va_list ap;
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	va_start(ap, fmt);
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", g_pname);
4120Sstevel@tonic-gate 	if (fmt != NULL)
4130Sstevel@tonic-gate 		(void) vfprintf(stderr, fmt, ap);
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	va_end(ap);
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') {
4180Sstevel@tonic-gate 		(void) fprintf(stderr, ": %s\n",
4190Sstevel@tonic-gate 		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
4200Sstevel@tonic-gate 	} else if (fmt == NULL) {
4210Sstevel@tonic-gate 		(void) fprintf(stderr, "%s\n",
4220Sstevel@tonic-gate 		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
4230Sstevel@tonic-gate 	}
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	if (g_pr != NULL) {
4260Sstevel@tonic-gate 		dtrace_proc_continue(g_dtp, g_pr);
4270Sstevel@tonic-gate 		dtrace_proc_release(g_dtp, g_pr);
4280Sstevel@tonic-gate 	}
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	exit(E_ERROR);
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate /*PRINTFLIKE1*/
4340Sstevel@tonic-gate static void
notice(const char * fmt,...)4350Sstevel@tonic-gate notice(const char *fmt, ...)
4360Sstevel@tonic-gate {
4370Sstevel@tonic-gate 	va_list ap;
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 	va_start(ap, fmt);
4400Sstevel@tonic-gate 	verror(fmt, ap);
4410Sstevel@tonic-gate 	va_end(ap);
4420Sstevel@tonic-gate }
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate static void
dprog_add(const char * prog)4450Sstevel@tonic-gate dprog_add(const char *prog)
4460Sstevel@tonic-gate {
4470Sstevel@tonic-gate 	size_t len = strlen(prog);
4480Sstevel@tonic-gate 	bcopy(prog, g_prog + g_proglen, len + 1);
4490Sstevel@tonic-gate 	g_proglen += len;
4501464Sahl 	assert(g_proglen < sizeof (g_prog));
4510Sstevel@tonic-gate }
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate static void
dprog_compile(void)4540Sstevel@tonic-gate dprog_compile(void)
4550Sstevel@tonic-gate {
4560Sstevel@tonic-gate 	dtrace_prog_t *prog;
4570Sstevel@tonic-gate 	dtrace_proginfo_t info;
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	if (g_opt_V) {
4600Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: vvvv D program vvvv\n", g_pname);
4610Sstevel@tonic-gate 		(void) fputs(g_prog, stderr);
4620Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ^^^^ D program ^^^^\n", g_pname);
4630Sstevel@tonic-gate 	}
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	if ((prog = dtrace_program_strcompile(g_dtp, g_prog,
4660Sstevel@tonic-gate 	    DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL)
4670Sstevel@tonic-gate 		dfatal("failed to compile program");
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 	if (dtrace_program_exec(g_dtp, prog, &info) == -1)
4700Sstevel@tonic-gate 		dfatal("failed to enable probes");
4710Sstevel@tonic-gate }
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate void
print_legend(void)4740Sstevel@tonic-gate print_legend(void)
4750Sstevel@tonic-gate {
4760Sstevel@tonic-gate 	(void) printf("%5s %8s %-28s %s\n", "Count", "nsec", "Lock", "Caller");
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate void
print_bar(void)4800Sstevel@tonic-gate print_bar(void)
4810Sstevel@tonic-gate {
4820Sstevel@tonic-gate 	(void) printf("---------------------------------------"
4830Sstevel@tonic-gate 	    "----------------------------------------\n");
4840Sstevel@tonic-gate }
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate void
print_histogram_header(void)4870Sstevel@tonic-gate print_histogram_header(void)
4880Sstevel@tonic-gate {
4890Sstevel@tonic-gate 	(void) printf("\n%10s ---- Time Distribution --- %5s %s\n",
4900Sstevel@tonic-gate 	    "nsec", "count", "Stack");
4910Sstevel@tonic-gate }
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate /*
4940Sstevel@tonic-gate  * Convert an address to a symbolic string or a numeric string. If nolocks
4950Sstevel@tonic-gate  * is set, we return an error code if this symbol appears to be a mutex- or
4960Sstevel@tonic-gate  * rwlock-related symbol in libc so the caller has a chance to find a more
4970Sstevel@tonic-gate  * helpful symbol.
4980Sstevel@tonic-gate  */
4990Sstevel@tonic-gate static int
getsym(struct ps_prochandle * P,uintptr_t addr,char * buf,size_t size,int nolocks)5000Sstevel@tonic-gate getsym(struct ps_prochandle *P, uintptr_t addr, char *buf, size_t size,
5010Sstevel@tonic-gate     int nolocks)
5020Sstevel@tonic-gate {
5030Sstevel@tonic-gate 	char name[256];
5040Sstevel@tonic-gate 	GElf_Sym sym;
5050Sstevel@tonic-gate 	prsyminfo_t info;
5060Sstevel@tonic-gate 	size_t len;
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	if (P == NULL || Pxlookup_by_addr(P, addr, name, sizeof (name),
5090Sstevel@tonic-gate 	    &sym, &info) != 0) {
5100Sstevel@tonic-gate 		(void) snprintf(buf, size, "%#lx", addr);
5110Sstevel@tonic-gate 		return (0);
5120Sstevel@tonic-gate 	}
513*2915Srh87107 	if (info.prs_object == NULL)
514*2915Srh87107 		info.prs_object = "<unknown>";
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	if (info.prs_lmid != LM_ID_BASE) {
5170Sstevel@tonic-gate 		len = snprintf(buf, size, "LM%lu`", info.prs_lmid);
5180Sstevel@tonic-gate 		buf += len;
5190Sstevel@tonic-gate 		size -= len;
5200Sstevel@tonic-gate 	}
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	len = snprintf(buf, size, "%s`%s", info.prs_object, info.prs_name);
5230Sstevel@tonic-gate 	buf += len;
5240Sstevel@tonic-gate 	size -= len;
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	if (sym.st_value != addr)
5270Sstevel@tonic-gate 		len = snprintf(buf, size, "+%#lx", addr - sym.st_value);
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	if (nolocks && strcmp("libc.so.1", info.prs_object) == 0 &&
5300Sstevel@tonic-gate 	    (strstr("mutex", info.prs_name) == 0 ||
5310Sstevel@tonic-gate 	    strstr("rw", info.prs_name) == 0))
5320Sstevel@tonic-gate 		return (-1);
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	return (0);
5350Sstevel@tonic-gate }
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate /*ARGSUSED*/
5380Sstevel@tonic-gate static int
process_aggregate(const dtrace_aggdata_t ** aggsdata,int naggvars,void * arg)5391464Sahl process_aggregate(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
5400Sstevel@tonic-gate {
541457Sbmc 	const dtrace_recdesc_t *rec;
5420Sstevel@tonic-gate 	uintptr_t lock;
5430Sstevel@tonic-gate 	uint64_t *stack;
5441464Sahl 	caddr_t data;
5451464Sahl 	pid_t pid;
5460Sstevel@tonic-gate 	struct ps_prochandle *P;
5471464Sahl 	char buf[256];
5480Sstevel@tonic-gate 	int i, j;
5491464Sahl 	uint64_t sum, count, avg;
5500Sstevel@tonic-gate 
5511464Sahl 	if ((*(uint_t *)arg)++ >= g_nent)
5521464Sahl 		return (DTRACE_AGGWALK_NEXT);
5530Sstevel@tonic-gate 
5541464Sahl 	rec = aggsdata[0]->dtada_desc->dtagd_rec;
5551464Sahl 	data = aggsdata[0]->dtada_data;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	/*LINTED - alignment*/
5580Sstevel@tonic-gate 	lock = (uintptr_t)*(uint64_t *)(data + rec[1].dtrd_offset);
5590Sstevel@tonic-gate 	/*LINTED - alignment*/
5600Sstevel@tonic-gate 	stack = (uint64_t *)(data + rec[2].dtrd_offset);
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	if (!g_opt_s) {
5631464Sahl 		/*LINTED - alignment*/
5641464Sahl 		sum = *(uint64_t *)(aggsdata[1]->dtada_data +
5651464Sahl 		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
5661464Sahl 		/*LINTED - alignment*/
5671464Sahl 		count = *(uint64_t *)(aggsdata[2]->dtada_data +
5681464Sahl 		    aggsdata[2]->dtada_desc->dtagd_rec[3].dtrd_offset);
5691464Sahl 	} else {
5701464Sahl 		uint64_t *a;
5710Sstevel@tonic-gate 
5721464Sahl 		/*LINTED - alignment*/
5731464Sahl 		a = (uint64_t *)(aggsdata[1]->dtada_data +
5741464Sahl 		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
5751464Sahl 
5760Sstevel@tonic-gate 		print_bar();
5770Sstevel@tonic-gate 		print_legend();
5780Sstevel@tonic-gate 
5791464Sahl 		for (count = sum = 0, i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0;
5800Sstevel@tonic-gate 		    i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) {
5810Sstevel@tonic-gate 			count += a[i];
5821464Sahl 			sum += a[i] << (j - 64);
5830Sstevel@tonic-gate 		}
5840Sstevel@tonic-gate 	}
5850Sstevel@tonic-gate 
5861464Sahl 	avg = sum / count;
5870Sstevel@tonic-gate 	(void) printf("%5llu %8llu ", (u_longlong_t)count, (u_longlong_t)avg);
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	pid = stack[0];
5900Sstevel@tonic-gate 	P = dtrace_proc_grab(g_dtp, pid, PGRAB_RDONLY);
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 	(void) getsym(P, lock, buf, sizeof (buf), 0);
5930Sstevel@tonic-gate 	(void) printf("%-28s ", buf);
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	for (i = 2; i <= 5; i++) {
5960Sstevel@tonic-gate 		if (getsym(P, stack[i], buf, sizeof (buf), 1) == 0)
5970Sstevel@tonic-gate 			break;
5980Sstevel@tonic-gate 	}
5990Sstevel@tonic-gate 	(void) printf("%s\n", buf);
6000Sstevel@tonic-gate 
6010Sstevel@tonic-gate 	if (g_opt_s) {
6020Sstevel@tonic-gate 		int stack_done = 0;
6030Sstevel@tonic-gate 		int quant_done = 0;
6040Sstevel@tonic-gate 		int first_bin, last_bin;
6051464Sahl 		uint64_t bin_size, *a;
6061464Sahl 
6071464Sahl 		/*LINTED - alignment*/
6081464Sahl 		a = (uint64_t *)(aggsdata[1]->dtada_data +
6091464Sahl 		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 		print_histogram_header();
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 		for (first_bin = DTRACE_QUANTIZE_ZEROBUCKET;
6140Sstevel@tonic-gate 		    a[first_bin] == 0; first_bin++)
6150Sstevel@tonic-gate 			continue;
6160Sstevel@tonic-gate 		for (last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 63;
6170Sstevel@tonic-gate 		    a[last_bin] == 0; last_bin--)
6180Sstevel@tonic-gate 			continue;
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 		for (i = 0; !stack_done || !quant_done; i++) {
6210Sstevel@tonic-gate 			if (!stack_done) {
6220Sstevel@tonic-gate 				(void) getsym(P, stack[i + 2], buf,
6230Sstevel@tonic-gate 				    sizeof (buf), 0);
6240Sstevel@tonic-gate 			} else {
6250Sstevel@tonic-gate 				buf[0] = '\0';
6260Sstevel@tonic-gate 			}
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 			if (!quant_done) {
6290Sstevel@tonic-gate 				bin_size = a[first_bin];
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 				(void) printf("%10llu |%-24.*s| %5llu %s\n",
6320Sstevel@tonic-gate 				    1ULL <<
6330Sstevel@tonic-gate 				    (first_bin - DTRACE_QUANTIZE_ZEROBUCKET),
6340Sstevel@tonic-gate 				    (int)(24.0 * bin_size / count),
6350Sstevel@tonic-gate 				    "@@@@@@@@@@@@@@@@@@@@@@@@@@",
6360Sstevel@tonic-gate 				    (u_longlong_t)bin_size, buf);
6370Sstevel@tonic-gate 			} else {
6380Sstevel@tonic-gate 				(void) printf("%43s %s\n", "", buf);
6390Sstevel@tonic-gate 			}
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 			if (i + 1 >= g_nframes || stack[i + 3] == 0)
6420Sstevel@tonic-gate 				stack_done = 1;
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 			if (first_bin++ == last_bin)
6450Sstevel@tonic-gate 				quant_done = 1;
6460Sstevel@tonic-gate 		}
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	dtrace_proc_release(g_dtp, P);
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 	return (DTRACE_AGGWALK_NEXT);
6520Sstevel@tonic-gate }
6530Sstevel@tonic-gate 
6541464Sahl /*ARGSUSED*/
6551464Sahl static void
prochandler(struct ps_prochandle * P,const char * msg,void * arg)6561464Sahl prochandler(struct ps_prochandle *P, const char *msg, void *arg)
6570Sstevel@tonic-gate {
6580Sstevel@tonic-gate 	const psinfo_t *prp = Ppsinfo(P);
6590Sstevel@tonic-gate 	int pid = Pstatus(P)->pr_pid;
6600Sstevel@tonic-gate 	char name[SIG2STR_MAX];
6610Sstevel@tonic-gate 
6621464Sahl 	if (msg != NULL) {
6631464Sahl 		notice("pid %d: %s\n", pid, msg);
6641464Sahl 		return;
6651464Sahl 	}
6661464Sahl 
6670Sstevel@tonic-gate 	switch (Pstate(P)) {
6680Sstevel@tonic-gate 	case PS_UNDEAD:
6690Sstevel@tonic-gate 		/*
6700Sstevel@tonic-gate 		 * Ideally we would like to always report pr_wstat here, but it
6710Sstevel@tonic-gate 		 * isn't possible given current /proc semantics.  If we grabbed
6720Sstevel@tonic-gate 		 * the process, Ppsinfo() will either fail or return a zeroed
6730Sstevel@tonic-gate 		 * psinfo_t depending on how far the parent is in reaping it.
6740Sstevel@tonic-gate 		 * When /proc provides a stable pr_wstat in the status file,
6750Sstevel@tonic-gate 		 * this code can be improved by examining this new pr_wstat.
6760Sstevel@tonic-gate 		 */
6770Sstevel@tonic-gate 		if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) {
6780Sstevel@tonic-gate 			notice("pid %d terminated by %s\n", pid,
6790Sstevel@tonic-gate 			    proc_signame(WTERMSIG(prp->pr_wstat),
6800Sstevel@tonic-gate 			    name, sizeof (name)));
6810Sstevel@tonic-gate 		} else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) {
6820Sstevel@tonic-gate 			notice("pid %d exited with status %d\n",
6830Sstevel@tonic-gate 			    pid, WEXITSTATUS(prp->pr_wstat));
6840Sstevel@tonic-gate 		} else {
6850Sstevel@tonic-gate 			notice("pid %d has exited\n", pid);
6860Sstevel@tonic-gate 		}
6871464Sahl 		g_exited = 1;
6881464Sahl 		break;
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	case PS_LOST:
6910Sstevel@tonic-gate 		notice("pid %d exec'd a set-id or unobservable program\n", pid);
6921464Sahl 		g_exited = 1;
6931464Sahl 		break;
6941464Sahl 	}
6951464Sahl }
6961464Sahl 
6971464Sahl /*ARGSUSED*/
6981464Sahl static int
chewrec(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,void * arg)6991464Sahl chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg)
7001464Sahl {
7011464Sahl 	dtrace_eprobedesc_t *epd = data->dtpda_edesc;
7021464Sahl 	dtrace_aggvarid_t aggvars[2];
7031464Sahl 	const void *buf;
7041464Sahl 	int i, nagv;
7051464Sahl 
7061464Sahl 	/*
7071464Sahl 	 * A NULL rec indicates that we've processed the last record.
7081464Sahl 	 */
7091464Sahl 	if (rec == NULL)
7101464Sahl 		return (DTRACE_CONSUME_NEXT);
7111464Sahl 
7121464Sahl 	buf = data->dtpda_data - rec->dtrd_offset;
7131464Sahl 
7141464Sahl 	switch (rec->dtrd_action) {
7151464Sahl 	case DTRACEACT_DIFEXPR:
7161464Sahl 		(void) printf("\n%s\n\n", (char *)buf + rec->dtrd_offset);
7171464Sahl 		if (!g_opt_s) {
7181464Sahl 			print_legend();
7191464Sahl 			print_bar();
7201464Sahl 		}
7211464Sahl 		return (DTRACE_CONSUME_NEXT);
7221464Sahl 
7231464Sahl 	case DTRACEACT_PRINTA:
7241464Sahl 		for (nagv = 0, i = 0; i < epd->dtepd_nrecs - 1; i++) {
7251464Sahl 			const dtrace_recdesc_t *nrec = &rec[i];
7261464Sahl 
7271464Sahl 			if (nrec->dtrd_uarg != rec->dtrd_uarg)
7281464Sahl 				break;
7291464Sahl 
7301464Sahl 			/*LINTED - alignment*/
7311464Sahl 			aggvars[nagv++] = *(dtrace_aggvarid_t *)((caddr_t)buf +
7321464Sahl 			    nrec->dtrd_offset);
7331464Sahl 		}
7341464Sahl 
7351464Sahl 		if (nagv == (g_opt_s ? 1 : 2)) {
7361464Sahl 			uint_t nent = 0;
7371464Sahl 			if (dtrace_aggregate_walk_joined(g_dtp, aggvars, nagv,
7381464Sahl 			    process_aggregate, &nent) != 0)
7391464Sahl 				dfatal("failed to walk aggregate");
7401464Sahl 		}
7411464Sahl 
7421464Sahl 		return (DTRACE_CONSUME_NEXT);
7430Sstevel@tonic-gate 	}
7440Sstevel@tonic-gate 
7451464Sahl 	return (DTRACE_CONSUME_THIS);
7460Sstevel@tonic-gate }
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate /*ARGSUSED*/
7490Sstevel@tonic-gate static void
intr(int signo)7500Sstevel@tonic-gate intr(int signo)
7510Sstevel@tonic-gate {
7520Sstevel@tonic-gate 	g_intr = 1;
7530Sstevel@tonic-gate }
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate int
main(int argc,char ** argv)7560Sstevel@tonic-gate main(int argc, char **argv)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate 	ucred_t *ucp;
7590Sstevel@tonic-gate 	int err;
7600Sstevel@tonic-gate 	int opt_C = 0, opt_H = 0, opt_p = 0, opt_v = 0;
7610Sstevel@tonic-gate 	char c, *p, *end;
7620Sstevel@tonic-gate 	struct sigaction act;
7630Sstevel@tonic-gate 	int done = 0;
7640Sstevel@tonic-gate 
7651464Sahl 	g_pname = basename(argv[0]);
7661464Sahl 	argv[0] = g_pname; /* rewrite argv[0] for getopt errors */
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 	/*
7690Sstevel@tonic-gate 	 * Make sure we have the required dtrace_proc privilege.
7700Sstevel@tonic-gate 	 */
7710Sstevel@tonic-gate 	if ((ucp = ucred_get(getpid())) != NULL) {
7720Sstevel@tonic-gate 		const priv_set_t *psp;
7730Sstevel@tonic-gate 		if ((psp = ucred_getprivset(ucp, PRIV_EFFECTIVE)) != NULL &&
7740Sstevel@tonic-gate 		    !priv_ismember(psp, PRIV_DTRACE_PROC)) {
7750Sstevel@tonic-gate 			fatal("dtrace_proc privilege required\n");
7760Sstevel@tonic-gate 		}
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate 		ucred_free(ucp);
7790Sstevel@tonic-gate 	}
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {
7820Sstevel@tonic-gate 		switch (c) {
7830Sstevel@tonic-gate 		case 'n':
7840Sstevel@tonic-gate 			errno = 0;
7850Sstevel@tonic-gate 			g_nent = strtoul(optarg, &end, 10);
7860Sstevel@tonic-gate 			if (*end != '\0' || errno != 0) {
7870Sstevel@tonic-gate 				(void) fprintf(stderr, "%s: invalid count "
7880Sstevel@tonic-gate 				    "'%s'\n", g_pname, optarg);
7890Sstevel@tonic-gate 				usage();
7900Sstevel@tonic-gate 			}
7910Sstevel@tonic-gate 			break;
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 		case 'p':
7940Sstevel@tonic-gate 			opt_p = 1;
7950Sstevel@tonic-gate 			break;
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate 		case 'v':
7980Sstevel@tonic-gate 			opt_v = 1;
7990Sstevel@tonic-gate 			break;
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 		case 'A':
8020Sstevel@tonic-gate 			opt_C = opt_H = 1;
8030Sstevel@tonic-gate 			break;
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 		case 'C':
8060Sstevel@tonic-gate 			opt_C = 1;
8070Sstevel@tonic-gate 			break;
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 		case 'H':
8100Sstevel@tonic-gate 			opt_H = 1;
8110Sstevel@tonic-gate 			break;
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 		case 'V':
8140Sstevel@tonic-gate 			g_opt_V = 1;
8150Sstevel@tonic-gate 			break;
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 		default:
8180Sstevel@tonic-gate 			if (strchr(PLOCKSTAT_OPTSTR, c) == NULL)
8190Sstevel@tonic-gate 				usage();
8200Sstevel@tonic-gate 		}
8210Sstevel@tonic-gate 	}
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	/*
8240Sstevel@tonic-gate 	 * We need a command or at least one pid.
8250Sstevel@tonic-gate 	 */
8260Sstevel@tonic-gate 	if (argc == optind)
8270Sstevel@tonic-gate 		usage();
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	if (opt_C == 0 && opt_H == 0)
8300Sstevel@tonic-gate 		opt_C = 1;
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL)
8330Sstevel@tonic-gate 		fatal("failed to initialize dtrace: %s\n",
8340Sstevel@tonic-gate 		    dtrace_errmsg(NULL, err));
8350Sstevel@tonic-gate 
8361464Sahl 	/*
8371464Sahl 	 * The longest string we trace is 23 bytes long -- so 32 is plenty.
8381464Sahl 	 */
8391464Sahl 	if (dtrace_setopt(g_dtp, "strsize", "32") == -1)
8401464Sahl 		dfatal("failed to set 'strsize'");
8411464Sahl 
8421464Sahl 	/*
8431464Sahl 	 * 1k should be more than enough for all trace() and printa() actions.
8441464Sahl 	 */
8451464Sahl 	if (dtrace_setopt(g_dtp, "bufsize", "1k") == -1)
8461464Sahl 		dfatal("failed to set 'bufsize'");
8471464Sahl 
8481464Sahl 	/*
8491464Sahl 	 * The table we produce has the hottest locks at the top.
8501464Sahl 	 */
8511464Sahl 	if (dtrace_setopt(g_dtp, "aggsortrev", NULL) == -1)
8521464Sahl 		dfatal("failed to set 'aggsortrev'");
8531464Sahl 
8541464Sahl 	/*
8551464Sahl 	 * These are two reasonable defaults which should suffice.
8561464Sahl 	 */
8570Sstevel@tonic-gate 	if (dtrace_setopt(g_dtp, "aggsize", "256k") == -1)
8580Sstevel@tonic-gate 		dfatal("failed to set 'aggsize'");
8590Sstevel@tonic-gate 	if (dtrace_setopt(g_dtp, "aggrate", "1sec") == -1)
8600Sstevel@tonic-gate 		dfatal("failed to set 'aggrate'");
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	/*
8630Sstevel@tonic-gate 	 * Take a second pass through to look for options that set options now
8640Sstevel@tonic-gate 	 * that we have an open dtrace handle.
8650Sstevel@tonic-gate 	 */
8660Sstevel@tonic-gate 	optind = 1;
8670Sstevel@tonic-gate 	while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {
8680Sstevel@tonic-gate 		switch (c) {
8690Sstevel@tonic-gate 		case 's':
8700Sstevel@tonic-gate 			g_opt_s = 1;
8710Sstevel@tonic-gate 			if (dtrace_setopt(g_dtp, "ustackframes", optarg) == -1)
8720Sstevel@tonic-gate 				dfatal("failed to set 'ustackframes'");
8730Sstevel@tonic-gate 			break;
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 		case 'x':
8760Sstevel@tonic-gate 			if ((p = strchr(optarg, '=')) != NULL)
8770Sstevel@tonic-gate 				*p++ = '\0';
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 			if (dtrace_setopt(g_dtp, optarg, p) != 0)
8800Sstevel@tonic-gate 				dfatal("failed to set -x %s", optarg);
8810Sstevel@tonic-gate 			break;
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 		case 'e':
8840Sstevel@tonic-gate 			errno = 0;
8850Sstevel@tonic-gate 			(void) strtoul(optarg, &end, 10);
8860Sstevel@tonic-gate 			if (*optarg == '-' || *end != '\0' || errno != 0) {
8870Sstevel@tonic-gate 				(void) fprintf(stderr, "%s: invalid timeout "
8880Sstevel@tonic-gate 				    "'%s'\n", g_pname, optarg);
8890Sstevel@tonic-gate 				usage();
8900Sstevel@tonic-gate 			}
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 			/*
8930Sstevel@tonic-gate 			 * Construct a DTrace enabling that will exit after
8940Sstevel@tonic-gate 			 * the specified number of seconds.
8950Sstevel@tonic-gate 			 */
8960Sstevel@tonic-gate 			dprog_add("BEGIN\n{\n\tend = timestamp + ");
8970Sstevel@tonic-gate 			dprog_add(optarg);
8980Sstevel@tonic-gate 			dprog_add(" * 1000000000;\n}\n");
8990Sstevel@tonic-gate 			dprog_add("tick-10hz\n/timestamp >= end/\n");
9000Sstevel@tonic-gate 			dprog_add("{\n\texit(0);\n}\n");
9010Sstevel@tonic-gate 			break;
9020Sstevel@tonic-gate 		}
9030Sstevel@tonic-gate 	}
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 	argc -= optind;
9060Sstevel@tonic-gate 	argv += optind;
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 	if (opt_H) {
9090Sstevel@tonic-gate 		dprog_add(g_hold_init);
9100Sstevel@tonic-gate 		if (g_opt_s == NULL)
9110Sstevel@tonic-gate 			dprog_add(g_hold_times);
9120Sstevel@tonic-gate 		else
9130Sstevel@tonic-gate 			dprog_add(g_hold_histogram);
9140Sstevel@tonic-gate 	}
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	if (opt_C) {
9170Sstevel@tonic-gate 		dprog_add(g_ctnd_init);
9180Sstevel@tonic-gate 		if (g_opt_s == NULL)
9190Sstevel@tonic-gate 			dprog_add(g_ctnd_times);
9200Sstevel@tonic-gate 		else
9210Sstevel@tonic-gate 			dprog_add(g_ctnd_histogram);
9220Sstevel@tonic-gate 	}
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	if (opt_p) {
9250Sstevel@tonic-gate 		ulong_t pid;
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 		if (argc > 1) {
9280Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: only one pid is allowed\n",
9290Sstevel@tonic-gate 			    g_pname);
9300Sstevel@tonic-gate 			usage();
9310Sstevel@tonic-gate 		}
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 		errno = 0;
9340Sstevel@tonic-gate 		pid = strtoul(argv[0], &end, 10);
9350Sstevel@tonic-gate 		if (*end != '\0' || errno != 0 || (pid_t)pid != pid) {
9360Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: invalid pid '%s'\n",
9370Sstevel@tonic-gate 			    g_pname, argv[0]);
9380Sstevel@tonic-gate 			usage();
9390Sstevel@tonic-gate 		}
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 		if ((g_pr = dtrace_proc_grab(g_dtp, (pid_t)pid, 0)) == NULL)
9420Sstevel@tonic-gate 			dfatal(NULL);
9430Sstevel@tonic-gate 	} else {
9440Sstevel@tonic-gate 		if ((g_pr = dtrace_proc_create(g_dtp, argv[0], argv)) == NULL)
9450Sstevel@tonic-gate 			dfatal(NULL);
9460Sstevel@tonic-gate 	}
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	dprog_compile();
9490Sstevel@tonic-gate 
9501464Sahl 	if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1)
9511464Sahl 		dfatal("failed to establish proc handler");
9521464Sahl 
9530Sstevel@tonic-gate 	(void) sigemptyset(&act.sa_mask);
9540Sstevel@tonic-gate 	act.sa_flags = 0;
9550Sstevel@tonic-gate 	act.sa_handler = intr;
9560Sstevel@tonic-gate 	(void) sigaction(SIGINT, &act, NULL);
9570Sstevel@tonic-gate 	(void) sigaction(SIGTERM, &act, NULL);
9580Sstevel@tonic-gate 
9590Sstevel@tonic-gate 	if (dtrace_go(g_dtp) != 0)
9600Sstevel@tonic-gate 		dfatal("dtrace_go()");
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 	if (dtrace_getopt(g_dtp, "ustackframes", &g_nframes) != 0)
9630Sstevel@tonic-gate 		dfatal("failed to get 'ustackframes'");
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	dtrace_proc_continue(g_dtp, g_pr);
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate 	if (opt_v)
9680Sstevel@tonic-gate 		(void) printf("%s: tracing enabled for pid %d\n", g_pname,
9690Sstevel@tonic-gate 		    (int)Pstatus(g_pr)->pr_pid);
9700Sstevel@tonic-gate 
9711464Sahl 	do {
9721464Sahl 		if (!g_intr && !done)
9731464Sahl 			dtrace_sleep(g_dtp);
9740Sstevel@tonic-gate 
9751464Sahl 		if (done || g_intr || g_exited) {
9760Sstevel@tonic-gate 			done = 1;
9771464Sahl 			if (dtrace_stop(g_dtp) == -1)
9781464Sahl 				dfatal("couldn't stop tracing");
9791464Sahl 		}
9800Sstevel@tonic-gate 
9811464Sahl 		switch (dtrace_work(g_dtp, stdout, NULL, chewrec, NULL)) {
9821464Sahl 		case DTRACE_WORKSTATUS_DONE:
9831464Sahl 			done = 1;
9841464Sahl 			break;
9851464Sahl 		case DTRACE_WORKSTATUS_OKAY:
9861464Sahl 			break;
9871464Sahl 		default:
9881464Sahl 			dfatal("processing aborted");
9891464Sahl 		}
9900Sstevel@tonic-gate 
9911464Sahl 	} while (!done);
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 	dtrace_close(g_dtp);
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	return (0);
9960Sstevel@tonic-gate }
997