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