xref: /illumos-gate/usr/src/cmd/plockstat/plockstat.c (revision 0dee7919e2f2a6479d16b370af93747b9416b242)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <dtrace.h>
31 #include <limits.h>
32 #include <link.h>
33 #include <priv.h>
34 #include <signal.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <errno.h>
41 #include <sys/wait.h>
42 #include <libproc.h>
43 
44 static char *g_pname;
45 static dtrace_hdl_t *g_dtp;
46 struct ps_prochandle *g_pr;
47 
48 #define	E_SUCCESS	0
49 #define	E_ERROR		1
50 #define	E_USAGE		2
51 
52 /*
53  * For hold times we use a global associative array since for mutexes, in
54  * user-land, it's not invalid to release a sychonization primitive that
55  * another thread acquired; rwlocks require a thread-local associative array
56  * since multiple thread can hold the same lock for reading. Note that we
57  * ignore recursive mutex acquisitions and releases as they don't truly
58  * affect lock contention.
59  */
60 static const char *g_hold_init =
61 "plockstat$target:::rw-acquire\n"
62 "{\n"
63 "	self->rwhold[arg0] = timestamp;\n"
64 "}\n"
65 "plockstat$target:::mutex-acquire\n"
66 "/arg1 == 0/\n"
67 "{\n"
68 "	mtxhold[arg0] = timestamp;\n"
69 "}\n";
70 
71 static const char *g_hold_histogram =
72 "plockstat$target:::rw-release\n"
73 "/self->rwhold[arg0] && arg1 == 1/\n"
74 "{\n"
75 "	@rw_w_hold[arg0, ustack()] =\n"
76 "	    quantize(timestamp - self->rwhold[arg0]);\n"
77 "	self->rwhold[arg0] = 0;\n"
78 "}\n"
79 "plockstat$target:::rw-release\n"
80 "/self->rwhold[arg0]/\n"
81 "{\n"
82 "	@rw_r_hold[arg0, ustack()] =\n"
83 "	    quantize(timestamp - self->rwhold[arg0]);\n"
84 "	self->rwhold[arg0] = 0;\n"
85 "}\n"
86 "plockstat$target:::mutex-release\n"
87 "/mtxhold[arg0] && arg1 == 0/\n"
88 "{\n"
89 "	@mtx_hold[arg0, ustack()] = quantize(timestamp - mtxhold[arg0]);\n"
90 "	mtxhold[arg0] = 0;\n"
91 "}\n";
92 
93 static const char *g_hold_times =
94 "plockstat$target:::rw-release\n"
95 "/self->rwhold[arg0] && arg1 == 1/\n"
96 "{\n"
97 "	@rw_w_hold[arg0, ustack(5)] = avg(timestamp - self->rwhold[arg0]);\n"
98 "	self->rwhold[arg0] = 0;\n"
99 "}\n"
100 "plockstat$target:::rw-release\n"
101 "/self->rwhold[arg0]/\n"
102 "{\n"
103 "	@rw_r_hold[arg0, ustack(5)] = avg(timestamp - self->rwhold[arg0]);\n"
104 "	self->rwhold[arg0] = 0;\n"
105 "}\n"
106 "plockstat$target:::mutex-release\n"
107 "/mtxhold[arg0] && arg1 == 0/\n"
108 "{\n"
109 "	@mtx_hold[arg0, ustack(5)] = avg(timestamp - mtxhold[arg0]);\n"
110 "	mtxhold[arg0] = 0;\n"
111 "}\n";
112 
113 
114 /*
115  * For contention, we use thread-local associative arrays since we're tracing
116  * a single thread's activity in libc and multiple threads can be blocking or
117  * spinning on the same sychonization primitive.
118  */
119 static const char *g_ctnd_init =
120 "plockstat$target:::rw-block\n"
121 "{\n"
122 "	self->rwblock[arg0] = timestamp;\n"
123 "}\n"
124 "plockstat$target:::mutex-block\n"
125 "{\n"
126 "	self->mtxblock[arg0] = timestamp;\n"
127 "}\n"
128 "plockstat$target:::mutex-spin\n"
129 "{\n"
130 "	self->mtxspin[arg0] = timestamp;\n"
131 "}\n";
132 
133 static const char *g_ctnd_histogram =
134 "plockstat$target:::rw-blocked\n"
135 "/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
136 "{\n"
137 "	@rw_w_block[arg0, ustack()] =\n"
138 "	    quantize(timestamp - self->rwblock[arg0]);\n"
139 "	self->rwblock[arg0] = 0;\n"
140 "}\n"
141 "plockstat$target:::rw-blocked\n"
142 "/self->rwblock[arg0] && arg2 != 0/\n"
143 "{\n"
144 "	@rw_r_block[arg0, ustack()] =\n"
145 "	    quantize(timestamp - self->rwblock[arg0]);\n"
146 "	self->rwblock[arg0] = 0;\n"
147 "}\n"
148 "plockstat$target:::rw-blocked\n"
149 "/self->rwblock[arg0]/\n"
150 "{\n"
151 "	self->rwblock[arg0] = 0;\n"
152 "}\n"
153 "plockstat$target:::mutex-spun\n"
154 "/self->mtxspin[arg0] && arg1 != 0/\n"
155 "{\n"
156 "	@mtx_spin[arg0, ustack()] =\n"
157 "	    quantize(timestamp - self->mtxspin[arg0]);\n"
158 "	self->mtxspin[arg0] = 0;\n"
159 "}\n"
160 "plockstat$target:::mutex-spun\n"
161 "/self->mtxspin[arg0]/\n"
162 "{\n"
163 "	@mtx_vain_spin[arg0, ustack()] =\n"
164 "	    quantize(timestamp - self->mtxspin[arg0]);\n"
165 "	self->mtxspin[arg0] = 0;\n"
166 "}\n"
167 "plockstat$target:::mutex-blocked\n"
168 "/self->mtxblock[arg0] && arg1 != 0/\n"
169 "{\n"
170 "	@mtx_block[arg0, ustack()] =\n"
171 "	    quantize(timestamp - self->mtxblock[arg0]);\n"
172 "	self->mtxblock[arg0] = 0;\n"
173 "}\n"
174 "plockstat$target:::mutex-blocked\n"
175 "/self->mtxblock[arg0]/\n"
176 "{\n"
177 "	self->mtxblock[arg0] = 0;\n"
178 "}\n";
179 
180 
181 static const char *g_ctnd_times =
182 "plockstat$target:::rw-blocked\n"
183 "/self->rwblock[arg0] && arg1 == 1 && arg2 != 0/\n"
184 "{\n"
185 "	@rw_w_block[arg0, ustack(5)] =\n"
186 "	    avg(timestamp - self->rwblock[arg0]);\n"
187 "	self->rwblock[arg0] = 0;\n"
188 "}\n"
189 "plockstat$target:::rw-blocked\n"
190 "/self->rwblock[arg0] && arg2 != 0/\n"
191 "{\n"
192 "	@rw_r_block[arg0, ustack(5)] =\n"
193 "	    avg(timestamp - self->rwblock[arg0]);\n"
194 "	self->rwblock[arg0] = 0;\n"
195 "}\n"
196 "plockstat$target:::rw-blocked\n"
197 "/self->rwblock[arg0]/\n"
198 "{\n"
199 "	self->rwblock[arg0] = 0;\n"
200 "}\n"
201 "plockstat$target:::mutex-spun\n"
202 "/self->mtxspin[arg0] && arg1 != 0/\n"
203 "{\n"
204 "	@mtx_spin[arg0, ustack(5)] =\n"
205 "	    avg(timestamp - self->mtxspin[arg0]);\n"
206 "	self->mtxspin[arg0] = 0;\n"
207 "}\n"
208 "plockstat$target:::mutex-spun\n"
209 "/self->mtxspin[arg0]/\n"
210 "{\n"
211 "	@mtx_vain_spin[arg0, ustack(5)] =\n"
212 "	    avg(timestamp - self->mtxspin[arg0]);\n"
213 "	self->mtxspin[arg0] = 0;\n"
214 "}\n"
215 "plockstat$target:::mutex-blocked\n"
216 "/self->mtxblock[arg0] && arg1 != 0/\n"
217 "{\n"
218 "	@mtx_block[arg0, ustack(5)] =\n"
219 "	    avg(timestamp - self->mtxblock[arg0]);\n"
220 "	self->mtxblock[arg0] = 0;\n"
221 "}\n"
222 "plockstat$target:::mutex-blocked\n"
223 "/self->mtxblock[arg0]/\n"
224 "{\n"
225 "	self->mtxblock[arg0] = 0;\n"
226 "}\n";
227 
228 static char g_prog[2048];
229 static size_t g_proglen;
230 static int g_opt_V, g_opt_s;
231 static int g_intr;
232 static dtrace_optval_t g_nframes;
233 static ulong_t g_nent = ULONG_MAX;
234 
235 #define	PLOCKSTAT_OPTSTR	"n:ps:e:vx:ACHV"
236 
237 static void
238 usage(void)
239 {
240 	(void) fprintf(stderr, "Usage:\n"
241 	    "\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
242 	    "\t    command [arg...]\n"
243 	    "\t%s [-vACHV] [-n count] [-s depth] [-e secs] [-x opt[=val]]\n"
244 	    "\t    -p pid\n", g_pname, g_pname);
245 
246 	exit(E_USAGE);
247 }
248 
249 static void
250 verror(const char *fmt, va_list ap)
251 {
252 	int error = errno;
253 
254 	(void) fprintf(stderr, "%s: ", g_pname);
255 	(void) vfprintf(stderr, fmt, ap);
256 
257 	if (fmt[strlen(fmt) - 1] != '\n')
258 		(void) fprintf(stderr, ": %s\n", strerror(error));
259 }
260 
261 /*PRINTFLIKE1*/
262 static void
263 fatal(const char *fmt, ...)
264 {
265 	va_list ap;
266 
267 	va_start(ap, fmt);
268 	verror(fmt, ap);
269 	va_end(ap);
270 
271 	if (g_pr != NULL && g_dtp != NULL)
272 		dtrace_proc_release(g_dtp, g_pr);
273 
274 	exit(E_ERROR);
275 }
276 
277 /*PRINTFLIKE1*/
278 static void
279 dfatal(const char *fmt, ...)
280 {
281 	va_list ap;
282 
283 	va_start(ap, fmt);
284 
285 	(void) fprintf(stderr, "%s: ", g_pname);
286 	if (fmt != NULL)
287 		(void) vfprintf(stderr, fmt, ap);
288 
289 	va_end(ap);
290 
291 	if (fmt != NULL && fmt[strlen(fmt) - 1] != '\n') {
292 		(void) fprintf(stderr, ": %s\n",
293 		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
294 	} else if (fmt == NULL) {
295 		(void) fprintf(stderr, "%s\n",
296 		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
297 	}
298 
299 	if (g_pr != NULL) {
300 		dtrace_proc_continue(g_dtp, g_pr);
301 		dtrace_proc_release(g_dtp, g_pr);
302 	}
303 
304 	exit(E_ERROR);
305 }
306 
307 /*PRINTFLIKE1*/
308 static void
309 notice(const char *fmt, ...)
310 {
311 	va_list ap;
312 
313 	va_start(ap, fmt);
314 	verror(fmt, ap);
315 	va_end(ap);
316 }
317 
318 static void
319 dprog_add(const char *prog)
320 {
321 	size_t len = strlen(prog);
322 	bcopy(prog, g_prog + g_proglen, len + 1);
323 	g_proglen += len;
324 }
325 
326 static void
327 dprog_compile(void)
328 {
329 	dtrace_prog_t *prog;
330 	dtrace_proginfo_t info;
331 
332 	if (g_opt_V) {
333 		(void) fprintf(stderr, "%s: vvvv D program vvvv\n", g_pname);
334 		(void) fputs(g_prog, stderr);
335 		(void) fprintf(stderr, "%s: ^^^^ D program ^^^^\n", g_pname);
336 	}
337 
338 	if ((prog = dtrace_program_strcompile(g_dtp, g_prog,
339 	    DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL)
340 		dfatal("failed to compile program");
341 
342 	if (dtrace_program_exec(g_dtp, prog, &info) == -1)
343 		dfatal("failed to enable probes");
344 }
345 
346 static void
347 print_header(const char *aggname)
348 {
349 	if (strcmp(aggname, "mtx_hold") == 0) {
350 		(void) printf("\nMutex hold\n\n");
351 	} else if (strcmp(aggname, "mtx_block") == 0) {
352 		(void) printf("\nMutex block\n\n");
353 	} else if (strcmp(aggname, "mtx_spin") == 0) {
354 		(void) printf("\nMutex spin\n\n");
355 	} else if (strcmp(aggname, "mtx_vain_spin") == 0) {
356 		(void) printf("\nMutex unsuccessful spin\n\n");
357 	} else if (strcmp(aggname, "rw_r_hold") == 0) {
358 		(void) printf("\nR/W reader hold\n\n");
359 	} else if (strcmp(aggname, "rw_w_hold") == 0) {
360 		(void) printf("\nR/W writer hold\n\n");
361 	} else if (strcmp(aggname, "rw_r_block") == 0) {
362 		(void) printf("\nR/W reader block\n\n");
363 	} else if (strcmp(aggname, "rw_w_block") == 0) {
364 		(void) printf("\nR/W writer block\n\n");
365 	}
366 }
367 
368 void
369 print_legend(void)
370 {
371 	(void) printf("%5s %8s %-28s %s\n", "Count", "nsec", "Lock", "Caller");
372 }
373 
374 void
375 print_bar(void)
376 {
377 	(void) printf("---------------------------------------"
378 	    "----------------------------------------\n");
379 }
380 
381 void
382 print_histogram_header(void)
383 {
384 	(void) printf("\n%10s ---- Time Distribution --- %5s %s\n",
385 	    "nsec", "count", "Stack");
386 }
387 
388 /*
389  * Convert an address to a symbolic string or a numeric string. If nolocks
390  * is set, we return an error code if this symbol appears to be a mutex- or
391  * rwlock-related symbol in libc so the caller has a chance to find a more
392  * helpful symbol.
393  */
394 static int
395 getsym(struct ps_prochandle *P, uintptr_t addr, char *buf, size_t size,
396     int nolocks)
397 {
398 	char name[256];
399 	GElf_Sym sym;
400 	prsyminfo_t info;
401 	size_t len;
402 
403 	if (P == NULL || Pxlookup_by_addr(P, addr, name, sizeof (name),
404 	    &sym, &info) != 0) {
405 		(void) snprintf(buf, size, "%#lx", addr);
406 		return (0);
407 	}
408 
409 	if (info.prs_lmid != LM_ID_BASE) {
410 		len = snprintf(buf, size, "LM%lu`", info.prs_lmid);
411 		buf += len;
412 		size -= len;
413 	}
414 
415 	len = snprintf(buf, size, "%s`%s", info.prs_object, info.prs_name);
416 	buf += len;
417 	size -= len;
418 
419 	if (sym.st_value != addr)
420 		len = snprintf(buf, size, "+%#lx", addr - sym.st_value);
421 
422 	if (nolocks && strcmp("libc.so.1", info.prs_object) == 0 &&
423 	    (strstr("mutex", info.prs_name) == 0 ||
424 	    strstr("rw", info.prs_name) == 0))
425 		return (-1);
426 
427 	return (0);
428 }
429 
430 /*ARGSUSED*/
431 static int
432 process_aggregate(const dtrace_aggdata_t *agg, void *arg)
433 {
434 	static dtrace_aggid_t last = DTRACE_AGGIDNONE;
435 	static uint_t nent;
436 	const dtrace_aggdesc_t *aggdesc = agg->dtada_desc;
437 	caddr_t data = agg->dtada_data;
438 	const dtrace_recdesc_t *rec;
439 	uint64_t *a, count, avg;
440 	char buf[256];
441 	uintptr_t lock;
442 	pid_t pid;
443 	uint64_t *stack;
444 	struct ps_prochandle *P;
445 	int i, j;
446 
447 	if (aggdesc->dtagd_id != last) {
448 		print_header(aggdesc->dtagd_name);
449 		nent = 0;
450 	}
451 	if (nent >= g_nent)
452 		goto out;
453 	nent++;
454 
455 	rec = aggdesc->dtagd_rec;
456 
457 	/*LINTED - alignment*/
458 	lock = (uintptr_t)*(uint64_t *)(data + rec[1].dtrd_offset);
459 	/*LINTED - alignment*/
460 	stack = (uint64_t *)(data + rec[2].dtrd_offset);
461 	/*LINTED - alignment*/
462 	a = (uint64_t *)(data + rec[aggdesc->dtagd_nrecs - 1].dtrd_offset);
463 
464 	if (!g_opt_s) {
465 		if (aggdesc->dtagd_id != last) {
466 			print_legend();
467 			print_bar();
468 		}
469 
470 		count = a[0];
471 		avg = a[1] / a[0];
472 	} else {
473 		print_bar();
474 		print_legend();
475 		count = avg = 0;
476 
477 		for (i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0;
478 		    i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) {
479 			count += a[i];
480 			avg += a[i] << (j - 64);
481 		}
482 
483 		avg /= count;
484 	}
485 
486 	(void) printf("%5llu %8llu ", (u_longlong_t)count, (u_longlong_t)avg);
487 
488 	pid = stack[0];
489 	P = dtrace_proc_grab(g_dtp, pid, PGRAB_RDONLY);
490 
491 	(void) getsym(P, lock, buf, sizeof (buf), 0);
492 	(void) printf("%-28s ", buf);
493 
494 	for (i = 2; i <= 5; i++) {
495 		if (getsym(P, stack[i], buf, sizeof (buf), 1) == 0)
496 			break;
497 	}
498 	(void) printf("%s\n", buf);
499 
500 	if (g_opt_s) {
501 		int stack_done = 0;
502 		int quant_done = 0;
503 		int first_bin, last_bin;
504 		uint64_t bin_size;
505 
506 		print_histogram_header();
507 
508 		for (first_bin = DTRACE_QUANTIZE_ZEROBUCKET;
509 		    a[first_bin] == 0; first_bin++)
510 			continue;
511 		for (last_bin = DTRACE_QUANTIZE_ZEROBUCKET + 63;
512 		    a[last_bin] == 0; last_bin--)
513 			continue;
514 
515 		for (i = 0; !stack_done || !quant_done; i++) {
516 			if (!stack_done) {
517 				(void) getsym(P, stack[i + 2], buf,
518 				    sizeof (buf), 0);
519 			} else {
520 				buf[0] = '\0';
521 			}
522 
523 			if (!quant_done) {
524 				bin_size = a[first_bin];
525 
526 				(void) printf("%10llu |%-24.*s| %5llu %s\n",
527 				    1ULL <<
528 				    (first_bin - DTRACE_QUANTIZE_ZEROBUCKET),
529 				    (int)(24.0 * bin_size / count),
530 				    "@@@@@@@@@@@@@@@@@@@@@@@@@@",
531 				    (u_longlong_t)bin_size, buf);
532 			} else {
533 				(void) printf("%43s %s\n", "", buf);
534 			}
535 
536 			if (i + 1 >= g_nframes || stack[i + 3] == 0)
537 				stack_done = 1;
538 
539 			if (first_bin++ == last_bin)
540 				quant_done = 1;
541 		}
542 	}
543 
544 	dtrace_proc_release(g_dtp, P);
545 
546 out:
547 	last = aggdesc->dtagd_id;
548 
549 	return (DTRACE_AGGWALK_NEXT);
550 }
551 
552 static int
553 prochandler(struct ps_prochandle *P)
554 {
555 	const psinfo_t *prp = Ppsinfo(P);
556 	int pid = Pstatus(P)->pr_pid;
557 	char name[SIG2STR_MAX];
558 
559 	switch (Pstate(P)) {
560 	case PS_UNDEAD:
561 		/*
562 		 * Ideally we would like to always report pr_wstat here, but it
563 		 * isn't possible given current /proc semantics.  If we grabbed
564 		 * the process, Ppsinfo() will either fail or return a zeroed
565 		 * psinfo_t depending on how far the parent is in reaping it.
566 		 * When /proc provides a stable pr_wstat in the status file,
567 		 * this code can be improved by examining this new pr_wstat.
568 		 */
569 		if (prp != NULL && WIFSIGNALED(prp->pr_wstat)) {
570 			notice("pid %d terminated by %s\n", pid,
571 			    proc_signame(WTERMSIG(prp->pr_wstat),
572 			    name, sizeof (name)));
573 		} else if (prp != NULL && WEXITSTATUS(prp->pr_wstat) != 0) {
574 			notice("pid %d exited with status %d\n",
575 			    pid, WEXITSTATUS(prp->pr_wstat));
576 		} else {
577 			notice("pid %d has exited\n", pid);
578 		}
579 		return (1);
580 
581 	case PS_LOST:
582 		notice("pid %d exec'd a set-id or unobservable program\n", pid);
583 		return (1);
584 	}
585 
586 	return (0);
587 }
588 
589 /*ARGSUSED*/
590 static void
591 intr(int signo)
592 {
593 	g_intr = 1;
594 }
595 
596 int
597 main(int argc, char **argv)
598 {
599 	ucred_t *ucp;
600 	int err;
601 	int opt_C = 0, opt_H = 0, opt_p = 0, opt_v = 0;
602 	char c, *p, *end;
603 	struct sigaction act;
604 	int done = 0;
605 
606 	if ((g_pname = strrchr(argv[0], '/')) == NULL)
607 		g_pname = argv[0];
608 	else
609 		argv[0] = ++g_pname;		/* for getopt() */
610 
611 	/*
612 	 * Make sure we have the required dtrace_proc privilege.
613 	 */
614 	if ((ucp = ucred_get(getpid())) != NULL) {
615 		const priv_set_t *psp;
616 		if ((psp = ucred_getprivset(ucp, PRIV_EFFECTIVE)) != NULL &&
617 		    !priv_ismember(psp, PRIV_DTRACE_PROC)) {
618 			fatal("dtrace_proc privilege required\n");
619 		}
620 
621 		ucred_free(ucp);
622 	}
623 
624 	while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {
625 		switch (c) {
626 		case 'n':
627 			errno = 0;
628 			g_nent = strtoul(optarg, &end, 10);
629 			if (*end != '\0' || errno != 0) {
630 				(void) fprintf(stderr, "%s: invalid count "
631 				    "'%s'\n", g_pname, optarg);
632 				usage();
633 			}
634 			break;
635 
636 		case 'p':
637 			opt_p = 1;
638 			break;
639 
640 		case 'v':
641 			opt_v = 1;
642 			break;
643 
644 		case 'A':
645 			opt_C = opt_H = 1;
646 			break;
647 
648 		case 'C':
649 			opt_C = 1;
650 			break;
651 
652 		case 'H':
653 			opt_H = 1;
654 			break;
655 
656 		case 'V':
657 			g_opt_V = 1;
658 			break;
659 
660 		default:
661 			if (strchr(PLOCKSTAT_OPTSTR, c) == NULL)
662 				usage();
663 		}
664 	}
665 
666 	/*
667 	 * We need a command or at least one pid.
668 	 */
669 	if (argc == optind)
670 		usage();
671 
672 	if (opt_C == 0 && opt_H == 0)
673 		opt_C = 1;
674 
675 	if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL)
676 		fatal("failed to initialize dtrace: %s\n",
677 		    dtrace_errmsg(NULL, err));
678 
679 	if (dtrace_setopt(g_dtp, "aggsize", "256k") == -1)
680 		dfatal("failed to set 'aggsize'");
681 
682 	if (dtrace_setopt(g_dtp, "aggrate", "1sec") == -1)
683 		dfatal("failed to set 'aggrate'");
684 
685 	/*
686 	 * Take a second pass through to look for options that set options now
687 	 * that we have an open dtrace handle.
688 	 */
689 	optind = 1;
690 	while ((c = getopt(argc, argv, PLOCKSTAT_OPTSTR)) != EOF) {
691 		switch (c) {
692 		case 's':
693 			g_opt_s = 1;
694 			if (dtrace_setopt(g_dtp, "ustackframes", optarg) == -1)
695 				dfatal("failed to set 'ustackframes'");
696 			break;
697 
698 		case 'x':
699 			if ((p = strchr(optarg, '=')) != NULL)
700 				*p++ = '\0';
701 
702 			if (dtrace_setopt(g_dtp, optarg, p) != 0)
703 				dfatal("failed to set -x %s", optarg);
704 			break;
705 
706 		case 'e':
707 			errno = 0;
708 			(void) strtoul(optarg, &end, 10);
709 			if (*optarg == '-' || *end != '\0' || errno != 0) {
710 				(void) fprintf(stderr, "%s: invalid timeout "
711 				    "'%s'\n", g_pname, optarg);
712 				usage();
713 			}
714 
715 			/*
716 			 * Construct a DTrace enabling that will exit after
717 			 * the specified number of seconds.
718 			 */
719 			dprog_add("BEGIN\n{\n\tend = timestamp + ");
720 			dprog_add(optarg);
721 			dprog_add(" * 1000000000;\n}\n");
722 			dprog_add("tick-10hz\n/timestamp >= end/\n");
723 			dprog_add("{\n\texit(0);\n}\n");
724 			break;
725 		}
726 	}
727 
728 	argc -= optind;
729 	argv += optind;
730 
731 	if (opt_H) {
732 		dprog_add(g_hold_init);
733 		if (g_opt_s == NULL)
734 			dprog_add(g_hold_times);
735 		else
736 			dprog_add(g_hold_histogram);
737 	}
738 
739 	if (opt_C) {
740 		dprog_add(g_ctnd_init);
741 		if (g_opt_s == NULL)
742 			dprog_add(g_ctnd_times);
743 		else
744 			dprog_add(g_ctnd_histogram);
745 	}
746 
747 	if (opt_p) {
748 		ulong_t pid;
749 
750 		if (argc > 1) {
751 			(void) fprintf(stderr, "%s: only one pid is allowed\n",
752 			    g_pname);
753 			usage();
754 		}
755 
756 		errno = 0;
757 		pid = strtoul(argv[0], &end, 10);
758 		if (*end != '\0' || errno != 0 || (pid_t)pid != pid) {
759 			(void) fprintf(stderr, "%s: invalid pid '%s'\n",
760 			    g_pname, argv[0]);
761 			usage();
762 		}
763 
764 		if ((g_pr = dtrace_proc_grab(g_dtp, (pid_t)pid, 0)) == NULL)
765 			dfatal(NULL);
766 	} else {
767 		if ((g_pr = dtrace_proc_create(g_dtp, argv[0], argv)) == NULL)
768 			dfatal(NULL);
769 	}
770 
771 	dprog_compile();
772 
773 	(void) sigemptyset(&act.sa_mask);
774 	act.sa_flags = 0;
775 	act.sa_handler = intr;
776 	(void) sigaction(SIGINT, &act, NULL);
777 	(void) sigaction(SIGTERM, &act, NULL);
778 
779 	if (dtrace_go(g_dtp) != 0)
780 		dfatal("dtrace_go()");
781 
782 	if (dtrace_getopt(g_dtp, "ustackframes", &g_nframes) != 0)
783 		dfatal("failed to get 'ustackframes'");
784 
785 	dtrace_proc_continue(g_dtp, g_pr);
786 
787 	if (opt_v)
788 		(void) printf("%s: tracing enabled for pid %d\n", g_pname,
789 		    (int)Pstatus(g_pr)->pr_pid);
790 
791 	while (!done) {
792 		(void) sleep(1);
793 
794 		if (dtrace_status(g_dtp) == DTRACE_STATUS_EXITED)
795 			done = 1;
796 
797 		/* Done if the user hits control-C. */
798 		if (g_intr)
799 			done = 1;
800 
801 		if (prochandler(g_pr) == 1)
802 			done = 1;
803 
804 		if (dtrace_aggregate_snap(g_dtp) != 0)
805 			dfatal("failed to add to aggregate");
806 	}
807 
808 	if (dtrace_aggregate_snap(g_dtp) != 0)
809 		dfatal("failed to add to aggregate");
810 
811 	if (dtrace_aggregate_walk_valrevsorted(g_dtp,
812 	    process_aggregate, NULL) != 0)
813 		dfatal("failed to print aggregations");
814 
815 	dtrace_close(g_dtp);
816 
817 	return (0);
818 }
819