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