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