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