xref: /freebsd-src/cddl/contrib/opensolaris/cmd/plockstat/plockstat.c (revision b626f5a73a48f44a31a200291b141e1da408a2ff)
12cd5e41aSRui Paulo /*
22cd5e41aSRui Paulo  * CDDL HEADER START
32cd5e41aSRui Paulo  *
42cd5e41aSRui Paulo  * The contents of this file are subject to the terms of the
52cd5e41aSRui Paulo  * Common Development and Distribution License (the "License").
62cd5e41aSRui Paulo  * You may not use this file except in compliance with the License.
72cd5e41aSRui Paulo  *
82cd5e41aSRui Paulo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92cd5e41aSRui Paulo  * or http://www.opensolaris.org/os/licensing.
102cd5e41aSRui Paulo  * See the License for the specific language governing permissions
112cd5e41aSRui Paulo  * and limitations under the License.
122cd5e41aSRui Paulo  *
132cd5e41aSRui Paulo  * When distributing Covered Code, include this CDDL HEADER in each
142cd5e41aSRui Paulo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152cd5e41aSRui Paulo  * If applicable, add the following below this CDDL HEADER, with the
162cd5e41aSRui Paulo  * fields enclosed by brackets "[]" replaced with your own identifying
172cd5e41aSRui Paulo  * information: Portions Copyright [yyyy] [name of copyright owner]
182cd5e41aSRui Paulo  *
192cd5e41aSRui Paulo  * CDDL HEADER END
202cd5e41aSRui Paulo  */
212cd5e41aSRui Paulo 
222cd5e41aSRui Paulo /*
232cd5e41aSRui Paulo  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
242cd5e41aSRui Paulo  * Use is subject to license terms.
252cd5e41aSRui Paulo  */
262cd5e41aSRui Paulo 
27bc96366cSSteven Hartland #ifdef illumos
282cd5e41aSRui Paulo #pragma ident	"%Z%%M%	%I%	%E% SMI"
296544c919SRui Paulo #endif
302cd5e41aSRui Paulo 
312cd5e41aSRui Paulo #include <assert.h>
322cd5e41aSRui Paulo #include <dtrace.h>
332cd5e41aSRui Paulo #include <limits.h>
342cd5e41aSRui Paulo #include <link.h>
352cd5e41aSRui Paulo #include <priv.h>
362cd5e41aSRui Paulo #include <signal.h>
372cd5e41aSRui Paulo #include <stdlib.h>
382cd5e41aSRui Paulo #include <stdarg.h>
392cd5e41aSRui Paulo #include <stdio.h>
402cd5e41aSRui Paulo #include <string.h>
412cd5e41aSRui Paulo #include <strings.h>
422cd5e41aSRui Paulo #include <errno.h>
432cd5e41aSRui Paulo #include <sys/wait.h>
442cd5e41aSRui Paulo #include <libgen.h>
452cd5e41aSRui Paulo #include <libproc.h>
466544c919SRui Paulo #include <libproc_compat.h>
472cd5e41aSRui Paulo 
482cd5e41aSRui Paulo static char *g_pname;
492cd5e41aSRui Paulo static dtrace_hdl_t *g_dtp;
502cd5e41aSRui Paulo struct ps_prochandle *g_pr;
512cd5e41aSRui Paulo 
522cd5e41aSRui Paulo #define	E_SUCCESS	0
532cd5e41aSRui Paulo #define	E_ERROR		1
542cd5e41aSRui Paulo #define	E_USAGE		2
552cd5e41aSRui Paulo 
562cd5e41aSRui Paulo /*
572cd5e41aSRui Paulo  * For hold times we use a global associative array since for mutexes, in
582cd5e41aSRui Paulo  * user-land, it's not invalid to release a sychonization primitive that
592cd5e41aSRui Paulo  * another thread acquired; rwlocks require a thread-local associative array
602cd5e41aSRui Paulo  * since multiple thread can hold the same lock for reading. Note that we
612cd5e41aSRui Paulo  * ignore recursive mutex acquisitions and releases as they don't truly
622cd5e41aSRui Paulo  * affect lock contention.
632cd5e41aSRui Paulo  */
642cd5e41aSRui Paulo static const char *g_hold_init =
652cd5e41aSRui Paulo "plockstat$target:::rw-acquire\n"
662cd5e41aSRui Paulo "{\n"
672cd5e41aSRui Paulo "	self->rwhold[arg0] = timestamp;\n"
682cd5e41aSRui Paulo "}\n"
692cd5e41aSRui Paulo "plockstat$target:::mutex-acquire\n"
702cd5e41aSRui Paulo "/arg1 == 0/\n"
712cd5e41aSRui Paulo "{\n"
722cd5e41aSRui Paulo "	mtxhold[arg0] = timestamp;\n"
732cd5e41aSRui Paulo "}\n";
742cd5e41aSRui Paulo 
752cd5e41aSRui Paulo static const char *g_hold_histogram =
762cd5e41aSRui Paulo "plockstat$target:::rw-release\n"
772cd5e41aSRui Paulo "/self->rwhold[arg0] && arg1 == 1/\n"
782cd5e41aSRui Paulo "{\n"
792cd5e41aSRui Paulo "	@rw_w_hold[arg0, ustack()] =\n"
802cd5e41aSRui Paulo "	    quantize(timestamp - self->rwhold[arg0]);\n"
812cd5e41aSRui Paulo "	self->rwhold[arg0] = 0;\n"
822cd5e41aSRui Paulo "	rw_w_hold_found = 1;\n"
832cd5e41aSRui Paulo "}\n"
842cd5e41aSRui Paulo "plockstat$target:::rw-release\n"
852cd5e41aSRui Paulo "/self->rwhold[arg0]/\n"
862cd5e41aSRui Paulo "{\n"
872cd5e41aSRui Paulo "	@rw_r_hold[arg0, ustack()] =\n"
882cd5e41aSRui Paulo "	    quantize(timestamp - self->rwhold[arg0]);\n"
892cd5e41aSRui Paulo "	self->rwhold[arg0] = 0;\n"
902cd5e41aSRui Paulo "	rw_r_hold_found = 1;\n"
912cd5e41aSRui Paulo "}\n"
922cd5e41aSRui Paulo "plockstat$target:::mutex-release\n"
932cd5e41aSRui Paulo "/mtxhold[arg0] && arg1 == 0/\n"
942cd5e41aSRui Paulo "{\n"
952cd5e41aSRui Paulo "	@mtx_hold[arg0, ustack()] = quantize(timestamp - mtxhold[arg0]);\n"
962cd5e41aSRui Paulo "	mtxhold[arg0] = 0;\n"
972cd5e41aSRui Paulo "	mtx_hold_found = 1;\n"
982cd5e41aSRui Paulo "}\n"
992cd5e41aSRui Paulo "\n"
1002cd5e41aSRui Paulo "END\n"
1012cd5e41aSRui Paulo "/mtx_hold_found/\n"
1022cd5e41aSRui Paulo "{\n"
1032cd5e41aSRui Paulo "	trace(\"Mutex hold\");\n"
1042cd5e41aSRui Paulo "	printa(@mtx_hold);\n"
1052cd5e41aSRui Paulo "}\n"
1062cd5e41aSRui Paulo "END\n"
1072cd5e41aSRui Paulo "/rw_r_hold_found/\n"
1082cd5e41aSRui Paulo "{\n"
1092cd5e41aSRui Paulo "	trace(\"R/W reader hold\");\n"
1102cd5e41aSRui Paulo "	printa(@rw_r_hold);\n"
1112cd5e41aSRui Paulo "}\n"
1122cd5e41aSRui Paulo "END\n"
1132cd5e41aSRui Paulo "/rw_w_hold_found/\n"
1142cd5e41aSRui Paulo "{\n"
1152cd5e41aSRui Paulo "	trace(\"R/W writer hold\");\n"
1162cd5e41aSRui Paulo "	printa(@rw_w_hold);\n"
1172cd5e41aSRui Paulo "}\n";
1182cd5e41aSRui Paulo 
1192cd5e41aSRui Paulo static const char *g_hold_times =
1202cd5e41aSRui Paulo "plockstat$target:::rw-release\n"
1212cd5e41aSRui Paulo "/self->rwhold[arg0] && arg1 == 1/\n"
1222cd5e41aSRui Paulo "{\n"
1232cd5e41aSRui Paulo "	@rw_w_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"
1242cd5e41aSRui Paulo "	@rw_w_hold_count[arg0, ustack(5)] = count();\n"
1252cd5e41aSRui Paulo "	self->rwhold[arg0] = 0;\n"
1262cd5e41aSRui Paulo "	rw_w_hold_found = 1;\n"
1272cd5e41aSRui Paulo "}\n"
1282cd5e41aSRui Paulo "plockstat$target:::rw-release\n"
1292cd5e41aSRui Paulo "/self->rwhold[arg0]/\n"
1302cd5e41aSRui Paulo "{\n"
1312cd5e41aSRui Paulo "	@rw_r_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"
1322cd5e41aSRui Paulo "	@rw_r_hold_count[arg0, ustack(5)] = count();\n"
1332cd5e41aSRui Paulo "	self->rwhold[arg0] = 0;\n"
1342cd5e41aSRui Paulo "	rw_r_hold_found = 1;\n"
1352cd5e41aSRui Paulo "}\n"
1362cd5e41aSRui Paulo "plockstat$target:::mutex-release\n"
1372cd5e41aSRui Paulo "/mtxhold[arg0] && arg1 == 0/\n"
1382cd5e41aSRui Paulo "{\n"
1392cd5e41aSRui Paulo "	@mtx_hold[arg0, ustack(5)] = sum(timestamp - mtxhold[arg0]);\n"
1402cd5e41aSRui Paulo "	@mtx_hold_count[arg0, ustack(5)] = count();\n"
1412cd5e41aSRui Paulo "	mtxhold[arg0] = 0;\n"
1422cd5e41aSRui Paulo "	mtx_hold_found = 1;\n"
1432cd5e41aSRui Paulo "}\n"
1442cd5e41aSRui Paulo "\n"
1452cd5e41aSRui Paulo "END\n"
1462cd5e41aSRui Paulo "/mtx_hold_found/\n"
1472cd5e41aSRui Paulo "{\n"
1482cd5e41aSRui Paulo "	trace(\"Mutex hold\");\n"
1492cd5e41aSRui Paulo "	printa(@mtx_hold, @mtx_hold_count);\n"
1502cd5e41aSRui Paulo "}\n"
1512cd5e41aSRui Paulo "END\n"
1522cd5e41aSRui Paulo "/rw_r_hold_found/\n"
1532cd5e41aSRui Paulo "{\n"
1542cd5e41aSRui Paulo "	trace(\"R/W reader hold\");\n"
1552cd5e41aSRui Paulo "	printa(@rw_r_hold, @rw_r_hold_count);\n"
1562cd5e41aSRui Paulo "}\n"
1572cd5e41aSRui Paulo "END\n"
1582cd5e41aSRui Paulo "/rw_w_hold_found/\n"
1592cd5e41aSRui Paulo "{\n"
1602cd5e41aSRui Paulo "	trace(\"R/W writer hold\");\n"
1612cd5e41aSRui Paulo "	printa(@rw_w_hold, @rw_w_hold_count);\n"
1622cd5e41aSRui Paulo "}\n";
1632cd5e41aSRui Paulo 
1642cd5e41aSRui Paulo 
1652cd5e41aSRui Paulo /*
1662cd5e41aSRui Paulo  * For contention, we use thread-local associative arrays since we're tracing
1672cd5e41aSRui Paulo  * a single thread's activity in libc and multiple threads can be blocking or
1682cd5e41aSRui Paulo  * spinning on the same sychonization primitive.
1692cd5e41aSRui Paulo  */
1702cd5e41aSRui Paulo static const char *g_ctnd_init =
1712cd5e41aSRui Paulo "plockstat$target:::rw-block\n"
1722cd5e41aSRui Paulo "{\n"
1732cd5e41aSRui Paulo "	self->rwblock[arg0] = timestamp;\n"
1742cd5e41aSRui Paulo "}\n"
1752cd5e41aSRui Paulo "plockstat$target:::mutex-block\n"
1762cd5e41aSRui Paulo "{\n"
1772cd5e41aSRui Paulo "	self->mtxblock[arg0] = timestamp;\n"
1782cd5e41aSRui Paulo "}\n"
1792cd5e41aSRui Paulo "plockstat$target:::mutex-spin\n"
1802cd5e41aSRui Paulo "{\n"
1812cd5e41aSRui Paulo "	self->mtxspin[arg0] = timestamp;\n"
1822cd5e41aSRui Paulo "}\n";
1832cd5e41aSRui Paulo 
1842cd5e41aSRui Paulo static const char *g_ctnd_histogram =
1852cd5e41aSRui Paulo "plockstat$target:::rw-blocked\n"
1862cd5e41aSRui Paulo "/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
1872cd5e41aSRui Paulo "{\n"
1882cd5e41aSRui Paulo "	@rw_w_block[arg0, ustack()] =\n"
1892cd5e41aSRui Paulo "	    quantize(timestamp - self->rwblock[arg0]);\n"
1902cd5e41aSRui Paulo "	self->rwblock[arg0] = 0;\n"
1912cd5e41aSRui Paulo "	rw_w_block_found = 1;\n"
1922cd5e41aSRui Paulo "}\n"
1932cd5e41aSRui Paulo "plockstat$target:::rw-blocked\n"
1942cd5e41aSRui Paulo "/self->rwblock[arg0] && arg2 != 0/\n"
1952cd5e41aSRui Paulo "{\n"
1962cd5e41aSRui Paulo "	@rw_r_block[arg0, ustack()] =\n"
1972cd5e41aSRui Paulo "	    quantize(timestamp - self->rwblock[arg0]);\n"
1982cd5e41aSRui Paulo "	self->rwblock[arg0] = 0;\n"
1992cd5e41aSRui Paulo "	rw_r_block_found = 1;\n"
2002cd5e41aSRui Paulo "}\n"
2012cd5e41aSRui Paulo "plockstat$target:::rw-blocked\n"
2022cd5e41aSRui Paulo "/self->rwblock[arg0]/\n"
2032cd5e41aSRui Paulo "{\n"
2042cd5e41aSRui Paulo "	self->rwblock[arg0] = 0;\n"
2052cd5e41aSRui Paulo "}\n"
2062cd5e41aSRui Paulo "plockstat$target:::mutex-spun\n"
2072cd5e41aSRui Paulo "/self->mtxspin[arg0] && arg1 != 0/\n"
2082cd5e41aSRui Paulo "{\n"
2092cd5e41aSRui Paulo "	@mtx_spin[arg0, ustack()] =\n"
2102cd5e41aSRui Paulo "	    quantize(timestamp - self->mtxspin[arg0]);\n"
2112cd5e41aSRui Paulo "	self->mtxspin[arg0] = 0;\n"
2122cd5e41aSRui Paulo "	mtx_spin_found = 1;\n"
2132cd5e41aSRui Paulo "}\n"
2142cd5e41aSRui Paulo "plockstat$target:::mutex-spun\n"
2152cd5e41aSRui Paulo "/self->mtxspin[arg0]/\n"
2162cd5e41aSRui Paulo "{\n"
2172cd5e41aSRui Paulo "	@mtx_vain_spin[arg0, ustack()] =\n"
2182cd5e41aSRui Paulo "	    quantize(timestamp - self->mtxspin[arg0]);\n"
2192cd5e41aSRui Paulo "	self->mtxspin[arg0] = 0;\n"
2202cd5e41aSRui Paulo "	mtx_vain_spin_found = 1;\n"
2212cd5e41aSRui Paulo "}\n"
2222cd5e41aSRui Paulo "plockstat$target:::mutex-blocked\n"
2232cd5e41aSRui Paulo "/self->mtxblock[arg0] && arg1 != 0/\n"
2242cd5e41aSRui Paulo "{\n"
2252cd5e41aSRui Paulo "	@mtx_block[arg0, ustack()] =\n"
2262cd5e41aSRui Paulo "	    quantize(timestamp - self->mtxblock[arg0]);\n"
2272cd5e41aSRui Paulo "	self->mtxblock[arg0] = 0;\n"
2282cd5e41aSRui Paulo "	mtx_block_found = 1;\n"
2292cd5e41aSRui Paulo "}\n"
2302cd5e41aSRui Paulo "plockstat$target:::mutex-blocked\n"
2312cd5e41aSRui Paulo "/self->mtxblock[arg0]/\n"
2322cd5e41aSRui Paulo "{\n"
2332cd5e41aSRui Paulo "	self->mtxblock[arg0] = 0;\n"
2342cd5e41aSRui Paulo "}\n"
2352cd5e41aSRui Paulo "\n"
2362cd5e41aSRui Paulo "END\n"
2372cd5e41aSRui Paulo "/mtx_block_found/\n"
2382cd5e41aSRui Paulo "{\n"
2392cd5e41aSRui Paulo "	trace(\"Mutex block\");\n"
2402cd5e41aSRui Paulo "	printa(@mtx_block);\n"
2412cd5e41aSRui Paulo "}\n"
2422cd5e41aSRui Paulo "END\n"
2432cd5e41aSRui Paulo "/mtx_spin_found/\n"
2442cd5e41aSRui Paulo "{\n"
2452cd5e41aSRui Paulo "	trace(\"Mutex spin\");\n"
2462cd5e41aSRui Paulo "	printa(@mtx_spin);\n"
2472cd5e41aSRui Paulo "}\n"
2482cd5e41aSRui Paulo "END\n"
2492cd5e41aSRui Paulo "/mtx_vain_spin_found/\n"
2502cd5e41aSRui Paulo "{\n"
2512cd5e41aSRui Paulo "	trace(\"Mutex unsuccessful spin\");\n"
2522cd5e41aSRui Paulo "	printa(@mtx_vain_spin);\n"
2532cd5e41aSRui Paulo "}\n"
2542cd5e41aSRui Paulo "END\n"
2552cd5e41aSRui Paulo "/rw_r_block_found/\n"
2562cd5e41aSRui Paulo "{\n"
2572cd5e41aSRui Paulo "	trace(\"R/W reader block\");\n"
2582cd5e41aSRui Paulo "	printa(@rw_r_block);\n"
2592cd5e41aSRui Paulo "}\n"
2602cd5e41aSRui Paulo "END\n"
2612cd5e41aSRui Paulo "/rw_w_block_found/\n"
2622cd5e41aSRui Paulo "{\n"
2632cd5e41aSRui Paulo "	trace(\"R/W writer block\");\n"
2642cd5e41aSRui Paulo "	printa(@rw_w_block);\n"
2652cd5e41aSRui Paulo "}\n";
2662cd5e41aSRui Paulo 
2672cd5e41aSRui Paulo 
2682cd5e41aSRui Paulo static const char *g_ctnd_times =
2692cd5e41aSRui Paulo "plockstat$target:::rw-blocked\n"
2702cd5e41aSRui Paulo "/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
2712cd5e41aSRui Paulo "{\n"
2722cd5e41aSRui Paulo "	@rw_w_block[arg0, ustack(5)] =\n"
2732cd5e41aSRui Paulo "	    sum(timestamp - self->rwblock[arg0]);\n"
2742cd5e41aSRui Paulo "	@rw_w_block_count[arg0, ustack(5)] = count();\n"
2752cd5e41aSRui Paulo "	self->rwblock[arg0] = 0;\n"
2762cd5e41aSRui Paulo "	rw_w_block_found = 1;\n"
2772cd5e41aSRui Paulo "}\n"
2782cd5e41aSRui Paulo "plockstat$target:::rw-blocked\n"
2792cd5e41aSRui Paulo "/self->rwblock[arg0] && arg2 != 0/\n"
2802cd5e41aSRui Paulo "{\n"
2812cd5e41aSRui Paulo "	@rw_r_block[arg0, ustack(5)] =\n"
2822cd5e41aSRui Paulo "	    sum(timestamp - self->rwblock[arg0]);\n"
2832cd5e41aSRui Paulo "	@rw_r_block_count[arg0, ustack(5)] = count();\n"
2842cd5e41aSRui Paulo "	self->rwblock[arg0] = 0;\n"
2852cd5e41aSRui Paulo "	rw_r_block_found = 1;\n"
2862cd5e41aSRui Paulo "}\n"
2872cd5e41aSRui Paulo "plockstat$target:::rw-blocked\n"
2882cd5e41aSRui Paulo "/self->rwblock[arg0]/\n"
2892cd5e41aSRui Paulo "{\n"
2902cd5e41aSRui Paulo "	self->rwblock[arg0] = 0;\n"
2912cd5e41aSRui Paulo "}\n"
2922cd5e41aSRui Paulo "plockstat$target:::mutex-spun\n"
2932cd5e41aSRui Paulo "/self->mtxspin[arg0] && arg1 != 0/\n"
2942cd5e41aSRui Paulo "{\n"
2952cd5e41aSRui Paulo "	@mtx_spin[arg0, ustack(5)] =\n"
2962cd5e41aSRui Paulo "	    sum(timestamp - self->mtxspin[arg0]);\n"
2972cd5e41aSRui Paulo "	@mtx_spin_count[arg0, ustack(5)] = count();\n"
2982cd5e41aSRui Paulo "	self->mtxspin[arg0] = 0;\n"
2992cd5e41aSRui Paulo "	mtx_spin_found = 1;\n"
3002cd5e41aSRui Paulo "}\n"
3012cd5e41aSRui Paulo "plockstat$target:::mutex-spun\n"
3022cd5e41aSRui Paulo "/self->mtxspin[arg0]/\n"
3032cd5e41aSRui Paulo "{\n"
3042cd5e41aSRui Paulo "	@mtx_vain_spin[arg0, ustack(5)] =\n"
3052cd5e41aSRui Paulo "	    sum(timestamp - self->mtxspin[arg0]);\n"
3062cd5e41aSRui Paulo "	@mtx_vain_spin_count[arg0, ustack(5)] = count();\n"
3072cd5e41aSRui Paulo "	self->mtxspin[arg0] = 0;\n"
3082cd5e41aSRui Paulo "	mtx_vain_spin_found = 1;\n"
3092cd5e41aSRui Paulo "}\n"
3102cd5e41aSRui Paulo "plockstat$target:::mutex-blocked\n"
3112cd5e41aSRui Paulo "/self->mtxblock[arg0] && arg1 != 0/\n"
3122cd5e41aSRui Paulo "{\n"
3132cd5e41aSRui Paulo "	@mtx_block[arg0, ustack(5)] =\n"
3142cd5e41aSRui Paulo "	    sum(timestamp - self->mtxblock[arg0]);\n"
3152cd5e41aSRui Paulo "	@mtx_block_count[arg0, ustack(5)] = count();\n"
3162cd5e41aSRui Paulo "	self->mtxblock[arg0] = 0;\n"
3172cd5e41aSRui Paulo "	mtx_block_found = 1;\n"
3182cd5e41aSRui Paulo "}\n"
3192cd5e41aSRui Paulo "plockstat$target:::mutex-blocked\n"
3202cd5e41aSRui Paulo "/self->mtxblock[arg0]/\n"
3212cd5e41aSRui Paulo "{\n"
3222cd5e41aSRui Paulo "	self->mtxblock[arg0] = 0;\n"
3232cd5e41aSRui Paulo "}\n"
3242cd5e41aSRui Paulo "\n"
3252cd5e41aSRui Paulo "END\n"
3262cd5e41aSRui Paulo "/mtx_block_found/\n"
3272cd5e41aSRui Paulo "{\n"
3282cd5e41aSRui Paulo "	trace(\"Mutex block\");\n"
3292cd5e41aSRui Paulo "	printa(@mtx_block, @mtx_block_count);\n"
3302cd5e41aSRui Paulo "}\n"
3312cd5e41aSRui Paulo "END\n"
3322cd5e41aSRui Paulo "/mtx_spin_found/\n"
3332cd5e41aSRui Paulo "{\n"
3342cd5e41aSRui Paulo "	trace(\"Mutex spin\");\n"
3352cd5e41aSRui Paulo "	printa(@mtx_spin, @mtx_spin_count);\n"
3362cd5e41aSRui Paulo "}\n"
3372cd5e41aSRui Paulo "END\n"
3382cd5e41aSRui Paulo "/mtx_vain_spin_found/\n"
3392cd5e41aSRui Paulo "{\n"
3402cd5e41aSRui Paulo "	trace(\"Mutex unsuccessful spin\");\n"
3412cd5e41aSRui Paulo "	printa(@mtx_vain_spin, @mtx_vain_spin_count);\n"
3422cd5e41aSRui Paulo "}\n"
3432cd5e41aSRui Paulo "END\n"
3442cd5e41aSRui Paulo "/rw_r_block_found/\n"
3452cd5e41aSRui Paulo "{\n"
3462cd5e41aSRui Paulo "	trace(\"R/W reader block\");\n"
3472cd5e41aSRui Paulo "	printa(@rw_r_block, @rw_r_block_count);\n"
3482cd5e41aSRui Paulo "}\n"
3492cd5e41aSRui Paulo "END\n"
3502cd5e41aSRui Paulo "/rw_w_block_found/\n"
3512cd5e41aSRui Paulo "{\n"
3522cd5e41aSRui Paulo "	trace(\"R/W writer block\");\n"
3532cd5e41aSRui Paulo "	printa(@rw_w_block, @rw_w_block_count);\n"
3542cd5e41aSRui Paulo "}\n";
3552cd5e41aSRui Paulo 
3562cd5e41aSRui Paulo static char g_prog[4096];
3572cd5e41aSRui Paulo static size_t g_proglen;
3582cd5e41aSRui Paulo static int g_opt_V, g_opt_s;
3592cd5e41aSRui Paulo static int g_intr;
3602cd5e41aSRui Paulo static int g_exited;
3612cd5e41aSRui Paulo static dtrace_optval_t g_nframes;
3622cd5e41aSRui Paulo static ulong_t g_nent = ULONG_MAX;
3632cd5e41aSRui Paulo 
3642cd5e41aSRui Paulo #define	PLOCKSTAT_OPTSTR	"n:ps:e:vx:ACHV"
3652cd5e41aSRui Paulo 
3662cd5e41aSRui Paulo static void
usage(void)3672cd5e41aSRui Paulo usage(void)
3682cd5e41aSRui Paulo {
3692cd5e41aSRui Paulo 	(void) fprintf(stderr, "Usage:\n"
3702cd5e41aSRui Paulo 	    "\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
3712cd5e41aSRui Paulo 	    "\t    command [arg...]\n"
3722cd5e41aSRui Paulo 	    "\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
3732cd5e41aSRui Paulo 	    "\t    -p pid\n", g_pname, g_pname);
3742cd5e41aSRui Paulo 
3752cd5e41aSRui Paulo 	exit(E_USAGE);
3762cd5e41aSRui Paulo }
3772cd5e41aSRui Paulo 
3782cd5e41aSRui Paulo static void
verror(const char * fmt,va_list ap)3792cd5e41aSRui Paulo verror(const char *fmt, va_list ap)
3802cd5e41aSRui Paulo {
3812cd5e41aSRui Paulo 	int error = errno;
3822cd5e41aSRui Paulo 
3832cd5e41aSRui Paulo 	(void) fprintf(stderr, "%s: ", g_pname);
3842cd5e41aSRui Paulo 	(void) vfprintf(stderr, fmt, ap);
3852cd5e41aSRui Paulo 
3862cd5e41aSRui Paulo 	if (fmt[strlen(fmt) - 1] != '\n')
3872cd5e41aSRui Paulo 		(void) fprintf(stderr, ": %s\n", strerror(error));
3882cd5e41aSRui Paulo }
3892cd5e41aSRui Paulo 
3902cd5e41aSRui Paulo /*PRINTFLIKE1*/
3912cd5e41aSRui Paulo static void
fatal(const char * fmt,...)3922cd5e41aSRui Paulo fatal(const char *fmt, ...)
3932cd5e41aSRui Paulo {
3942cd5e41aSRui Paulo 	va_list ap;
3952cd5e41aSRui Paulo 
3962cd5e41aSRui Paulo 	va_start(ap, fmt);
3972cd5e41aSRui Paulo 	verror(fmt, ap);
3982cd5e41aSRui Paulo 	va_end(ap);
3992cd5e41aSRui Paulo 
4002cd5e41aSRui Paulo 	if (g_pr != NULL && g_dtp != NULL)
4012cd5e41aSRui Paulo 		dtrace_proc_release(g_dtp, g_pr);
4022cd5e41aSRui Paulo 
4032cd5e41aSRui Paulo 	exit(E_ERROR);
4042cd5e41aSRui Paulo }
4052cd5e41aSRui Paulo 
4062cd5e41aSRui Paulo /*PRINTFLIKE1*/
4072cd5e41aSRui Paulo static void
dfatal(const char * fmt,...)4082cd5e41aSRui Paulo dfatal(const char *fmt, ...)
4092cd5e41aSRui Paulo {
4102cd5e41aSRui Paulo 	va_list ap;
4112cd5e41aSRui Paulo 
4122cd5e41aSRui Paulo 	va_start(ap, fmt);
4132cd5e41aSRui Paulo 
4142cd5e41aSRui Paulo 	(void) fprintf(stderr, "%s: ", g_pname);
4152cd5e41aSRui Paulo 	if (fmt != NULL)
4162cd5e41aSRui Paulo 		(void) vfprintf(stderr, fmt, ap);
4172cd5e41aSRui Paulo 
4182cd5e41aSRui Paulo 	va_end(ap);
4192cd5e41aSRui Paulo 
4202cd5e41aSRui Paulo 	if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') {
4212cd5e41aSRui Paulo 		(void) fprintf(stderr, ": %s\n",
4222cd5e41aSRui Paulo 		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
4232cd5e41aSRui Paulo 	} else if (fmt == NULL) {
4242cd5e41aSRui Paulo 		(void) fprintf(stderr, "%s\n",
4252cd5e41aSRui Paulo 		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
4262cd5e41aSRui Paulo 	}
4272cd5e41aSRui Paulo 
4282cd5e41aSRui Paulo 	if (g_pr != NULL) {
4292cd5e41aSRui Paulo 		dtrace_proc_continue(g_dtp, g_pr);
4302cd5e41aSRui Paulo 		dtrace_proc_release(g_dtp, g_pr);
4312cd5e41aSRui Paulo 	}
4322cd5e41aSRui Paulo 
4332cd5e41aSRui Paulo 	exit(E_ERROR);
4342cd5e41aSRui Paulo }
4352cd5e41aSRui Paulo 
4362cd5e41aSRui Paulo /*PRINTFLIKE1*/
4372cd5e41aSRui Paulo static void
notice(const char * fmt,...)4382cd5e41aSRui Paulo notice(const char *fmt, ...)
4392cd5e41aSRui Paulo {
4402cd5e41aSRui Paulo 	va_list ap;
4412cd5e41aSRui Paulo 
4422cd5e41aSRui Paulo 	va_start(ap, fmt);
4432cd5e41aSRui Paulo 	verror(fmt, ap);
4442cd5e41aSRui Paulo 	va_end(ap);
4452cd5e41aSRui Paulo }
4462cd5e41aSRui Paulo 
4472cd5e41aSRui Paulo static void
dprog_add(const char * prog)4482cd5e41aSRui Paulo dprog_add(const char *prog)
4492cd5e41aSRui Paulo {
4502cd5e41aSRui Paulo 	size_t len = strlen(prog);
4512cd5e41aSRui Paulo 	bcopy(prog, g_prog + g_proglen, len + 1);
4522cd5e41aSRui Paulo 	g_proglen += len;
4532cd5e41aSRui Paulo 	assert(g_proglen < sizeof (g_prog));
4542cd5e41aSRui Paulo }
4552cd5e41aSRui Paulo 
4562cd5e41aSRui Paulo static void
dprog_compile(void)4572cd5e41aSRui Paulo dprog_compile(void)
4582cd5e41aSRui Paulo {
4592cd5e41aSRui Paulo 	dtrace_prog_t *prog;
4602cd5e41aSRui Paulo 	dtrace_proginfo_t info;
4612cd5e41aSRui Paulo 
4622cd5e41aSRui Paulo 	if (g_opt_V) {
4632cd5e41aSRui Paulo 		(void) fprintf(stderr, "%s: vvvv D program vvvv\n", g_pname);
4642cd5e41aSRui Paulo 		(void) fputs(g_prog, stderr);
4652cd5e41aSRui Paulo 		(void) fprintf(stderr, "%s: ^^^^ D program ^^^^\n", g_pname);
4662cd5e41aSRui Paulo 	}
4672cd5e41aSRui Paulo 
4682cd5e41aSRui Paulo 	if ((prog = dtrace_program_strcompile(g_dtp, g_prog,
4692cd5e41aSRui Paulo 	    DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL)
4702cd5e41aSRui Paulo 		dfatal("failed to compile program");
4712cd5e41aSRui Paulo 
4722cd5e41aSRui Paulo 	if (dtrace_program_exec(g_dtp, prog, &info) == -1)
4732cd5e41aSRui Paulo 		dfatal("failed to enable probes");
4742cd5e41aSRui Paulo }
4752cd5e41aSRui Paulo 
4762cd5e41aSRui Paulo void
print_legend(void)4772cd5e41aSRui Paulo print_legend(void)
4782cd5e41aSRui Paulo {
4792cd5e41aSRui Paulo 	(void) printf("%5s %8s %-28s %s\n", "Count", "nsec", "Lock", "Caller");
4802cd5e41aSRui Paulo }
4812cd5e41aSRui Paulo 
4822cd5e41aSRui Paulo void
print_bar(void)4832cd5e41aSRui Paulo print_bar(void)
4842cd5e41aSRui Paulo {
4852cd5e41aSRui Paulo 	(void) printf("---------------------------------------"
4862cd5e41aSRui Paulo 	    "----------------------------------------\n");
4872cd5e41aSRui Paulo }
4882cd5e41aSRui Paulo 
4892cd5e41aSRui Paulo void
print_histogram_header(void)4902cd5e41aSRui Paulo print_histogram_header(void)
4912cd5e41aSRui Paulo {
4922cd5e41aSRui Paulo 	(void) printf("\n%10s ---- Time Distribution --- %5s %s\n",
4932cd5e41aSRui Paulo 	    "nsec", "count", "Stack");
4942cd5e41aSRui Paulo }
4952cd5e41aSRui Paulo 
4962cd5e41aSRui Paulo /*
4972cd5e41aSRui Paulo  * Convert an address to a symbolic string or a numeric string. If nolocks
4982cd5e41aSRui Paulo  * is set, we return an error code if this symbol appears to be a mutex- or
4992cd5e41aSRui Paulo  * rwlock-related symbol in libc so the caller has a chance to find a more
5002cd5e41aSRui Paulo  * helpful symbol.
5012cd5e41aSRui Paulo  */
5022cd5e41aSRui Paulo static int
getsym(struct ps_prochandle * P,uintptr_t addr,char * buf,size_t size,int nolocks)5032cd5e41aSRui Paulo getsym(struct ps_prochandle *P, uintptr_t addr, char *buf, size_t size,
5042cd5e41aSRui Paulo     int nolocks)
5052cd5e41aSRui Paulo {
5062cd5e41aSRui Paulo 	char name[256];
5072cd5e41aSRui Paulo 	GElf_Sym sym;
508bc96366cSSteven Hartland #ifdef illumos
5092cd5e41aSRui Paulo 	prsyminfo_t info;
5106544c919SRui Paulo #else
5116544c919SRui Paulo 	prmap_t *map;
5126544c919SRui Paulo 	int info; /* XXX unused */
5136544c919SRui Paulo #endif
5142cd5e41aSRui Paulo 	size_t len;
5152cd5e41aSRui Paulo 
5162cd5e41aSRui Paulo 	if (P == NULL || Pxlookup_by_addr(P, addr, name, sizeof (name),
5172cd5e41aSRui Paulo 	    &sym, &info) != 0) {
518*e3c6864eSBryan Drewery 		(void) snprintf(buf, size, "%#lx", (unsigned long)addr);
5192cd5e41aSRui Paulo 		return (0);
5202cd5e41aSRui Paulo 	}
521bc96366cSSteven Hartland #ifdef illumos
5222cd5e41aSRui Paulo 	if (info.prs_object == NULL)
5232cd5e41aSRui Paulo 		info.prs_object = "<unknown>";
5242cd5e41aSRui Paulo 
5252cd5e41aSRui Paulo 	if (info.prs_lmid != LM_ID_BASE) {
5262cd5e41aSRui Paulo 		len = snprintf(buf, size, "LM%lu`", info.prs_lmid);
5272cd5e41aSRui Paulo 		buf += len;
5282cd5e41aSRui Paulo 		size -= len;
5292cd5e41aSRui Paulo 	}
5302cd5e41aSRui Paulo 
5312cd5e41aSRui Paulo 	len = snprintf(buf, size, "%s`%s", info.prs_object, info.prs_name);
5326544c919SRui Paulo #else
5336544c919SRui Paulo 	map = proc_addr2map(P, addr);
5346544c919SRui Paulo 	len = snprintf(buf, size, "%s`%s", map->pr_mapname, name);
5356544c919SRui Paulo #endif
5362cd5e41aSRui Paulo 	buf += len;
5372cd5e41aSRui Paulo 	size -= len;
5382cd5e41aSRui Paulo 
5392cd5e41aSRui Paulo 	if (sym.st_value != addr)
540*e3c6864eSBryan Drewery 		len = snprintf(buf, size, "+%#lx", (unsigned long)(addr - sym.st_value));
5412cd5e41aSRui Paulo 
5426544c919SRui Paulo 	if (nolocks && strcmp("libc.so.1", map->pr_mapname) == 0 &&
5436544c919SRui Paulo 	    (strstr("mutex", name) == 0 ||
5446544c919SRui Paulo 	    strstr("rw", name) == 0))
5452cd5e41aSRui Paulo 		return (-1);
5462cd5e41aSRui Paulo 
5472cd5e41aSRui Paulo 	return (0);
5482cd5e41aSRui Paulo }
5492cd5e41aSRui Paulo 
5502cd5e41aSRui Paulo /*ARGSUSED*/
5512cd5e41aSRui Paulo static int
process_aggregate(const dtrace_aggdata_t ** aggsdata,int naggvars,void * arg)5522cd5e41aSRui Paulo process_aggregate(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
5532cd5e41aSRui Paulo {
5542cd5e41aSRui Paulo 	const dtrace_recdesc_t *rec;
5552cd5e41aSRui Paulo 	uintptr_t lock;
5562cd5e41aSRui Paulo 	uint64_t *stack;
5572cd5e41aSRui Paulo 	caddr_t data;
5582cd5e41aSRui Paulo 	pid_t pid;
5592cd5e41aSRui Paulo 	struct ps_prochandle *P;
5602cd5e41aSRui Paulo 	char buf[256];
5612cd5e41aSRui Paulo 	int i, j;
5622cd5e41aSRui Paulo 	uint64_t sum, count, avg;
5632cd5e41aSRui Paulo 
5642cd5e41aSRui Paulo 	if ((*(uint_t *)arg)++ >= g_nent)
5652cd5e41aSRui Paulo 		return (DTRACE_AGGWALK_NEXT);
5662cd5e41aSRui Paulo 
5672cd5e41aSRui Paulo 	rec = aggsdata[0]->dtada_desc->dtagd_rec;
5682cd5e41aSRui Paulo 	data = aggsdata[0]->dtada_data;
5692cd5e41aSRui Paulo 
5702cd5e41aSRui Paulo 	/*LINTED - alignment*/
5712cd5e41aSRui Paulo 	lock = (uintptr_t)*(uint64_t *)(data + rec[1].dtrd_offset);
5722cd5e41aSRui Paulo 	/*LINTED - alignment*/
5732cd5e41aSRui Paulo 	stack = (uint64_t *)(data + rec[2].dtrd_offset);
5742cd5e41aSRui Paulo 
5752cd5e41aSRui Paulo 	if (!g_opt_s) {
5762cd5e41aSRui Paulo 		/*LINTED - alignment*/
5772cd5e41aSRui Paulo 		sum = *(uint64_t *)(aggsdata[1]->dtada_data +
5782cd5e41aSRui Paulo 		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
5792cd5e41aSRui Paulo 		/*LINTED - alignment*/
5802cd5e41aSRui Paulo 		count = *(uint64_t *)(aggsdata[2]->dtada_data +
5812cd5e41aSRui Paulo 		    aggsdata[2]->dtada_desc->dtagd_rec[3].dtrd_offset);
5822cd5e41aSRui Paulo 	} else {
5832cd5e41aSRui Paulo 		uint64_t *a;
5842cd5e41aSRui Paulo 
5852cd5e41aSRui Paulo 		/*LINTED - alignment*/
5862cd5e41aSRui Paulo 		a = (uint64_t *)(aggsdata[1]->dtada_data +
5872cd5e41aSRui Paulo 		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
5882cd5e41aSRui Paulo 
5892cd5e41aSRui Paulo 		print_bar();
5902cd5e41aSRui Paulo 		print_legend();
5912cd5e41aSRui Paulo 
5922cd5e41aSRui Paulo 		for (count = sum = 0, i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0;
5932cd5e41aSRui Paulo 		    i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) {
5942cd5e41aSRui Paulo 			count += a[i];
5952cd5e41aSRui Paulo 			sum += a[i] << (j - 64);
5962cd5e41aSRui Paulo 		}
5972cd5e41aSRui Paulo 	}
5982cd5e41aSRui Paulo 
5992cd5e41aSRui Paulo 	avg = sum / count;
6002cd5e41aSRui Paulo 	(void) printf("%5llu %8llu ", (u_longlong_t)count, (u_longlong_t)avg);
6012cd5e41aSRui Paulo 
6022cd5e41aSRui Paulo 	pid = stack[0];
6032cd5e41aSRui Paulo 	P = dtrace_proc_grab(g_dtp, pid, PGRAB_RDONLY);
6042cd5e41aSRui Paulo 
6052cd5e41aSRui Paulo 	(void) getsym(P, lock, buf, sizeof (buf), 0);
6062cd5e41aSRui Paulo 	(void) printf("%-28s ", buf);
6072cd5e41aSRui Paulo 
6082cd5e41aSRui Paulo 	for (i = 2; i <= 5; i++) {
6092cd5e41aSRui Paulo 		if (getsym(P, stack[i], buf, sizeof (buf), 1) == 0)
6102cd5e41aSRui Paulo 			break;
6112cd5e41aSRui Paulo 	}
6122cd5e41aSRui Paulo 	(void) printf("%s\n", buf);
6132cd5e41aSRui Paulo 
6142cd5e41aSRui Paulo 	if (g_opt_s) {
6152cd5e41aSRui Paulo 		int stack_done = 0;
6162cd5e41aSRui Paulo 		int quant_done = 0;
6172cd5e41aSRui Paulo 		int first_bin, last_bin;
6182cd5e41aSRui Paulo 		uint64_t bin_size, *a;
6192cd5e41aSRui Paulo 
6202cd5e41aSRui Paulo 		/*LINTED - alignment*/
6212cd5e41aSRui Paulo 		a = (uint64_t *)(aggsdata[1]->dtada_data +
6222cd5e41aSRui Paulo 		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
6232cd5e41aSRui Paulo 
6242cd5e41aSRui Paulo 		print_histogram_header();
6252cd5e41aSRui Paulo 
6262cd5e41aSRui Paulo 		for (first_bin = DTRACE_QUANTIZE_ZEROBUCKET;
6272cd5e41aSRui Paulo 		    a[first_bin] == 0; first_bin++)
6282cd5e41aSRui Paulo 			continue;
6292cd5e41aSRui Paulo 		for (last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 63;
6302cd5e41aSRui Paulo 		    a[last_bin] == 0; last_bin--)
6312cd5e41aSRui Paulo 			continue;
6322cd5e41aSRui Paulo 
6332cd5e41aSRui Paulo 		for (i = 0; !stack_done || !quant_done; i++) {
6342cd5e41aSRui Paulo 			if (!stack_done) {
6352cd5e41aSRui Paulo 				(void) getsym(P, stack[i + 2], buf,
6362cd5e41aSRui Paulo 				    sizeof (buf), 0);
6372cd5e41aSRui Paulo 			} else {
6382cd5e41aSRui Paulo 				buf[0] = '\0';
6392cd5e41aSRui Paulo 			}
6402cd5e41aSRui Paulo 
6412cd5e41aSRui Paulo 			if (!quant_done) {
6422cd5e41aSRui Paulo 				bin_size = a[first_bin];
6432cd5e41aSRui Paulo 
6442cd5e41aSRui Paulo 				(void) printf("%10llu |%-24.*s| %5llu %s\n",
6452cd5e41aSRui Paulo 				    1ULL <<
6462cd5e41aSRui Paulo 				    (first_bin - DTRACE_QUANTIZE_ZEROBUCKET),
6472cd5e41aSRui Paulo 				    (int)(24.0 * bin_size / count),
6482cd5e41aSRui Paulo 				    "@@@@@@@@@@@@@@@@@@@@@@@@@@",
6492cd5e41aSRui Paulo 				    (u_longlong_t)bin_size, buf);
6502cd5e41aSRui Paulo 			} else {
6512cd5e41aSRui Paulo 				(void) printf("%43s %s\n", "", buf);
6522cd5e41aSRui Paulo 			}
6532cd5e41aSRui Paulo 
6542cd5e41aSRui Paulo 			if (i + 1 >= g_nframes || stack[i + 3] == 0)
6552cd5e41aSRui Paulo 				stack_done = 1;
6562cd5e41aSRui Paulo 
6572cd5e41aSRui Paulo 			if (first_bin++ == last_bin)
6582cd5e41aSRui Paulo 				quant_done = 1;
6592cd5e41aSRui Paulo 		}
6602cd5e41aSRui Paulo 	}
6612cd5e41aSRui Paulo 
6622cd5e41aSRui Paulo 	dtrace_proc_release(g_dtp, P);
6632cd5e41aSRui Paulo 
6642cd5e41aSRui Paulo 	return (DTRACE_AGGWALK_NEXT);
6652cd5e41aSRui Paulo }
6662cd5e41aSRui Paulo 
6672cd5e41aSRui Paulo /*ARGSUSED*/
6682cd5e41aSRui Paulo static void
prochandler(struct ps_prochandle * P,const char * msg,void * arg)6692cd5e41aSRui Paulo prochandler(struct ps_prochandle *P, const char *msg, void *arg)
6702cd5e41aSRui Paulo {
671bc96366cSSteven Hartland #ifdef illumos
6722cd5e41aSRui Paulo 	const psinfo_t *prp = Ppsinfo(P);
6732cd5e41aSRui Paulo 	int pid = Pstatus(P)->pr_pid;
6746544c919SRui Paulo #else
6756544c919SRui Paulo 	int pid = proc_getpid(P);
6766544c919SRui Paulo 	int wstat = proc_getwstat(P);
6776544c919SRui Paulo #endif
6782cd5e41aSRui Paulo 	char name[SIG2STR_MAX];
6792cd5e41aSRui Paulo 
6802cd5e41aSRui Paulo 	if (msg != NULL) {
6812cd5e41aSRui Paulo 		notice("pid %d: %s\n", pid, msg);
6822cd5e41aSRui Paulo 		return;
6832cd5e41aSRui Paulo 	}
6842cd5e41aSRui Paulo 
6852cd5e41aSRui Paulo 	switch (Pstate(P)) {
6862cd5e41aSRui Paulo 	case PS_UNDEAD:
6872cd5e41aSRui Paulo 		/*
6882cd5e41aSRui Paulo 		 * Ideally we would like to always report pr_wstat here, but it
6892cd5e41aSRui Paulo 		 * isn't possible given current /proc semantics.  If we grabbed
6902cd5e41aSRui Paulo 		 * the process, Ppsinfo() will either fail or return a zeroed
6912cd5e41aSRui Paulo 		 * psinfo_t depending on how far the parent is in reaping it.
6922cd5e41aSRui Paulo 		 * When /proc provides a stable pr_wstat in the status file,
6932cd5e41aSRui Paulo 		 * this code can be improved by examining this new pr_wstat.
6942cd5e41aSRui Paulo 		 */
6956544c919SRui Paulo 		if (WIFSIGNALED(wstat)) {
6962cd5e41aSRui Paulo 			notice("pid %d terminated by %s\n", pid,
6976544c919SRui Paulo 			    proc_signame(WTERMSIG(wstat),
6982cd5e41aSRui Paulo 			    name, sizeof (name)));
6996544c919SRui Paulo 		} else if (WEXITSTATUS(wstat) != 0) {
7002cd5e41aSRui Paulo 			notice("pid %d exited with status %d\n",
7016544c919SRui Paulo 			    pid, WEXITSTATUS(wstat));
7022cd5e41aSRui Paulo 		} else {
7032cd5e41aSRui Paulo 			notice("pid %d has exited\n", pid);
7042cd5e41aSRui Paulo 		}
7052cd5e41aSRui Paulo 		g_exited = 1;
7062cd5e41aSRui Paulo 		break;
7072cd5e41aSRui Paulo 
7082cd5e41aSRui Paulo 	case PS_LOST:
7092cd5e41aSRui Paulo 		notice("pid %d exec'd a set-id or unobservable program\n", pid);
7102cd5e41aSRui Paulo 		g_exited = 1;
7112cd5e41aSRui Paulo 		break;
7122cd5e41aSRui Paulo 	}
7132cd5e41aSRui Paulo }
7142cd5e41aSRui Paulo 
7152cd5e41aSRui Paulo /*ARGSUSED*/
7162cd5e41aSRui Paulo static int
chewrec(const dtrace_probedata_t * data,const dtrace_recdesc_t * rec,void * arg)7172cd5e41aSRui Paulo chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg)
7182cd5e41aSRui Paulo {
7192cd5e41aSRui Paulo 	dtrace_eprobedesc_t *epd = data->dtpda_edesc;
7202cd5e41aSRui Paulo 	dtrace_aggvarid_t aggvars[2];
7212cd5e41aSRui Paulo 	const void *buf;
7222cd5e41aSRui Paulo 	int i, nagv;
7232cd5e41aSRui Paulo 
7242cd5e41aSRui Paulo 	/*
7252cd5e41aSRui Paulo 	 * A NULL rec indicates that we've processed the last record.
7262cd5e41aSRui Paulo 	 */
7272cd5e41aSRui Paulo 	if (rec == NULL)
7282cd5e41aSRui Paulo 		return (DTRACE_CONSUME_NEXT);
7292cd5e41aSRui Paulo 
7302cd5e41aSRui Paulo 	buf = data->dtpda_data - rec->dtrd_offset;
7312cd5e41aSRui Paulo 
7322cd5e41aSRui Paulo 	switch (rec->dtrd_action) {
7332cd5e41aSRui Paulo 	case DTRACEACT_DIFEXPR:
7342cd5e41aSRui Paulo 		(void) printf("\n%s\n\n", (char *)buf + rec->dtrd_offset);
7352cd5e41aSRui Paulo 		if (!g_opt_s) {
7362cd5e41aSRui Paulo 			print_legend();
7372cd5e41aSRui Paulo 			print_bar();
7382cd5e41aSRui Paulo 		}
7392cd5e41aSRui Paulo 		return (DTRACE_CONSUME_NEXT);
7402cd5e41aSRui Paulo 
7412cd5e41aSRui Paulo 	case DTRACEACT_PRINTA:
7422cd5e41aSRui Paulo 		for (nagv = 0, i = 0; i < epd->dtepd_nrecs - 1; i++) {
7432cd5e41aSRui Paulo 			const dtrace_recdesc_t *nrec = &rec[i];
7442cd5e41aSRui Paulo 
7452cd5e41aSRui Paulo 			if (nrec->dtrd_uarg != rec->dtrd_uarg)
7462cd5e41aSRui Paulo 				break;
7472cd5e41aSRui Paulo 
7482cd5e41aSRui Paulo 			/*LINTED - alignment*/
7492cd5e41aSRui Paulo 			aggvars[nagv++] = *(dtrace_aggvarid_t *)((caddr_t)buf +
7502cd5e41aSRui Paulo 			    nrec->dtrd_offset);
7512cd5e41aSRui Paulo 		}
7522cd5e41aSRui Paulo 
7532cd5e41aSRui Paulo 		if (nagv == (g_opt_s ? 1 : 2)) {
7542cd5e41aSRui Paulo 			uint_t nent = 0;
7552cd5e41aSRui Paulo 			if (dtrace_aggregate_walk_joined(g_dtp, aggvars, nagv,
7562cd5e41aSRui Paulo 			    process_aggregate, &nent) != 0)
7572cd5e41aSRui Paulo 				dfatal("failed to walk aggregate");
7582cd5e41aSRui Paulo 		}
7592cd5e41aSRui Paulo 
7602cd5e41aSRui Paulo 		return (DTRACE_CONSUME_NEXT);
7612cd5e41aSRui Paulo 	}
7622cd5e41aSRui Paulo 
7632cd5e41aSRui Paulo 	return (DTRACE_CONSUME_THIS);
7642cd5e41aSRui Paulo }
7652cd5e41aSRui Paulo 
7662cd5e41aSRui Paulo /*ARGSUSED*/
7672cd5e41aSRui Paulo static void
intr(int signo)7682cd5e41aSRui Paulo intr(int signo)
7692cd5e41aSRui Paulo {
7702cd5e41aSRui Paulo 	g_intr = 1;
7712cd5e41aSRui Paulo }
7722cd5e41aSRui Paulo 
7732cd5e41aSRui Paulo int
main(int argc,char ** argv)7742cd5e41aSRui Paulo main(int argc, char **argv)
7752cd5e41aSRui Paulo {
776bc96366cSSteven Hartland #ifdef illumos
7772cd5e41aSRui Paulo 	ucred_t *ucp;
7786544c919SRui Paulo #endif
7792cd5e41aSRui Paulo 	int err;
7802cd5e41aSRui Paulo 	int opt_C = 0, opt_H = 0, opt_p = 0, opt_v = 0;
78159d74a35SJustin Hibbits 	int c;
78259d74a35SJustin Hibbits 	char *p, *end;
7832cd5e41aSRui Paulo 	struct sigaction act;
7842cd5e41aSRui Paulo 	int done = 0;
7852cd5e41aSRui Paulo 
7862cd5e41aSRui Paulo 	g_pname = basename(argv[0]);
7872cd5e41aSRui Paulo 	argv[0] = g_pname; /* rewrite argv[0] for getopt errors */
788bc96366cSSteven Hartland #ifdef illumos
7892cd5e41aSRui Paulo 	/*
7902cd5e41aSRui Paulo 	 * Make sure we have the required dtrace_proc privilege.
7912cd5e41aSRui Paulo 	 */
7922cd5e41aSRui Paulo 	if ((ucp = ucred_get(getpid())) != NULL) {
7932cd5e41aSRui Paulo 		const priv_set_t *psp;
7942cd5e41aSRui Paulo 		if ((psp = ucred_getprivset(ucp, PRIV_EFFECTIVE)) != NULL &&
7952cd5e41aSRui Paulo 		    !priv_ismember(psp, PRIV_DTRACE_PROC)) {
7962cd5e41aSRui Paulo 			fatal("dtrace_proc privilege required\n");
7972cd5e41aSRui Paulo 		}
7982cd5e41aSRui Paulo 
7992cd5e41aSRui Paulo 		ucred_free(ucp);
8002cd5e41aSRui Paulo 	}
8016544c919SRui Paulo #endif
8022cd5e41aSRui Paulo 
8032cd5e41aSRui Paulo 	while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {
8042cd5e41aSRui Paulo 		switch (c) {
8052cd5e41aSRui Paulo 		case 'n':
8062cd5e41aSRui Paulo 			errno = 0;
8072cd5e41aSRui Paulo 			g_nent = strtoul(optarg, &end, 10);
8082cd5e41aSRui Paulo 			if (*end != '\0' || errno != 0) {
8092cd5e41aSRui Paulo 				(void) fprintf(stderr, "%s: invalid count "
8102cd5e41aSRui Paulo 				    "'%s'\n", g_pname, optarg);
8112cd5e41aSRui Paulo 				usage();
8122cd5e41aSRui Paulo 			}
8132cd5e41aSRui Paulo 			break;
8142cd5e41aSRui Paulo 
8152cd5e41aSRui Paulo 		case 'p':
8162cd5e41aSRui Paulo 			opt_p = 1;
8172cd5e41aSRui Paulo 			break;
8182cd5e41aSRui Paulo 
8192cd5e41aSRui Paulo 		case 'v':
8202cd5e41aSRui Paulo 			opt_v = 1;
8212cd5e41aSRui Paulo 			break;
8222cd5e41aSRui Paulo 
8232cd5e41aSRui Paulo 		case 'A':
8242cd5e41aSRui Paulo 			opt_C = opt_H = 1;
8252cd5e41aSRui Paulo 			break;
8262cd5e41aSRui Paulo 
8272cd5e41aSRui Paulo 		case 'C':
8282cd5e41aSRui Paulo 			opt_C = 1;
8292cd5e41aSRui Paulo 			break;
8302cd5e41aSRui Paulo 
8312cd5e41aSRui Paulo 		case 'H':
8322cd5e41aSRui Paulo 			opt_H = 1;
8332cd5e41aSRui Paulo 			break;
8342cd5e41aSRui Paulo 
8352cd5e41aSRui Paulo 		case 'V':
8362cd5e41aSRui Paulo 			g_opt_V = 1;
8372cd5e41aSRui Paulo 			break;
8382cd5e41aSRui Paulo 
8392cd5e41aSRui Paulo 		default:
8402cd5e41aSRui Paulo 			if (strchr(PLOCKSTAT_OPTSTR, c) == NULL)
8412cd5e41aSRui Paulo 				usage();
8422cd5e41aSRui Paulo 		}
8432cd5e41aSRui Paulo 	}
8442cd5e41aSRui Paulo 
8452cd5e41aSRui Paulo 	/*
8462cd5e41aSRui Paulo 	 * We need a command or at least one pid.
8472cd5e41aSRui Paulo 	 */
8482cd5e41aSRui Paulo 	if (argc == optind)
8492cd5e41aSRui Paulo 		usage();
8502cd5e41aSRui Paulo 
8512cd5e41aSRui Paulo 	if (opt_C == 0 && opt_H == 0)
8522cd5e41aSRui Paulo 		opt_C = 1;
8532cd5e41aSRui Paulo 
8542cd5e41aSRui Paulo 	if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL)
8552cd5e41aSRui Paulo 		fatal("failed to initialize dtrace: %s\n",
8562cd5e41aSRui Paulo 		    dtrace_errmsg(NULL, err));
8572cd5e41aSRui Paulo 
8582cd5e41aSRui Paulo 	/*
8592cd5e41aSRui Paulo 	 * The longest string we trace is 23 bytes long -- so 32 is plenty.
8602cd5e41aSRui Paulo 	 */
8612cd5e41aSRui Paulo 	if (dtrace_setopt(g_dtp, "strsize", "32") == -1)
8622cd5e41aSRui Paulo 		dfatal("failed to set 'strsize'");
8632cd5e41aSRui Paulo 
8642cd5e41aSRui Paulo 	/*
8652cd5e41aSRui Paulo 	 * 1k should be more than enough for all trace() and printa() actions.
8662cd5e41aSRui Paulo 	 */
8672cd5e41aSRui Paulo 	if (dtrace_setopt(g_dtp, "bufsize", "1k") == -1)
8682cd5e41aSRui Paulo 		dfatal("failed to set 'bufsize'");
8692cd5e41aSRui Paulo 
8702cd5e41aSRui Paulo 	/*
8712cd5e41aSRui Paulo 	 * The table we produce has the hottest locks at the top.
8722cd5e41aSRui Paulo 	 */
8732cd5e41aSRui Paulo 	if (dtrace_setopt(g_dtp, "aggsortrev", NULL) == -1)
8742cd5e41aSRui Paulo 		dfatal("failed to set 'aggsortrev'");
8752cd5e41aSRui Paulo 
8762cd5e41aSRui Paulo 	/*
8772cd5e41aSRui Paulo 	 * These are two reasonable defaults which should suffice.
8782cd5e41aSRui Paulo 	 */
8792cd5e41aSRui Paulo 	if (dtrace_setopt(g_dtp, "aggsize", "256k") == -1)
8802cd5e41aSRui Paulo 		dfatal("failed to set 'aggsize'");
8812cd5e41aSRui Paulo 	if (dtrace_setopt(g_dtp, "aggrate", "1sec") == -1)
8822cd5e41aSRui Paulo 		dfatal("failed to set 'aggrate'");
8832cd5e41aSRui Paulo 
8842cd5e41aSRui Paulo 	/*
8852cd5e41aSRui Paulo 	 * Take a second pass through to look for options that set options now
8862cd5e41aSRui Paulo 	 * that we have an open dtrace handle.
8872cd5e41aSRui Paulo 	 */
8882cd5e41aSRui Paulo 	optind = 1;
8892cd5e41aSRui Paulo 	while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {
8902cd5e41aSRui Paulo 		switch (c) {
8912cd5e41aSRui Paulo 		case 's':
8922cd5e41aSRui Paulo 			g_opt_s = 1;
8932cd5e41aSRui Paulo 			if (dtrace_setopt(g_dtp, "ustackframes", optarg) == -1)
8942cd5e41aSRui Paulo 				dfatal("failed to set 'ustackframes'");
8952cd5e41aSRui Paulo 			break;
8962cd5e41aSRui Paulo 
8972cd5e41aSRui Paulo 		case 'x':
8982cd5e41aSRui Paulo 			if ((p = strchr(optarg, '=')) != NULL)
8992cd5e41aSRui Paulo 				*p++ = '\0';
9002cd5e41aSRui Paulo 
9012cd5e41aSRui Paulo 			if (dtrace_setopt(g_dtp, optarg, p) != 0)
9022cd5e41aSRui Paulo 				dfatal("failed to set -x %s", optarg);
9032cd5e41aSRui Paulo 			break;
9042cd5e41aSRui Paulo 
9052cd5e41aSRui Paulo 		case 'e':
9062cd5e41aSRui Paulo 			errno = 0;
9072cd5e41aSRui Paulo 			(void) strtoul(optarg, &end, 10);
9082cd5e41aSRui Paulo 			if (*optarg == '-' || *end != '\0' || errno != 0) {
9092cd5e41aSRui Paulo 				(void) fprintf(stderr, "%s: invalid timeout "
9102cd5e41aSRui Paulo 				    "'%s'\n", g_pname, optarg);
9112cd5e41aSRui Paulo 				usage();
9122cd5e41aSRui Paulo 			}
9132cd5e41aSRui Paulo 
9142cd5e41aSRui Paulo 			/*
9152cd5e41aSRui Paulo 			 * Construct a DTrace enabling that will exit after
9162cd5e41aSRui Paulo 			 * the specified number of seconds.
9172cd5e41aSRui Paulo 			 */
9182cd5e41aSRui Paulo 			dprog_add("BEGIN\n{\n\tend = timestamp + ");
9192cd5e41aSRui Paulo 			dprog_add(optarg);
9202cd5e41aSRui Paulo 			dprog_add(" * 1000000000;\n}\n");
9212cd5e41aSRui Paulo 			dprog_add("tick-10hz\n/timestamp >= end/\n");
9222cd5e41aSRui Paulo 			dprog_add("{\n\texit(0);\n}\n");
9232cd5e41aSRui Paulo 			break;
9242cd5e41aSRui Paulo 		}
9252cd5e41aSRui Paulo 	}
9262cd5e41aSRui Paulo 
9272cd5e41aSRui Paulo 	argc -= optind;
9282cd5e41aSRui Paulo 	argv += optind;
9292cd5e41aSRui Paulo 
9302cd5e41aSRui Paulo 	if (opt_H) {
9312cd5e41aSRui Paulo 		dprog_add(g_hold_init);
9326544c919SRui Paulo 		if (!g_opt_s)
9332cd5e41aSRui Paulo 			dprog_add(g_hold_times);
9342cd5e41aSRui Paulo 		else
9352cd5e41aSRui Paulo 			dprog_add(g_hold_histogram);
9362cd5e41aSRui Paulo 	}
9372cd5e41aSRui Paulo 
9382cd5e41aSRui Paulo 	if (opt_C) {
9392cd5e41aSRui Paulo 		dprog_add(g_ctnd_init);
9406544c919SRui Paulo 		if (!g_opt_s)
9412cd5e41aSRui Paulo 			dprog_add(g_ctnd_times);
9422cd5e41aSRui Paulo 		else
9432cd5e41aSRui Paulo 			dprog_add(g_ctnd_histogram);
9442cd5e41aSRui Paulo 	}
9452cd5e41aSRui Paulo 
9462cd5e41aSRui Paulo 	if (opt_p) {
9472cd5e41aSRui Paulo 		ulong_t pid;
9482cd5e41aSRui Paulo 
9492cd5e41aSRui Paulo 		if (argc > 1) {
9502cd5e41aSRui Paulo 			(void) fprintf(stderr, "%s: only one pid is allowed\n",
9512cd5e41aSRui Paulo 			    g_pname);
9522cd5e41aSRui Paulo 			usage();
9532cd5e41aSRui Paulo 		}
9542cd5e41aSRui Paulo 
9552cd5e41aSRui Paulo 		errno = 0;
9562cd5e41aSRui Paulo 		pid = strtoul(argv[0], &end, 10);
9572cd5e41aSRui Paulo 		if (*end != '\0' || errno != 0 || (pid_t)pid != pid) {
9582cd5e41aSRui Paulo 			(void) fprintf(stderr, "%s: invalid pid '%s'\n",
9592cd5e41aSRui Paulo 			    g_pname, argv[0]);
9602cd5e41aSRui Paulo 			usage();
9612cd5e41aSRui Paulo 		}
9622cd5e41aSRui Paulo 
9632cd5e41aSRui Paulo 		if ((g_pr = dtrace_proc_grab(g_dtp, (pid_t)pid, 0)) == NULL)
9642cd5e41aSRui Paulo 			dfatal(NULL);
9652cd5e41aSRui Paulo 	} else {
9666544c919SRui Paulo 		if ((g_pr = dtrace_proc_create(g_dtp, argv[0], argv, NULL, NULL)) == NULL)
9672cd5e41aSRui Paulo 			dfatal(NULL);
9682cd5e41aSRui Paulo 	}
9692cd5e41aSRui Paulo 
9702cd5e41aSRui Paulo 	dprog_compile();
9712cd5e41aSRui Paulo 
9722cd5e41aSRui Paulo 	if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1)
9732cd5e41aSRui Paulo 		dfatal("failed to establish proc handler");
9742cd5e41aSRui Paulo 
9752cd5e41aSRui Paulo 	(void) sigemptyset(&act.sa_mask);
9762cd5e41aSRui Paulo 	act.sa_flags = 0;
9772cd5e41aSRui Paulo 	act.sa_handler = intr;
9782cd5e41aSRui Paulo 	(void) sigaction(SIGINT, &act, NULL);
9792cd5e41aSRui Paulo 	(void) sigaction(SIGTERM, &act, NULL);
9802cd5e41aSRui Paulo 
9812cd5e41aSRui Paulo 	if (dtrace_go(g_dtp) != 0)
9822cd5e41aSRui Paulo 		dfatal("dtrace_go()");
9832cd5e41aSRui Paulo 
9842cd5e41aSRui Paulo 	if (dtrace_getopt(g_dtp, "ustackframes", &g_nframes) != 0)
9852cd5e41aSRui Paulo 		dfatal("failed to get 'ustackframes'");
9862cd5e41aSRui Paulo 
9872cd5e41aSRui Paulo 	dtrace_proc_continue(g_dtp, g_pr);
9882cd5e41aSRui Paulo 
9892cd5e41aSRui Paulo 	if (opt_v)
9902cd5e41aSRui Paulo 		(void) printf("%s: tracing enabled for pid %d\n", g_pname,
991bc96366cSSteven Hartland #ifdef illumos
9922cd5e41aSRui Paulo 		    (int)Pstatus(g_pr)->pr_pid);
9936544c919SRui Paulo #else
9946544c919SRui Paulo 		    (int)proc_getpid(g_pr));
9956544c919SRui Paulo #endif
9962cd5e41aSRui Paulo 
9972cd5e41aSRui Paulo 	do {
9982cd5e41aSRui Paulo 		if (!g_intr && !done)
9992cd5e41aSRui Paulo 			dtrace_sleep(g_dtp);
10002cd5e41aSRui Paulo 
10012cd5e41aSRui Paulo 		if (done || g_intr || g_exited) {
10022cd5e41aSRui Paulo 			done = 1;
10032cd5e41aSRui Paulo 			if (dtrace_stop(g_dtp) == -1)
10042cd5e41aSRui Paulo 				dfatal("couldn't stop tracing");
10052cd5e41aSRui Paulo 		}
10062cd5e41aSRui Paulo 
10072cd5e41aSRui Paulo 		switch (dtrace_work(g_dtp, stdout, NULL, chewrec, NULL)) {
10082cd5e41aSRui Paulo 		case DTRACE_WORKSTATUS_DONE:
10092cd5e41aSRui Paulo 			done = 1;
10102cd5e41aSRui Paulo 			break;
10112cd5e41aSRui Paulo 		case DTRACE_WORKSTATUS_OKAY:
10122cd5e41aSRui Paulo 			break;
10132cd5e41aSRui Paulo 		default:
10142cd5e41aSRui Paulo 			dfatal("processing aborted");
10152cd5e41aSRui Paulo 		}
10162cd5e41aSRui Paulo 
10172cd5e41aSRui Paulo 	} while (!done);
10182cd5e41aSRui Paulo 
10192cd5e41aSRui Paulo 	dtrace_close(g_dtp);
10202cd5e41aSRui Paulo 
10212cd5e41aSRui Paulo 	return (0);
10222cd5e41aSRui Paulo }
1023