1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #include <stdio.h> 30*7c478bd9Sstevel@tonic-gate #include <stddef.h> 31*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 32*7c478bd9Sstevel@tonic-gate #include <stdarg.h> 33*7c478bd9Sstevel@tonic-gate #include <string.h> 34*7c478bd9Sstevel@tonic-gate #include <strings.h> 35*7c478bd9Sstevel@tonic-gate #include <ctype.h> 36*7c478bd9Sstevel@tonic-gate #include <fcntl.h> 37*7c478bd9Sstevel@tonic-gate #include <unistd.h> 38*7c478bd9Sstevel@tonic-gate #include <errno.h> 39*7c478bd9Sstevel@tonic-gate #include <limits.h> 40*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 41*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 42*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 43*7c478bd9Sstevel@tonic-gate #include <sys/wait.h> 44*7c478bd9Sstevel@tonic-gate #include <dtrace.h> 45*7c478bd9Sstevel@tonic-gate #include <sys/lockstat.h> 46*7c478bd9Sstevel@tonic-gate #include <alloca.h> 47*7c478bd9Sstevel@tonic-gate #include <signal.h> 48*7c478bd9Sstevel@tonic-gate #include <assert.h> 49*7c478bd9Sstevel@tonic-gate 50*7c478bd9Sstevel@tonic-gate #define LOCKSTAT_OPTSTR "x:bths:n:d:i:l:f:e:ckwWgCHEATID:RpPo:V" 51*7c478bd9Sstevel@tonic-gate 52*7c478bd9Sstevel@tonic-gate #define LS_MAX_STACK_DEPTH 50 53*7c478bd9Sstevel@tonic-gate #define LS_MAX_EVENTS 64 54*7c478bd9Sstevel@tonic-gate 55*7c478bd9Sstevel@tonic-gate typedef struct lsrec { 56*7c478bd9Sstevel@tonic-gate struct lsrec *ls_next; /* next in hash chain */ 57*7c478bd9Sstevel@tonic-gate uintptr_t ls_lock; /* lock address */ 58*7c478bd9Sstevel@tonic-gate uintptr_t ls_caller; /* caller address */ 59*7c478bd9Sstevel@tonic-gate uint32_t ls_count; /* cumulative event count */ 60*7c478bd9Sstevel@tonic-gate uint32_t ls_event; /* type of event */ 61*7c478bd9Sstevel@tonic-gate uintptr_t ls_refcnt; /* cumulative reference count */ 62*7c478bd9Sstevel@tonic-gate uint64_t ls_time; /* cumulative event duration */ 63*7c478bd9Sstevel@tonic-gate uint32_t ls_hist[64]; /* log2(duration) histogram */ 64*7c478bd9Sstevel@tonic-gate uintptr_t ls_stack[LS_MAX_STACK_DEPTH]; 65*7c478bd9Sstevel@tonic-gate } lsrec_t; 66*7c478bd9Sstevel@tonic-gate 67*7c478bd9Sstevel@tonic-gate typedef struct lsdata { 68*7c478bd9Sstevel@tonic-gate struct lsrec *lsd_next; /* next available */ 69*7c478bd9Sstevel@tonic-gate int lsd_count; /* number of records */ 70*7c478bd9Sstevel@tonic-gate } lsdata_t; 71*7c478bd9Sstevel@tonic-gate 72*7c478bd9Sstevel@tonic-gate /* 73*7c478bd9Sstevel@tonic-gate * Definitions for the types of experiments which can be run. They are 74*7c478bd9Sstevel@tonic-gate * listed in increasing order of memory cost and processing time cost. 75*7c478bd9Sstevel@tonic-gate * The numerical value of each type is the number of bytes needed per record. 76*7c478bd9Sstevel@tonic-gate */ 77*7c478bd9Sstevel@tonic-gate #define LS_BASIC offsetof(lsrec_t, ls_time) 78*7c478bd9Sstevel@tonic-gate #define LS_TIME offsetof(lsrec_t, ls_hist[0]) 79*7c478bd9Sstevel@tonic-gate #define LS_HIST offsetof(lsrec_t, ls_stack[0]) 80*7c478bd9Sstevel@tonic-gate #define LS_STACK(depth) offsetof(lsrec_t, ls_stack[depth]) 81*7c478bd9Sstevel@tonic-gate 82*7c478bd9Sstevel@tonic-gate static void report_stats(FILE *, lsrec_t **, size_t, uint64_t, uint64_t); 83*7c478bd9Sstevel@tonic-gate static void report_trace(FILE *, lsrec_t **); 84*7c478bd9Sstevel@tonic-gate 85*7c478bd9Sstevel@tonic-gate extern int symtab_init(void); 86*7c478bd9Sstevel@tonic-gate extern char *addr_to_sym(uintptr_t, uintptr_t *, size_t *); 87*7c478bd9Sstevel@tonic-gate extern uintptr_t sym_to_addr(char *name); 88*7c478bd9Sstevel@tonic-gate extern size_t sym_size(char *name); 89*7c478bd9Sstevel@tonic-gate extern char *strtok_r(char *, const char *, char **); 90*7c478bd9Sstevel@tonic-gate 91*7c478bd9Sstevel@tonic-gate #define DEFAULT_NRECS 10000 92*7c478bd9Sstevel@tonic-gate #define DEFAULT_HZ 97 93*7c478bd9Sstevel@tonic-gate #define MAX_HZ 1000 94*7c478bd9Sstevel@tonic-gate #define MIN_AGGSIZE (16 * 1024) 95*7c478bd9Sstevel@tonic-gate #define MAX_AGGSIZE (32 * 1024 * 1024) 96*7c478bd9Sstevel@tonic-gate 97*7c478bd9Sstevel@tonic-gate static int g_stkdepth; 98*7c478bd9Sstevel@tonic-gate static int g_topn = INT_MAX; 99*7c478bd9Sstevel@tonic-gate static hrtime_t g_elapsed; 100*7c478bd9Sstevel@tonic-gate static int g_rates = 0; 101*7c478bd9Sstevel@tonic-gate static int g_pflag = 0; 102*7c478bd9Sstevel@tonic-gate static int g_Pflag = 0; 103*7c478bd9Sstevel@tonic-gate static int g_wflag = 0; 104*7c478bd9Sstevel@tonic-gate static int g_Wflag = 0; 105*7c478bd9Sstevel@tonic-gate static int g_cflag = 0; 106*7c478bd9Sstevel@tonic-gate static int g_kflag = 0; 107*7c478bd9Sstevel@tonic-gate static int g_gflag = 0; 108*7c478bd9Sstevel@tonic-gate static int g_Vflag = 0; 109*7c478bd9Sstevel@tonic-gate static int g_tracing = 0; 110*7c478bd9Sstevel@tonic-gate static size_t g_recsize; 111*7c478bd9Sstevel@tonic-gate static size_t g_nrecs; 112*7c478bd9Sstevel@tonic-gate static int g_nrecs_used; 113*7c478bd9Sstevel@tonic-gate static uchar_t g_enabled[LS_MAX_EVENTS]; 114*7c478bd9Sstevel@tonic-gate static hrtime_t g_min_duration[LS_MAX_EVENTS]; 115*7c478bd9Sstevel@tonic-gate static dtrace_hdl_t *g_dtp; 116*7c478bd9Sstevel@tonic-gate static char *g_predicate; 117*7c478bd9Sstevel@tonic-gate static char *g_ipredicate; 118*7c478bd9Sstevel@tonic-gate static char *g_prog; 119*7c478bd9Sstevel@tonic-gate static int g_proglen; 120*7c478bd9Sstevel@tonic-gate static int g_dropped; 121*7c478bd9Sstevel@tonic-gate 122*7c478bd9Sstevel@tonic-gate typedef struct ls_event_info { 123*7c478bd9Sstevel@tonic-gate char ev_type; 124*7c478bd9Sstevel@tonic-gate char ev_lhdr[20]; 125*7c478bd9Sstevel@tonic-gate char ev_desc[80]; 126*7c478bd9Sstevel@tonic-gate char ev_units[10]; 127*7c478bd9Sstevel@tonic-gate char ev_name[DTRACE_NAMELEN]; 128*7c478bd9Sstevel@tonic-gate char *ev_predicate; 129*7c478bd9Sstevel@tonic-gate char *ev_acquire; 130*7c478bd9Sstevel@tonic-gate } ls_event_info_t; 131*7c478bd9Sstevel@tonic-gate 132*7c478bd9Sstevel@tonic-gate static ls_event_info_t g_event_info[LS_MAX_EVENTS] = { 133*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Adaptive mutex spin", "spin", 134*7c478bd9Sstevel@tonic-gate "lockstat:::adaptive-spin" }, 135*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Adaptive mutex block", "nsec", 136*7c478bd9Sstevel@tonic-gate "lockstat:::adaptive-block" }, 137*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Spin lock spin", "spin", 138*7c478bd9Sstevel@tonic-gate "lockstat:::spin-spin" }, 139*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Thread lock spin", "spin", 140*7c478bd9Sstevel@tonic-gate "lockstat:::thread-spin" }, 141*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "R/W writer blocked by writer", "nsec", 142*7c478bd9Sstevel@tonic-gate "lockstat:::rw-block", "arg2 == 0 && arg3 == 1" }, 143*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "R/W writer blocked by readers", "nsec", 144*7c478bd9Sstevel@tonic-gate "lockstat:::rw-block", "arg2 == 0 && arg3 == 0 && arg4" }, 145*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "R/W reader blocked by writer", "nsec", 146*7c478bd9Sstevel@tonic-gate "lockstat:::rw-block", "arg2 != 0 && arg3 == 1" }, 147*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "R/W reader blocked by write wanted", "nsec", 148*7c478bd9Sstevel@tonic-gate "lockstat:::rw-block", "arg2 != 0 && arg3 == 0 && arg4" }, 149*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 8)", "units" }, 150*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 9)", "units" }, 151*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 10)", "units" }, 152*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 11)", "units" }, 153*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 12)", "units" }, 154*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 13)", "units" }, 155*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 14)", "units" }, 156*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 15)", "units" }, 157*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 16)", "units" }, 158*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 17)", "units" }, 159*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 18)", "units" }, 160*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 19)", "units" }, 161*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 20)", "units" }, 162*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 21)", "units" }, 163*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 22)", "units" }, 164*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 23)", "units" }, 165*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 24)", "units" }, 166*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 25)", "units" }, 167*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 26)", "units" }, 168*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 27)", "units" }, 169*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 28)", "units" }, 170*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 29)", "units" }, 171*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 30)", "units" }, 172*7c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 31)", "units" }, 173*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Adaptive mutex hold", "nsec", 174*7c478bd9Sstevel@tonic-gate "lockstat:::adaptive-release", NULL, 175*7c478bd9Sstevel@tonic-gate "lockstat:::adaptive-acquire" }, 176*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Spin lock hold", "nsec", 177*7c478bd9Sstevel@tonic-gate "lockstat:::spin-release", NULL, 178*7c478bd9Sstevel@tonic-gate "lockstat:::spin-acquire" }, 179*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "R/W writer hold", "nsec", 180*7c478bd9Sstevel@tonic-gate "lockstat:::rw-release", "arg1 == 0", 181*7c478bd9Sstevel@tonic-gate "lockstat:::rw-acquire" }, 182*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "R/W reader hold", "nsec", 183*7c478bd9Sstevel@tonic-gate "lockstat:::rw-release", "arg1 != 0", 184*7c478bd9Sstevel@tonic-gate "lockstat:::rw-acquire" }, 185*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 36)", "units" }, 186*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 37)", "units" }, 187*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 38)", "units" }, 188*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 39)", "units" }, 189*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 40)", "units" }, 190*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 41)", "units" }, 191*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 42)", "units" }, 192*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 43)", "units" }, 193*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 44)", "units" }, 194*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 45)", "units" }, 195*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 46)", "units" }, 196*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 47)", "units" }, 197*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 48)", "units" }, 198*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 49)", "units" }, 199*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 50)", "units" }, 200*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 51)", "units" }, 201*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 52)", "units" }, 202*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 53)", "units" }, 203*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 54)", "units" }, 204*7c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 55)", "units" }, 205*7c478bd9Sstevel@tonic-gate { 'I', "CPU+PIL", "Profiling interrupt", "nsec", 206*7c478bd9Sstevel@tonic-gate "profile:::profile-97", NULL }, 207*7c478bd9Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 57)", "units" }, 208*7c478bd9Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 58)", "units" }, 209*7c478bd9Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 59)", "units" }, 210*7c478bd9Sstevel@tonic-gate { 'E', "Lock", "Recursive lock entry detected", "(N/A)", 211*7c478bd9Sstevel@tonic-gate "lockstat:::rw-release", NULL, "lockstat:::rw-acquire" }, 212*7c478bd9Sstevel@tonic-gate { 'E', "Lock", "Lockstat enter failure", "(N/A)" }, 213*7c478bd9Sstevel@tonic-gate { 'E', "Lock", "Lockstat exit failure", "nsec" }, 214*7c478bd9Sstevel@tonic-gate { 'E', "Lock", "Lockstat record failure", "(N/A)" }, 215*7c478bd9Sstevel@tonic-gate }; 216*7c478bd9Sstevel@tonic-gate 217*7c478bd9Sstevel@tonic-gate static void 218*7c478bd9Sstevel@tonic-gate fail(int do_perror, const char *message, ...) 219*7c478bd9Sstevel@tonic-gate { 220*7c478bd9Sstevel@tonic-gate va_list args; 221*7c478bd9Sstevel@tonic-gate int save_errno = errno; 222*7c478bd9Sstevel@tonic-gate 223*7c478bd9Sstevel@tonic-gate va_start(args, message); 224*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: "); 225*7c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, message, args); 226*7c478bd9Sstevel@tonic-gate va_end(args); 227*7c478bd9Sstevel@tonic-gate if (do_perror) 228*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s", strerror(save_errno)); 229*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 230*7c478bd9Sstevel@tonic-gate exit(2); 231*7c478bd9Sstevel@tonic-gate } 232*7c478bd9Sstevel@tonic-gate 233*7c478bd9Sstevel@tonic-gate static void 234*7c478bd9Sstevel@tonic-gate dfail(const char *message, ...) 235*7c478bd9Sstevel@tonic-gate { 236*7c478bd9Sstevel@tonic-gate va_list args; 237*7c478bd9Sstevel@tonic-gate 238*7c478bd9Sstevel@tonic-gate va_start(args, message); 239*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: "); 240*7c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, message, args); 241*7c478bd9Sstevel@tonic-gate va_end(args); 242*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", 243*7c478bd9Sstevel@tonic-gate dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); 244*7c478bd9Sstevel@tonic-gate 245*7c478bd9Sstevel@tonic-gate exit(2); 246*7c478bd9Sstevel@tonic-gate } 247*7c478bd9Sstevel@tonic-gate 248*7c478bd9Sstevel@tonic-gate static void 249*7c478bd9Sstevel@tonic-gate show_events(char event_type, char *desc) 250*7c478bd9Sstevel@tonic-gate { 251*7c478bd9Sstevel@tonic-gate int i, first = -1, last; 252*7c478bd9Sstevel@tonic-gate 253*7c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 254*7c478bd9Sstevel@tonic-gate ls_event_info_t *evp = &g_event_info[i]; 255*7c478bd9Sstevel@tonic-gate if (evp->ev_type != event_type || 256*7c478bd9Sstevel@tonic-gate strncmp(evp->ev_desc, "Unknown event", 13) == 0) 257*7c478bd9Sstevel@tonic-gate continue; 258*7c478bd9Sstevel@tonic-gate if (first == -1) 259*7c478bd9Sstevel@tonic-gate first = i; 260*7c478bd9Sstevel@tonic-gate last = i; 261*7c478bd9Sstevel@tonic-gate } 262*7c478bd9Sstevel@tonic-gate 263*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 264*7c478bd9Sstevel@tonic-gate "\n%s events (lockstat -%c or lockstat -e %d-%d):\n\n", 265*7c478bd9Sstevel@tonic-gate desc, event_type, first, last); 266*7c478bd9Sstevel@tonic-gate 267*7c478bd9Sstevel@tonic-gate for (i = first; i <= last; i++) 268*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 269*7c478bd9Sstevel@tonic-gate "%4d = %s\n", i, g_event_info[i].ev_desc); 270*7c478bd9Sstevel@tonic-gate } 271*7c478bd9Sstevel@tonic-gate 272*7c478bd9Sstevel@tonic-gate static void 273*7c478bd9Sstevel@tonic-gate usage(void) 274*7c478bd9Sstevel@tonic-gate { 275*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 276*7c478bd9Sstevel@tonic-gate "Usage: lockstat [options] command [args]\n" 277*7c478bd9Sstevel@tonic-gate "\nEvent selection options:\n\n" 278*7c478bd9Sstevel@tonic-gate " -C watch contention events [on by default]\n" 279*7c478bd9Sstevel@tonic-gate " -E watch error events [off by default]\n" 280*7c478bd9Sstevel@tonic-gate " -H watch hold events [off by default]\n" 281*7c478bd9Sstevel@tonic-gate " -I watch interrupt events [off by default]\n" 282*7c478bd9Sstevel@tonic-gate " -A watch all lock events [equivalent to -CH]\n" 283*7c478bd9Sstevel@tonic-gate " -e event_list only watch the specified events (shown below);\n" 284*7c478bd9Sstevel@tonic-gate " <event_list> is a comma-separated list of\n" 285*7c478bd9Sstevel@tonic-gate " events or ranges of events, e.g. 1,4-7,35\n" 286*7c478bd9Sstevel@tonic-gate " -i rate interrupt rate for -I [default: %d Hz]\n" 287*7c478bd9Sstevel@tonic-gate "\nData gathering options:\n\n" 288*7c478bd9Sstevel@tonic-gate " -b basic statistics (lock, caller, event count)\n" 289*7c478bd9Sstevel@tonic-gate " -t timing for all events [default]\n" 290*7c478bd9Sstevel@tonic-gate " -h histograms for event times\n" 291*7c478bd9Sstevel@tonic-gate " -s depth stack traces <depth> deep\n" 292*7c478bd9Sstevel@tonic-gate " -x opt[=val] enable or modify DTrace options\n" 293*7c478bd9Sstevel@tonic-gate "\nData filtering options:\n\n" 294*7c478bd9Sstevel@tonic-gate " -n nrecords maximum number of data records [default: %d]\n" 295*7c478bd9Sstevel@tonic-gate " -l lock[,size] only watch <lock>, which can be specified as a\n" 296*7c478bd9Sstevel@tonic-gate " symbolic name or hex address; <size> defaults\n" 297*7c478bd9Sstevel@tonic-gate " to the ELF symbol size if available, 1 if not\n" 298*7c478bd9Sstevel@tonic-gate " -f func[,size] only watch events generated by <func>\n" 299*7c478bd9Sstevel@tonic-gate " -d duration only watch events longer than <duration>\n" 300*7c478bd9Sstevel@tonic-gate " -T trace (rather than sample) events\n" 301*7c478bd9Sstevel@tonic-gate "\nData reporting options:\n\n" 302*7c478bd9Sstevel@tonic-gate " -c coalesce lock data for arrays like pse_mutex[]\n" 303*7c478bd9Sstevel@tonic-gate " -k coalesce PCs within functions\n" 304*7c478bd9Sstevel@tonic-gate " -g show total events generated by function\n" 305*7c478bd9Sstevel@tonic-gate " -w wherever: don't distinguish events by caller\n" 306*7c478bd9Sstevel@tonic-gate " -W whichever: don't distinguish events by lock\n" 307*7c478bd9Sstevel@tonic-gate " -R display rates rather than counts\n" 308*7c478bd9Sstevel@tonic-gate " -p parsable output format (awk(1)-friendly)\n" 309*7c478bd9Sstevel@tonic-gate " -P sort lock data by (count * avg_time) product\n" 310*7c478bd9Sstevel@tonic-gate " -D n only display top <n> events of each type\n" 311*7c478bd9Sstevel@tonic-gate " -o filename send output to <filename>\n", 312*7c478bd9Sstevel@tonic-gate DEFAULT_HZ, DEFAULT_NRECS); 313*7c478bd9Sstevel@tonic-gate 314*7c478bd9Sstevel@tonic-gate show_events('C', "Contention"); 315*7c478bd9Sstevel@tonic-gate show_events('H', "Hold-time"); 316*7c478bd9Sstevel@tonic-gate show_events('I', "Interrupt"); 317*7c478bd9Sstevel@tonic-gate show_events('E', "Error"); 318*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 319*7c478bd9Sstevel@tonic-gate 320*7c478bd9Sstevel@tonic-gate exit(1); 321*7c478bd9Sstevel@tonic-gate } 322*7c478bd9Sstevel@tonic-gate 323*7c478bd9Sstevel@tonic-gate static int 324*7c478bd9Sstevel@tonic-gate lockcmp(lsrec_t *a, lsrec_t *b) 325*7c478bd9Sstevel@tonic-gate { 326*7c478bd9Sstevel@tonic-gate int i; 327*7c478bd9Sstevel@tonic-gate 328*7c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 329*7c478bd9Sstevel@tonic-gate return (-1); 330*7c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 331*7c478bd9Sstevel@tonic-gate return (1); 332*7c478bd9Sstevel@tonic-gate 333*7c478bd9Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 334*7c478bd9Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 335*7c478bd9Sstevel@tonic-gate return (-1); 336*7c478bd9Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 337*7c478bd9Sstevel@tonic-gate return (1); 338*7c478bd9Sstevel@tonic-gate } 339*7c478bd9Sstevel@tonic-gate 340*7c478bd9Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 341*7c478bd9Sstevel@tonic-gate return (-1); 342*7c478bd9Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 343*7c478bd9Sstevel@tonic-gate return (1); 344*7c478bd9Sstevel@tonic-gate 345*7c478bd9Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 346*7c478bd9Sstevel@tonic-gate return (-1); 347*7c478bd9Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 348*7c478bd9Sstevel@tonic-gate return (1); 349*7c478bd9Sstevel@tonic-gate 350*7c478bd9Sstevel@tonic-gate return (0); 351*7c478bd9Sstevel@tonic-gate } 352*7c478bd9Sstevel@tonic-gate 353*7c478bd9Sstevel@tonic-gate static int 354*7c478bd9Sstevel@tonic-gate countcmp(lsrec_t *a, lsrec_t *b) 355*7c478bd9Sstevel@tonic-gate { 356*7c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 357*7c478bd9Sstevel@tonic-gate return (-1); 358*7c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 359*7c478bd9Sstevel@tonic-gate return (1); 360*7c478bd9Sstevel@tonic-gate 361*7c478bd9Sstevel@tonic-gate return (b->ls_count - a->ls_count); 362*7c478bd9Sstevel@tonic-gate } 363*7c478bd9Sstevel@tonic-gate 364*7c478bd9Sstevel@tonic-gate static int 365*7c478bd9Sstevel@tonic-gate timecmp(lsrec_t *a, lsrec_t *b) 366*7c478bd9Sstevel@tonic-gate { 367*7c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 368*7c478bd9Sstevel@tonic-gate return (-1); 369*7c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 370*7c478bd9Sstevel@tonic-gate return (1); 371*7c478bd9Sstevel@tonic-gate 372*7c478bd9Sstevel@tonic-gate if (a->ls_time < b->ls_time) 373*7c478bd9Sstevel@tonic-gate return (1); 374*7c478bd9Sstevel@tonic-gate if (a->ls_time > b->ls_time) 375*7c478bd9Sstevel@tonic-gate return (-1); 376*7c478bd9Sstevel@tonic-gate 377*7c478bd9Sstevel@tonic-gate return (0); 378*7c478bd9Sstevel@tonic-gate } 379*7c478bd9Sstevel@tonic-gate 380*7c478bd9Sstevel@tonic-gate static int 381*7c478bd9Sstevel@tonic-gate lockcmp_anywhere(lsrec_t *a, lsrec_t *b) 382*7c478bd9Sstevel@tonic-gate { 383*7c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 384*7c478bd9Sstevel@tonic-gate return (-1); 385*7c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 386*7c478bd9Sstevel@tonic-gate return (1); 387*7c478bd9Sstevel@tonic-gate 388*7c478bd9Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 389*7c478bd9Sstevel@tonic-gate return (-1); 390*7c478bd9Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 391*7c478bd9Sstevel@tonic-gate return (1); 392*7c478bd9Sstevel@tonic-gate 393*7c478bd9Sstevel@tonic-gate return (0); 394*7c478bd9Sstevel@tonic-gate } 395*7c478bd9Sstevel@tonic-gate 396*7c478bd9Sstevel@tonic-gate static int 397*7c478bd9Sstevel@tonic-gate lock_and_count_cmp_anywhere(lsrec_t *a, lsrec_t *b) 398*7c478bd9Sstevel@tonic-gate { 399*7c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 400*7c478bd9Sstevel@tonic-gate return (-1); 401*7c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 402*7c478bd9Sstevel@tonic-gate return (1); 403*7c478bd9Sstevel@tonic-gate 404*7c478bd9Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 405*7c478bd9Sstevel@tonic-gate return (-1); 406*7c478bd9Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 407*7c478bd9Sstevel@tonic-gate return (1); 408*7c478bd9Sstevel@tonic-gate 409*7c478bd9Sstevel@tonic-gate return (b->ls_count - a->ls_count); 410*7c478bd9Sstevel@tonic-gate } 411*7c478bd9Sstevel@tonic-gate 412*7c478bd9Sstevel@tonic-gate static int 413*7c478bd9Sstevel@tonic-gate sitecmp_anylock(lsrec_t *a, lsrec_t *b) 414*7c478bd9Sstevel@tonic-gate { 415*7c478bd9Sstevel@tonic-gate int i; 416*7c478bd9Sstevel@tonic-gate 417*7c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 418*7c478bd9Sstevel@tonic-gate return (-1); 419*7c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 420*7c478bd9Sstevel@tonic-gate return (1); 421*7c478bd9Sstevel@tonic-gate 422*7c478bd9Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 423*7c478bd9Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 424*7c478bd9Sstevel@tonic-gate return (-1); 425*7c478bd9Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 426*7c478bd9Sstevel@tonic-gate return (1); 427*7c478bd9Sstevel@tonic-gate } 428*7c478bd9Sstevel@tonic-gate 429*7c478bd9Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 430*7c478bd9Sstevel@tonic-gate return (-1); 431*7c478bd9Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 432*7c478bd9Sstevel@tonic-gate return (1); 433*7c478bd9Sstevel@tonic-gate 434*7c478bd9Sstevel@tonic-gate return (0); 435*7c478bd9Sstevel@tonic-gate } 436*7c478bd9Sstevel@tonic-gate 437*7c478bd9Sstevel@tonic-gate static int 438*7c478bd9Sstevel@tonic-gate site_and_count_cmp_anylock(lsrec_t *a, lsrec_t *b) 439*7c478bd9Sstevel@tonic-gate { 440*7c478bd9Sstevel@tonic-gate int i; 441*7c478bd9Sstevel@tonic-gate 442*7c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 443*7c478bd9Sstevel@tonic-gate return (-1); 444*7c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 445*7c478bd9Sstevel@tonic-gate return (1); 446*7c478bd9Sstevel@tonic-gate 447*7c478bd9Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 448*7c478bd9Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 449*7c478bd9Sstevel@tonic-gate return (-1); 450*7c478bd9Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 451*7c478bd9Sstevel@tonic-gate return (1); 452*7c478bd9Sstevel@tonic-gate } 453*7c478bd9Sstevel@tonic-gate 454*7c478bd9Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 455*7c478bd9Sstevel@tonic-gate return (-1); 456*7c478bd9Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 457*7c478bd9Sstevel@tonic-gate return (1); 458*7c478bd9Sstevel@tonic-gate 459*7c478bd9Sstevel@tonic-gate return (b->ls_count - a->ls_count); 460*7c478bd9Sstevel@tonic-gate } 461*7c478bd9Sstevel@tonic-gate 462*7c478bd9Sstevel@tonic-gate static void 463*7c478bd9Sstevel@tonic-gate mergesort(int (*cmp)(lsrec_t *, lsrec_t *), lsrec_t **a, lsrec_t **b, int n) 464*7c478bd9Sstevel@tonic-gate { 465*7c478bd9Sstevel@tonic-gate int m = n / 2; 466*7c478bd9Sstevel@tonic-gate int i, j; 467*7c478bd9Sstevel@tonic-gate 468*7c478bd9Sstevel@tonic-gate if (m > 1) 469*7c478bd9Sstevel@tonic-gate mergesort(cmp, a, b, m); 470*7c478bd9Sstevel@tonic-gate if (n - m > 1) 471*7c478bd9Sstevel@tonic-gate mergesort(cmp, a + m, b + m, n - m); 472*7c478bd9Sstevel@tonic-gate for (i = m; i > 0; i--) 473*7c478bd9Sstevel@tonic-gate b[i - 1] = a[i - 1]; 474*7c478bd9Sstevel@tonic-gate for (j = m - 1; j < n - 1; j++) 475*7c478bd9Sstevel@tonic-gate b[n + m - j - 2] = a[j + 1]; 476*7c478bd9Sstevel@tonic-gate while (i < j) 477*7c478bd9Sstevel@tonic-gate *a++ = cmp(b[i], b[j]) < 0 ? b[i++] : b[j--]; 478*7c478bd9Sstevel@tonic-gate *a = b[i]; 479*7c478bd9Sstevel@tonic-gate } 480*7c478bd9Sstevel@tonic-gate 481*7c478bd9Sstevel@tonic-gate static void 482*7c478bd9Sstevel@tonic-gate coalesce(int (*cmp)(lsrec_t *, lsrec_t *), lsrec_t **lock, int n) 483*7c478bd9Sstevel@tonic-gate { 484*7c478bd9Sstevel@tonic-gate int i, j; 485*7c478bd9Sstevel@tonic-gate lsrec_t *target, *current; 486*7c478bd9Sstevel@tonic-gate 487*7c478bd9Sstevel@tonic-gate target = lock[0]; 488*7c478bd9Sstevel@tonic-gate 489*7c478bd9Sstevel@tonic-gate for (i = 1; i < n; i++) { 490*7c478bd9Sstevel@tonic-gate current = lock[i]; 491*7c478bd9Sstevel@tonic-gate if (cmp(current, target) != 0) { 492*7c478bd9Sstevel@tonic-gate target = current; 493*7c478bd9Sstevel@tonic-gate continue; 494*7c478bd9Sstevel@tonic-gate } 495*7c478bd9Sstevel@tonic-gate current->ls_event = LS_MAX_EVENTS; 496*7c478bd9Sstevel@tonic-gate target->ls_count += current->ls_count; 497*7c478bd9Sstevel@tonic-gate target->ls_refcnt += current->ls_refcnt; 498*7c478bd9Sstevel@tonic-gate if (g_recsize < LS_TIME) 499*7c478bd9Sstevel@tonic-gate continue; 500*7c478bd9Sstevel@tonic-gate target->ls_time += current->ls_time; 501*7c478bd9Sstevel@tonic-gate if (g_recsize < LS_HIST) 502*7c478bd9Sstevel@tonic-gate continue; 503*7c478bd9Sstevel@tonic-gate for (j = 0; j < 64; j++) 504*7c478bd9Sstevel@tonic-gate target->ls_hist[j] += current->ls_hist[j]; 505*7c478bd9Sstevel@tonic-gate } 506*7c478bd9Sstevel@tonic-gate } 507*7c478bd9Sstevel@tonic-gate 508*7c478bd9Sstevel@tonic-gate static void 509*7c478bd9Sstevel@tonic-gate coalesce_symbol(uintptr_t *addrp) 510*7c478bd9Sstevel@tonic-gate { 511*7c478bd9Sstevel@tonic-gate uintptr_t symoff; 512*7c478bd9Sstevel@tonic-gate size_t symsize; 513*7c478bd9Sstevel@tonic-gate 514*7c478bd9Sstevel@tonic-gate if (addr_to_sym(*addrp, &symoff, &symsize) != NULL && symoff < symsize) 515*7c478bd9Sstevel@tonic-gate *addrp -= symoff; 516*7c478bd9Sstevel@tonic-gate } 517*7c478bd9Sstevel@tonic-gate 518*7c478bd9Sstevel@tonic-gate static void 519*7c478bd9Sstevel@tonic-gate predicate_add(char **pred, char *what, char *cmp, uintptr_t value) 520*7c478bd9Sstevel@tonic-gate { 521*7c478bd9Sstevel@tonic-gate char *new; 522*7c478bd9Sstevel@tonic-gate int len, newlen; 523*7c478bd9Sstevel@tonic-gate 524*7c478bd9Sstevel@tonic-gate if (what == NULL) 525*7c478bd9Sstevel@tonic-gate return; 526*7c478bd9Sstevel@tonic-gate 527*7c478bd9Sstevel@tonic-gate if (*pred == NULL) { 528*7c478bd9Sstevel@tonic-gate *pred = malloc(1); 529*7c478bd9Sstevel@tonic-gate *pred[0] = '\0'; 530*7c478bd9Sstevel@tonic-gate } 531*7c478bd9Sstevel@tonic-gate 532*7c478bd9Sstevel@tonic-gate len = strlen(*pred); 533*7c478bd9Sstevel@tonic-gate newlen = len + strlen(what) + 32 + strlen("( && )"); 534*7c478bd9Sstevel@tonic-gate new = malloc(newlen); 535*7c478bd9Sstevel@tonic-gate 536*7c478bd9Sstevel@tonic-gate if (*pred[0] != '\0') { 537*7c478bd9Sstevel@tonic-gate if (cmp != NULL) { 538*7c478bd9Sstevel@tonic-gate (void) sprintf(new, "(%s) && (%s %s 0x%p)", 539*7c478bd9Sstevel@tonic-gate *pred, what, cmp, (void *)value); 540*7c478bd9Sstevel@tonic-gate } else { 541*7c478bd9Sstevel@tonic-gate (void) sprintf(new, "(%s) && (%s)", *pred, what); 542*7c478bd9Sstevel@tonic-gate } 543*7c478bd9Sstevel@tonic-gate } else { 544*7c478bd9Sstevel@tonic-gate if (cmp != NULL) { 545*7c478bd9Sstevel@tonic-gate (void) sprintf(new, "%s %s 0x%p", 546*7c478bd9Sstevel@tonic-gate what, cmp, (void *)value); 547*7c478bd9Sstevel@tonic-gate } else { 548*7c478bd9Sstevel@tonic-gate (void) sprintf(new, "%s", what); 549*7c478bd9Sstevel@tonic-gate } 550*7c478bd9Sstevel@tonic-gate } 551*7c478bd9Sstevel@tonic-gate 552*7c478bd9Sstevel@tonic-gate free(*pred); 553*7c478bd9Sstevel@tonic-gate *pred = new; 554*7c478bd9Sstevel@tonic-gate } 555*7c478bd9Sstevel@tonic-gate 556*7c478bd9Sstevel@tonic-gate static void 557*7c478bd9Sstevel@tonic-gate predicate_destroy(char **pred) 558*7c478bd9Sstevel@tonic-gate { 559*7c478bd9Sstevel@tonic-gate free(*pred); 560*7c478bd9Sstevel@tonic-gate *pred = NULL; 561*7c478bd9Sstevel@tonic-gate } 562*7c478bd9Sstevel@tonic-gate 563*7c478bd9Sstevel@tonic-gate static void 564*7c478bd9Sstevel@tonic-gate filter_add(char **filt, char *what, uintptr_t base, uintptr_t size) 565*7c478bd9Sstevel@tonic-gate { 566*7c478bd9Sstevel@tonic-gate char buf[256], *c = buf, *new; 567*7c478bd9Sstevel@tonic-gate int len, newlen; 568*7c478bd9Sstevel@tonic-gate 569*7c478bd9Sstevel@tonic-gate if (*filt == NULL) { 570*7c478bd9Sstevel@tonic-gate *filt = malloc(1); 571*7c478bd9Sstevel@tonic-gate *filt[0] = '\0'; 572*7c478bd9Sstevel@tonic-gate } 573*7c478bd9Sstevel@tonic-gate 574*7c478bd9Sstevel@tonic-gate (void) sprintf(c, "%s(%s >= 0x%p && %s < 0x%p)", *filt[0] != '\0' ? 575*7c478bd9Sstevel@tonic-gate " || " : "", what, (void *)base, what, (void *)(base + size)); 576*7c478bd9Sstevel@tonic-gate 577*7c478bd9Sstevel@tonic-gate newlen = (len = strlen(*filt) + 1) + strlen(c); 578*7c478bd9Sstevel@tonic-gate new = malloc(newlen); 579*7c478bd9Sstevel@tonic-gate bcopy(*filt, new, len); 580*7c478bd9Sstevel@tonic-gate (void) strcat(new, c); 581*7c478bd9Sstevel@tonic-gate free(*filt); 582*7c478bd9Sstevel@tonic-gate *filt = new; 583*7c478bd9Sstevel@tonic-gate } 584*7c478bd9Sstevel@tonic-gate 585*7c478bd9Sstevel@tonic-gate static void 586*7c478bd9Sstevel@tonic-gate filter_destroy(char **filt) 587*7c478bd9Sstevel@tonic-gate { 588*7c478bd9Sstevel@tonic-gate free(*filt); 589*7c478bd9Sstevel@tonic-gate *filt = NULL; 590*7c478bd9Sstevel@tonic-gate } 591*7c478bd9Sstevel@tonic-gate 592*7c478bd9Sstevel@tonic-gate static void 593*7c478bd9Sstevel@tonic-gate dprog_add(const char *fmt, ...) 594*7c478bd9Sstevel@tonic-gate { 595*7c478bd9Sstevel@tonic-gate va_list args; 596*7c478bd9Sstevel@tonic-gate int size, offs; 597*7c478bd9Sstevel@tonic-gate char c; 598*7c478bd9Sstevel@tonic-gate 599*7c478bd9Sstevel@tonic-gate va_start(args, fmt); 600*7c478bd9Sstevel@tonic-gate size = vsnprintf(&c, 1, fmt, args) + 1; 601*7c478bd9Sstevel@tonic-gate 602*7c478bd9Sstevel@tonic-gate if (g_proglen == 0) { 603*7c478bd9Sstevel@tonic-gate offs = 0; 604*7c478bd9Sstevel@tonic-gate } else { 605*7c478bd9Sstevel@tonic-gate offs = g_proglen - 1; 606*7c478bd9Sstevel@tonic-gate } 607*7c478bd9Sstevel@tonic-gate 608*7c478bd9Sstevel@tonic-gate g_proglen = offs + size; 609*7c478bd9Sstevel@tonic-gate 610*7c478bd9Sstevel@tonic-gate if ((g_prog = realloc(g_prog, g_proglen)) == NULL) 611*7c478bd9Sstevel@tonic-gate fail(1, "failed to reallocate program text"); 612*7c478bd9Sstevel@tonic-gate 613*7c478bd9Sstevel@tonic-gate (void) vsnprintf(&g_prog[offs], size, fmt, args); 614*7c478bd9Sstevel@tonic-gate } 615*7c478bd9Sstevel@tonic-gate 616*7c478bd9Sstevel@tonic-gate /* 617*7c478bd9Sstevel@tonic-gate * This function may read like an open sewer, but keep in mind that programs 618*7c478bd9Sstevel@tonic-gate * that generate other programs are rarely pretty. If one has the unenviable 619*7c478bd9Sstevel@tonic-gate * task of maintaining or -- worse -- extending this code, use the -V option 620*7c478bd9Sstevel@tonic-gate * to examine the D program as generated by this function. 621*7c478bd9Sstevel@tonic-gate */ 622*7c478bd9Sstevel@tonic-gate static void 623*7c478bd9Sstevel@tonic-gate dprog_addevent(int event) 624*7c478bd9Sstevel@tonic-gate { 625*7c478bd9Sstevel@tonic-gate ls_event_info_t *info = &g_event_info[event]; 626*7c478bd9Sstevel@tonic-gate char *pred = NULL; 627*7c478bd9Sstevel@tonic-gate char stack[20]; 628*7c478bd9Sstevel@tonic-gate const char *arg0, *caller; 629*7c478bd9Sstevel@tonic-gate char *arg1 = "arg1"; 630*7c478bd9Sstevel@tonic-gate char buf[80]; 631*7c478bd9Sstevel@tonic-gate hrtime_t dur; 632*7c478bd9Sstevel@tonic-gate int depth; 633*7c478bd9Sstevel@tonic-gate 634*7c478bd9Sstevel@tonic-gate if (info->ev_name[0] == '\0') 635*7c478bd9Sstevel@tonic-gate return; 636*7c478bd9Sstevel@tonic-gate 637*7c478bd9Sstevel@tonic-gate if (info->ev_type == 'I') { 638*7c478bd9Sstevel@tonic-gate /* 639*7c478bd9Sstevel@tonic-gate * For interrupt events, arg0 (normally the lock pointer) is 640*7c478bd9Sstevel@tonic-gate * the CPU address plus the current pil, and arg1 (normally 641*7c478bd9Sstevel@tonic-gate * the number of nanoseconds) is the number of nanoseconds 642*7c478bd9Sstevel@tonic-gate * late -- and it's stored in arg2. 643*7c478bd9Sstevel@tonic-gate */ 644*7c478bd9Sstevel@tonic-gate arg0 = "(uintptr_t)curthread->t_cpu + \n" 645*7c478bd9Sstevel@tonic-gate "\t curthread->t_cpu->cpu_profile_pil"; 646*7c478bd9Sstevel@tonic-gate caller = "(uintptr_t)arg0"; 647*7c478bd9Sstevel@tonic-gate arg1 = "arg2"; 648*7c478bd9Sstevel@tonic-gate } else { 649*7c478bd9Sstevel@tonic-gate arg0 = "(uintptr_t)arg0"; 650*7c478bd9Sstevel@tonic-gate caller = "caller"; 651*7c478bd9Sstevel@tonic-gate } 652*7c478bd9Sstevel@tonic-gate 653*7c478bd9Sstevel@tonic-gate if (g_recsize > LS_HIST) { 654*7c478bd9Sstevel@tonic-gate for (depth = 0; g_recsize > LS_STACK(depth); depth++) 655*7c478bd9Sstevel@tonic-gate continue; 656*7c478bd9Sstevel@tonic-gate 657*7c478bd9Sstevel@tonic-gate if (g_tracing) { 658*7c478bd9Sstevel@tonic-gate (void) sprintf(stack, "\tstack(%d);\n", depth); 659*7c478bd9Sstevel@tonic-gate } else { 660*7c478bd9Sstevel@tonic-gate (void) sprintf(stack, ", stack(%d)", depth); 661*7c478bd9Sstevel@tonic-gate } 662*7c478bd9Sstevel@tonic-gate } else { 663*7c478bd9Sstevel@tonic-gate (void) sprintf(stack, ""); 664*7c478bd9Sstevel@tonic-gate } 665*7c478bd9Sstevel@tonic-gate 666*7c478bd9Sstevel@tonic-gate if (info->ev_acquire != NULL) { 667*7c478bd9Sstevel@tonic-gate /* 668*7c478bd9Sstevel@tonic-gate * If this is a hold event, we need to generate an additional 669*7c478bd9Sstevel@tonic-gate * clause for the acquire; the clause for the release will be 670*7c478bd9Sstevel@tonic-gate * generated with the aggregating statement, below. 671*7c478bd9Sstevel@tonic-gate */ 672*7c478bd9Sstevel@tonic-gate dprog_add("%s\n", info->ev_acquire); 673*7c478bd9Sstevel@tonic-gate predicate_add(&pred, info->ev_predicate, NULL, 0); 674*7c478bd9Sstevel@tonic-gate predicate_add(&pred, g_predicate, NULL, 0); 675*7c478bd9Sstevel@tonic-gate if (pred != NULL) 676*7c478bd9Sstevel@tonic-gate dprog_add("/%s/\n", pred); 677*7c478bd9Sstevel@tonic-gate 678*7c478bd9Sstevel@tonic-gate dprog_add("{\n"); 679*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "self->ev%d[(uintptr_t)arg0]", event); 680*7c478bd9Sstevel@tonic-gate 681*7c478bd9Sstevel@tonic-gate if (info->ev_type == 'H') { 682*7c478bd9Sstevel@tonic-gate dprog_add("\t%s = timestamp;\n", buf); 683*7c478bd9Sstevel@tonic-gate } else { 684*7c478bd9Sstevel@tonic-gate /* 685*7c478bd9Sstevel@tonic-gate * If this isn't a hold event, it's the recursive 686*7c478bd9Sstevel@tonic-gate * error event. For this, we simply bump the 687*7c478bd9Sstevel@tonic-gate * thread-local, per-lock count. 688*7c478bd9Sstevel@tonic-gate */ 689*7c478bd9Sstevel@tonic-gate dprog_add("\t%s++;\n", buf); 690*7c478bd9Sstevel@tonic-gate } 691*7c478bd9Sstevel@tonic-gate 692*7c478bd9Sstevel@tonic-gate dprog_add("}\n\n"); 693*7c478bd9Sstevel@tonic-gate predicate_destroy(&pred); 694*7c478bd9Sstevel@tonic-gate pred = NULL; 695*7c478bd9Sstevel@tonic-gate 696*7c478bd9Sstevel@tonic-gate if (info->ev_type == 'E') { 697*7c478bd9Sstevel@tonic-gate /* 698*7c478bd9Sstevel@tonic-gate * If this is the recursive lock error event, we need 699*7c478bd9Sstevel@tonic-gate * to generate an additional clause to decrement the 700*7c478bd9Sstevel@tonic-gate * thread-local, per-lock count. This assures that we 701*7c478bd9Sstevel@tonic-gate * only execute the aggregating clause if we have 702*7c478bd9Sstevel@tonic-gate * recursive entry. 703*7c478bd9Sstevel@tonic-gate */ 704*7c478bd9Sstevel@tonic-gate dprog_add("%s\n", info->ev_name); 705*7c478bd9Sstevel@tonic-gate dprog_add("/%s/\n{\n\t%s--;\n}\n\n", buf, buf); 706*7c478bd9Sstevel@tonic-gate } 707*7c478bd9Sstevel@tonic-gate 708*7c478bd9Sstevel@tonic-gate predicate_add(&pred, buf, NULL, 0); 709*7c478bd9Sstevel@tonic-gate 710*7c478bd9Sstevel@tonic-gate if (info->ev_type == 'H') { 711*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "timestamp -\n\t " 712*7c478bd9Sstevel@tonic-gate "self->ev%d[(uintptr_t)arg0]", event); 713*7c478bd9Sstevel@tonic-gate } 714*7c478bd9Sstevel@tonic-gate 715*7c478bd9Sstevel@tonic-gate arg1 = buf; 716*7c478bd9Sstevel@tonic-gate } else { 717*7c478bd9Sstevel@tonic-gate predicate_add(&pred, info->ev_predicate, NULL, 0); 718*7c478bd9Sstevel@tonic-gate if (info->ev_type != 'I') 719*7c478bd9Sstevel@tonic-gate predicate_add(&pred, g_predicate, NULL, 0); 720*7c478bd9Sstevel@tonic-gate else 721*7c478bd9Sstevel@tonic-gate predicate_add(&pred, g_ipredicate, NULL, 0); 722*7c478bd9Sstevel@tonic-gate } 723*7c478bd9Sstevel@tonic-gate 724*7c478bd9Sstevel@tonic-gate if ((dur = g_min_duration[event]) != 0) 725*7c478bd9Sstevel@tonic-gate predicate_add(&pred, arg1, ">=", dur); 726*7c478bd9Sstevel@tonic-gate 727*7c478bd9Sstevel@tonic-gate dprog_add("%s\n", info->ev_name); 728*7c478bd9Sstevel@tonic-gate 729*7c478bd9Sstevel@tonic-gate if (pred != NULL) 730*7c478bd9Sstevel@tonic-gate dprog_add("/%s/\n", pred); 731*7c478bd9Sstevel@tonic-gate predicate_destroy(&pred); 732*7c478bd9Sstevel@tonic-gate 733*7c478bd9Sstevel@tonic-gate dprog_add("{\n"); 734*7c478bd9Sstevel@tonic-gate 735*7c478bd9Sstevel@tonic-gate if (g_tracing) { 736*7c478bd9Sstevel@tonic-gate dprog_add("\ttrace(%dULL);\n", event); 737*7c478bd9Sstevel@tonic-gate dprog_add("\ttrace(%s);\n", arg0); 738*7c478bd9Sstevel@tonic-gate dprog_add("\ttrace(%s);\n", caller); 739*7c478bd9Sstevel@tonic-gate dprog_add(stack); 740*7c478bd9Sstevel@tonic-gate } else { 741*7c478bd9Sstevel@tonic-gate dprog_add("\t@avg[%dULL, %s, %s%s] = avg(%s);\n", 742*7c478bd9Sstevel@tonic-gate event, arg0, caller, stack, arg1); 743*7c478bd9Sstevel@tonic-gate 744*7c478bd9Sstevel@tonic-gate if (g_recsize >= LS_HIST) { 745*7c478bd9Sstevel@tonic-gate dprog_add("\t@hist[%dULL, %s, %s%s] = quantize" 746*7c478bd9Sstevel@tonic-gate "(%s);\n", event, arg0, caller, stack, arg1); 747*7c478bd9Sstevel@tonic-gate } 748*7c478bd9Sstevel@tonic-gate } 749*7c478bd9Sstevel@tonic-gate 750*7c478bd9Sstevel@tonic-gate if (info->ev_acquire != NULL) 751*7c478bd9Sstevel@tonic-gate dprog_add("\tself->ev%d[arg0] = 0;\n", event); 752*7c478bd9Sstevel@tonic-gate 753*7c478bd9Sstevel@tonic-gate dprog_add("}\n\n"); 754*7c478bd9Sstevel@tonic-gate } 755*7c478bd9Sstevel@tonic-gate 756*7c478bd9Sstevel@tonic-gate static void 757*7c478bd9Sstevel@tonic-gate dprog_compile() 758*7c478bd9Sstevel@tonic-gate { 759*7c478bd9Sstevel@tonic-gate dtrace_prog_t *prog; 760*7c478bd9Sstevel@tonic-gate dtrace_proginfo_t info; 761*7c478bd9Sstevel@tonic-gate 762*7c478bd9Sstevel@tonic-gate if (g_Vflag) { 763*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: vvvv D program vvvv\n"); 764*7c478bd9Sstevel@tonic-gate (void) fputs(g_prog, stderr); 765*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: ^^^^ D program ^^^^\n"); 766*7c478bd9Sstevel@tonic-gate } 767*7c478bd9Sstevel@tonic-gate 768*7c478bd9Sstevel@tonic-gate if ((prog = dtrace_program_strcompile(g_dtp, g_prog, 769*7c478bd9Sstevel@tonic-gate DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) 770*7c478bd9Sstevel@tonic-gate dfail("failed to compile program"); 771*7c478bd9Sstevel@tonic-gate 772*7c478bd9Sstevel@tonic-gate if (dtrace_program_exec(g_dtp, prog, &info) == -1) 773*7c478bd9Sstevel@tonic-gate dfail("failed to enable probes"); 774*7c478bd9Sstevel@tonic-gate 775*7c478bd9Sstevel@tonic-gate if (dtrace_go(g_dtp) != 0) 776*7c478bd9Sstevel@tonic-gate dfail("couldn't start tracing"); 777*7c478bd9Sstevel@tonic-gate } 778*7c478bd9Sstevel@tonic-gate 779*7c478bd9Sstevel@tonic-gate static void 780*7c478bd9Sstevel@tonic-gate status_fire(void) 781*7c478bd9Sstevel@tonic-gate {} 782*7c478bd9Sstevel@tonic-gate 783*7c478bd9Sstevel@tonic-gate static void 784*7c478bd9Sstevel@tonic-gate status_init(void) 785*7c478bd9Sstevel@tonic-gate { 786*7c478bd9Sstevel@tonic-gate dtrace_optval_t val, status, agg; 787*7c478bd9Sstevel@tonic-gate struct sigaction act; 788*7c478bd9Sstevel@tonic-gate struct itimerspec ts; 789*7c478bd9Sstevel@tonic-gate struct sigevent ev; 790*7c478bd9Sstevel@tonic-gate timer_t tid; 791*7c478bd9Sstevel@tonic-gate 792*7c478bd9Sstevel@tonic-gate if (dtrace_getopt(g_dtp, "statusrate", &status) == -1) 793*7c478bd9Sstevel@tonic-gate dfail("failed to get 'statusrate'"); 794*7c478bd9Sstevel@tonic-gate 795*7c478bd9Sstevel@tonic-gate if (dtrace_getopt(g_dtp, "aggrate", &agg) == -1) 796*7c478bd9Sstevel@tonic-gate dfail("failed to get 'statusrate'"); 797*7c478bd9Sstevel@tonic-gate 798*7c478bd9Sstevel@tonic-gate /* 799*7c478bd9Sstevel@tonic-gate * We would want to awaken at a rate that is the GCD of the statusrate 800*7c478bd9Sstevel@tonic-gate * and the aggrate -- but that seems a bit absurd. Instead, we'll 801*7c478bd9Sstevel@tonic-gate * simply awaken at a rate that is the more frequent of the two, which 802*7c478bd9Sstevel@tonic-gate * assures that we're never later than the interval implied by the 803*7c478bd9Sstevel@tonic-gate * more frequent rate. 804*7c478bd9Sstevel@tonic-gate */ 805*7c478bd9Sstevel@tonic-gate val = status < agg ? status : agg; 806*7c478bd9Sstevel@tonic-gate 807*7c478bd9Sstevel@tonic-gate (void) sigemptyset(&act.sa_mask); 808*7c478bd9Sstevel@tonic-gate act.sa_flags = 0; 809*7c478bd9Sstevel@tonic-gate act.sa_handler = status_fire; 810*7c478bd9Sstevel@tonic-gate (void) sigaction(SIGUSR1, &act, NULL); 811*7c478bd9Sstevel@tonic-gate 812*7c478bd9Sstevel@tonic-gate ev.sigev_notify = SIGEV_SIGNAL; 813*7c478bd9Sstevel@tonic-gate ev.sigev_signo = SIGUSR1; 814*7c478bd9Sstevel@tonic-gate 815*7c478bd9Sstevel@tonic-gate if (timer_create(CLOCK_REALTIME, &ev, &tid) == -1) 816*7c478bd9Sstevel@tonic-gate dfail("cannot create CLOCK_REALTIME timer"); 817*7c478bd9Sstevel@tonic-gate 818*7c478bd9Sstevel@tonic-gate ts.it_value.tv_sec = val / NANOSEC; 819*7c478bd9Sstevel@tonic-gate ts.it_value.tv_nsec = val % NANOSEC; 820*7c478bd9Sstevel@tonic-gate ts.it_interval = ts.it_value; 821*7c478bd9Sstevel@tonic-gate 822*7c478bd9Sstevel@tonic-gate if (timer_settime(tid, TIMER_RELTIME, &ts, NULL) == -1) 823*7c478bd9Sstevel@tonic-gate dfail("cannot set time on CLOCK_REALTIME timer"); 824*7c478bd9Sstevel@tonic-gate } 825*7c478bd9Sstevel@tonic-gate 826*7c478bd9Sstevel@tonic-gate static void 827*7c478bd9Sstevel@tonic-gate status_check(void) 828*7c478bd9Sstevel@tonic-gate { 829*7c478bd9Sstevel@tonic-gate if (!g_tracing && dtrace_aggregate_snap(g_dtp) != 0) 830*7c478bd9Sstevel@tonic-gate dfail("failed to snap aggregate"); 831*7c478bd9Sstevel@tonic-gate 832*7c478bd9Sstevel@tonic-gate if (dtrace_status(g_dtp) == -1) 833*7c478bd9Sstevel@tonic-gate dfail("dtrace_status()"); 834*7c478bd9Sstevel@tonic-gate } 835*7c478bd9Sstevel@tonic-gate 836*7c478bd9Sstevel@tonic-gate static void 837*7c478bd9Sstevel@tonic-gate lsrec_fill(lsrec_t *lsrec, dtrace_recdesc_t *rec, int nrecs, caddr_t data) 838*7c478bd9Sstevel@tonic-gate { 839*7c478bd9Sstevel@tonic-gate bzero(lsrec, g_recsize); 840*7c478bd9Sstevel@tonic-gate lsrec->ls_count = 1; 841*7c478bd9Sstevel@tonic-gate 842*7c478bd9Sstevel@tonic-gate if ((g_recsize > LS_HIST && nrecs < 4) || (nrecs < 3)) 843*7c478bd9Sstevel@tonic-gate fail(0, "truncated DTrace record"); 844*7c478bd9Sstevel@tonic-gate 845*7c478bd9Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uint64_t)) 846*7c478bd9Sstevel@tonic-gate fail(0, "bad event size in first record"); 847*7c478bd9Sstevel@tonic-gate 848*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 849*7c478bd9Sstevel@tonic-gate lsrec->ls_event = (uint32_t)*((uint64_t *)(data + rec->dtrd_offset)); 850*7c478bd9Sstevel@tonic-gate rec++; 851*7c478bd9Sstevel@tonic-gate 852*7c478bd9Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uintptr_t)) 853*7c478bd9Sstevel@tonic-gate fail(0, "bad lock address size in second record"); 854*7c478bd9Sstevel@tonic-gate 855*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 856*7c478bd9Sstevel@tonic-gate lsrec->ls_lock = *((uintptr_t *)(data + rec->dtrd_offset)); 857*7c478bd9Sstevel@tonic-gate rec++; 858*7c478bd9Sstevel@tonic-gate 859*7c478bd9Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uintptr_t)) 860*7c478bd9Sstevel@tonic-gate fail(0, "bad caller size in third record"); 861*7c478bd9Sstevel@tonic-gate 862*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 863*7c478bd9Sstevel@tonic-gate lsrec->ls_caller = *((uintptr_t *)(data + rec->dtrd_offset)); 864*7c478bd9Sstevel@tonic-gate rec++; 865*7c478bd9Sstevel@tonic-gate 866*7c478bd9Sstevel@tonic-gate if (g_recsize > LS_HIST) { 867*7c478bd9Sstevel@tonic-gate int frames, i; 868*7c478bd9Sstevel@tonic-gate pc_t *stack; 869*7c478bd9Sstevel@tonic-gate 870*7c478bd9Sstevel@tonic-gate frames = rec->dtrd_size / sizeof (pc_t); 871*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 872*7c478bd9Sstevel@tonic-gate stack = (pc_t *)(data + rec->dtrd_offset); 873*7c478bd9Sstevel@tonic-gate 874*7c478bd9Sstevel@tonic-gate for (i = 1; i < frames; i++) 875*7c478bd9Sstevel@tonic-gate lsrec->ls_stack[i - 1] = stack[i]; 876*7c478bd9Sstevel@tonic-gate } 877*7c478bd9Sstevel@tonic-gate } 878*7c478bd9Sstevel@tonic-gate 879*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 880*7c478bd9Sstevel@tonic-gate static int 881*7c478bd9Sstevel@tonic-gate count_aggregate(dtrace_aggdata_t *agg, void *arg) 882*7c478bd9Sstevel@tonic-gate { 883*7c478bd9Sstevel@tonic-gate *((size_t *)arg) += 1; 884*7c478bd9Sstevel@tonic-gate 885*7c478bd9Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 886*7c478bd9Sstevel@tonic-gate } 887*7c478bd9Sstevel@tonic-gate 888*7c478bd9Sstevel@tonic-gate static int 889*7c478bd9Sstevel@tonic-gate process_aggregate(dtrace_aggdata_t *agg, void *arg) 890*7c478bd9Sstevel@tonic-gate { 891*7c478bd9Sstevel@tonic-gate dtrace_aggdesc_t *aggdesc = agg->dtada_desc; 892*7c478bd9Sstevel@tonic-gate caddr_t data = agg->dtada_data; 893*7c478bd9Sstevel@tonic-gate lsdata_t *lsdata = arg; 894*7c478bd9Sstevel@tonic-gate lsrec_t *lsrec = lsdata->lsd_next; 895*7c478bd9Sstevel@tonic-gate dtrace_recdesc_t *rec; 896*7c478bd9Sstevel@tonic-gate uint64_t *avg, *quantized; 897*7c478bd9Sstevel@tonic-gate int i, j; 898*7c478bd9Sstevel@tonic-gate 899*7c478bd9Sstevel@tonic-gate assert(lsdata->lsd_count < g_nrecs); 900*7c478bd9Sstevel@tonic-gate 901*7c478bd9Sstevel@tonic-gate rec = &aggdesc->dtagd_rec[0]; 902*7c478bd9Sstevel@tonic-gate 903*7c478bd9Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uint64_t)) 904*7c478bd9Sstevel@tonic-gate fail(0, "bad variable size in zeroth record"); 905*7c478bd9Sstevel@tonic-gate 906*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 907*7c478bd9Sstevel@tonic-gate if (*((uint64_t *)(data + rec->dtrd_offset))) { 908*7c478bd9Sstevel@tonic-gate /* 909*7c478bd9Sstevel@tonic-gate * If the variable is non-zero, this is the histogram entry. 910*7c478bd9Sstevel@tonic-gate * We'll copy the quantized data into lc_hist, and jump over 911*7c478bd9Sstevel@tonic-gate * the rest. 912*7c478bd9Sstevel@tonic-gate */ 913*7c478bd9Sstevel@tonic-gate rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; 914*7c478bd9Sstevel@tonic-gate 915*7c478bd9Sstevel@tonic-gate if (rec->dtrd_size != 916*7c478bd9Sstevel@tonic-gate DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) 917*7c478bd9Sstevel@tonic-gate fail(0, "bad quantize size in aggregation record"); 918*7c478bd9Sstevel@tonic-gate 919*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 920*7c478bd9Sstevel@tonic-gate quantized = (uint64_t *)(data + rec->dtrd_offset); 921*7c478bd9Sstevel@tonic-gate 922*7c478bd9Sstevel@tonic-gate for (i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0; 923*7c478bd9Sstevel@tonic-gate i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) 924*7c478bd9Sstevel@tonic-gate lsrec->ls_hist[j] = quantized[i]; 925*7c478bd9Sstevel@tonic-gate 926*7c478bd9Sstevel@tonic-gate goto out; 927*7c478bd9Sstevel@tonic-gate } 928*7c478bd9Sstevel@tonic-gate 929*7c478bd9Sstevel@tonic-gate lsrec_fill(lsrec, &aggdesc->dtagd_rec[1], 930*7c478bd9Sstevel@tonic-gate aggdesc->dtagd_nrecs - 1, data); 931*7c478bd9Sstevel@tonic-gate 932*7c478bd9Sstevel@tonic-gate rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; 933*7c478bd9Sstevel@tonic-gate 934*7c478bd9Sstevel@tonic-gate if (rec->dtrd_size != 2 * sizeof (uint64_t)) 935*7c478bd9Sstevel@tonic-gate fail(0, "bad avg size in aggregation record"); 936*7c478bd9Sstevel@tonic-gate 937*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 938*7c478bd9Sstevel@tonic-gate avg = (uint64_t *)(data + rec->dtrd_offset); 939*7c478bd9Sstevel@tonic-gate lsrec->ls_count = (uint32_t)avg[0]; 940*7c478bd9Sstevel@tonic-gate lsrec->ls_time = (uintptr_t)avg[1]; 941*7c478bd9Sstevel@tonic-gate 942*7c478bd9Sstevel@tonic-gate if (g_recsize >= LS_HIST) 943*7c478bd9Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 944*7c478bd9Sstevel@tonic-gate 945*7c478bd9Sstevel@tonic-gate out: 946*7c478bd9Sstevel@tonic-gate lsdata->lsd_next = (lsrec_t *)((uintptr_t)lsrec + g_recsize); 947*7c478bd9Sstevel@tonic-gate lsdata->lsd_count++; 948*7c478bd9Sstevel@tonic-gate 949*7c478bd9Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 950*7c478bd9Sstevel@tonic-gate } 951*7c478bd9Sstevel@tonic-gate 952*7c478bd9Sstevel@tonic-gate static int 953*7c478bd9Sstevel@tonic-gate process_trace(const dtrace_probedata_t *pdata, void *arg) 954*7c478bd9Sstevel@tonic-gate { 955*7c478bd9Sstevel@tonic-gate lsdata_t *lsdata = arg; 956*7c478bd9Sstevel@tonic-gate lsrec_t *lsrec = lsdata->lsd_next; 957*7c478bd9Sstevel@tonic-gate dtrace_eprobedesc_t *edesc = pdata->dtpda_edesc; 958*7c478bd9Sstevel@tonic-gate caddr_t data = pdata->dtpda_data; 959*7c478bd9Sstevel@tonic-gate 960*7c478bd9Sstevel@tonic-gate if (lsdata->lsd_count >= g_nrecs) 961*7c478bd9Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 962*7c478bd9Sstevel@tonic-gate 963*7c478bd9Sstevel@tonic-gate lsrec_fill(lsrec, edesc->dtepd_rec, edesc->dtepd_nrecs, data); 964*7c478bd9Sstevel@tonic-gate 965*7c478bd9Sstevel@tonic-gate lsdata->lsd_next = (lsrec_t *)((uintptr_t)lsrec + g_recsize); 966*7c478bd9Sstevel@tonic-gate lsdata->lsd_count++; 967*7c478bd9Sstevel@tonic-gate 968*7c478bd9Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 969*7c478bd9Sstevel@tonic-gate } 970*7c478bd9Sstevel@tonic-gate 971*7c478bd9Sstevel@tonic-gate static int 972*7c478bd9Sstevel@tonic-gate process_data(FILE *out, char *data) 973*7c478bd9Sstevel@tonic-gate { 974*7c478bd9Sstevel@tonic-gate lsdata_t lsdata; 975*7c478bd9Sstevel@tonic-gate 976*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 977*7c478bd9Sstevel@tonic-gate lsdata.lsd_next = (lsrec_t *)data; 978*7c478bd9Sstevel@tonic-gate lsdata.lsd_count = 0; 979*7c478bd9Sstevel@tonic-gate 980*7c478bd9Sstevel@tonic-gate if (g_tracing) { 981*7c478bd9Sstevel@tonic-gate if (dtrace_consume(g_dtp, out, 982*7c478bd9Sstevel@tonic-gate process_trace, NULL, &lsdata) != 0) 983*7c478bd9Sstevel@tonic-gate dfail("failed to consume buffer"); 984*7c478bd9Sstevel@tonic-gate 985*7c478bd9Sstevel@tonic-gate return (lsdata.lsd_count); 986*7c478bd9Sstevel@tonic-gate } 987*7c478bd9Sstevel@tonic-gate 988*7c478bd9Sstevel@tonic-gate if (dtrace_aggregate_walk_keyvarsorted(g_dtp, 989*7c478bd9Sstevel@tonic-gate process_aggregate, &lsdata) != 0) 990*7c478bd9Sstevel@tonic-gate dfail("failed to walk aggregate"); 991*7c478bd9Sstevel@tonic-gate 992*7c478bd9Sstevel@tonic-gate return (lsdata.lsd_count); 993*7c478bd9Sstevel@tonic-gate } 994*7c478bd9Sstevel@tonic-gate 995*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 996*7c478bd9Sstevel@tonic-gate static int 997*7c478bd9Sstevel@tonic-gate drophandler(dtrace_dropdata_t *data, void *arg) 998*7c478bd9Sstevel@tonic-gate { 999*7c478bd9Sstevel@tonic-gate g_dropped++; 1000*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: warning: %s", data->dtdda_msg); 1001*7c478bd9Sstevel@tonic-gate return (DTRACE_HANDLE_OK); 1002*7c478bd9Sstevel@tonic-gate } 1003*7c478bd9Sstevel@tonic-gate 1004*7c478bd9Sstevel@tonic-gate int 1005*7c478bd9Sstevel@tonic-gate main(int argc, char **argv) 1006*7c478bd9Sstevel@tonic-gate { 1007*7c478bd9Sstevel@tonic-gate char *data_buf; 1008*7c478bd9Sstevel@tonic-gate lsrec_t *lsp, **current, **first, **sort_buf, **merge_buf; 1009*7c478bd9Sstevel@tonic-gate FILE *out = stdout; 1010*7c478bd9Sstevel@tonic-gate char c; 1011*7c478bd9Sstevel@tonic-gate pid_t child; 1012*7c478bd9Sstevel@tonic-gate int status; 1013*7c478bd9Sstevel@tonic-gate int i, j; 1014*7c478bd9Sstevel@tonic-gate hrtime_t duration; 1015*7c478bd9Sstevel@tonic-gate char *addrp, *offp, *sizep, *evp, *lastp, *p; 1016*7c478bd9Sstevel@tonic-gate uintptr_t addr; 1017*7c478bd9Sstevel@tonic-gate size_t size, off; 1018*7c478bd9Sstevel@tonic-gate int events_specified = 0; 1019*7c478bd9Sstevel@tonic-gate int exec_errno = 0; 1020*7c478bd9Sstevel@tonic-gate uint32_t event; 1021*7c478bd9Sstevel@tonic-gate char *filt = NULL, *ifilt = NULL; 1022*7c478bd9Sstevel@tonic-gate static uint64_t ev_count[LS_MAX_EVENTS + 1]; 1023*7c478bd9Sstevel@tonic-gate static uint64_t ev_time[LS_MAX_EVENTS + 1]; 1024*7c478bd9Sstevel@tonic-gate dtrace_optval_t aggsize; 1025*7c478bd9Sstevel@tonic-gate char aggstr[10]; 1026*7c478bd9Sstevel@tonic-gate long ncpus; 1027*7c478bd9Sstevel@tonic-gate int dynvar = 0; 1028*7c478bd9Sstevel@tonic-gate int err; 1029*7c478bd9Sstevel@tonic-gate 1030*7c478bd9Sstevel@tonic-gate if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { 1031*7c478bd9Sstevel@tonic-gate fail(0, "cannot open dtrace library: %s", 1032*7c478bd9Sstevel@tonic-gate dtrace_errmsg(NULL, err)); 1033*7c478bd9Sstevel@tonic-gate } 1034*7c478bd9Sstevel@tonic-gate 1035*7c478bd9Sstevel@tonic-gate if (dtrace_handle_drop(g_dtp, &drophandler, NULL) == -1) 1036*7c478bd9Sstevel@tonic-gate dfail("couldn't establish drop handler"); 1037*7c478bd9Sstevel@tonic-gate 1038*7c478bd9Sstevel@tonic-gate if (symtab_init() == -1) 1039*7c478bd9Sstevel@tonic-gate fail(1, "can't load kernel symbols"); 1040*7c478bd9Sstevel@tonic-gate 1041*7c478bd9Sstevel@tonic-gate g_nrecs = DEFAULT_NRECS; 1042*7c478bd9Sstevel@tonic-gate 1043*7c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, LOCKSTAT_OPTSTR)) != EOF) { 1044*7c478bd9Sstevel@tonic-gate switch (c) { 1045*7c478bd9Sstevel@tonic-gate case 'b': 1046*7c478bd9Sstevel@tonic-gate g_recsize = LS_BASIC; 1047*7c478bd9Sstevel@tonic-gate break; 1048*7c478bd9Sstevel@tonic-gate 1049*7c478bd9Sstevel@tonic-gate case 't': 1050*7c478bd9Sstevel@tonic-gate g_recsize = LS_TIME; 1051*7c478bd9Sstevel@tonic-gate break; 1052*7c478bd9Sstevel@tonic-gate 1053*7c478bd9Sstevel@tonic-gate case 'h': 1054*7c478bd9Sstevel@tonic-gate g_recsize = LS_HIST; 1055*7c478bd9Sstevel@tonic-gate break; 1056*7c478bd9Sstevel@tonic-gate 1057*7c478bd9Sstevel@tonic-gate case 's': 1058*7c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 1059*7c478bd9Sstevel@tonic-gate usage(); 1060*7c478bd9Sstevel@tonic-gate g_stkdepth = atoi(optarg); 1061*7c478bd9Sstevel@tonic-gate if (g_stkdepth > LS_MAX_STACK_DEPTH) 1062*7c478bd9Sstevel@tonic-gate fail(0, "max stack depth is %d", 1063*7c478bd9Sstevel@tonic-gate LS_MAX_STACK_DEPTH); 1064*7c478bd9Sstevel@tonic-gate g_recsize = LS_STACK(g_stkdepth); 1065*7c478bd9Sstevel@tonic-gate break; 1066*7c478bd9Sstevel@tonic-gate 1067*7c478bd9Sstevel@tonic-gate case 'n': 1068*7c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 1069*7c478bd9Sstevel@tonic-gate usage(); 1070*7c478bd9Sstevel@tonic-gate g_nrecs = atoi(optarg); 1071*7c478bd9Sstevel@tonic-gate break; 1072*7c478bd9Sstevel@tonic-gate 1073*7c478bd9Sstevel@tonic-gate case 'd': 1074*7c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 1075*7c478bd9Sstevel@tonic-gate usage(); 1076*7c478bd9Sstevel@tonic-gate duration = atoll(optarg); 1077*7c478bd9Sstevel@tonic-gate 1078*7c478bd9Sstevel@tonic-gate /* 1079*7c478bd9Sstevel@tonic-gate * XXX -- durations really should be per event 1080*7c478bd9Sstevel@tonic-gate * since the units are different, but it's hard 1081*7c478bd9Sstevel@tonic-gate * to express this nicely in the interface. 1082*7c478bd9Sstevel@tonic-gate * Not clear yet what the cleanest solution is. 1083*7c478bd9Sstevel@tonic-gate */ 1084*7c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 1085*7c478bd9Sstevel@tonic-gate if (g_event_info[i].ev_type != 'E') 1086*7c478bd9Sstevel@tonic-gate g_min_duration[i] = duration; 1087*7c478bd9Sstevel@tonic-gate 1088*7c478bd9Sstevel@tonic-gate break; 1089*7c478bd9Sstevel@tonic-gate 1090*7c478bd9Sstevel@tonic-gate case 'i': 1091*7c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 1092*7c478bd9Sstevel@tonic-gate usage(); 1093*7c478bd9Sstevel@tonic-gate i = atoi(optarg); 1094*7c478bd9Sstevel@tonic-gate if (i <= 0) 1095*7c478bd9Sstevel@tonic-gate usage(); 1096*7c478bd9Sstevel@tonic-gate if (i > MAX_HZ) 1097*7c478bd9Sstevel@tonic-gate fail(0, "max interrupt rate is %d Hz", MAX_HZ); 1098*7c478bd9Sstevel@tonic-gate 1099*7c478bd9Sstevel@tonic-gate for (j = 0; j < LS_MAX_EVENTS; j++) 1100*7c478bd9Sstevel@tonic-gate if (strcmp(g_event_info[j].ev_desc, 1101*7c478bd9Sstevel@tonic-gate "Profiling interrupt") == 0) 1102*7c478bd9Sstevel@tonic-gate break; 1103*7c478bd9Sstevel@tonic-gate 1104*7c478bd9Sstevel@tonic-gate (void) sprintf(g_event_info[j].ev_name, 1105*7c478bd9Sstevel@tonic-gate "profile:::profile-%d", i); 1106*7c478bd9Sstevel@tonic-gate break; 1107*7c478bd9Sstevel@tonic-gate 1108*7c478bd9Sstevel@tonic-gate case 'l': 1109*7c478bd9Sstevel@tonic-gate case 'f': 1110*7c478bd9Sstevel@tonic-gate addrp = strtok(optarg, ","); 1111*7c478bd9Sstevel@tonic-gate sizep = strtok(NULL, ","); 1112*7c478bd9Sstevel@tonic-gate addrp = strtok(optarg, ",+"); 1113*7c478bd9Sstevel@tonic-gate offp = strtok(NULL, ","); 1114*7c478bd9Sstevel@tonic-gate 1115*7c478bd9Sstevel@tonic-gate size = sizep ? strtoul(sizep, NULL, 0) : 1; 1116*7c478bd9Sstevel@tonic-gate off = offp ? strtoul(offp, NULL, 0) : 0; 1117*7c478bd9Sstevel@tonic-gate 1118*7c478bd9Sstevel@tonic-gate if (addrp[0] == '0') { 1119*7c478bd9Sstevel@tonic-gate addr = strtoul(addrp, NULL, 16) + off; 1120*7c478bd9Sstevel@tonic-gate } else { 1121*7c478bd9Sstevel@tonic-gate addr = sym_to_addr(addrp) + off; 1122*7c478bd9Sstevel@tonic-gate if (sizep == NULL) 1123*7c478bd9Sstevel@tonic-gate size = sym_size(addrp) - off; 1124*7c478bd9Sstevel@tonic-gate if (addr - off == 0) 1125*7c478bd9Sstevel@tonic-gate fail(0, "symbol '%s' not found", addrp); 1126*7c478bd9Sstevel@tonic-gate if (size == 0) 1127*7c478bd9Sstevel@tonic-gate size = 1; 1128*7c478bd9Sstevel@tonic-gate } 1129*7c478bd9Sstevel@tonic-gate 1130*7c478bd9Sstevel@tonic-gate 1131*7c478bd9Sstevel@tonic-gate if (c == 'l') { 1132*7c478bd9Sstevel@tonic-gate filter_add(&filt, "arg0", addr, size); 1133*7c478bd9Sstevel@tonic-gate } else { 1134*7c478bd9Sstevel@tonic-gate filter_add(&filt, "caller", addr, size); 1135*7c478bd9Sstevel@tonic-gate filter_add(&ifilt, "arg0", addr, size); 1136*7c478bd9Sstevel@tonic-gate } 1137*7c478bd9Sstevel@tonic-gate break; 1138*7c478bd9Sstevel@tonic-gate 1139*7c478bd9Sstevel@tonic-gate case 'e': 1140*7c478bd9Sstevel@tonic-gate evp = strtok_r(optarg, ",", &lastp); 1141*7c478bd9Sstevel@tonic-gate while (evp) { 1142*7c478bd9Sstevel@tonic-gate int ev1, ev2; 1143*7c478bd9Sstevel@tonic-gate char *evp2; 1144*7c478bd9Sstevel@tonic-gate 1145*7c478bd9Sstevel@tonic-gate (void) strtok(evp, "-"); 1146*7c478bd9Sstevel@tonic-gate evp2 = strtok(NULL, "-"); 1147*7c478bd9Sstevel@tonic-gate ev1 = atoi(evp); 1148*7c478bd9Sstevel@tonic-gate ev2 = evp2 ? atoi(evp2) : ev1; 1149*7c478bd9Sstevel@tonic-gate if ((uint_t)ev1 >= LS_MAX_EVENTS || 1150*7c478bd9Sstevel@tonic-gate (uint_t)ev2 >= LS_MAX_EVENTS || ev1 > ev2) 1151*7c478bd9Sstevel@tonic-gate fail(0, "-e events out of range"); 1152*7c478bd9Sstevel@tonic-gate for (i = ev1; i <= ev2; i++) 1153*7c478bd9Sstevel@tonic-gate g_enabled[i] = 1; 1154*7c478bd9Sstevel@tonic-gate evp = strtok_r(NULL, ",", &lastp); 1155*7c478bd9Sstevel@tonic-gate } 1156*7c478bd9Sstevel@tonic-gate events_specified = 1; 1157*7c478bd9Sstevel@tonic-gate break; 1158*7c478bd9Sstevel@tonic-gate 1159*7c478bd9Sstevel@tonic-gate case 'c': 1160*7c478bd9Sstevel@tonic-gate g_cflag = 1; 1161*7c478bd9Sstevel@tonic-gate break; 1162*7c478bd9Sstevel@tonic-gate 1163*7c478bd9Sstevel@tonic-gate case 'k': 1164*7c478bd9Sstevel@tonic-gate g_kflag = 1; 1165*7c478bd9Sstevel@tonic-gate break; 1166*7c478bd9Sstevel@tonic-gate 1167*7c478bd9Sstevel@tonic-gate case 'w': 1168*7c478bd9Sstevel@tonic-gate g_wflag = 1; 1169*7c478bd9Sstevel@tonic-gate break; 1170*7c478bd9Sstevel@tonic-gate 1171*7c478bd9Sstevel@tonic-gate case 'W': 1172*7c478bd9Sstevel@tonic-gate g_Wflag = 1; 1173*7c478bd9Sstevel@tonic-gate break; 1174*7c478bd9Sstevel@tonic-gate 1175*7c478bd9Sstevel@tonic-gate case 'g': 1176*7c478bd9Sstevel@tonic-gate g_gflag = 1; 1177*7c478bd9Sstevel@tonic-gate break; 1178*7c478bd9Sstevel@tonic-gate 1179*7c478bd9Sstevel@tonic-gate case 'C': 1180*7c478bd9Sstevel@tonic-gate case 'E': 1181*7c478bd9Sstevel@tonic-gate case 'H': 1182*7c478bd9Sstevel@tonic-gate case 'I': 1183*7c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 1184*7c478bd9Sstevel@tonic-gate if (g_event_info[i].ev_type == c) 1185*7c478bd9Sstevel@tonic-gate g_enabled[i] = 1; 1186*7c478bd9Sstevel@tonic-gate events_specified = 1; 1187*7c478bd9Sstevel@tonic-gate break; 1188*7c478bd9Sstevel@tonic-gate 1189*7c478bd9Sstevel@tonic-gate case 'A': 1190*7c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 1191*7c478bd9Sstevel@tonic-gate if (strchr("CH", g_event_info[i].ev_type)) 1192*7c478bd9Sstevel@tonic-gate g_enabled[i] = 1; 1193*7c478bd9Sstevel@tonic-gate events_specified = 1; 1194*7c478bd9Sstevel@tonic-gate break; 1195*7c478bd9Sstevel@tonic-gate 1196*7c478bd9Sstevel@tonic-gate case 'T': 1197*7c478bd9Sstevel@tonic-gate g_tracing = 1; 1198*7c478bd9Sstevel@tonic-gate break; 1199*7c478bd9Sstevel@tonic-gate 1200*7c478bd9Sstevel@tonic-gate case 'D': 1201*7c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 1202*7c478bd9Sstevel@tonic-gate usage(); 1203*7c478bd9Sstevel@tonic-gate g_topn = atoi(optarg); 1204*7c478bd9Sstevel@tonic-gate break; 1205*7c478bd9Sstevel@tonic-gate 1206*7c478bd9Sstevel@tonic-gate case 'R': 1207*7c478bd9Sstevel@tonic-gate g_rates = 1; 1208*7c478bd9Sstevel@tonic-gate break; 1209*7c478bd9Sstevel@tonic-gate 1210*7c478bd9Sstevel@tonic-gate case 'p': 1211*7c478bd9Sstevel@tonic-gate g_pflag = 1; 1212*7c478bd9Sstevel@tonic-gate break; 1213*7c478bd9Sstevel@tonic-gate 1214*7c478bd9Sstevel@tonic-gate case 'P': 1215*7c478bd9Sstevel@tonic-gate g_Pflag = 1; 1216*7c478bd9Sstevel@tonic-gate break; 1217*7c478bd9Sstevel@tonic-gate 1218*7c478bd9Sstevel@tonic-gate case 'o': 1219*7c478bd9Sstevel@tonic-gate if ((out = fopen(optarg, "w")) == NULL) 1220*7c478bd9Sstevel@tonic-gate fail(1, "error opening file"); 1221*7c478bd9Sstevel@tonic-gate break; 1222*7c478bd9Sstevel@tonic-gate 1223*7c478bd9Sstevel@tonic-gate case 'V': 1224*7c478bd9Sstevel@tonic-gate g_Vflag = 1; 1225*7c478bd9Sstevel@tonic-gate break; 1226*7c478bd9Sstevel@tonic-gate 1227*7c478bd9Sstevel@tonic-gate default: 1228*7c478bd9Sstevel@tonic-gate if (strchr(LOCKSTAT_OPTSTR, c) == NULL) 1229*7c478bd9Sstevel@tonic-gate usage(); 1230*7c478bd9Sstevel@tonic-gate } 1231*7c478bd9Sstevel@tonic-gate } 1232*7c478bd9Sstevel@tonic-gate 1233*7c478bd9Sstevel@tonic-gate if (filt != NULL) { 1234*7c478bd9Sstevel@tonic-gate predicate_add(&g_predicate, filt, NULL, 0); 1235*7c478bd9Sstevel@tonic-gate filter_destroy(&filt); 1236*7c478bd9Sstevel@tonic-gate } 1237*7c478bd9Sstevel@tonic-gate 1238*7c478bd9Sstevel@tonic-gate if (ifilt != NULL) { 1239*7c478bd9Sstevel@tonic-gate predicate_add(&g_ipredicate, ifilt, NULL, 0); 1240*7c478bd9Sstevel@tonic-gate filter_destroy(&ifilt); 1241*7c478bd9Sstevel@tonic-gate } 1242*7c478bd9Sstevel@tonic-gate 1243*7c478bd9Sstevel@tonic-gate if (g_recsize == 0) { 1244*7c478bd9Sstevel@tonic-gate if (g_gflag) { 1245*7c478bd9Sstevel@tonic-gate g_stkdepth = LS_MAX_STACK_DEPTH; 1246*7c478bd9Sstevel@tonic-gate g_recsize = LS_STACK(g_stkdepth); 1247*7c478bd9Sstevel@tonic-gate } else { 1248*7c478bd9Sstevel@tonic-gate g_recsize = LS_TIME; 1249*7c478bd9Sstevel@tonic-gate } 1250*7c478bd9Sstevel@tonic-gate } 1251*7c478bd9Sstevel@tonic-gate 1252*7c478bd9Sstevel@tonic-gate if (g_gflag && g_recsize <= LS_STACK(0)) 1253*7c478bd9Sstevel@tonic-gate fail(0, "'-g' requires at least '-s 1' data gathering"); 1254*7c478bd9Sstevel@tonic-gate 1255*7c478bd9Sstevel@tonic-gate /* 1256*7c478bd9Sstevel@tonic-gate * Make sure the alignment is reasonable 1257*7c478bd9Sstevel@tonic-gate */ 1258*7c478bd9Sstevel@tonic-gate g_recsize = -(-g_recsize & -sizeof (uint64_t)); 1259*7c478bd9Sstevel@tonic-gate 1260*7c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 1261*7c478bd9Sstevel@tonic-gate /* 1262*7c478bd9Sstevel@tonic-gate * If no events were specified, enable -C. 1263*7c478bd9Sstevel@tonic-gate */ 1264*7c478bd9Sstevel@tonic-gate if (!events_specified && g_event_info[i].ev_type == 'C') 1265*7c478bd9Sstevel@tonic-gate g_enabled[i] = 1; 1266*7c478bd9Sstevel@tonic-gate } 1267*7c478bd9Sstevel@tonic-gate 1268*7c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 1269*7c478bd9Sstevel@tonic-gate if (!g_enabled[i]) 1270*7c478bd9Sstevel@tonic-gate continue; 1271*7c478bd9Sstevel@tonic-gate 1272*7c478bd9Sstevel@tonic-gate if (g_event_info[i].ev_acquire != NULL) { 1273*7c478bd9Sstevel@tonic-gate /* 1274*7c478bd9Sstevel@tonic-gate * If we've enabled a hold event, we must explicitly 1275*7c478bd9Sstevel@tonic-gate * allocate dynamic variable space. 1276*7c478bd9Sstevel@tonic-gate */ 1277*7c478bd9Sstevel@tonic-gate dynvar = 1; 1278*7c478bd9Sstevel@tonic-gate } 1279*7c478bd9Sstevel@tonic-gate 1280*7c478bd9Sstevel@tonic-gate dprog_addevent(i); 1281*7c478bd9Sstevel@tonic-gate } 1282*7c478bd9Sstevel@tonic-gate 1283*7c478bd9Sstevel@tonic-gate /* 1284*7c478bd9Sstevel@tonic-gate * Make sure there are remaining arguments to specify a child command 1285*7c478bd9Sstevel@tonic-gate * to execute. 1286*7c478bd9Sstevel@tonic-gate */ 1287*7c478bd9Sstevel@tonic-gate if (argc <= optind) 1288*7c478bd9Sstevel@tonic-gate usage(); 1289*7c478bd9Sstevel@tonic-gate 1290*7c478bd9Sstevel@tonic-gate if ((ncpus = sysconf(_SC_NPROCESSORS_ONLN)) == -1) 1291*7c478bd9Sstevel@tonic-gate dfail("couldn't determine number of online CPUs"); 1292*7c478bd9Sstevel@tonic-gate 1293*7c478bd9Sstevel@tonic-gate /* 1294*7c478bd9Sstevel@tonic-gate * By default, we set our data buffer size to be the number of records 1295*7c478bd9Sstevel@tonic-gate * multiplied by the size of the record, doubled to account for some 1296*7c478bd9Sstevel@tonic-gate * DTrace slop and divided by the number of CPUs. We silently clamp 1297*7c478bd9Sstevel@tonic-gate * the aggregation size at both a minimum and a maximum to prevent 1298*7c478bd9Sstevel@tonic-gate * absurdly low or high values. 1299*7c478bd9Sstevel@tonic-gate */ 1300*7c478bd9Sstevel@tonic-gate if ((aggsize = (g_nrecs * g_recsize * 2) / ncpus) < MIN_AGGSIZE) 1301*7c478bd9Sstevel@tonic-gate aggsize = MIN_AGGSIZE; 1302*7c478bd9Sstevel@tonic-gate 1303*7c478bd9Sstevel@tonic-gate if (aggsize > MAX_AGGSIZE) 1304*7c478bd9Sstevel@tonic-gate aggsize = MAX_AGGSIZE; 1305*7c478bd9Sstevel@tonic-gate 1306*7c478bd9Sstevel@tonic-gate (void) sprintf(aggstr, "%lld", (long long)aggsize); 1307*7c478bd9Sstevel@tonic-gate 1308*7c478bd9Sstevel@tonic-gate if (!g_tracing) { 1309*7c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "bufsize", "4k") == -1) 1310*7c478bd9Sstevel@tonic-gate dfail("failed to set 'bufsize'"); 1311*7c478bd9Sstevel@tonic-gate 1312*7c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "aggsize", aggstr) == -1) 1313*7c478bd9Sstevel@tonic-gate dfail("failed to set 'aggsize'"); 1314*7c478bd9Sstevel@tonic-gate 1315*7c478bd9Sstevel@tonic-gate if (dynvar) { 1316*7c478bd9Sstevel@tonic-gate /* 1317*7c478bd9Sstevel@tonic-gate * If we're using dynamic variables, we set our 1318*7c478bd9Sstevel@tonic-gate * dynamic variable size to be one megabyte per CPU, 1319*7c478bd9Sstevel@tonic-gate * with a hard-limit of 32 megabytes. This may still 1320*7c478bd9Sstevel@tonic-gate * be too small in some cases, but it can be tuned 1321*7c478bd9Sstevel@tonic-gate * manually via -x if need be. 1322*7c478bd9Sstevel@tonic-gate */ 1323*7c478bd9Sstevel@tonic-gate (void) sprintf(aggstr, "%ldm", ncpus < 32 ? ncpus : 32); 1324*7c478bd9Sstevel@tonic-gate 1325*7c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "dynvarsize", aggstr) == -1) 1326*7c478bd9Sstevel@tonic-gate dfail("failed to set 'dynvarsize'"); 1327*7c478bd9Sstevel@tonic-gate } 1328*7c478bd9Sstevel@tonic-gate } else { 1329*7c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "bufsize", aggstr) == -1) 1330*7c478bd9Sstevel@tonic-gate dfail("failed to set 'bufsize'"); 1331*7c478bd9Sstevel@tonic-gate } 1332*7c478bd9Sstevel@tonic-gate 1333*7c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "statusrate", "10sec") == -1) 1334*7c478bd9Sstevel@tonic-gate dfail("failed to set 'statusrate'"); 1335*7c478bd9Sstevel@tonic-gate 1336*7c478bd9Sstevel@tonic-gate optind = 1; 1337*7c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, LOCKSTAT_OPTSTR)) != EOF) { 1338*7c478bd9Sstevel@tonic-gate switch (c) { 1339*7c478bd9Sstevel@tonic-gate case 'x': 1340*7c478bd9Sstevel@tonic-gate if ((p = strchr(optarg, '=')) != NULL) 1341*7c478bd9Sstevel@tonic-gate *p++ = '\0'; 1342*7c478bd9Sstevel@tonic-gate 1343*7c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, optarg, p) != 0) 1344*7c478bd9Sstevel@tonic-gate dfail("failed to set -x %s", optarg); 1345*7c478bd9Sstevel@tonic-gate break; 1346*7c478bd9Sstevel@tonic-gate } 1347*7c478bd9Sstevel@tonic-gate } 1348*7c478bd9Sstevel@tonic-gate 1349*7c478bd9Sstevel@tonic-gate argc -= optind; 1350*7c478bd9Sstevel@tonic-gate argv += optind; 1351*7c478bd9Sstevel@tonic-gate 1352*7c478bd9Sstevel@tonic-gate dprog_compile(); 1353*7c478bd9Sstevel@tonic-gate status_init(); 1354*7c478bd9Sstevel@tonic-gate 1355*7c478bd9Sstevel@tonic-gate g_elapsed = -gethrtime(); 1356*7c478bd9Sstevel@tonic-gate 1357*7c478bd9Sstevel@tonic-gate /* 1358*7c478bd9Sstevel@tonic-gate * Spawn the specified command and wait for it to complete. 1359*7c478bd9Sstevel@tonic-gate */ 1360*7c478bd9Sstevel@tonic-gate child = fork(); 1361*7c478bd9Sstevel@tonic-gate if (child == -1) 1362*7c478bd9Sstevel@tonic-gate fail(1, "cannot fork"); 1363*7c478bd9Sstevel@tonic-gate if (child == 0) { 1364*7c478bd9Sstevel@tonic-gate (void) dtrace_close(g_dtp); 1365*7c478bd9Sstevel@tonic-gate (void) execvp(argv[0], &argv[0]); 1366*7c478bd9Sstevel@tonic-gate exec_errno = errno; 1367*7c478bd9Sstevel@tonic-gate exit(127); 1368*7c478bd9Sstevel@tonic-gate } 1369*7c478bd9Sstevel@tonic-gate 1370*7c478bd9Sstevel@tonic-gate while (waitpid(child, &status, WEXITED) != child) 1371*7c478bd9Sstevel@tonic-gate status_check(); 1372*7c478bd9Sstevel@tonic-gate 1373*7c478bd9Sstevel@tonic-gate g_elapsed += gethrtime(); 1374*7c478bd9Sstevel@tonic-gate 1375*7c478bd9Sstevel@tonic-gate if (WIFEXITED(status)) { 1376*7c478bd9Sstevel@tonic-gate if (WEXITSTATUS(status) != 0) { 1377*7c478bd9Sstevel@tonic-gate if (exec_errno != 0) { 1378*7c478bd9Sstevel@tonic-gate errno = exec_errno; 1379*7c478bd9Sstevel@tonic-gate fail(1, "could not execute %s", argv[0]); 1380*7c478bd9Sstevel@tonic-gate } 1381*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1382*7c478bd9Sstevel@tonic-gate "lockstat: warning: %s exited with code %d\n", 1383*7c478bd9Sstevel@tonic-gate argv[0], WEXITSTATUS(status)); 1384*7c478bd9Sstevel@tonic-gate } 1385*7c478bd9Sstevel@tonic-gate } else { 1386*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 1387*7c478bd9Sstevel@tonic-gate "lockstat: warning: %s died on signal %d\n", 1388*7c478bd9Sstevel@tonic-gate argv[0], WTERMSIG(status)); 1389*7c478bd9Sstevel@tonic-gate } 1390*7c478bd9Sstevel@tonic-gate 1391*7c478bd9Sstevel@tonic-gate if (dtrace_stop(g_dtp) == -1) 1392*7c478bd9Sstevel@tonic-gate dfail("failed to stop dtrace"); 1393*7c478bd9Sstevel@tonic-gate 1394*7c478bd9Sstevel@tonic-gate /* 1395*7c478bd9Sstevel@tonic-gate * Before we read out the results, we need to allocate our buffer. 1396*7c478bd9Sstevel@tonic-gate * If we're tracing, then we'll just use the precalculated size. If 1397*7c478bd9Sstevel@tonic-gate * we're not, then we'll take a snapshot of the aggregate, and walk 1398*7c478bd9Sstevel@tonic-gate * it to count the number of records. 1399*7c478bd9Sstevel@tonic-gate */ 1400*7c478bd9Sstevel@tonic-gate if (!g_tracing) { 1401*7c478bd9Sstevel@tonic-gate if (dtrace_aggregate_snap(g_dtp) != 0) 1402*7c478bd9Sstevel@tonic-gate dfail("failed to snap aggregate"); 1403*7c478bd9Sstevel@tonic-gate 1404*7c478bd9Sstevel@tonic-gate g_nrecs = 0; 1405*7c478bd9Sstevel@tonic-gate 1406*7c478bd9Sstevel@tonic-gate if (dtrace_aggregate_walk(g_dtp, 1407*7c478bd9Sstevel@tonic-gate count_aggregate, &g_nrecs) != 0) 1408*7c478bd9Sstevel@tonic-gate dfail("failed to walk aggregate"); 1409*7c478bd9Sstevel@tonic-gate } 1410*7c478bd9Sstevel@tonic-gate 1411*7c478bd9Sstevel@tonic-gate if ((data_buf = memalign(sizeof (uint64_t), 1412*7c478bd9Sstevel@tonic-gate (g_nrecs + 1) * g_recsize)) == NULL) 1413*7c478bd9Sstevel@tonic-gate fail(1, "Memory allocation failed"); 1414*7c478bd9Sstevel@tonic-gate 1415*7c478bd9Sstevel@tonic-gate /* 1416*7c478bd9Sstevel@tonic-gate * Read out the DTrace data. 1417*7c478bd9Sstevel@tonic-gate */ 1418*7c478bd9Sstevel@tonic-gate g_nrecs_used = process_data(out, data_buf); 1419*7c478bd9Sstevel@tonic-gate 1420*7c478bd9Sstevel@tonic-gate if (g_nrecs_used > g_nrecs || g_dropped) 1421*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: warning: " 1422*7c478bd9Sstevel@tonic-gate "ran out of data records (use -n for more)\n"); 1423*7c478bd9Sstevel@tonic-gate 1424*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 1425*7c478bd9Sstevel@tonic-gate for (i = 0, lsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 1426*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 1427*7c478bd9Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + g_recsize)) { 1428*7c478bd9Sstevel@tonic-gate ev_count[lsp->ls_event] += lsp->ls_count; 1429*7c478bd9Sstevel@tonic-gate ev_time[lsp->ls_event] += lsp->ls_time; 1430*7c478bd9Sstevel@tonic-gate } 1431*7c478bd9Sstevel@tonic-gate 1432*7c478bd9Sstevel@tonic-gate /* 1433*7c478bd9Sstevel@tonic-gate * If -g was specified, convert stacks into individual records. 1434*7c478bd9Sstevel@tonic-gate */ 1435*7c478bd9Sstevel@tonic-gate if (g_gflag) { 1436*7c478bd9Sstevel@tonic-gate lsrec_t *newlsp, *oldlsp; 1437*7c478bd9Sstevel@tonic-gate 1438*7c478bd9Sstevel@tonic-gate newlsp = memalign(sizeof (uint64_t), 1439*7c478bd9Sstevel@tonic-gate g_nrecs_used * LS_TIME * (g_stkdepth + 1)); 1440*7c478bd9Sstevel@tonic-gate if (newlsp == NULL) 1441*7c478bd9Sstevel@tonic-gate fail(1, "Cannot allocate space for -g processing"); 1442*7c478bd9Sstevel@tonic-gate lsp = newlsp; 1443*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 1444*7c478bd9Sstevel@tonic-gate for (i = 0, oldlsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 1445*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 1446*7c478bd9Sstevel@tonic-gate oldlsp = (lsrec_t *)((char *)oldlsp + g_recsize)) { 1447*7c478bd9Sstevel@tonic-gate int fr; 1448*7c478bd9Sstevel@tonic-gate int caller_in_stack = 0; 1449*7c478bd9Sstevel@tonic-gate 1450*7c478bd9Sstevel@tonic-gate if (oldlsp->ls_count == 0) 1451*7c478bd9Sstevel@tonic-gate continue; 1452*7c478bd9Sstevel@tonic-gate 1453*7c478bd9Sstevel@tonic-gate for (fr = 0; fr < g_stkdepth; fr++) { 1454*7c478bd9Sstevel@tonic-gate if (oldlsp->ls_stack[fr] == 0) 1455*7c478bd9Sstevel@tonic-gate break; 1456*7c478bd9Sstevel@tonic-gate if (oldlsp->ls_stack[fr] == oldlsp->ls_caller) 1457*7c478bd9Sstevel@tonic-gate caller_in_stack = 1; 1458*7c478bd9Sstevel@tonic-gate bcopy(oldlsp, lsp, LS_TIME); 1459*7c478bd9Sstevel@tonic-gate lsp->ls_caller = oldlsp->ls_stack[fr]; 1460*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 1461*7c478bd9Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + LS_TIME); 1462*7c478bd9Sstevel@tonic-gate } 1463*7c478bd9Sstevel@tonic-gate if (!caller_in_stack) { 1464*7c478bd9Sstevel@tonic-gate bcopy(oldlsp, lsp, LS_TIME); 1465*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 1466*7c478bd9Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + LS_TIME); 1467*7c478bd9Sstevel@tonic-gate } 1468*7c478bd9Sstevel@tonic-gate } 1469*7c478bd9Sstevel@tonic-gate g_nrecs = g_nrecs_used = 1470*7c478bd9Sstevel@tonic-gate ((uintptr_t)lsp - (uintptr_t)newlsp) / LS_TIME; 1471*7c478bd9Sstevel@tonic-gate g_recsize = LS_TIME; 1472*7c478bd9Sstevel@tonic-gate g_stkdepth = 0; 1473*7c478bd9Sstevel@tonic-gate free(data_buf); 1474*7c478bd9Sstevel@tonic-gate data_buf = (char *)newlsp; 1475*7c478bd9Sstevel@tonic-gate } 1476*7c478bd9Sstevel@tonic-gate 1477*7c478bd9Sstevel@tonic-gate if ((sort_buf = calloc(2 * (g_nrecs + 1), 1478*7c478bd9Sstevel@tonic-gate sizeof (void *))) == NULL) 1479*7c478bd9Sstevel@tonic-gate fail(1, "Sort buffer allocation failed"); 1480*7c478bd9Sstevel@tonic-gate merge_buf = sort_buf + (g_nrecs + 1); 1481*7c478bd9Sstevel@tonic-gate 1482*7c478bd9Sstevel@tonic-gate /* 1483*7c478bd9Sstevel@tonic-gate * Build the sort buffer, discarding zero-count records along the way. 1484*7c478bd9Sstevel@tonic-gate */ 1485*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 1486*7c478bd9Sstevel@tonic-gate for (i = 0, lsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 1487*7c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 1488*7c478bd9Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + g_recsize)) { 1489*7c478bd9Sstevel@tonic-gate if (lsp->ls_count == 0) 1490*7c478bd9Sstevel@tonic-gate lsp->ls_event = LS_MAX_EVENTS; 1491*7c478bd9Sstevel@tonic-gate sort_buf[i] = lsp; 1492*7c478bd9Sstevel@tonic-gate } 1493*7c478bd9Sstevel@tonic-gate 1494*7c478bd9Sstevel@tonic-gate if (g_nrecs_used == 0) 1495*7c478bd9Sstevel@tonic-gate exit(0); 1496*7c478bd9Sstevel@tonic-gate 1497*7c478bd9Sstevel@tonic-gate /* 1498*7c478bd9Sstevel@tonic-gate * Add a sentinel after the last record 1499*7c478bd9Sstevel@tonic-gate */ 1500*7c478bd9Sstevel@tonic-gate sort_buf[i] = lsp; 1501*7c478bd9Sstevel@tonic-gate lsp->ls_event = LS_MAX_EVENTS; 1502*7c478bd9Sstevel@tonic-gate 1503*7c478bd9Sstevel@tonic-gate if (g_tracing) { 1504*7c478bd9Sstevel@tonic-gate report_trace(out, sort_buf); 1505*7c478bd9Sstevel@tonic-gate return (0); 1506*7c478bd9Sstevel@tonic-gate } 1507*7c478bd9Sstevel@tonic-gate 1508*7c478bd9Sstevel@tonic-gate /* 1509*7c478bd9Sstevel@tonic-gate * Application of -g may have resulted in multiple records 1510*7c478bd9Sstevel@tonic-gate * with the same signature; coalesce them. 1511*7c478bd9Sstevel@tonic-gate */ 1512*7c478bd9Sstevel@tonic-gate if (g_gflag) { 1513*7c478bd9Sstevel@tonic-gate mergesort(lockcmp, sort_buf, merge_buf, g_nrecs_used); 1514*7c478bd9Sstevel@tonic-gate coalesce(lockcmp, sort_buf, g_nrecs_used); 1515*7c478bd9Sstevel@tonic-gate } 1516*7c478bd9Sstevel@tonic-gate 1517*7c478bd9Sstevel@tonic-gate /* 1518*7c478bd9Sstevel@tonic-gate * Coalesce locks within the same symbol if -c option specified. 1519*7c478bd9Sstevel@tonic-gate * Coalesce PCs within the same function if -k option specified. 1520*7c478bd9Sstevel@tonic-gate */ 1521*7c478bd9Sstevel@tonic-gate if (g_cflag || g_kflag) { 1522*7c478bd9Sstevel@tonic-gate for (i = 0; i < g_nrecs_used; i++) { 1523*7c478bd9Sstevel@tonic-gate int fr; 1524*7c478bd9Sstevel@tonic-gate lsp = sort_buf[i]; 1525*7c478bd9Sstevel@tonic-gate if (g_cflag) 1526*7c478bd9Sstevel@tonic-gate coalesce_symbol(&lsp->ls_lock); 1527*7c478bd9Sstevel@tonic-gate if (g_kflag) { 1528*7c478bd9Sstevel@tonic-gate for (fr = 0; fr < g_stkdepth; fr++) 1529*7c478bd9Sstevel@tonic-gate coalesce_symbol(&lsp->ls_stack[fr]); 1530*7c478bd9Sstevel@tonic-gate coalesce_symbol(&lsp->ls_caller); 1531*7c478bd9Sstevel@tonic-gate } 1532*7c478bd9Sstevel@tonic-gate } 1533*7c478bd9Sstevel@tonic-gate mergesort(lockcmp, sort_buf, merge_buf, g_nrecs_used); 1534*7c478bd9Sstevel@tonic-gate coalesce(lockcmp, sort_buf, g_nrecs_used); 1535*7c478bd9Sstevel@tonic-gate } 1536*7c478bd9Sstevel@tonic-gate 1537*7c478bd9Sstevel@tonic-gate /* 1538*7c478bd9Sstevel@tonic-gate * Coalesce callers if -w option specified 1539*7c478bd9Sstevel@tonic-gate */ 1540*7c478bd9Sstevel@tonic-gate if (g_wflag) { 1541*7c478bd9Sstevel@tonic-gate mergesort(lock_and_count_cmp_anywhere, 1542*7c478bd9Sstevel@tonic-gate sort_buf, merge_buf, g_nrecs_used); 1543*7c478bd9Sstevel@tonic-gate coalesce(lockcmp_anywhere, sort_buf, g_nrecs_used); 1544*7c478bd9Sstevel@tonic-gate } 1545*7c478bd9Sstevel@tonic-gate 1546*7c478bd9Sstevel@tonic-gate /* 1547*7c478bd9Sstevel@tonic-gate * Coalesce locks if -W option specified 1548*7c478bd9Sstevel@tonic-gate */ 1549*7c478bd9Sstevel@tonic-gate if (g_Wflag) { 1550*7c478bd9Sstevel@tonic-gate mergesort(site_and_count_cmp_anylock, 1551*7c478bd9Sstevel@tonic-gate sort_buf, merge_buf, g_nrecs_used); 1552*7c478bd9Sstevel@tonic-gate coalesce(sitecmp_anylock, sort_buf, g_nrecs_used); 1553*7c478bd9Sstevel@tonic-gate } 1554*7c478bd9Sstevel@tonic-gate 1555*7c478bd9Sstevel@tonic-gate /* 1556*7c478bd9Sstevel@tonic-gate * Sort data by contention count (ls_count) or total time (ls_time), 1557*7c478bd9Sstevel@tonic-gate * depending on g_Pflag. Override g_Pflag if time wasn't measured. 1558*7c478bd9Sstevel@tonic-gate */ 1559*7c478bd9Sstevel@tonic-gate if (g_recsize < LS_TIME) 1560*7c478bd9Sstevel@tonic-gate g_Pflag = 0; 1561*7c478bd9Sstevel@tonic-gate 1562*7c478bd9Sstevel@tonic-gate if (g_Pflag) 1563*7c478bd9Sstevel@tonic-gate mergesort(timecmp, sort_buf, merge_buf, g_nrecs_used); 1564*7c478bd9Sstevel@tonic-gate else 1565*7c478bd9Sstevel@tonic-gate mergesort(countcmp, sort_buf, merge_buf, g_nrecs_used); 1566*7c478bd9Sstevel@tonic-gate 1567*7c478bd9Sstevel@tonic-gate /* 1568*7c478bd9Sstevel@tonic-gate * Display data by event type 1569*7c478bd9Sstevel@tonic-gate */ 1570*7c478bd9Sstevel@tonic-gate first = &sort_buf[0]; 1571*7c478bd9Sstevel@tonic-gate while ((event = (*first)->ls_event) < LS_MAX_EVENTS) { 1572*7c478bd9Sstevel@tonic-gate current = first; 1573*7c478bd9Sstevel@tonic-gate while ((lsp = *current)->ls_event == event) 1574*7c478bd9Sstevel@tonic-gate current++; 1575*7c478bd9Sstevel@tonic-gate report_stats(out, first, current - first, ev_count[event], 1576*7c478bd9Sstevel@tonic-gate ev_time[event]); 1577*7c478bd9Sstevel@tonic-gate first = current; 1578*7c478bd9Sstevel@tonic-gate } 1579*7c478bd9Sstevel@tonic-gate 1580*7c478bd9Sstevel@tonic-gate return (0); 1581*7c478bd9Sstevel@tonic-gate } 1582*7c478bd9Sstevel@tonic-gate 1583*7c478bd9Sstevel@tonic-gate static char * 1584*7c478bd9Sstevel@tonic-gate format_symbol(char *buf, uintptr_t addr, int show_size) 1585*7c478bd9Sstevel@tonic-gate { 1586*7c478bd9Sstevel@tonic-gate uintptr_t symoff; 1587*7c478bd9Sstevel@tonic-gate char *symname; 1588*7c478bd9Sstevel@tonic-gate size_t symsize; 1589*7c478bd9Sstevel@tonic-gate 1590*7c478bd9Sstevel@tonic-gate symname = addr_to_sym(addr, &symoff, &symsize); 1591*7c478bd9Sstevel@tonic-gate 1592*7c478bd9Sstevel@tonic-gate if (show_size && symoff == 0) 1593*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s[%ld]", symname, (long)symsize); 1594*7c478bd9Sstevel@tonic-gate else if (symoff == 0) 1595*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s", symname); 1596*7c478bd9Sstevel@tonic-gate else if (symoff < 16 && bcmp(symname, "cpu[", 4) == 0) /* CPU+PIL */ 1597*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s+%ld", symname, (long)symoff); 1598*7c478bd9Sstevel@tonic-gate else if (symoff <= symsize || (symoff < 256 && addr != symoff)) 1599*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s+0x%llx", symname, 1600*7c478bd9Sstevel@tonic-gate (unsigned long long)symoff); 1601*7c478bd9Sstevel@tonic-gate else 1602*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "0x%llx", (unsigned long long)addr); 1603*7c478bd9Sstevel@tonic-gate return (buf); 1604*7c478bd9Sstevel@tonic-gate } 1605*7c478bd9Sstevel@tonic-gate 1606*7c478bd9Sstevel@tonic-gate static void 1607*7c478bd9Sstevel@tonic-gate report_stats(FILE *out, lsrec_t **sort_buf, size_t nrecs, uint64_t total_count, 1608*7c478bd9Sstevel@tonic-gate uint64_t total_time) 1609*7c478bd9Sstevel@tonic-gate { 1610*7c478bd9Sstevel@tonic-gate uint32_t event = sort_buf[0]->ls_event; 1611*7c478bd9Sstevel@tonic-gate lsrec_t *lsp; 1612*7c478bd9Sstevel@tonic-gate double ptotal = 0.0; 1613*7c478bd9Sstevel@tonic-gate double percent; 1614*7c478bd9Sstevel@tonic-gate int i, j, fr; 1615*7c478bd9Sstevel@tonic-gate int displayed; 1616*7c478bd9Sstevel@tonic-gate int first_bin, last_bin, max_bin_count, total_bin_count; 1617*7c478bd9Sstevel@tonic-gate int rectype; 1618*7c478bd9Sstevel@tonic-gate char buf[256]; 1619*7c478bd9Sstevel@tonic-gate char lhdr[80], chdr[80]; 1620*7c478bd9Sstevel@tonic-gate 1621*7c478bd9Sstevel@tonic-gate rectype = g_recsize; 1622*7c478bd9Sstevel@tonic-gate 1623*7c478bd9Sstevel@tonic-gate if (g_topn == 0) { 1624*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%20llu %s\n", 1625*7c478bd9Sstevel@tonic-gate g_rates == 0 ? total_count : 1626*7c478bd9Sstevel@tonic-gate ((unsigned long long)total_count * NANOSEC) / g_elapsed, 1627*7c478bd9Sstevel@tonic-gate g_event_info[event].ev_desc); 1628*7c478bd9Sstevel@tonic-gate return; 1629*7c478bd9Sstevel@tonic-gate } 1630*7c478bd9Sstevel@tonic-gate 1631*7c478bd9Sstevel@tonic-gate (void) sprintf(lhdr, "%s%s", 1632*7c478bd9Sstevel@tonic-gate g_Wflag ? "Hottest " : "", g_event_info[event].ev_lhdr); 1633*7c478bd9Sstevel@tonic-gate (void) sprintf(chdr, "%s%s", 1634*7c478bd9Sstevel@tonic-gate g_wflag ? "Hottest " : "", "Caller"); 1635*7c478bd9Sstevel@tonic-gate 1636*7c478bd9Sstevel@tonic-gate if (!g_pflag) 1637*7c478bd9Sstevel@tonic-gate (void) fprintf(out, 1638*7c478bd9Sstevel@tonic-gate "\n%s: %.0f events in %.3f seconds (%.0f events/sec)\n\n", 1639*7c478bd9Sstevel@tonic-gate g_event_info[event].ev_desc, (double)total_count, 1640*7c478bd9Sstevel@tonic-gate (double)g_elapsed / NANOSEC, 1641*7c478bd9Sstevel@tonic-gate (double)total_count * NANOSEC / g_elapsed); 1642*7c478bd9Sstevel@tonic-gate 1643*7c478bd9Sstevel@tonic-gate if (!g_pflag && rectype < LS_HIST) { 1644*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s", g_event_info[event].ev_units); 1645*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%5s %4s %4s %4s %8s %-22s %-24s\n", 1646*7c478bd9Sstevel@tonic-gate g_rates ? "ops/s" : "Count", 1647*7c478bd9Sstevel@tonic-gate g_gflag ? "genr" : "indv", 1648*7c478bd9Sstevel@tonic-gate "cuml", "rcnt", rectype >= LS_TIME ? buf : "", lhdr, chdr); 1649*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 1650*7c478bd9Sstevel@tonic-gate "----------------------------------------------\n"); 1651*7c478bd9Sstevel@tonic-gate } 1652*7c478bd9Sstevel@tonic-gate 1653*7c478bd9Sstevel@tonic-gate displayed = 0; 1654*7c478bd9Sstevel@tonic-gate for (i = 0; i < nrecs; i++) { 1655*7c478bd9Sstevel@tonic-gate lsp = sort_buf[i]; 1656*7c478bd9Sstevel@tonic-gate 1657*7c478bd9Sstevel@tonic-gate if (displayed++ >= g_topn) 1658*7c478bd9Sstevel@tonic-gate break; 1659*7c478bd9Sstevel@tonic-gate 1660*7c478bd9Sstevel@tonic-gate if (g_pflag) { 1661*7c478bd9Sstevel@tonic-gate int j; 1662*7c478bd9Sstevel@tonic-gate 1663*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%u %u", 1664*7c478bd9Sstevel@tonic-gate lsp->ls_event, lsp->ls_count); 1665*7c478bd9Sstevel@tonic-gate (void) fprintf(out, " %s", 1666*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, g_cflag)); 1667*7c478bd9Sstevel@tonic-gate (void) fprintf(out, " %s", 1668*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_caller, 0)); 1669*7c478bd9Sstevel@tonic-gate (void) fprintf(out, " %f", 1670*7c478bd9Sstevel@tonic-gate (double)lsp->ls_refcnt / lsp->ls_count); 1671*7c478bd9Sstevel@tonic-gate if (rectype >= LS_TIME) 1672*7c478bd9Sstevel@tonic-gate (void) fprintf(out, " %llu", 1673*7c478bd9Sstevel@tonic-gate (unsigned long long)lsp->ls_time); 1674*7c478bd9Sstevel@tonic-gate if (rectype >= LS_HIST) { 1675*7c478bd9Sstevel@tonic-gate for (j = 0; j < 64; j++) 1676*7c478bd9Sstevel@tonic-gate (void) fprintf(out, " %u", 1677*7c478bd9Sstevel@tonic-gate lsp->ls_hist[j]); 1678*7c478bd9Sstevel@tonic-gate } 1679*7c478bd9Sstevel@tonic-gate for (j = 0; j < LS_MAX_STACK_DEPTH; j++) { 1680*7c478bd9Sstevel@tonic-gate if (rectype <= LS_STACK(j) || 1681*7c478bd9Sstevel@tonic-gate lsp->ls_stack[j] == 0) 1682*7c478bd9Sstevel@tonic-gate break; 1683*7c478bd9Sstevel@tonic-gate (void) fprintf(out, " %s", 1684*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[j], 0)); 1685*7c478bd9Sstevel@tonic-gate } 1686*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "\n"); 1687*7c478bd9Sstevel@tonic-gate continue; 1688*7c478bd9Sstevel@tonic-gate } 1689*7c478bd9Sstevel@tonic-gate 1690*7c478bd9Sstevel@tonic-gate if (rectype >= LS_HIST) { 1691*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 1692*7c478bd9Sstevel@tonic-gate "----------------------------------------------\n"); 1693*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s", 1694*7c478bd9Sstevel@tonic-gate g_event_info[event].ev_units); 1695*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%5s %4s %4s %4s %8s %-22s %-24s\n", 1696*7c478bd9Sstevel@tonic-gate g_rates ? "ops/s" : "Count", 1697*7c478bd9Sstevel@tonic-gate g_gflag ? "genr" : "indv", 1698*7c478bd9Sstevel@tonic-gate "cuml", "rcnt", buf, lhdr, chdr); 1699*7c478bd9Sstevel@tonic-gate } 1700*7c478bd9Sstevel@tonic-gate 1701*7c478bd9Sstevel@tonic-gate if (g_Pflag && total_time != 0) 1702*7c478bd9Sstevel@tonic-gate percent = (lsp->ls_time * 100.00) / total_time; 1703*7c478bd9Sstevel@tonic-gate else 1704*7c478bd9Sstevel@tonic-gate percent = (lsp->ls_count * 100.00) / total_count; 1705*7c478bd9Sstevel@tonic-gate 1706*7c478bd9Sstevel@tonic-gate ptotal += percent; 1707*7c478bd9Sstevel@tonic-gate 1708*7c478bd9Sstevel@tonic-gate if (rectype >= LS_TIME) 1709*7c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%llu", 1710*7c478bd9Sstevel@tonic-gate (unsigned long long)(lsp->ls_time / lsp->ls_count)); 1711*7c478bd9Sstevel@tonic-gate else 1712*7c478bd9Sstevel@tonic-gate buf[0] = '\0'; 1713*7c478bd9Sstevel@tonic-gate 1714*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%5llu ", 1715*7c478bd9Sstevel@tonic-gate g_rates == 0 ? lsp->ls_count : 1716*7c478bd9Sstevel@tonic-gate ((uint64_t)lsp->ls_count * NANOSEC) / g_elapsed); 1717*7c478bd9Sstevel@tonic-gate 1718*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%3.0f%% ", percent); 1719*7c478bd9Sstevel@tonic-gate 1720*7c478bd9Sstevel@tonic-gate if (g_gflag) 1721*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "---- "); 1722*7c478bd9Sstevel@tonic-gate else 1723*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%3.0f%% ", ptotal); 1724*7c478bd9Sstevel@tonic-gate 1725*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%4.2f %8s ", 1726*7c478bd9Sstevel@tonic-gate (double)lsp->ls_refcnt / lsp->ls_count, buf); 1727*7c478bd9Sstevel@tonic-gate 1728*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%-22s ", 1729*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, g_cflag)); 1730*7c478bd9Sstevel@tonic-gate 1731*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%-24s\n", 1732*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_caller, 0)); 1733*7c478bd9Sstevel@tonic-gate 1734*7c478bd9Sstevel@tonic-gate if (rectype < LS_HIST) 1735*7c478bd9Sstevel@tonic-gate continue; 1736*7c478bd9Sstevel@tonic-gate 1737*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "\n"); 1738*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%10s %31s %-9s %-24s\n", 1739*7c478bd9Sstevel@tonic-gate g_event_info[event].ev_units, 1740*7c478bd9Sstevel@tonic-gate "------ Time Distribution ------", 1741*7c478bd9Sstevel@tonic-gate g_rates ? "ops/s" : "count", 1742*7c478bd9Sstevel@tonic-gate rectype > LS_STACK(0) ? "Stack" : ""); 1743*7c478bd9Sstevel@tonic-gate 1744*7c478bd9Sstevel@tonic-gate first_bin = 0; 1745*7c478bd9Sstevel@tonic-gate while (lsp->ls_hist[first_bin] == 0) 1746*7c478bd9Sstevel@tonic-gate first_bin++; 1747*7c478bd9Sstevel@tonic-gate 1748*7c478bd9Sstevel@tonic-gate last_bin = 63; 1749*7c478bd9Sstevel@tonic-gate while (lsp->ls_hist[last_bin] == 0) 1750*7c478bd9Sstevel@tonic-gate last_bin--; 1751*7c478bd9Sstevel@tonic-gate 1752*7c478bd9Sstevel@tonic-gate max_bin_count = 0; 1753*7c478bd9Sstevel@tonic-gate total_bin_count = 0; 1754*7c478bd9Sstevel@tonic-gate for (j = first_bin; j <= last_bin; j++) { 1755*7c478bd9Sstevel@tonic-gate total_bin_count += lsp->ls_hist[j]; 1756*7c478bd9Sstevel@tonic-gate if (lsp->ls_hist[j] > max_bin_count) 1757*7c478bd9Sstevel@tonic-gate max_bin_count = lsp->ls_hist[j]; 1758*7c478bd9Sstevel@tonic-gate } 1759*7c478bd9Sstevel@tonic-gate 1760*7c478bd9Sstevel@tonic-gate /* 1761*7c478bd9Sstevel@tonic-gate * If we went a few frames below the caller, ignore them 1762*7c478bd9Sstevel@tonic-gate */ 1763*7c478bd9Sstevel@tonic-gate for (fr = 3; fr > 0; fr--) 1764*7c478bd9Sstevel@tonic-gate if (lsp->ls_stack[fr] == lsp->ls_caller) 1765*7c478bd9Sstevel@tonic-gate break; 1766*7c478bd9Sstevel@tonic-gate 1767*7c478bd9Sstevel@tonic-gate for (j = first_bin; j <= last_bin; j++) { 1768*7c478bd9Sstevel@tonic-gate uint_t depth = (lsp->ls_hist[j] * 30) / total_bin_count; 1769*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%10llu |%s%s %-9u ", 1770*7c478bd9Sstevel@tonic-gate 1ULL << j, 1771*7c478bd9Sstevel@tonic-gate "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + 30 - depth, 1772*7c478bd9Sstevel@tonic-gate " " + depth, 1773*7c478bd9Sstevel@tonic-gate g_rates == 0 ? lsp->ls_hist[j] : 1774*7c478bd9Sstevel@tonic-gate (uint_t)(((uint64_t)lsp->ls_hist[j] * NANOSEC) / 1775*7c478bd9Sstevel@tonic-gate g_elapsed)); 1776*7c478bd9Sstevel@tonic-gate if (rectype <= LS_STACK(fr) || lsp->ls_stack[fr] == 0) { 1777*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "\n"); 1778*7c478bd9Sstevel@tonic-gate continue; 1779*7c478bd9Sstevel@tonic-gate } 1780*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%-24s\n", 1781*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 1782*7c478bd9Sstevel@tonic-gate fr++; 1783*7c478bd9Sstevel@tonic-gate } 1784*7c478bd9Sstevel@tonic-gate while (rectype > LS_STACK(fr) && lsp->ls_stack[fr] != 0) { 1785*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%15s %-36s %-24s\n", "", "", 1786*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 1787*7c478bd9Sstevel@tonic-gate fr++; 1788*7c478bd9Sstevel@tonic-gate } 1789*7c478bd9Sstevel@tonic-gate } 1790*7c478bd9Sstevel@tonic-gate 1791*7c478bd9Sstevel@tonic-gate if (!g_pflag) 1792*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 1793*7c478bd9Sstevel@tonic-gate "----------------------------------------------\n"); 1794*7c478bd9Sstevel@tonic-gate 1795*7c478bd9Sstevel@tonic-gate (void) fflush(out); 1796*7c478bd9Sstevel@tonic-gate } 1797*7c478bd9Sstevel@tonic-gate 1798*7c478bd9Sstevel@tonic-gate static void 1799*7c478bd9Sstevel@tonic-gate report_trace(FILE *out, lsrec_t **sort_buf) 1800*7c478bd9Sstevel@tonic-gate { 1801*7c478bd9Sstevel@tonic-gate lsrec_t *lsp; 1802*7c478bd9Sstevel@tonic-gate int i, fr; 1803*7c478bd9Sstevel@tonic-gate int rectype; 1804*7c478bd9Sstevel@tonic-gate char buf[256], buf2[256]; 1805*7c478bd9Sstevel@tonic-gate 1806*7c478bd9Sstevel@tonic-gate rectype = g_recsize; 1807*7c478bd9Sstevel@tonic-gate 1808*7c478bd9Sstevel@tonic-gate if (!g_pflag) { 1809*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%5s %7s %11s %-24s %-24s\n", 1810*7c478bd9Sstevel@tonic-gate "Event", "Time", "Owner", "Lock", "Caller"); 1811*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 1812*7c478bd9Sstevel@tonic-gate "----------------------------------------------\n"); 1813*7c478bd9Sstevel@tonic-gate } 1814*7c478bd9Sstevel@tonic-gate 1815*7c478bd9Sstevel@tonic-gate for (i = 0; i < g_nrecs_used; i++) { 1816*7c478bd9Sstevel@tonic-gate 1817*7c478bd9Sstevel@tonic-gate lsp = sort_buf[i]; 1818*7c478bd9Sstevel@tonic-gate 1819*7c478bd9Sstevel@tonic-gate if (lsp->ls_event >= LS_MAX_EVENTS || lsp->ls_count == 0) 1820*7c478bd9Sstevel@tonic-gate continue; 1821*7c478bd9Sstevel@tonic-gate 1822*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%2d %10llu %11p %-24s %-24s\n", 1823*7c478bd9Sstevel@tonic-gate lsp->ls_event, (unsigned long long)lsp->ls_time, 1824*7c478bd9Sstevel@tonic-gate (void *)lsp->ls_next, 1825*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, 0), 1826*7c478bd9Sstevel@tonic-gate format_symbol(buf2, lsp->ls_caller, 0)); 1827*7c478bd9Sstevel@tonic-gate 1828*7c478bd9Sstevel@tonic-gate if (rectype <= LS_STACK(0)) 1829*7c478bd9Sstevel@tonic-gate continue; 1830*7c478bd9Sstevel@tonic-gate 1831*7c478bd9Sstevel@tonic-gate /* 1832*7c478bd9Sstevel@tonic-gate * If we went a few frames below the caller, ignore them 1833*7c478bd9Sstevel@tonic-gate */ 1834*7c478bd9Sstevel@tonic-gate for (fr = 3; fr > 0; fr--) 1835*7c478bd9Sstevel@tonic-gate if (lsp->ls_stack[fr] == lsp->ls_caller) 1836*7c478bd9Sstevel@tonic-gate break; 1837*7c478bd9Sstevel@tonic-gate 1838*7c478bd9Sstevel@tonic-gate while (rectype > LS_STACK(fr) && lsp->ls_stack[fr] != 0) { 1839*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "%53s %-24s\n", "", 1840*7c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 1841*7c478bd9Sstevel@tonic-gate fr++; 1842*7c478bd9Sstevel@tonic-gate } 1843*7c478bd9Sstevel@tonic-gate (void) fprintf(out, "\n"); 1844*7c478bd9Sstevel@tonic-gate } 1845*7c478bd9Sstevel@tonic-gate 1846*7c478bd9Sstevel@tonic-gate (void) fflush(out); 1847*7c478bd9Sstevel@tonic-gate } 1848