1210236Srpaulo/*
2210236Srpaulo * CDDL HEADER START
3210236Srpaulo *
4210236Srpaulo * The contents of this file are subject to the terms of the
5210236Srpaulo * Common Development and Distribution License (the "License").
6210236Srpaulo * You may not use this file except in compliance with the License.
7210236Srpaulo *
8210236Srpaulo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9210236Srpaulo * or http://www.opensolaris.org/os/licensing.
10210236Srpaulo * See the License for the specific language governing permissions
11210236Srpaulo * and limitations under the License.
12210236Srpaulo *
13210236Srpaulo * When distributing Covered Code, include this CDDL HEADER in each
14210236Srpaulo * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15210236Srpaulo * If applicable, add the following below this CDDL HEADER, with the
16210236Srpaulo * fields enclosed by brackets "[]" replaced with your own identifying
17210236Srpaulo * information: Portions Copyright [yyyy] [name of copyright owner]
18210236Srpaulo *
19210236Srpaulo * CDDL HEADER END
20210236Srpaulo */
21210236Srpaulo
22210236Srpaulo/*
23210236Srpaulo * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24210236Srpaulo * Use is subject to license terms.
25210236Srpaulo */
26210236Srpaulo
27211562Srpaulo#if defined(sun)
28210236Srpaulo#pragma ident	"%Z%%M%	%I%	%E% SMI"
29211562Srpaulo#endif
30210236Srpaulo
31210236Srpaulo#include <assert.h>
32210236Srpaulo#include <dtrace.h>
33210236Srpaulo#include <limits.h>
34210236Srpaulo#include <link.h>
35210236Srpaulo#include <priv.h>
36210236Srpaulo#include <signal.h>
37210236Srpaulo#include <stdlib.h>
38210236Srpaulo#include <stdarg.h>
39210236Srpaulo#include <stdio.h>
40210236Srpaulo#include <string.h>
41210236Srpaulo#include <strings.h>
42210236Srpaulo#include <errno.h>
43210236Srpaulo#include <sys/wait.h>
44210236Srpaulo#include <libgen.h>
45210236Srpaulo#include <libproc.h>
46211562Srpaulo#include <libproc_compat.h>
47210236Srpaulo
48210236Srpaulostatic char *g_pname;
49210236Srpaulostatic dtrace_hdl_t *g_dtp;
50210236Srpaulostruct ps_prochandle *g_pr;
51210236Srpaulo
52210236Srpaulo#define	E_SUCCESS	0
53210236Srpaulo#define	E_ERROR		1
54210236Srpaulo#define	E_USAGE		2
55210236Srpaulo
56210236Srpaulo/*
57210236Srpaulo * For hold times we use a global associative array since for mutexes, in
58210236Srpaulo * user-land, it's not invalid to release a sychonization primitive that
59210236Srpaulo * another thread acquired; rwlocks require a thread-local associative array
60210236Srpaulo * since multiple thread can hold the same lock for reading. Note that we
61210236Srpaulo * ignore recursive mutex acquisitions and releases as they don't truly
62210236Srpaulo * affect lock contention.
63210236Srpaulo */
64210236Srpaulostatic const char *g_hold_init =
65210236Srpaulo"plockstat$target:::rw-acquire\n"
66210236Srpaulo"{\n"
67210236Srpaulo"	self->rwhold[arg0] = timestamp;\n"
68210236Srpaulo"}\n"
69210236Srpaulo"plockstat$target:::mutex-acquire\n"
70210236Srpaulo"/arg1 == 0/\n"
71210236Srpaulo"{\n"
72210236Srpaulo"	mtxhold[arg0] = timestamp;\n"
73210236Srpaulo"}\n";
74210236Srpaulo
75210236Srpaulostatic const char *g_hold_histogram =
76210236Srpaulo"plockstat$target:::rw-release\n"
77210236Srpaulo"/self->rwhold[arg0] && arg1 == 1/\n"
78210236Srpaulo"{\n"
79210236Srpaulo"	@rw_w_hold[arg0, ustack()] =\n"
80210236Srpaulo"	    quantize(timestamp - self->rwhold[arg0]);\n"
81210236Srpaulo"	self->rwhold[arg0] = 0;\n"
82210236Srpaulo"	rw_w_hold_found = 1;\n"
83210236Srpaulo"}\n"
84210236Srpaulo"plockstat$target:::rw-release\n"
85210236Srpaulo"/self->rwhold[arg0]/\n"
86210236Srpaulo"{\n"
87210236Srpaulo"	@rw_r_hold[arg0, ustack()] =\n"
88210236Srpaulo"	    quantize(timestamp - self->rwhold[arg0]);\n"
89210236Srpaulo"	self->rwhold[arg0] = 0;\n"
90210236Srpaulo"	rw_r_hold_found = 1;\n"
91210236Srpaulo"}\n"
92210236Srpaulo"plockstat$target:::mutex-release\n"
93210236Srpaulo"/mtxhold[arg0] && arg1 == 0/\n"
94210236Srpaulo"{\n"
95210236Srpaulo"	@mtx_hold[arg0, ustack()] = quantize(timestamp - mtxhold[arg0]);\n"
96210236Srpaulo"	mtxhold[arg0] = 0;\n"
97210236Srpaulo"	mtx_hold_found = 1;\n"
98210236Srpaulo"}\n"
99210236Srpaulo"\n"
100210236Srpaulo"END\n"
101210236Srpaulo"/mtx_hold_found/\n"
102210236Srpaulo"{\n"
103210236Srpaulo"	trace(\"Mutex hold\");\n"
104210236Srpaulo"	printa(@mtx_hold);\n"
105210236Srpaulo"}\n"
106210236Srpaulo"END\n"
107210236Srpaulo"/rw_r_hold_found/\n"
108210236Srpaulo"{\n"
109210236Srpaulo"	trace(\"R/W reader hold\");\n"
110210236Srpaulo"	printa(@rw_r_hold);\n"
111210236Srpaulo"}\n"
112210236Srpaulo"END\n"
113210236Srpaulo"/rw_w_hold_found/\n"
114210236Srpaulo"{\n"
115210236Srpaulo"	trace(\"R/W writer hold\");\n"
116210236Srpaulo"	printa(@rw_w_hold);\n"
117210236Srpaulo"}\n";
118210236Srpaulo
119210236Srpaulostatic const char *g_hold_times =
120210236Srpaulo"plockstat$target:::rw-release\n"
121210236Srpaulo"/self->rwhold[arg0] && arg1 == 1/\n"
122210236Srpaulo"{\n"
123210236Srpaulo"	@rw_w_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"
124210236Srpaulo"	@rw_w_hold_count[arg0, ustack(5)] = count();\n"
125210236Srpaulo"	self->rwhold[arg0] = 0;\n"
126210236Srpaulo"	rw_w_hold_found = 1;\n"
127210236Srpaulo"}\n"
128210236Srpaulo"plockstat$target:::rw-release\n"
129210236Srpaulo"/self->rwhold[arg0]/\n"
130210236Srpaulo"{\n"
131210236Srpaulo"	@rw_r_hold[arg0, ustack(5)] = sum(timestamp - self->rwhold[arg0]);\n"
132210236Srpaulo"	@rw_r_hold_count[arg0, ustack(5)] = count();\n"
133210236Srpaulo"	self->rwhold[arg0] = 0;\n"
134210236Srpaulo"	rw_r_hold_found = 1;\n"
135210236Srpaulo"}\n"
136210236Srpaulo"plockstat$target:::mutex-release\n"
137210236Srpaulo"/mtxhold[arg0] && arg1 == 0/\n"
138210236Srpaulo"{\n"
139210236Srpaulo"	@mtx_hold[arg0, ustack(5)] = sum(timestamp - mtxhold[arg0]);\n"
140210236Srpaulo"	@mtx_hold_count[arg0, ustack(5)] = count();\n"
141210236Srpaulo"	mtxhold[arg0] = 0;\n"
142210236Srpaulo"	mtx_hold_found = 1;\n"
143210236Srpaulo"}\n"
144210236Srpaulo"\n"
145210236Srpaulo"END\n"
146210236Srpaulo"/mtx_hold_found/\n"
147210236Srpaulo"{\n"
148210236Srpaulo"	trace(\"Mutex hold\");\n"
149210236Srpaulo"	printa(@mtx_hold, @mtx_hold_count);\n"
150210236Srpaulo"}\n"
151210236Srpaulo"END\n"
152210236Srpaulo"/rw_r_hold_found/\n"
153210236Srpaulo"{\n"
154210236Srpaulo"	trace(\"R/W reader hold\");\n"
155210236Srpaulo"	printa(@rw_r_hold, @rw_r_hold_count);\n"
156210236Srpaulo"}\n"
157210236Srpaulo"END\n"
158210236Srpaulo"/rw_w_hold_found/\n"
159210236Srpaulo"{\n"
160210236Srpaulo"	trace(\"R/W writer hold\");\n"
161210236Srpaulo"	printa(@rw_w_hold, @rw_w_hold_count);\n"
162210236Srpaulo"}\n";
163210236Srpaulo
164210236Srpaulo
165210236Srpaulo/*
166210236Srpaulo * For contention, we use thread-local associative arrays since we're tracing
167210236Srpaulo * a single thread's activity in libc and multiple threads can be blocking or
168210236Srpaulo * spinning on the same sychonization primitive.
169210236Srpaulo */
170210236Srpaulostatic const char *g_ctnd_init =
171210236Srpaulo"plockstat$target:::rw-block\n"
172210236Srpaulo"{\n"
173210236Srpaulo"	self->rwblock[arg0] = timestamp;\n"
174210236Srpaulo"}\n"
175210236Srpaulo"plockstat$target:::mutex-block\n"
176210236Srpaulo"{\n"
177210236Srpaulo"	self->mtxblock[arg0] = timestamp;\n"
178210236Srpaulo"}\n"
179210236Srpaulo"plockstat$target:::mutex-spin\n"
180210236Srpaulo"{\n"
181210236Srpaulo"	self->mtxspin[arg0] = timestamp;\n"
182210236Srpaulo"}\n";
183210236Srpaulo
184210236Srpaulostatic const char *g_ctnd_histogram =
185210236Srpaulo"plockstat$target:::rw-blocked\n"
186210236Srpaulo"/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
187210236Srpaulo"{\n"
188210236Srpaulo"	@rw_w_block[arg0, ustack()] =\n"
189210236Srpaulo"	    quantize(timestamp - self->rwblock[arg0]);\n"
190210236Srpaulo"	self->rwblock[arg0] = 0;\n"
191210236Srpaulo"	rw_w_block_found = 1;\n"
192210236Srpaulo"}\n"
193210236Srpaulo"plockstat$target:::rw-blocked\n"
194210236Srpaulo"/self->rwblock[arg0] && arg2 != 0/\n"
195210236Srpaulo"{\n"
196210236Srpaulo"	@rw_r_block[arg0, ustack()] =\n"
197210236Srpaulo"	    quantize(timestamp - self->rwblock[arg0]);\n"
198210236Srpaulo"	self->rwblock[arg0] = 0;\n"
199210236Srpaulo"	rw_r_block_found = 1;\n"
200210236Srpaulo"}\n"
201210236Srpaulo"plockstat$target:::rw-blocked\n"
202210236Srpaulo"/self->rwblock[arg0]/\n"
203210236Srpaulo"{\n"
204210236Srpaulo"	self->rwblock[arg0] = 0;\n"
205210236Srpaulo"}\n"
206210236Srpaulo"plockstat$target:::mutex-spun\n"
207210236Srpaulo"/self->mtxspin[arg0] && arg1 != 0/\n"
208210236Srpaulo"{\n"
209210236Srpaulo"	@mtx_spin[arg0, ustack()] =\n"
210210236Srpaulo"	    quantize(timestamp - self->mtxspin[arg0]);\n"
211210236Srpaulo"	self->mtxspin[arg0] = 0;\n"
212210236Srpaulo"	mtx_spin_found = 1;\n"
213210236Srpaulo"}\n"
214210236Srpaulo"plockstat$target:::mutex-spun\n"
215210236Srpaulo"/self->mtxspin[arg0]/\n"
216210236Srpaulo"{\n"
217210236Srpaulo"	@mtx_vain_spin[arg0, ustack()] =\n"
218210236Srpaulo"	    quantize(timestamp - self->mtxspin[arg0]);\n"
219210236Srpaulo"	self->mtxspin[arg0] = 0;\n"
220210236Srpaulo"	mtx_vain_spin_found = 1;\n"
221210236Srpaulo"}\n"
222210236Srpaulo"plockstat$target:::mutex-blocked\n"
223210236Srpaulo"/self->mtxblock[arg0] && arg1 != 0/\n"
224210236Srpaulo"{\n"
225210236Srpaulo"	@mtx_block[arg0, ustack()] =\n"
226210236Srpaulo"	    quantize(timestamp - self->mtxblock[arg0]);\n"
227210236Srpaulo"	self->mtxblock[arg0] = 0;\n"
228210236Srpaulo"	mtx_block_found = 1;\n"
229210236Srpaulo"}\n"
230210236Srpaulo"plockstat$target:::mutex-blocked\n"
231210236Srpaulo"/self->mtxblock[arg0]/\n"
232210236Srpaulo"{\n"
233210236Srpaulo"	self->mtxblock[arg0] = 0;\n"
234210236Srpaulo"}\n"
235210236Srpaulo"\n"
236210236Srpaulo"END\n"
237210236Srpaulo"/mtx_block_found/\n"
238210236Srpaulo"{\n"
239210236Srpaulo"	trace(\"Mutex block\");\n"
240210236Srpaulo"	printa(@mtx_block);\n"
241210236Srpaulo"}\n"
242210236Srpaulo"END\n"
243210236Srpaulo"/mtx_spin_found/\n"
244210236Srpaulo"{\n"
245210236Srpaulo"	trace(\"Mutex spin\");\n"
246210236Srpaulo"	printa(@mtx_spin);\n"
247210236Srpaulo"}\n"
248210236Srpaulo"END\n"
249210236Srpaulo"/mtx_vain_spin_found/\n"
250210236Srpaulo"{\n"
251210236Srpaulo"	trace(\"Mutex unsuccessful spin\");\n"
252210236Srpaulo"	printa(@mtx_vain_spin);\n"
253210236Srpaulo"}\n"
254210236Srpaulo"END\n"
255210236Srpaulo"/rw_r_block_found/\n"
256210236Srpaulo"{\n"
257210236Srpaulo"	trace(\"R/W reader block\");\n"
258210236Srpaulo"	printa(@rw_r_block);\n"
259210236Srpaulo"}\n"
260210236Srpaulo"END\n"
261210236Srpaulo"/rw_w_block_found/\n"
262210236Srpaulo"{\n"
263210236Srpaulo"	trace(\"R/W writer block\");\n"
264210236Srpaulo"	printa(@rw_w_block);\n"
265210236Srpaulo"}\n";
266210236Srpaulo
267210236Srpaulo
268210236Srpaulostatic const char *g_ctnd_times =
269210236Srpaulo"plockstat$target:::rw-blocked\n"
270210236Srpaulo"/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
271210236Srpaulo"{\n"
272210236Srpaulo"	@rw_w_block[arg0, ustack(5)] =\n"
273210236Srpaulo"	    sum(timestamp - self->rwblock[arg0]);\n"
274210236Srpaulo"	@rw_w_block_count[arg0, ustack(5)] = count();\n"
275210236Srpaulo"	self->rwblock[arg0] = 0;\n"
276210236Srpaulo"	rw_w_block_found = 1;\n"
277210236Srpaulo"}\n"
278210236Srpaulo"plockstat$target:::rw-blocked\n"
279210236Srpaulo"/self->rwblock[arg0] && arg2 != 0/\n"
280210236Srpaulo"{\n"
281210236Srpaulo"	@rw_r_block[arg0, ustack(5)] =\n"
282210236Srpaulo"	    sum(timestamp - self->rwblock[arg0]);\n"
283210236Srpaulo"	@rw_r_block_count[arg0, ustack(5)] = count();\n"
284210236Srpaulo"	self->rwblock[arg0] = 0;\n"
285210236Srpaulo"	rw_r_block_found = 1;\n"
286210236Srpaulo"}\n"
287210236Srpaulo"plockstat$target:::rw-blocked\n"
288210236Srpaulo"/self->rwblock[arg0]/\n"
289210236Srpaulo"{\n"
290210236Srpaulo"	self->rwblock[arg0] = 0;\n"
291210236Srpaulo"}\n"
292210236Srpaulo"plockstat$target:::mutex-spun\n"
293210236Srpaulo"/self->mtxspin[arg0] && arg1 != 0/\n"
294210236Srpaulo"{\n"
295210236Srpaulo"	@mtx_spin[arg0, ustack(5)] =\n"
296210236Srpaulo"	    sum(timestamp - self->mtxspin[arg0]);\n"
297210236Srpaulo"	@mtx_spin_count[arg0, ustack(5)] = count();\n"
298210236Srpaulo"	self->mtxspin[arg0] = 0;\n"
299210236Srpaulo"	mtx_spin_found = 1;\n"
300210236Srpaulo"}\n"
301210236Srpaulo"plockstat$target:::mutex-spun\n"
302210236Srpaulo"/self->mtxspin[arg0]/\n"
303210236Srpaulo"{\n"
304210236Srpaulo"	@mtx_vain_spin[arg0, ustack(5)] =\n"
305210236Srpaulo"	    sum(timestamp - self->mtxspin[arg0]);\n"
306210236Srpaulo"	@mtx_vain_spin_count[arg0, ustack(5)] = count();\n"
307210236Srpaulo"	self->mtxspin[arg0] = 0;\n"
308210236Srpaulo"	mtx_vain_spin_found = 1;\n"
309210236Srpaulo"}\n"
310210236Srpaulo"plockstat$target:::mutex-blocked\n"
311210236Srpaulo"/self->mtxblock[arg0] && arg1 != 0/\n"
312210236Srpaulo"{\n"
313210236Srpaulo"	@mtx_block[arg0, ustack(5)] =\n"
314210236Srpaulo"	    sum(timestamp - self->mtxblock[arg0]);\n"
315210236Srpaulo"	@mtx_block_count[arg0, ustack(5)] = count();\n"
316210236Srpaulo"	self->mtxblock[arg0] = 0;\n"
317210236Srpaulo"	mtx_block_found = 1;\n"
318210236Srpaulo"}\n"
319210236Srpaulo"plockstat$target:::mutex-blocked\n"
320210236Srpaulo"/self->mtxblock[arg0]/\n"
321210236Srpaulo"{\n"
322210236Srpaulo"	self->mtxblock[arg0] = 0;\n"
323210236Srpaulo"}\n"
324210236Srpaulo"\n"
325210236Srpaulo"END\n"
326210236Srpaulo"/mtx_block_found/\n"
327210236Srpaulo"{\n"
328210236Srpaulo"	trace(\"Mutex block\");\n"
329210236Srpaulo"	printa(@mtx_block, @mtx_block_count);\n"
330210236Srpaulo"}\n"
331210236Srpaulo"END\n"
332210236Srpaulo"/mtx_spin_found/\n"
333210236Srpaulo"{\n"
334210236Srpaulo"	trace(\"Mutex spin\");\n"
335210236Srpaulo"	printa(@mtx_spin, @mtx_spin_count);\n"
336210236Srpaulo"}\n"
337210236Srpaulo"END\n"
338210236Srpaulo"/mtx_vain_spin_found/\n"
339210236Srpaulo"{\n"
340210236Srpaulo"	trace(\"Mutex unsuccessful spin\");\n"
341210236Srpaulo"	printa(@mtx_vain_spin, @mtx_vain_spin_count);\n"
342210236Srpaulo"}\n"
343210236Srpaulo"END\n"
344210236Srpaulo"/rw_r_block_found/\n"
345210236Srpaulo"{\n"
346210236Srpaulo"	trace(\"R/W reader block\");\n"
347210236Srpaulo"	printa(@rw_r_block, @rw_r_block_count);\n"
348210236Srpaulo"}\n"
349210236Srpaulo"END\n"
350210236Srpaulo"/rw_w_block_found/\n"
351210236Srpaulo"{\n"
352210236Srpaulo"	trace(\"R/W writer block\");\n"
353210236Srpaulo"	printa(@rw_w_block, @rw_w_block_count);\n"
354210236Srpaulo"}\n";
355210236Srpaulo
356210236Srpaulostatic char g_prog[4096];
357210236Srpaulostatic size_t g_proglen;
358210236Srpaulostatic int g_opt_V, g_opt_s;
359210236Srpaulostatic int g_intr;
360210236Srpaulostatic int g_exited;
361210236Srpaulostatic dtrace_optval_t g_nframes;
362210236Srpaulostatic ulong_t g_nent = ULONG_MAX;
363210236Srpaulo
364210236Srpaulo#define	PLOCKSTAT_OPTSTR	"n:ps:e:vx:ACHV"
365210236Srpaulo
366210236Srpaulostatic void
367210236Srpaulousage(void)
368210236Srpaulo{
369210236Srpaulo	(void) fprintf(stderr, "Usage:\n"
370210236Srpaulo	    "\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
371210236Srpaulo	    "\t    command [arg...]\n"
372210236Srpaulo	    "\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
373210236Srpaulo	    "\t    -p pid\n", g_pname, g_pname);
374210236Srpaulo
375210236Srpaulo	exit(E_USAGE);
376210236Srpaulo}
377210236Srpaulo
378210236Srpaulostatic void
379210236Srpauloverror(const char *fmt, va_list ap)
380210236Srpaulo{
381210236Srpaulo	int error = errno;
382210236Srpaulo
383210236Srpaulo	(void) fprintf(stderr, "%s: ", g_pname);
384210236Srpaulo	(void) vfprintf(stderr, fmt, ap);
385210236Srpaulo
386210236Srpaulo	if (fmt[strlen(fmt) - 1] != '\n')
387210236Srpaulo		(void) fprintf(stderr, ": %s\n", strerror(error));
388210236Srpaulo}
389210236Srpaulo
390210236Srpaulo/*PRINTFLIKE1*/
391210236Srpaulostatic void
392210236Srpaulofatal(const char *fmt, ...)
393210236Srpaulo{
394210236Srpaulo	va_list ap;
395210236Srpaulo
396210236Srpaulo	va_start(ap, fmt);
397210236Srpaulo	verror(fmt, ap);
398210236Srpaulo	va_end(ap);
399210236Srpaulo
400210236Srpaulo	if (g_pr != NULL && g_dtp != NULL)
401210236Srpaulo		dtrace_proc_release(g_dtp, g_pr);
402210236Srpaulo
403210236Srpaulo	exit(E_ERROR);
404210236Srpaulo}
405210236Srpaulo
406210236Srpaulo/*PRINTFLIKE1*/
407210236Srpaulostatic void
408210236Srpaulodfatal(const char *fmt, ...)
409210236Srpaulo{
410210236Srpaulo	va_list ap;
411210236Srpaulo
412210236Srpaulo	va_start(ap, fmt);
413210236Srpaulo
414210236Srpaulo	(void) fprintf(stderr, "%s: ", g_pname);
415210236Srpaulo	if (fmt != NULL)
416210236Srpaulo		(void) vfprintf(stderr, fmt, ap);
417210236Srpaulo
418210236Srpaulo	va_end(ap);
419210236Srpaulo
420210236Srpaulo	if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') {
421210236Srpaulo		(void) fprintf(stderr, ": %s\n",
422210236Srpaulo		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
423210236Srpaulo	} else if (fmt == NULL) {
424210236Srpaulo		(void) fprintf(stderr, "%s\n",
425210236Srpaulo		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
426210236Srpaulo	}
427210236Srpaulo
428210236Srpaulo	if (g_pr != NULL) {
429210236Srpaulo		dtrace_proc_continue(g_dtp, g_pr);
430210236Srpaulo		dtrace_proc_release(g_dtp, g_pr);
431210236Srpaulo	}
432210236Srpaulo
433210236Srpaulo	exit(E_ERROR);
434210236Srpaulo}
435210236Srpaulo
436210236Srpaulo/*PRINTFLIKE1*/
437210236Srpaulostatic void
438210236Srpaulonotice(const char *fmt, ...)
439210236Srpaulo{
440210236Srpaulo	va_list ap;
441210236Srpaulo
442210236Srpaulo	va_start(ap, fmt);
443210236Srpaulo	verror(fmt, ap);
444210236Srpaulo	va_end(ap);
445210236Srpaulo}
446210236Srpaulo
447210236Srpaulostatic void
448210236Srpaulodprog_add(const char *prog)
449210236Srpaulo{
450210236Srpaulo	size_t len = strlen(prog);
451210236Srpaulo	bcopy(prog, g_prog + g_proglen, len + 1);
452210236Srpaulo	g_proglen += len;
453210236Srpaulo	assert(g_proglen < sizeof (g_prog));
454210236Srpaulo}
455210236Srpaulo
456210236Srpaulostatic void
457210236Srpaulodprog_compile(void)
458210236Srpaulo{
459210236Srpaulo	dtrace_prog_t *prog;
460210236Srpaulo	dtrace_proginfo_t info;
461210236Srpaulo
462210236Srpaulo	if (g_opt_V) {
463210236Srpaulo		(void) fprintf(stderr, "%s: vvvv D program vvvv\n", g_pname);
464210236Srpaulo		(void) fputs(g_prog, stderr);
465210236Srpaulo		(void) fprintf(stderr, "%s: ^^^^ D program ^^^^\n", g_pname);
466210236Srpaulo	}
467210236Srpaulo
468210236Srpaulo	if ((prog = dtrace_program_strcompile(g_dtp, g_prog,
469210236Srpaulo	    DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL)
470210236Srpaulo		dfatal("failed to compile program");
471210236Srpaulo
472210236Srpaulo	if (dtrace_program_exec(g_dtp, prog, &info) == -1)
473210236Srpaulo		dfatal("failed to enable probes");
474210236Srpaulo}
475210236Srpaulo
476210236Srpaulovoid
477210236Srpauloprint_legend(void)
478210236Srpaulo{
479210236Srpaulo	(void) printf("%5s %8s %-28s %s\n", "Count", "nsec", "Lock", "Caller");
480210236Srpaulo}
481210236Srpaulo
482210236Srpaulovoid
483210236Srpauloprint_bar(void)
484210236Srpaulo{
485210236Srpaulo	(void) printf("---------------------------------------"
486210236Srpaulo	    "----------------------------------------\n");
487210236Srpaulo}
488210236Srpaulo
489210236Srpaulovoid
490210236Srpauloprint_histogram_header(void)
491210236Srpaulo{
492210236Srpaulo	(void) printf("\n%10s ---- Time Distribution --- %5s %s\n",
493210236Srpaulo	    "nsec", "count", "Stack");
494210236Srpaulo}
495210236Srpaulo
496210236Srpaulo/*
497210236Srpaulo * Convert an address to a symbolic string or a numeric string. If nolocks
498210236Srpaulo * is set, we return an error code if this symbol appears to be a mutex- or
499210236Srpaulo * rwlock-related symbol in libc so the caller has a chance to find a more
500210236Srpaulo * helpful symbol.
501210236Srpaulo */
502210236Srpaulostatic int
503210236Srpaulogetsym(struct ps_prochandle *P, uintptr_t addr, char *buf, size_t size,
504210236Srpaulo    int nolocks)
505210236Srpaulo{
506210236Srpaulo	char name[256];
507210236Srpaulo	GElf_Sym sym;
508211562Srpaulo#if defined(sun)
509210236Srpaulo	prsyminfo_t info;
510211562Srpaulo#else
511211562Srpaulo	prmap_t *map;
512211562Srpaulo	int info; /* XXX unused */
513211562Srpaulo#endif
514210236Srpaulo	size_t len;
515210236Srpaulo
516210236Srpaulo	if (P == NULL || Pxlookup_by_addr(P, addr, name, sizeof (name),
517210236Srpaulo	    &sym, &info) != 0) {
518210236Srpaulo		(void) snprintf(buf, size, "%#lx", addr);
519210236Srpaulo		return (0);
520210236Srpaulo	}
521211562Srpaulo#if defined(sun)
522210236Srpaulo	if (info.prs_object == NULL)
523210236Srpaulo		info.prs_object = "<unknown>";
524210236Srpaulo
525210236Srpaulo	if (info.prs_lmid != LM_ID_BASE) {
526210236Srpaulo		len = snprintf(buf, size, "LM%lu`", info.prs_lmid);
527210236Srpaulo		buf += len;
528210236Srpaulo		size -= len;
529210236Srpaulo	}
530210236Srpaulo
531210236Srpaulo	len = snprintf(buf, size, "%s`%s", info.prs_object, info.prs_name);
532211562Srpaulo#else
533211562Srpaulo	map = proc_addr2map(P, addr);
534211562Srpaulo	len = snprintf(buf, size, "%s`%s", map->pr_mapname, name);
535211562Srpaulo#endif
536210236Srpaulo	buf += len;
537210236Srpaulo	size -= len;
538210236Srpaulo
539210236Srpaulo	if (sym.st_value != addr)
540210236Srpaulo		len = snprintf(buf, size, "+%#lx", addr - sym.st_value);
541210236Srpaulo
542211562Srpaulo	if (nolocks && strcmp("libc.so.1", map->pr_mapname) == 0 &&
543211562Srpaulo	    (strstr("mutex", name) == 0 ||
544211562Srpaulo	    strstr("rw", name) == 0))
545210236Srpaulo		return (-1);
546210236Srpaulo
547210236Srpaulo	return (0);
548210236Srpaulo}
549210236Srpaulo
550210236Srpaulo/*ARGSUSED*/
551210236Srpaulostatic int
552210236Srpauloprocess_aggregate(const dtrace_aggdata_t **aggsdata, int naggvars, void *arg)
553210236Srpaulo{
554210236Srpaulo	const dtrace_recdesc_t *rec;
555210236Srpaulo	uintptr_t lock;
556210236Srpaulo	uint64_t *stack;
557210236Srpaulo	caddr_t data;
558210236Srpaulo	pid_t pid;
559210236Srpaulo	struct ps_prochandle *P;
560210236Srpaulo	char buf[256];
561210236Srpaulo	int i, j;
562210236Srpaulo	uint64_t sum, count, avg;
563210236Srpaulo
564210236Srpaulo	if ((*(uint_t *)arg)++ >= g_nent)
565210236Srpaulo		return (DTRACE_AGGWALK_NEXT);
566210236Srpaulo
567210236Srpaulo	rec = aggsdata[0]->dtada_desc->dtagd_rec;
568210236Srpaulo	data = aggsdata[0]->dtada_data;
569210236Srpaulo
570210236Srpaulo	/*LINTED - alignment*/
571210236Srpaulo	lock = (uintptr_t)*(uint64_t *)(data + rec[1].dtrd_offset);
572210236Srpaulo	/*LINTED - alignment*/
573210236Srpaulo	stack = (uint64_t *)(data + rec[2].dtrd_offset);
574210236Srpaulo
575210236Srpaulo	if (!g_opt_s) {
576210236Srpaulo		/*LINTED - alignment*/
577210236Srpaulo		sum = *(uint64_t *)(aggsdata[1]->dtada_data +
578210236Srpaulo		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
579210236Srpaulo		/*LINTED - alignment*/
580210236Srpaulo		count = *(uint64_t *)(aggsdata[2]->dtada_data +
581210236Srpaulo		    aggsdata[2]->dtada_desc->dtagd_rec[3].dtrd_offset);
582210236Srpaulo	} else {
583210236Srpaulo		uint64_t *a;
584210236Srpaulo
585210236Srpaulo		/*LINTED - alignment*/
586210236Srpaulo		a = (uint64_t *)(aggsdata[1]->dtada_data +
587210236Srpaulo		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
588210236Srpaulo
589210236Srpaulo		print_bar();
590210236Srpaulo		print_legend();
591210236Srpaulo
592210236Srpaulo		for (count = sum = 0, i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0;
593210236Srpaulo		    i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) {
594210236Srpaulo			count += a[i];
595210236Srpaulo			sum += a[i] << (j - 64);
596210236Srpaulo		}
597210236Srpaulo	}
598210236Srpaulo
599210236Srpaulo	avg = sum / count;
600210236Srpaulo	(void) printf("%5llu %8llu ", (u_longlong_t)count, (u_longlong_t)avg);
601210236Srpaulo
602210236Srpaulo	pid = stack[0];
603210236Srpaulo	P = dtrace_proc_grab(g_dtp, pid, PGRAB_RDONLY);
604210236Srpaulo
605210236Srpaulo	(void) getsym(P, lock, buf, sizeof (buf), 0);
606210236Srpaulo	(void) printf("%-28s ", buf);
607210236Srpaulo
608210236Srpaulo	for (i = 2; i <= 5; i++) {
609210236Srpaulo		if (getsym(P, stack[i], buf, sizeof (buf), 1) == 0)
610210236Srpaulo			break;
611210236Srpaulo	}
612210236Srpaulo	(void) printf("%s\n", buf);
613210236Srpaulo
614210236Srpaulo	if (g_opt_s) {
615210236Srpaulo		int stack_done = 0;
616210236Srpaulo		int quant_done = 0;
617210236Srpaulo		int first_bin, last_bin;
618210236Srpaulo		uint64_t bin_size, *a;
619210236Srpaulo
620210236Srpaulo		/*LINTED - alignment*/
621210236Srpaulo		a = (uint64_t *)(aggsdata[1]->dtada_data +
622210236Srpaulo		    aggsdata[1]->dtada_desc->dtagd_rec[3].dtrd_offset);
623210236Srpaulo
624210236Srpaulo		print_histogram_header();
625210236Srpaulo
626210236Srpaulo		for (first_bin = DTRACE_QUANTIZE_ZEROBUCKET;
627210236Srpaulo		    a[first_bin] == 0; first_bin++)
628210236Srpaulo			continue;
629210236Srpaulo		for (last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 63;
630210236Srpaulo		    a[last_bin] == 0; last_bin--)
631210236Srpaulo			continue;
632210236Srpaulo
633210236Srpaulo		for (i = 0; !stack_done || !quant_done; i++) {
634210236Srpaulo			if (!stack_done) {
635210236Srpaulo				(void) getsym(P, stack[i + 2], buf,
636210236Srpaulo				    sizeof (buf), 0);
637210236Srpaulo			} else {
638210236Srpaulo				buf[0] = '\0';
639210236Srpaulo			}
640210236Srpaulo
641210236Srpaulo			if (!quant_done) {
642210236Srpaulo				bin_size = a[first_bin];
643210236Srpaulo
644210236Srpaulo				(void) printf("%10llu |%-24.*s| %5llu %s\n",
645210236Srpaulo				    1ULL <<
646210236Srpaulo				    (first_bin - DTRACE_QUANTIZE_ZEROBUCKET),
647210236Srpaulo				    (int)(24.0 * bin_size / count),
648210236Srpaulo				    "@@@@@@@@@@@@@@@@@@@@@@@@@@",
649210236Srpaulo				    (u_longlong_t)bin_size, buf);
650210236Srpaulo			} else {
651210236Srpaulo				(void) printf("%43s %s\n", "", buf);
652210236Srpaulo			}
653210236Srpaulo
654210236Srpaulo			if (i + 1 >= g_nframes || stack[i + 3] == 0)
655210236Srpaulo				stack_done = 1;
656210236Srpaulo
657210236Srpaulo			if (first_bin++ == last_bin)
658210236Srpaulo				quant_done = 1;
659210236Srpaulo		}
660210236Srpaulo	}
661210236Srpaulo
662210236Srpaulo	dtrace_proc_release(g_dtp, P);
663210236Srpaulo
664210236Srpaulo	return (DTRACE_AGGWALK_NEXT);
665210236Srpaulo}
666210236Srpaulo
667210236Srpaulo/*ARGSUSED*/
668210236Srpaulostatic void
669210236Srpauloprochandler(struct ps_prochandle *P, const char *msg, void *arg)
670210236Srpaulo{
671211562Srpaulo#if defined(sun)
672210236Srpaulo	const psinfo_t *prp = Ppsinfo(P);
673210236Srpaulo	int pid = Pstatus(P)->pr_pid;
674211562Srpaulo#else
675211562Srpaulo	int pid = proc_getpid(P);
676211562Srpaulo	int wstat = proc_getwstat(P);
677211562Srpaulo#endif
678210236Srpaulo	char name[SIG2STR_MAX];
679210236Srpaulo
680210236Srpaulo	if (msg != NULL) {
681210236Srpaulo		notice("pid %d: %s\n", pid, msg);
682210236Srpaulo		return;
683210236Srpaulo	}
684210236Srpaulo
685210236Srpaulo	switch (Pstate(P)) {
686210236Srpaulo	case PS_UNDEAD:
687210236Srpaulo		/*
688210236Srpaulo		 * Ideally we would like to always report pr_wstat here, but it
689210236Srpaulo		 * isn't possible given current /proc semantics.  If we grabbed
690210236Srpaulo		 * the process, Ppsinfo() will either fail or return a zeroed
691210236Srpaulo		 * psinfo_t depending on how far the parent is in reaping it.
692210236Srpaulo		 * When /proc provides a stable pr_wstat in the status file,
693210236Srpaulo		 * this code can be improved by examining this new pr_wstat.
694210236Srpaulo		 */
695211562Srpaulo		if (WIFSIGNALED(wstat)) {
696210236Srpaulo			notice("pid %d terminated by %s\n", pid,
697211562Srpaulo			    proc_signame(WTERMSIG(wstat),
698210236Srpaulo			    name, sizeof (name)));
699211562Srpaulo		} else if (WEXITSTATUS(wstat) != 0) {
700210236Srpaulo			notice("pid %d exited with status %d\n",
701211562Srpaulo			    pid, WEXITSTATUS(wstat));
702210236Srpaulo		} else {
703210236Srpaulo			notice("pid %d has exited\n", pid);
704210236Srpaulo		}
705210236Srpaulo		g_exited = 1;
706210236Srpaulo		break;
707210236Srpaulo
708210236Srpaulo	case PS_LOST:
709210236Srpaulo		notice("pid %d exec'd a set-id or unobservable program\n", pid);
710210236Srpaulo		g_exited = 1;
711210236Srpaulo		break;
712210236Srpaulo	}
713210236Srpaulo}
714210236Srpaulo
715210236Srpaulo/*ARGSUSED*/
716210236Srpaulostatic int
717210236Srpaulochewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg)
718210236Srpaulo{
719210236Srpaulo	dtrace_eprobedesc_t *epd = data->dtpda_edesc;
720210236Srpaulo	dtrace_aggvarid_t aggvars[2];
721210236Srpaulo	const void *buf;
722210236Srpaulo	int i, nagv;
723210236Srpaulo
724210236Srpaulo	/*
725210236Srpaulo	 * A NULL rec indicates that we've processed the last record.
726210236Srpaulo	 */
727210236Srpaulo	if (rec == NULL)
728210236Srpaulo		return (DTRACE_CONSUME_NEXT);
729210236Srpaulo
730210236Srpaulo	buf = data->dtpda_data - rec->dtrd_offset;
731210236Srpaulo
732210236Srpaulo	switch (rec->dtrd_action) {
733210236Srpaulo	case DTRACEACT_DIFEXPR:
734210236Srpaulo		(void) printf("\n%s\n\n", (char *)buf + rec->dtrd_offset);
735210236Srpaulo		if (!g_opt_s) {
736210236Srpaulo			print_legend();
737210236Srpaulo			print_bar();
738210236Srpaulo		}
739210236Srpaulo		return (DTRACE_CONSUME_NEXT);
740210236Srpaulo
741210236Srpaulo	case DTRACEACT_PRINTA:
742210236Srpaulo		for (nagv = 0, i = 0; i < epd->dtepd_nrecs - 1; i++) {
743210236Srpaulo			const dtrace_recdesc_t *nrec = &rec[i];
744210236Srpaulo
745210236Srpaulo			if (nrec->dtrd_uarg != rec->dtrd_uarg)
746210236Srpaulo				break;
747210236Srpaulo
748210236Srpaulo			/*LINTED - alignment*/
749210236Srpaulo			aggvars[nagv++] = *(dtrace_aggvarid_t *)((caddr_t)buf +
750210236Srpaulo			    nrec->dtrd_offset);
751210236Srpaulo		}
752210236Srpaulo
753210236Srpaulo		if (nagv == (g_opt_s ? 1 : 2)) {
754210236Srpaulo			uint_t nent = 0;
755210236Srpaulo			if (dtrace_aggregate_walk_joined(g_dtp, aggvars, nagv,
756210236Srpaulo			    process_aggregate, &nent) != 0)
757210236Srpaulo				dfatal("failed to walk aggregate");
758210236Srpaulo		}
759210236Srpaulo
760210236Srpaulo		return (DTRACE_CONSUME_NEXT);
761210236Srpaulo	}
762210236Srpaulo
763210236Srpaulo	return (DTRACE_CONSUME_THIS);
764210236Srpaulo}
765210236Srpaulo
766210236Srpaulo/*ARGSUSED*/
767210236Srpaulostatic void
768210236Srpaulointr(int signo)
769210236Srpaulo{
770210236Srpaulo	g_intr = 1;
771210236Srpaulo}
772210236Srpaulo
773210236Srpauloint
774210236Srpaulomain(int argc, char **argv)
775210236Srpaulo{
776211562Srpaulo#if defined(sun)
777210236Srpaulo	ucred_t *ucp;
778211562Srpaulo#endif
779210236Srpaulo	int err;
780210236Srpaulo	int opt_C = 0, opt_H = 0, opt_p = 0, opt_v = 0;
781210236Srpaulo	char c, *p, *end;
782210236Srpaulo	struct sigaction act;
783210236Srpaulo	int done = 0;
784210236Srpaulo
785210236Srpaulo	g_pname = basename(argv[0]);
786210236Srpaulo	argv[0] = g_pname; /* rewrite argv[0] for getopt errors */
787211562Srpaulo#if defined(sun)
788210236Srpaulo	/*
789210236Srpaulo	 * Make sure we have the required dtrace_proc privilege.
790210236Srpaulo	 */
791210236Srpaulo	if ((ucp = ucred_get(getpid())) != NULL) {
792210236Srpaulo		const priv_set_t *psp;
793210236Srpaulo		if ((psp = ucred_getprivset(ucp, PRIV_EFFECTIVE)) != NULL &&
794210236Srpaulo		    !priv_ismember(psp, PRIV_DTRACE_PROC)) {
795210236Srpaulo			fatal("dtrace_proc privilege required\n");
796210236Srpaulo		}
797210236Srpaulo
798210236Srpaulo		ucred_free(ucp);
799210236Srpaulo	}
800211562Srpaulo#endif
801210236Srpaulo
802210236Srpaulo	while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {
803210236Srpaulo		switch (c) {
804210236Srpaulo		case 'n':
805210236Srpaulo			errno = 0;
806210236Srpaulo			g_nent = strtoul(optarg, &end, 10);
807210236Srpaulo			if (*end != '\0' || errno != 0) {
808210236Srpaulo				(void) fprintf(stderr, "%s: invalid count "
809210236Srpaulo				    "'%s'\n", g_pname, optarg);
810210236Srpaulo				usage();
811210236Srpaulo			}
812210236Srpaulo			break;
813210236Srpaulo
814210236Srpaulo		case 'p':
815210236Srpaulo			opt_p = 1;
816210236Srpaulo			break;
817210236Srpaulo
818210236Srpaulo		case 'v':
819210236Srpaulo			opt_v = 1;
820210236Srpaulo			break;
821210236Srpaulo
822210236Srpaulo		case 'A':
823210236Srpaulo			opt_C = opt_H = 1;
824210236Srpaulo			break;
825210236Srpaulo
826210236Srpaulo		case 'C':
827210236Srpaulo			opt_C = 1;
828210236Srpaulo			break;
829210236Srpaulo
830210236Srpaulo		case 'H':
831210236Srpaulo			opt_H = 1;
832210236Srpaulo			break;
833210236Srpaulo
834210236Srpaulo		case 'V':
835210236Srpaulo			g_opt_V = 1;
836210236Srpaulo			break;
837210236Srpaulo
838210236Srpaulo		default:
839210236Srpaulo			if (strchr(PLOCKSTAT_OPTSTR, c) == NULL)
840210236Srpaulo				usage();
841210236Srpaulo		}
842210236Srpaulo	}
843210236Srpaulo
844210236Srpaulo	/*
845210236Srpaulo	 * We need a command or at least one pid.
846210236Srpaulo	 */
847210236Srpaulo	if (argc == optind)
848210236Srpaulo		usage();
849210236Srpaulo
850210236Srpaulo	if (opt_C == 0 && opt_H == 0)
851210236Srpaulo		opt_C = 1;
852210236Srpaulo
853210236Srpaulo	if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL)
854210236Srpaulo		fatal("failed to initialize dtrace: %s\n",
855210236Srpaulo		    dtrace_errmsg(NULL, err));
856210236Srpaulo
857210236Srpaulo	/*
858210236Srpaulo	 * The longest string we trace is 23 bytes long -- so 32 is plenty.
859210236Srpaulo	 */
860210236Srpaulo	if (dtrace_setopt(g_dtp, "strsize", "32") == -1)
861210236Srpaulo		dfatal("failed to set 'strsize'");
862210236Srpaulo
863210236Srpaulo	/*
864210236Srpaulo	 * 1k should be more than enough for all trace() and printa() actions.
865210236Srpaulo	 */
866210236Srpaulo	if (dtrace_setopt(g_dtp, "bufsize", "1k") == -1)
867210236Srpaulo		dfatal("failed to set 'bufsize'");
868210236Srpaulo
869210236Srpaulo	/*
870210236Srpaulo	 * The table we produce has the hottest locks at the top.
871210236Srpaulo	 */
872210236Srpaulo	if (dtrace_setopt(g_dtp, "aggsortrev", NULL) == -1)
873210236Srpaulo		dfatal("failed to set 'aggsortrev'");
874210236Srpaulo
875210236Srpaulo	/*
876210236Srpaulo	 * These are two reasonable defaults which should suffice.
877210236Srpaulo	 */
878210236Srpaulo	if (dtrace_setopt(g_dtp, "aggsize", "256k") == -1)
879210236Srpaulo		dfatal("failed to set 'aggsize'");
880210236Srpaulo	if (dtrace_setopt(g_dtp, "aggrate", "1sec") == -1)
881210236Srpaulo		dfatal("failed to set 'aggrate'");
882210236Srpaulo
883210236Srpaulo	/*
884210236Srpaulo	 * Take a second pass through to look for options that set options now
885210236Srpaulo	 * that we have an open dtrace handle.
886210236Srpaulo	 */
887210236Srpaulo	optind = 1;
888210236Srpaulo	while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {
889210236Srpaulo		switch (c) {
890210236Srpaulo		case 's':
891210236Srpaulo			g_opt_s = 1;
892210236Srpaulo			if (dtrace_setopt(g_dtp, "ustackframes", optarg) == -1)
893210236Srpaulo				dfatal("failed to set 'ustackframes'");
894210236Srpaulo			break;
895210236Srpaulo
896210236Srpaulo		case 'x':
897210236Srpaulo			if ((p = strchr(optarg, '=')) != NULL)
898210236Srpaulo				*p++ = '\0';
899210236Srpaulo
900210236Srpaulo			if (dtrace_setopt(g_dtp, optarg, p) != 0)
901210236Srpaulo				dfatal("failed to set -x %s", optarg);
902210236Srpaulo			break;
903210236Srpaulo
904210236Srpaulo		case 'e':
905210236Srpaulo			errno = 0;
906210236Srpaulo			(void) strtoul(optarg, &end, 10);
907210236Srpaulo			if (*optarg == '-' || *end != '\0' || errno != 0) {
908210236Srpaulo				(void) fprintf(stderr, "%s: invalid timeout "
909210236Srpaulo				    "'%s'\n", g_pname, optarg);
910210236Srpaulo				usage();
911210236Srpaulo			}
912210236Srpaulo
913210236Srpaulo			/*
914210236Srpaulo			 * Construct a DTrace enabling that will exit after
915210236Srpaulo			 * the specified number of seconds.
916210236Srpaulo			 */
917210236Srpaulo			dprog_add("BEGIN\n{\n\tend = timestamp + ");
918210236Srpaulo			dprog_add(optarg);
919210236Srpaulo			dprog_add(" * 1000000000;\n}\n");
920210236Srpaulo			dprog_add("tick-10hz\n/timestamp >= end/\n");
921210236Srpaulo			dprog_add("{\n\texit(0);\n}\n");
922210236Srpaulo			break;
923210236Srpaulo		}
924210236Srpaulo	}
925210236Srpaulo
926210236Srpaulo	argc -= optind;
927210236Srpaulo	argv += optind;
928210236Srpaulo
929210236Srpaulo	if (opt_H) {
930210236Srpaulo		dprog_add(g_hold_init);
931211562Srpaulo		if (!g_opt_s)
932210236Srpaulo			dprog_add(g_hold_times);
933210236Srpaulo		else
934210236Srpaulo			dprog_add(g_hold_histogram);
935210236Srpaulo	}
936210236Srpaulo
937210236Srpaulo	if (opt_C) {
938210236Srpaulo		dprog_add(g_ctnd_init);
939211562Srpaulo		if (!g_opt_s)
940210236Srpaulo			dprog_add(g_ctnd_times);
941210236Srpaulo		else
942210236Srpaulo			dprog_add(g_ctnd_histogram);
943210236Srpaulo	}
944210236Srpaulo
945210236Srpaulo	if (opt_p) {
946210236Srpaulo		ulong_t pid;
947210236Srpaulo
948210236Srpaulo		if (argc > 1) {
949210236Srpaulo			(void) fprintf(stderr, "%s: only one pid is allowed\n",
950210236Srpaulo			    g_pname);
951210236Srpaulo			usage();
952210236Srpaulo		}
953210236Srpaulo
954210236Srpaulo		errno = 0;
955210236Srpaulo		pid = strtoul(argv[0], &end, 10);
956210236Srpaulo		if (*end != '\0' || errno != 0 || (pid_t)pid != pid) {
957210236Srpaulo			(void) fprintf(stderr, "%s: invalid pid '%s'\n",
958210236Srpaulo			    g_pname, argv[0]);
959210236Srpaulo			usage();
960210236Srpaulo		}
961210236Srpaulo
962210236Srpaulo		if ((g_pr = dtrace_proc_grab(g_dtp, (pid_t)pid, 0)) == NULL)
963210236Srpaulo			dfatal(NULL);
964210236Srpaulo	} else {
965211562Srpaulo		if ((g_pr = dtrace_proc_create(g_dtp, argv[0], argv, NULL, NULL)) == NULL)
966210236Srpaulo			dfatal(NULL);
967210236Srpaulo	}
968210236Srpaulo
969210236Srpaulo	dprog_compile();
970210236Srpaulo
971210236Srpaulo	if (dtrace_handle_proc(g_dtp, &prochandler, NULL) == -1)
972210236Srpaulo		dfatal("failed to establish proc handler");
973210236Srpaulo
974210236Srpaulo	(void) sigemptyset(&act.sa_mask);
975210236Srpaulo	act.sa_flags = 0;
976210236Srpaulo	act.sa_handler = intr;
977210236Srpaulo	(void) sigaction(SIGINT, &act, NULL);
978210236Srpaulo	(void) sigaction(SIGTERM, &act, NULL);
979210236Srpaulo
980210236Srpaulo	if (dtrace_go(g_dtp) != 0)
981210236Srpaulo		dfatal("dtrace_go()");
982210236Srpaulo
983210236Srpaulo	if (dtrace_getopt(g_dtp, "ustackframes", &g_nframes) != 0)
984210236Srpaulo		dfatal("failed to get 'ustackframes'");
985210236Srpaulo
986210236Srpaulo	dtrace_proc_continue(g_dtp, g_pr);
987210236Srpaulo
988210236Srpaulo	if (opt_v)
989210236Srpaulo		(void) printf("%s: tracing enabled for pid %d\n", g_pname,
990211562Srpaulo#if defined(sun)
991210236Srpaulo		    (int)Pstatus(g_pr)->pr_pid);
992211562Srpaulo#else
993211562Srpaulo		    (int)proc_getpid(g_pr));
994211562Srpaulo#endif
995210236Srpaulo
996210236Srpaulo	do {
997210236Srpaulo		if (!g_intr && !done)
998210236Srpaulo			dtrace_sleep(g_dtp);
999210236Srpaulo
1000210236Srpaulo		if (done || g_intr || g_exited) {
1001210236Srpaulo			done = 1;
1002210236Srpaulo			if (dtrace_stop(g_dtp) == -1)
1003210236Srpaulo				dfatal("couldn't stop tracing");
1004210236Srpaulo		}
1005210236Srpaulo
1006210236Srpaulo		switch (dtrace_work(g_dtp, stdout, NULL, chewrec, NULL)) {
1007210236Srpaulo		case DTRACE_WORKSTATUS_DONE:
1008210236Srpaulo			done = 1;
1009210236Srpaulo			break;
1010210236Srpaulo		case DTRACE_WORKSTATUS_OKAY:
1011210236Srpaulo			break;
1012210236Srpaulo		default:
1013210236Srpaulo			dfatal("processing aborted");
1014210236Srpaulo		}
1015210236Srpaulo
1016210236Srpaulo	} while (!done);
1017210236Srpaulo
1018210236Srpaulo	dtrace_close(g_dtp);
1019210236Srpaulo
1020210236Srpaulo	return (0);
1021210236Srpaulo}
1022