10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate /* 230Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include <stdio.h> 300Sstevel@tonic-gate #include <stddef.h> 310Sstevel@tonic-gate #include <stdlib.h> 320Sstevel@tonic-gate #include <stdarg.h> 330Sstevel@tonic-gate #include <string.h> 340Sstevel@tonic-gate #include <strings.h> 350Sstevel@tonic-gate #include <ctype.h> 360Sstevel@tonic-gate #include <fcntl.h> 370Sstevel@tonic-gate #include <unistd.h> 380Sstevel@tonic-gate #include <errno.h> 390Sstevel@tonic-gate #include <limits.h> 400Sstevel@tonic-gate #include <sys/types.h> 410Sstevel@tonic-gate #include <sys/modctl.h> 420Sstevel@tonic-gate #include <sys/stat.h> 430Sstevel@tonic-gate #include <sys/wait.h> 440Sstevel@tonic-gate #include <dtrace.h> 450Sstevel@tonic-gate #include <sys/lockstat.h> 460Sstevel@tonic-gate #include <alloca.h> 470Sstevel@tonic-gate #include <signal.h> 480Sstevel@tonic-gate #include <assert.h> 490Sstevel@tonic-gate 500Sstevel@tonic-gate #define LOCKSTAT_OPTSTR "x:bths:n:d:i:l:f:e:ckwWgCHEATID:RpPo:V" 510Sstevel@tonic-gate 520Sstevel@tonic-gate #define LS_MAX_STACK_DEPTH 50 530Sstevel@tonic-gate #define LS_MAX_EVENTS 64 540Sstevel@tonic-gate 550Sstevel@tonic-gate typedef struct lsrec { 560Sstevel@tonic-gate struct lsrec *ls_next; /* next in hash chain */ 570Sstevel@tonic-gate uintptr_t ls_lock; /* lock address */ 580Sstevel@tonic-gate uintptr_t ls_caller; /* caller address */ 590Sstevel@tonic-gate uint32_t ls_count; /* cumulative event count */ 600Sstevel@tonic-gate uint32_t ls_event; /* type of event */ 610Sstevel@tonic-gate uintptr_t ls_refcnt; /* cumulative reference count */ 620Sstevel@tonic-gate uint64_t ls_time; /* cumulative event duration */ 630Sstevel@tonic-gate uint32_t ls_hist[64]; /* log2(duration) histogram */ 640Sstevel@tonic-gate uintptr_t ls_stack[LS_MAX_STACK_DEPTH]; 650Sstevel@tonic-gate } lsrec_t; 660Sstevel@tonic-gate 670Sstevel@tonic-gate typedef struct lsdata { 680Sstevel@tonic-gate struct lsrec *lsd_next; /* next available */ 690Sstevel@tonic-gate int lsd_count; /* number of records */ 700Sstevel@tonic-gate } lsdata_t; 710Sstevel@tonic-gate 720Sstevel@tonic-gate /* 730Sstevel@tonic-gate * Definitions for the types of experiments which can be run. They are 740Sstevel@tonic-gate * listed in increasing order of memory cost and processing time cost. 750Sstevel@tonic-gate * The numerical value of each type is the number of bytes needed per record. 760Sstevel@tonic-gate */ 770Sstevel@tonic-gate #define LS_BASIC offsetof(lsrec_t, ls_time) 780Sstevel@tonic-gate #define LS_TIME offsetof(lsrec_t, ls_hist[0]) 790Sstevel@tonic-gate #define LS_HIST offsetof(lsrec_t, ls_stack[0]) 800Sstevel@tonic-gate #define LS_STACK(depth) offsetof(lsrec_t, ls_stack[depth]) 810Sstevel@tonic-gate 820Sstevel@tonic-gate static void report_stats(FILE *, lsrec_t **, size_t, uint64_t, uint64_t); 830Sstevel@tonic-gate static void report_trace(FILE *, lsrec_t **); 840Sstevel@tonic-gate 850Sstevel@tonic-gate extern int symtab_init(void); 860Sstevel@tonic-gate extern char *addr_to_sym(uintptr_t, uintptr_t *, size_t *); 870Sstevel@tonic-gate extern uintptr_t sym_to_addr(char *name); 880Sstevel@tonic-gate extern size_t sym_size(char *name); 890Sstevel@tonic-gate extern char *strtok_r(char *, const char *, char **); 900Sstevel@tonic-gate 910Sstevel@tonic-gate #define DEFAULT_NRECS 10000 920Sstevel@tonic-gate #define DEFAULT_HZ 97 930Sstevel@tonic-gate #define MAX_HZ 1000 940Sstevel@tonic-gate #define MIN_AGGSIZE (16 * 1024) 950Sstevel@tonic-gate #define MAX_AGGSIZE (32 * 1024 * 1024) 960Sstevel@tonic-gate 970Sstevel@tonic-gate static int g_stkdepth; 980Sstevel@tonic-gate static int g_topn = INT_MAX; 990Sstevel@tonic-gate static hrtime_t g_elapsed; 1000Sstevel@tonic-gate static int g_rates = 0; 1010Sstevel@tonic-gate static int g_pflag = 0; 1020Sstevel@tonic-gate static int g_Pflag = 0; 1030Sstevel@tonic-gate static int g_wflag = 0; 1040Sstevel@tonic-gate static int g_Wflag = 0; 1050Sstevel@tonic-gate static int g_cflag = 0; 1060Sstevel@tonic-gate static int g_kflag = 0; 1070Sstevel@tonic-gate static int g_gflag = 0; 1080Sstevel@tonic-gate static int g_Vflag = 0; 1090Sstevel@tonic-gate static int g_tracing = 0; 1100Sstevel@tonic-gate static size_t g_recsize; 1110Sstevel@tonic-gate static size_t g_nrecs; 1120Sstevel@tonic-gate static int g_nrecs_used; 1130Sstevel@tonic-gate static uchar_t g_enabled[LS_MAX_EVENTS]; 1140Sstevel@tonic-gate static hrtime_t g_min_duration[LS_MAX_EVENTS]; 1150Sstevel@tonic-gate static dtrace_hdl_t *g_dtp; 1160Sstevel@tonic-gate static char *g_predicate; 1170Sstevel@tonic-gate static char *g_ipredicate; 1180Sstevel@tonic-gate static char *g_prog; 1190Sstevel@tonic-gate static int g_proglen; 1200Sstevel@tonic-gate static int g_dropped; 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate typedef struct ls_event_info { 1230Sstevel@tonic-gate char ev_type; 1240Sstevel@tonic-gate char ev_lhdr[20]; 1250Sstevel@tonic-gate char ev_desc[80]; 1260Sstevel@tonic-gate char ev_units[10]; 1270Sstevel@tonic-gate char ev_name[DTRACE_NAMELEN]; 1280Sstevel@tonic-gate char *ev_predicate; 1290Sstevel@tonic-gate char *ev_acquire; 1300Sstevel@tonic-gate } ls_event_info_t; 1310Sstevel@tonic-gate 1320Sstevel@tonic-gate static ls_event_info_t g_event_info[LS_MAX_EVENTS] = { 1330Sstevel@tonic-gate { 'C', "Lock", "Adaptive mutex spin", "spin", 1340Sstevel@tonic-gate "lockstat:::adaptive-spin" }, 1350Sstevel@tonic-gate { 'C', "Lock", "Adaptive mutex block", "nsec", 1360Sstevel@tonic-gate "lockstat:::adaptive-block" }, 1370Sstevel@tonic-gate { 'C', "Lock", "Spin lock spin", "spin", 1380Sstevel@tonic-gate "lockstat:::spin-spin" }, 1390Sstevel@tonic-gate { 'C', "Lock", "Thread lock spin", "spin", 1400Sstevel@tonic-gate "lockstat:::thread-spin" }, 1410Sstevel@tonic-gate { 'C', "Lock", "R/W writer blocked by writer", "nsec", 1420Sstevel@tonic-gate "lockstat:::rw-block", "arg2 == 0 && arg3 == 1" }, 1430Sstevel@tonic-gate { 'C', "Lock", "R/W writer blocked by readers", "nsec", 1440Sstevel@tonic-gate "lockstat:::rw-block", "arg2 == 0 && arg3 == 0 && arg4" }, 1450Sstevel@tonic-gate { 'C', "Lock", "R/W reader blocked by writer", "nsec", 1460Sstevel@tonic-gate "lockstat:::rw-block", "arg2 != 0 && arg3 == 1" }, 1470Sstevel@tonic-gate { 'C', "Lock", "R/W reader blocked by write wanted", "nsec", 1480Sstevel@tonic-gate "lockstat:::rw-block", "arg2 != 0 && arg3 == 0 && arg4" }, 1490Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 8)", "units" }, 1500Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 9)", "units" }, 1510Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 10)", "units" }, 1520Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 11)", "units" }, 1530Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 12)", "units" }, 1540Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 13)", "units" }, 1550Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 14)", "units" }, 1560Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 15)", "units" }, 1570Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 16)", "units" }, 1580Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 17)", "units" }, 1590Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 18)", "units" }, 1600Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 19)", "units" }, 1610Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 20)", "units" }, 1620Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 21)", "units" }, 1630Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 22)", "units" }, 1640Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 23)", "units" }, 1650Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 24)", "units" }, 1660Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 25)", "units" }, 1670Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 26)", "units" }, 1680Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 27)", "units" }, 1690Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 28)", "units" }, 1700Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 29)", "units" }, 1710Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 30)", "units" }, 1720Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 31)", "units" }, 1730Sstevel@tonic-gate { 'H', "Lock", "Adaptive mutex hold", "nsec", 1740Sstevel@tonic-gate "lockstat:::adaptive-release", NULL, 1750Sstevel@tonic-gate "lockstat:::adaptive-acquire" }, 1760Sstevel@tonic-gate { 'H', "Lock", "Spin lock hold", "nsec", 1770Sstevel@tonic-gate "lockstat:::spin-release", NULL, 1780Sstevel@tonic-gate "lockstat:::spin-acquire" }, 1790Sstevel@tonic-gate { 'H', "Lock", "R/W writer hold", "nsec", 1800Sstevel@tonic-gate "lockstat:::rw-release", "arg1 == 0", 1810Sstevel@tonic-gate "lockstat:::rw-acquire" }, 1820Sstevel@tonic-gate { 'H', "Lock", "R/W reader hold", "nsec", 1830Sstevel@tonic-gate "lockstat:::rw-release", "arg1 != 0", 1840Sstevel@tonic-gate "lockstat:::rw-acquire" }, 1850Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 36)", "units" }, 1860Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 37)", "units" }, 1870Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 38)", "units" }, 1880Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 39)", "units" }, 1890Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 40)", "units" }, 1900Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 41)", "units" }, 1910Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 42)", "units" }, 1920Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 43)", "units" }, 1930Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 44)", "units" }, 1940Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 45)", "units" }, 1950Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 46)", "units" }, 1960Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 47)", "units" }, 1970Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 48)", "units" }, 1980Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 49)", "units" }, 1990Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 50)", "units" }, 2000Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 51)", "units" }, 2010Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 52)", "units" }, 2020Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 53)", "units" }, 2030Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 54)", "units" }, 2040Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 55)", "units" }, 2050Sstevel@tonic-gate { 'I', "CPU+PIL", "Profiling interrupt", "nsec", 2060Sstevel@tonic-gate "profile:::profile-97", NULL }, 2070Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 57)", "units" }, 2080Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 58)", "units" }, 2090Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 59)", "units" }, 2100Sstevel@tonic-gate { 'E', "Lock", "Recursive lock entry detected", "(N/A)", 2110Sstevel@tonic-gate "lockstat:::rw-release", NULL, "lockstat:::rw-acquire" }, 2120Sstevel@tonic-gate { 'E', "Lock", "Lockstat enter failure", "(N/A)" }, 2130Sstevel@tonic-gate { 'E', "Lock", "Lockstat exit failure", "nsec" }, 2140Sstevel@tonic-gate { 'E', "Lock", "Lockstat record failure", "(N/A)" }, 2150Sstevel@tonic-gate }; 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate static void 2180Sstevel@tonic-gate fail(int do_perror, const char *message, ...) 2190Sstevel@tonic-gate { 2200Sstevel@tonic-gate va_list args; 2210Sstevel@tonic-gate int save_errno = errno; 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate va_start(args, message); 2240Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: "); 2250Sstevel@tonic-gate (void) vfprintf(stderr, message, args); 2260Sstevel@tonic-gate va_end(args); 2270Sstevel@tonic-gate if (do_perror) 2280Sstevel@tonic-gate (void) fprintf(stderr, ": %s", strerror(save_errno)); 2290Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 2300Sstevel@tonic-gate exit(2); 2310Sstevel@tonic-gate } 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate static void 2340Sstevel@tonic-gate dfail(const char *message, ...) 2350Sstevel@tonic-gate { 2360Sstevel@tonic-gate va_list args; 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate va_start(args, message); 2390Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: "); 2400Sstevel@tonic-gate (void) vfprintf(stderr, message, args); 2410Sstevel@tonic-gate va_end(args); 2420Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", 2430Sstevel@tonic-gate dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate exit(2); 2460Sstevel@tonic-gate } 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate static void 2490Sstevel@tonic-gate show_events(char event_type, char *desc) 2500Sstevel@tonic-gate { 2510Sstevel@tonic-gate int i, first = -1, last; 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 2540Sstevel@tonic-gate ls_event_info_t *evp = &g_event_info[i]; 2550Sstevel@tonic-gate if (evp->ev_type != event_type || 2560Sstevel@tonic-gate strncmp(evp->ev_desc, "Unknown event", 13) == 0) 2570Sstevel@tonic-gate continue; 2580Sstevel@tonic-gate if (first == -1) 2590Sstevel@tonic-gate first = i; 2600Sstevel@tonic-gate last = i; 2610Sstevel@tonic-gate } 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate (void) fprintf(stderr, 2640Sstevel@tonic-gate "\n%s events (lockstat -%c or lockstat -e %d-%d):\n\n", 2650Sstevel@tonic-gate desc, event_type, first, last); 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate for (i = first; i <= last; i++) 2680Sstevel@tonic-gate (void) fprintf(stderr, 2690Sstevel@tonic-gate "%4d = %s\n", i, g_event_info[i].ev_desc); 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate static void 2730Sstevel@tonic-gate usage(void) 2740Sstevel@tonic-gate { 2750Sstevel@tonic-gate (void) fprintf(stderr, 2760Sstevel@tonic-gate "Usage: lockstat [options] command [args]\n" 2770Sstevel@tonic-gate "\nEvent selection options:\n\n" 2780Sstevel@tonic-gate " -C watch contention events [on by default]\n" 2790Sstevel@tonic-gate " -E watch error events [off by default]\n" 2800Sstevel@tonic-gate " -H watch hold events [off by default]\n" 2810Sstevel@tonic-gate " -I watch interrupt events [off by default]\n" 2820Sstevel@tonic-gate " -A watch all lock events [equivalent to -CH]\n" 2830Sstevel@tonic-gate " -e event_list only watch the specified events (shown below);\n" 2840Sstevel@tonic-gate " <event_list> is a comma-separated list of\n" 2850Sstevel@tonic-gate " events or ranges of events, e.g. 1,4-7,35\n" 2860Sstevel@tonic-gate " -i rate interrupt rate for -I [default: %d Hz]\n" 2870Sstevel@tonic-gate "\nData gathering options:\n\n" 2880Sstevel@tonic-gate " -b basic statistics (lock, caller, event count)\n" 2890Sstevel@tonic-gate " -t timing for all events [default]\n" 2900Sstevel@tonic-gate " -h histograms for event times\n" 2910Sstevel@tonic-gate " -s depth stack traces <depth> deep\n" 2920Sstevel@tonic-gate " -x opt[=val] enable or modify DTrace options\n" 2930Sstevel@tonic-gate "\nData filtering options:\n\n" 2940Sstevel@tonic-gate " -n nrecords maximum number of data records [default: %d]\n" 2950Sstevel@tonic-gate " -l lock[,size] only watch <lock>, which can be specified as a\n" 2960Sstevel@tonic-gate " symbolic name or hex address; <size> defaults\n" 2970Sstevel@tonic-gate " to the ELF symbol size if available, 1 if not\n" 2980Sstevel@tonic-gate " -f func[,size] only watch events generated by <func>\n" 2990Sstevel@tonic-gate " -d duration only watch events longer than <duration>\n" 3000Sstevel@tonic-gate " -T trace (rather than sample) events\n" 3010Sstevel@tonic-gate "\nData reporting options:\n\n" 3020Sstevel@tonic-gate " -c coalesce lock data for arrays like pse_mutex[]\n" 3030Sstevel@tonic-gate " -k coalesce PCs within functions\n" 3040Sstevel@tonic-gate " -g show total events generated by function\n" 3050Sstevel@tonic-gate " -w wherever: don't distinguish events by caller\n" 3060Sstevel@tonic-gate " -W whichever: don't distinguish events by lock\n" 3070Sstevel@tonic-gate " -R display rates rather than counts\n" 3080Sstevel@tonic-gate " -p parsable output format (awk(1)-friendly)\n" 3090Sstevel@tonic-gate " -P sort lock data by (count * avg_time) product\n" 3100Sstevel@tonic-gate " -D n only display top <n> events of each type\n" 3110Sstevel@tonic-gate " -o filename send output to <filename>\n", 3120Sstevel@tonic-gate DEFAULT_HZ, DEFAULT_NRECS); 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate show_events('C', "Contention"); 3150Sstevel@tonic-gate show_events('H', "Hold-time"); 3160Sstevel@tonic-gate show_events('I', "Interrupt"); 3170Sstevel@tonic-gate show_events('E', "Error"); 3180Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate exit(1); 3210Sstevel@tonic-gate } 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate static int 3240Sstevel@tonic-gate lockcmp(lsrec_t *a, lsrec_t *b) 3250Sstevel@tonic-gate { 3260Sstevel@tonic-gate int i; 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3290Sstevel@tonic-gate return (-1); 3300Sstevel@tonic-gate if (a->ls_event > b->ls_event) 3310Sstevel@tonic-gate return (1); 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 3340Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 3350Sstevel@tonic-gate return (-1); 3360Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 3370Sstevel@tonic-gate return (1); 3380Sstevel@tonic-gate } 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 3410Sstevel@tonic-gate return (-1); 3420Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 3430Sstevel@tonic-gate return (1); 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 3460Sstevel@tonic-gate return (-1); 3470Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 3480Sstevel@tonic-gate return (1); 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate return (0); 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate static int 3540Sstevel@tonic-gate countcmp(lsrec_t *a, lsrec_t *b) 3550Sstevel@tonic-gate { 3560Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3570Sstevel@tonic-gate return (-1); 3580Sstevel@tonic-gate if (a->ls_event > b->ls_event) 3590Sstevel@tonic-gate return (1); 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate return (b->ls_count - a->ls_count); 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate static int 3650Sstevel@tonic-gate timecmp(lsrec_t *a, lsrec_t *b) 3660Sstevel@tonic-gate { 3670Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3680Sstevel@tonic-gate return (-1); 3690Sstevel@tonic-gate if (a->ls_event > b->ls_event) 3700Sstevel@tonic-gate return (1); 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate if (a->ls_time < b->ls_time) 3730Sstevel@tonic-gate return (1); 3740Sstevel@tonic-gate if (a->ls_time > b->ls_time) 3750Sstevel@tonic-gate return (-1); 3760Sstevel@tonic-gate 3770Sstevel@tonic-gate return (0); 3780Sstevel@tonic-gate } 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate static int 3810Sstevel@tonic-gate lockcmp_anywhere(lsrec_t *a, lsrec_t *b) 3820Sstevel@tonic-gate { 3830Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3840Sstevel@tonic-gate return (-1); 3850Sstevel@tonic-gate if (a->ls_event > b->ls_event) 3860Sstevel@tonic-gate return (1); 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 3890Sstevel@tonic-gate return (-1); 3900Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 3910Sstevel@tonic-gate return (1); 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate return (0); 3940Sstevel@tonic-gate } 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate static int 3970Sstevel@tonic-gate lock_and_count_cmp_anywhere(lsrec_t *a, lsrec_t *b) 3980Sstevel@tonic-gate { 3990Sstevel@tonic-gate if (a->ls_event < b->ls_event) 4000Sstevel@tonic-gate return (-1); 4010Sstevel@tonic-gate if (a->ls_event > b->ls_event) 4020Sstevel@tonic-gate return (1); 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 4050Sstevel@tonic-gate return (-1); 4060Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 4070Sstevel@tonic-gate return (1); 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate return (b->ls_count - a->ls_count); 4100Sstevel@tonic-gate } 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate static int 4130Sstevel@tonic-gate sitecmp_anylock(lsrec_t *a, lsrec_t *b) 4140Sstevel@tonic-gate { 4150Sstevel@tonic-gate int i; 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate if (a->ls_event < b->ls_event) 4180Sstevel@tonic-gate return (-1); 4190Sstevel@tonic-gate if (a->ls_event > b->ls_event) 4200Sstevel@tonic-gate return (1); 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 4230Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 4240Sstevel@tonic-gate return (-1); 4250Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 4260Sstevel@tonic-gate return (1); 4270Sstevel@tonic-gate } 4280Sstevel@tonic-gate 4290Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 4300Sstevel@tonic-gate return (-1); 4310Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 4320Sstevel@tonic-gate return (1); 4330Sstevel@tonic-gate 4340Sstevel@tonic-gate return (0); 4350Sstevel@tonic-gate } 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate static int 4380Sstevel@tonic-gate site_and_count_cmp_anylock(lsrec_t *a, lsrec_t *b) 4390Sstevel@tonic-gate { 4400Sstevel@tonic-gate int i; 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate if (a->ls_event < b->ls_event) 4430Sstevel@tonic-gate return (-1); 4440Sstevel@tonic-gate if (a->ls_event > b->ls_event) 4450Sstevel@tonic-gate return (1); 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 4480Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 4490Sstevel@tonic-gate return (-1); 4500Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 4510Sstevel@tonic-gate return (1); 4520Sstevel@tonic-gate } 4530Sstevel@tonic-gate 4540Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 4550Sstevel@tonic-gate return (-1); 4560Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 4570Sstevel@tonic-gate return (1); 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate return (b->ls_count - a->ls_count); 4600Sstevel@tonic-gate } 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate static void 4630Sstevel@tonic-gate mergesort(int (*cmp)(lsrec_t *, lsrec_t *), lsrec_t **a, lsrec_t **b, int n) 4640Sstevel@tonic-gate { 4650Sstevel@tonic-gate int m = n / 2; 4660Sstevel@tonic-gate int i, j; 4670Sstevel@tonic-gate 4680Sstevel@tonic-gate if (m > 1) 4690Sstevel@tonic-gate mergesort(cmp, a, b, m); 4700Sstevel@tonic-gate if (n - m > 1) 4710Sstevel@tonic-gate mergesort(cmp, a + m, b + m, n - m); 4720Sstevel@tonic-gate for (i = m; i > 0; i--) 4730Sstevel@tonic-gate b[i - 1] = a[i - 1]; 4740Sstevel@tonic-gate for (j = m - 1; j < n - 1; j++) 4750Sstevel@tonic-gate b[n + m - j - 2] = a[j + 1]; 4760Sstevel@tonic-gate while (i < j) 4770Sstevel@tonic-gate *a++ = cmp(b[i], b[j]) < 0 ? b[i++] : b[j--]; 4780Sstevel@tonic-gate *a = b[i]; 4790Sstevel@tonic-gate } 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate static void 4820Sstevel@tonic-gate coalesce(int (*cmp)(lsrec_t *, lsrec_t *), lsrec_t **lock, int n) 4830Sstevel@tonic-gate { 4840Sstevel@tonic-gate int i, j; 4850Sstevel@tonic-gate lsrec_t *target, *current; 4860Sstevel@tonic-gate 4870Sstevel@tonic-gate target = lock[0]; 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate for (i = 1; i < n; i++) { 4900Sstevel@tonic-gate current = lock[i]; 4910Sstevel@tonic-gate if (cmp(current, target) != 0) { 4920Sstevel@tonic-gate target = current; 4930Sstevel@tonic-gate continue; 4940Sstevel@tonic-gate } 4950Sstevel@tonic-gate current->ls_event = LS_MAX_EVENTS; 4960Sstevel@tonic-gate target->ls_count += current->ls_count; 4970Sstevel@tonic-gate target->ls_refcnt += current->ls_refcnt; 4980Sstevel@tonic-gate if (g_recsize < LS_TIME) 4990Sstevel@tonic-gate continue; 5000Sstevel@tonic-gate target->ls_time += current->ls_time; 5010Sstevel@tonic-gate if (g_recsize < LS_HIST) 5020Sstevel@tonic-gate continue; 5030Sstevel@tonic-gate for (j = 0; j < 64; j++) 5040Sstevel@tonic-gate target->ls_hist[j] += current->ls_hist[j]; 5050Sstevel@tonic-gate } 5060Sstevel@tonic-gate } 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate static void 5090Sstevel@tonic-gate coalesce_symbol(uintptr_t *addrp) 5100Sstevel@tonic-gate { 5110Sstevel@tonic-gate uintptr_t symoff; 5120Sstevel@tonic-gate size_t symsize; 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate if (addr_to_sym(*addrp, &symoff, &symsize) != NULL && symoff < symsize) 5150Sstevel@tonic-gate *addrp -= symoff; 5160Sstevel@tonic-gate } 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate static void 5190Sstevel@tonic-gate predicate_add(char **pred, char *what, char *cmp, uintptr_t value) 5200Sstevel@tonic-gate { 5210Sstevel@tonic-gate char *new; 5220Sstevel@tonic-gate int len, newlen; 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate if (what == NULL) 5250Sstevel@tonic-gate return; 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate if (*pred == NULL) { 5280Sstevel@tonic-gate *pred = malloc(1); 5290Sstevel@tonic-gate *pred[0] = '\0'; 5300Sstevel@tonic-gate } 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate len = strlen(*pred); 5330Sstevel@tonic-gate newlen = len + strlen(what) + 32 + strlen("( && )"); 5340Sstevel@tonic-gate new = malloc(newlen); 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate if (*pred[0] != '\0') { 5370Sstevel@tonic-gate if (cmp != NULL) { 5380Sstevel@tonic-gate (void) sprintf(new, "(%s) && (%s %s 0x%p)", 5390Sstevel@tonic-gate *pred, what, cmp, (void *)value); 5400Sstevel@tonic-gate } else { 5410Sstevel@tonic-gate (void) sprintf(new, "(%s) && (%s)", *pred, what); 5420Sstevel@tonic-gate } 5430Sstevel@tonic-gate } else { 5440Sstevel@tonic-gate if (cmp != NULL) { 5450Sstevel@tonic-gate (void) sprintf(new, "%s %s 0x%p", 5460Sstevel@tonic-gate what, cmp, (void *)value); 5470Sstevel@tonic-gate } else { 5480Sstevel@tonic-gate (void) sprintf(new, "%s", what); 5490Sstevel@tonic-gate } 5500Sstevel@tonic-gate } 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate free(*pred); 5530Sstevel@tonic-gate *pred = new; 5540Sstevel@tonic-gate } 5550Sstevel@tonic-gate 5560Sstevel@tonic-gate static void 5570Sstevel@tonic-gate predicate_destroy(char **pred) 5580Sstevel@tonic-gate { 5590Sstevel@tonic-gate free(*pred); 5600Sstevel@tonic-gate *pred = NULL; 5610Sstevel@tonic-gate } 5620Sstevel@tonic-gate 5630Sstevel@tonic-gate static void 5640Sstevel@tonic-gate filter_add(char **filt, char *what, uintptr_t base, uintptr_t size) 5650Sstevel@tonic-gate { 5660Sstevel@tonic-gate char buf[256], *c = buf, *new; 5670Sstevel@tonic-gate int len, newlen; 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate if (*filt == NULL) { 5700Sstevel@tonic-gate *filt = malloc(1); 5710Sstevel@tonic-gate *filt[0] = '\0'; 5720Sstevel@tonic-gate } 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate (void) sprintf(c, "%s(%s >= 0x%p && %s < 0x%p)", *filt[0] != '\0' ? 5750Sstevel@tonic-gate " || " : "", what, (void *)base, what, (void *)(base + size)); 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate newlen = (len = strlen(*filt) + 1) + strlen(c); 5780Sstevel@tonic-gate new = malloc(newlen); 5790Sstevel@tonic-gate bcopy(*filt, new, len); 5800Sstevel@tonic-gate (void) strcat(new, c); 5810Sstevel@tonic-gate free(*filt); 5820Sstevel@tonic-gate *filt = new; 5830Sstevel@tonic-gate } 5840Sstevel@tonic-gate 5850Sstevel@tonic-gate static void 5860Sstevel@tonic-gate filter_destroy(char **filt) 5870Sstevel@tonic-gate { 5880Sstevel@tonic-gate free(*filt); 5890Sstevel@tonic-gate *filt = NULL; 5900Sstevel@tonic-gate } 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate static void 5930Sstevel@tonic-gate dprog_add(const char *fmt, ...) 5940Sstevel@tonic-gate { 5950Sstevel@tonic-gate va_list args; 5960Sstevel@tonic-gate int size, offs; 5970Sstevel@tonic-gate char c; 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate va_start(args, fmt); 6000Sstevel@tonic-gate size = vsnprintf(&c, 1, fmt, args) + 1; 6010Sstevel@tonic-gate 6020Sstevel@tonic-gate if (g_proglen == 0) { 6030Sstevel@tonic-gate offs = 0; 6040Sstevel@tonic-gate } else { 6050Sstevel@tonic-gate offs = g_proglen - 1; 6060Sstevel@tonic-gate } 6070Sstevel@tonic-gate 6080Sstevel@tonic-gate g_proglen = offs + size; 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate if ((g_prog = realloc(g_prog, g_proglen)) == NULL) 6110Sstevel@tonic-gate fail(1, "failed to reallocate program text"); 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate (void) vsnprintf(&g_prog[offs], size, fmt, args); 6140Sstevel@tonic-gate } 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate /* 6170Sstevel@tonic-gate * This function may read like an open sewer, but keep in mind that programs 6180Sstevel@tonic-gate * that generate other programs are rarely pretty. If one has the unenviable 6190Sstevel@tonic-gate * task of maintaining or -- worse -- extending this code, use the -V option 6200Sstevel@tonic-gate * to examine the D program as generated by this function. 6210Sstevel@tonic-gate */ 6220Sstevel@tonic-gate static void 6230Sstevel@tonic-gate dprog_addevent(int event) 6240Sstevel@tonic-gate { 6250Sstevel@tonic-gate ls_event_info_t *info = &g_event_info[event]; 6260Sstevel@tonic-gate char *pred = NULL; 6270Sstevel@tonic-gate char stack[20]; 6280Sstevel@tonic-gate const char *arg0, *caller; 6290Sstevel@tonic-gate char *arg1 = "arg1"; 6300Sstevel@tonic-gate char buf[80]; 6310Sstevel@tonic-gate hrtime_t dur; 6320Sstevel@tonic-gate int depth; 6330Sstevel@tonic-gate 6340Sstevel@tonic-gate if (info->ev_name[0] == '\0') 6350Sstevel@tonic-gate return; 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate if (info->ev_type == 'I') { 6380Sstevel@tonic-gate /* 6390Sstevel@tonic-gate * For interrupt events, arg0 (normally the lock pointer) is 6400Sstevel@tonic-gate * the CPU address plus the current pil, and arg1 (normally 6410Sstevel@tonic-gate * the number of nanoseconds) is the number of nanoseconds 6420Sstevel@tonic-gate * late -- and it's stored in arg2. 6430Sstevel@tonic-gate */ 6440Sstevel@tonic-gate arg0 = "(uintptr_t)curthread->t_cpu + \n" 6450Sstevel@tonic-gate "\t curthread->t_cpu->cpu_profile_pil"; 6460Sstevel@tonic-gate caller = "(uintptr_t)arg0"; 6470Sstevel@tonic-gate arg1 = "arg2"; 6480Sstevel@tonic-gate } else { 6490Sstevel@tonic-gate arg0 = "(uintptr_t)arg0"; 6500Sstevel@tonic-gate caller = "caller"; 6510Sstevel@tonic-gate } 6520Sstevel@tonic-gate 6530Sstevel@tonic-gate if (g_recsize > LS_HIST) { 6540Sstevel@tonic-gate for (depth = 0; g_recsize > LS_STACK(depth); depth++) 6550Sstevel@tonic-gate continue; 6560Sstevel@tonic-gate 6570Sstevel@tonic-gate if (g_tracing) { 6580Sstevel@tonic-gate (void) sprintf(stack, "\tstack(%d);\n", depth); 6590Sstevel@tonic-gate } else { 6600Sstevel@tonic-gate (void) sprintf(stack, ", stack(%d)", depth); 6610Sstevel@tonic-gate } 6620Sstevel@tonic-gate } else { 6630Sstevel@tonic-gate (void) sprintf(stack, ""); 6640Sstevel@tonic-gate } 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate if (info->ev_acquire != NULL) { 6670Sstevel@tonic-gate /* 6680Sstevel@tonic-gate * If this is a hold event, we need to generate an additional 6690Sstevel@tonic-gate * clause for the acquire; the clause for the release will be 6700Sstevel@tonic-gate * generated with the aggregating statement, below. 6710Sstevel@tonic-gate */ 6720Sstevel@tonic-gate dprog_add("%s\n", info->ev_acquire); 6730Sstevel@tonic-gate predicate_add(&pred, info->ev_predicate, NULL, 0); 6740Sstevel@tonic-gate predicate_add(&pred, g_predicate, NULL, 0); 6750Sstevel@tonic-gate if (pred != NULL) 6760Sstevel@tonic-gate dprog_add("/%s/\n", pred); 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate dprog_add("{\n"); 6790Sstevel@tonic-gate (void) sprintf(buf, "self->ev%d[(uintptr_t)arg0]", event); 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate if (info->ev_type == 'H') { 6820Sstevel@tonic-gate dprog_add("\t%s = timestamp;\n", buf); 6830Sstevel@tonic-gate } else { 6840Sstevel@tonic-gate /* 6850Sstevel@tonic-gate * If this isn't a hold event, it's the recursive 6860Sstevel@tonic-gate * error event. For this, we simply bump the 6870Sstevel@tonic-gate * thread-local, per-lock count. 6880Sstevel@tonic-gate */ 6890Sstevel@tonic-gate dprog_add("\t%s++;\n", buf); 6900Sstevel@tonic-gate } 6910Sstevel@tonic-gate 6920Sstevel@tonic-gate dprog_add("}\n\n"); 6930Sstevel@tonic-gate predicate_destroy(&pred); 6940Sstevel@tonic-gate pred = NULL; 6950Sstevel@tonic-gate 6960Sstevel@tonic-gate if (info->ev_type == 'E') { 6970Sstevel@tonic-gate /* 6980Sstevel@tonic-gate * If this is the recursive lock error event, we need 6990Sstevel@tonic-gate * to generate an additional clause to decrement the 7000Sstevel@tonic-gate * thread-local, per-lock count. This assures that we 7010Sstevel@tonic-gate * only execute the aggregating clause if we have 7020Sstevel@tonic-gate * recursive entry. 7030Sstevel@tonic-gate */ 7040Sstevel@tonic-gate dprog_add("%s\n", info->ev_name); 7050Sstevel@tonic-gate dprog_add("/%s/\n{\n\t%s--;\n}\n\n", buf, buf); 7060Sstevel@tonic-gate } 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate predicate_add(&pred, buf, NULL, 0); 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate if (info->ev_type == 'H') { 7110Sstevel@tonic-gate (void) sprintf(buf, "timestamp -\n\t " 7120Sstevel@tonic-gate "self->ev%d[(uintptr_t)arg0]", event); 7130Sstevel@tonic-gate } 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate arg1 = buf; 7160Sstevel@tonic-gate } else { 7170Sstevel@tonic-gate predicate_add(&pred, info->ev_predicate, NULL, 0); 7180Sstevel@tonic-gate if (info->ev_type != 'I') 7190Sstevel@tonic-gate predicate_add(&pred, g_predicate, NULL, 0); 7200Sstevel@tonic-gate else 7210Sstevel@tonic-gate predicate_add(&pred, g_ipredicate, NULL, 0); 7220Sstevel@tonic-gate } 7230Sstevel@tonic-gate 7240Sstevel@tonic-gate if ((dur = g_min_duration[event]) != 0) 7250Sstevel@tonic-gate predicate_add(&pred, arg1, ">=", dur); 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate dprog_add("%s\n", info->ev_name); 7280Sstevel@tonic-gate 7290Sstevel@tonic-gate if (pred != NULL) 7300Sstevel@tonic-gate dprog_add("/%s/\n", pred); 7310Sstevel@tonic-gate predicate_destroy(&pred); 7320Sstevel@tonic-gate 7330Sstevel@tonic-gate dprog_add("{\n"); 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate if (g_tracing) { 7360Sstevel@tonic-gate dprog_add("\ttrace(%dULL);\n", event); 7370Sstevel@tonic-gate dprog_add("\ttrace(%s);\n", arg0); 7380Sstevel@tonic-gate dprog_add("\ttrace(%s);\n", caller); 7390Sstevel@tonic-gate dprog_add(stack); 7400Sstevel@tonic-gate } else { 741*1028Sbmc /* 742*1028Sbmc * The ordering here is important: when we process the 743*1028Sbmc * aggregate, we count on the fact that @avg appears before 744*1028Sbmc * @hist in program order to assure that @avg is assigned the 745*1028Sbmc * first aggregation variable ID and @hist assigned the 746*1028Sbmc * second; see the comment in process_aggregate() for details. 747*1028Sbmc */ 7480Sstevel@tonic-gate dprog_add("\t@avg[%dULL, %s, %s%s] = avg(%s);\n", 7490Sstevel@tonic-gate event, arg0, caller, stack, arg1); 7500Sstevel@tonic-gate 7510Sstevel@tonic-gate if (g_recsize >= LS_HIST) { 7520Sstevel@tonic-gate dprog_add("\t@hist[%dULL, %s, %s%s] = quantize" 7530Sstevel@tonic-gate "(%s);\n", event, arg0, caller, stack, arg1); 7540Sstevel@tonic-gate } 7550Sstevel@tonic-gate } 7560Sstevel@tonic-gate 7570Sstevel@tonic-gate if (info->ev_acquire != NULL) 7580Sstevel@tonic-gate dprog_add("\tself->ev%d[arg0] = 0;\n", event); 7590Sstevel@tonic-gate 7600Sstevel@tonic-gate dprog_add("}\n\n"); 7610Sstevel@tonic-gate } 7620Sstevel@tonic-gate 7630Sstevel@tonic-gate static void 7640Sstevel@tonic-gate dprog_compile() 7650Sstevel@tonic-gate { 7660Sstevel@tonic-gate dtrace_prog_t *prog; 7670Sstevel@tonic-gate dtrace_proginfo_t info; 7680Sstevel@tonic-gate 7690Sstevel@tonic-gate if (g_Vflag) { 7700Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: vvvv D program vvvv\n"); 7710Sstevel@tonic-gate (void) fputs(g_prog, stderr); 7720Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: ^^^^ D program ^^^^\n"); 7730Sstevel@tonic-gate } 7740Sstevel@tonic-gate 7750Sstevel@tonic-gate if ((prog = dtrace_program_strcompile(g_dtp, g_prog, 7760Sstevel@tonic-gate DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) 7770Sstevel@tonic-gate dfail("failed to compile program"); 7780Sstevel@tonic-gate 7790Sstevel@tonic-gate if (dtrace_program_exec(g_dtp, prog, &info) == -1) 7800Sstevel@tonic-gate dfail("failed to enable probes"); 7810Sstevel@tonic-gate 7820Sstevel@tonic-gate if (dtrace_go(g_dtp) != 0) 7830Sstevel@tonic-gate dfail("couldn't start tracing"); 7840Sstevel@tonic-gate } 7850Sstevel@tonic-gate 7860Sstevel@tonic-gate static void 7870Sstevel@tonic-gate status_fire(void) 7880Sstevel@tonic-gate {} 7890Sstevel@tonic-gate 7900Sstevel@tonic-gate static void 7910Sstevel@tonic-gate status_init(void) 7920Sstevel@tonic-gate { 7930Sstevel@tonic-gate dtrace_optval_t val, status, agg; 7940Sstevel@tonic-gate struct sigaction act; 7950Sstevel@tonic-gate struct itimerspec ts; 7960Sstevel@tonic-gate struct sigevent ev; 7970Sstevel@tonic-gate timer_t tid; 7980Sstevel@tonic-gate 7990Sstevel@tonic-gate if (dtrace_getopt(g_dtp, "statusrate", &status) == -1) 8000Sstevel@tonic-gate dfail("failed to get 'statusrate'"); 8010Sstevel@tonic-gate 8020Sstevel@tonic-gate if (dtrace_getopt(g_dtp, "aggrate", &agg) == -1) 8030Sstevel@tonic-gate dfail("failed to get 'statusrate'"); 8040Sstevel@tonic-gate 8050Sstevel@tonic-gate /* 8060Sstevel@tonic-gate * We would want to awaken at a rate that is the GCD of the statusrate 8070Sstevel@tonic-gate * and the aggrate -- but that seems a bit absurd. Instead, we'll 8080Sstevel@tonic-gate * simply awaken at a rate that is the more frequent of the two, which 8090Sstevel@tonic-gate * assures that we're never later than the interval implied by the 8100Sstevel@tonic-gate * more frequent rate. 8110Sstevel@tonic-gate */ 8120Sstevel@tonic-gate val = status < agg ? status : agg; 8130Sstevel@tonic-gate 8140Sstevel@tonic-gate (void) sigemptyset(&act.sa_mask); 8150Sstevel@tonic-gate act.sa_flags = 0; 8160Sstevel@tonic-gate act.sa_handler = status_fire; 8170Sstevel@tonic-gate (void) sigaction(SIGUSR1, &act, NULL); 8180Sstevel@tonic-gate 8190Sstevel@tonic-gate ev.sigev_notify = SIGEV_SIGNAL; 8200Sstevel@tonic-gate ev.sigev_signo = SIGUSR1; 8210Sstevel@tonic-gate 8220Sstevel@tonic-gate if (timer_create(CLOCK_REALTIME, &ev, &tid) == -1) 8230Sstevel@tonic-gate dfail("cannot create CLOCK_REALTIME timer"); 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate ts.it_value.tv_sec = val / NANOSEC; 8260Sstevel@tonic-gate ts.it_value.tv_nsec = val % NANOSEC; 8270Sstevel@tonic-gate ts.it_interval = ts.it_value; 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate if (timer_settime(tid, TIMER_RELTIME, &ts, NULL) == -1) 8300Sstevel@tonic-gate dfail("cannot set time on CLOCK_REALTIME timer"); 8310Sstevel@tonic-gate } 8320Sstevel@tonic-gate 8330Sstevel@tonic-gate static void 8340Sstevel@tonic-gate status_check(void) 8350Sstevel@tonic-gate { 8360Sstevel@tonic-gate if (!g_tracing && dtrace_aggregate_snap(g_dtp) != 0) 8370Sstevel@tonic-gate dfail("failed to snap aggregate"); 8380Sstevel@tonic-gate 8390Sstevel@tonic-gate if (dtrace_status(g_dtp) == -1) 8400Sstevel@tonic-gate dfail("dtrace_status()"); 8410Sstevel@tonic-gate } 8420Sstevel@tonic-gate 8430Sstevel@tonic-gate static void 844457Sbmc lsrec_fill(lsrec_t *lsrec, const dtrace_recdesc_t *rec, int nrecs, caddr_t data) 8450Sstevel@tonic-gate { 8460Sstevel@tonic-gate bzero(lsrec, g_recsize); 8470Sstevel@tonic-gate lsrec->ls_count = 1; 8480Sstevel@tonic-gate 8490Sstevel@tonic-gate if ((g_recsize > LS_HIST && nrecs < 4) || (nrecs < 3)) 8500Sstevel@tonic-gate fail(0, "truncated DTrace record"); 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uint64_t)) 8530Sstevel@tonic-gate fail(0, "bad event size in first record"); 8540Sstevel@tonic-gate 8550Sstevel@tonic-gate /* LINTED - alignment */ 8560Sstevel@tonic-gate lsrec->ls_event = (uint32_t)*((uint64_t *)(data + rec->dtrd_offset)); 8570Sstevel@tonic-gate rec++; 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uintptr_t)) 8600Sstevel@tonic-gate fail(0, "bad lock address size in second record"); 8610Sstevel@tonic-gate 8620Sstevel@tonic-gate /* LINTED - alignment */ 8630Sstevel@tonic-gate lsrec->ls_lock = *((uintptr_t *)(data + rec->dtrd_offset)); 8640Sstevel@tonic-gate rec++; 8650Sstevel@tonic-gate 8660Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uintptr_t)) 8670Sstevel@tonic-gate fail(0, "bad caller size in third record"); 8680Sstevel@tonic-gate 8690Sstevel@tonic-gate /* LINTED - alignment */ 8700Sstevel@tonic-gate lsrec->ls_caller = *((uintptr_t *)(data + rec->dtrd_offset)); 8710Sstevel@tonic-gate rec++; 8720Sstevel@tonic-gate 8730Sstevel@tonic-gate if (g_recsize > LS_HIST) { 8740Sstevel@tonic-gate int frames, i; 8750Sstevel@tonic-gate pc_t *stack; 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate frames = rec->dtrd_size / sizeof (pc_t); 8780Sstevel@tonic-gate /* LINTED - alignment */ 8790Sstevel@tonic-gate stack = (pc_t *)(data + rec->dtrd_offset); 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate for (i = 1; i < frames; i++) 8820Sstevel@tonic-gate lsrec->ls_stack[i - 1] = stack[i]; 8830Sstevel@tonic-gate } 8840Sstevel@tonic-gate } 8850Sstevel@tonic-gate 8860Sstevel@tonic-gate /*ARGSUSED*/ 8870Sstevel@tonic-gate static int 888457Sbmc count_aggregate(const dtrace_aggdata_t *agg, void *arg) 8890Sstevel@tonic-gate { 8900Sstevel@tonic-gate *((size_t *)arg) += 1; 8910Sstevel@tonic-gate 8920Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 8930Sstevel@tonic-gate } 8940Sstevel@tonic-gate 8950Sstevel@tonic-gate static int 896457Sbmc process_aggregate(const dtrace_aggdata_t *agg, void *arg) 8970Sstevel@tonic-gate { 898457Sbmc const dtrace_aggdesc_t *aggdesc = agg->dtada_desc; 8990Sstevel@tonic-gate caddr_t data = agg->dtada_data; 9000Sstevel@tonic-gate lsdata_t *lsdata = arg; 9010Sstevel@tonic-gate lsrec_t *lsrec = lsdata->lsd_next; 902457Sbmc const dtrace_recdesc_t *rec; 9030Sstevel@tonic-gate uint64_t *avg, *quantized; 9040Sstevel@tonic-gate int i, j; 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate assert(lsdata->lsd_count < g_nrecs); 9070Sstevel@tonic-gate 908*1028Sbmc /* 909*1028Sbmc * Aggregation variable IDs are guaranteed to be generated in program 910*1028Sbmc * order, and they are guaranteed to start from DTRACE_AGGVARIDNONE 911*1028Sbmc * plus one. As "avg" appears before "hist" in program order, we know 912*1028Sbmc * that "avg" will be allocated the first aggregation variable ID, and 913*1028Sbmc * "hist" will be allocated the second aggregation variable ID -- and 914*1028Sbmc * we therefore use the aggregation variable ID to differentiate the 915*1028Sbmc * cases. 916*1028Sbmc */ 917*1028Sbmc if (aggdesc->dtagd_varid > DTRACE_AGGVARIDNONE + 1) { 9180Sstevel@tonic-gate /* 919*1028Sbmc * If this is the histogram entry. We'll copy the quantized 920*1028Sbmc * data into lc_hist, and jump over the rest. 9210Sstevel@tonic-gate */ 9220Sstevel@tonic-gate rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; 9230Sstevel@tonic-gate 924*1028Sbmc if (aggdesc->dtagd_varid != DTRACE_AGGVARIDNONE + 2) 925*1028Sbmc fail(0, "bad variable ID in aggregation record"); 926*1028Sbmc 9270Sstevel@tonic-gate if (rec->dtrd_size != 9280Sstevel@tonic-gate DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) 9290Sstevel@tonic-gate fail(0, "bad quantize size in aggregation record"); 9300Sstevel@tonic-gate 9310Sstevel@tonic-gate /* LINTED - alignment */ 9320Sstevel@tonic-gate quantized = (uint64_t *)(data + rec->dtrd_offset); 9330Sstevel@tonic-gate 9340Sstevel@tonic-gate for (i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0; 9350Sstevel@tonic-gate i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) 9360Sstevel@tonic-gate lsrec->ls_hist[j] = quantized[i]; 9370Sstevel@tonic-gate 9380Sstevel@tonic-gate goto out; 9390Sstevel@tonic-gate } 9400Sstevel@tonic-gate 9410Sstevel@tonic-gate lsrec_fill(lsrec, &aggdesc->dtagd_rec[1], 9420Sstevel@tonic-gate aggdesc->dtagd_nrecs - 1, data); 9430Sstevel@tonic-gate 9440Sstevel@tonic-gate rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; 9450Sstevel@tonic-gate 9460Sstevel@tonic-gate if (rec->dtrd_size != 2 * sizeof (uint64_t)) 9470Sstevel@tonic-gate fail(0, "bad avg size in aggregation record"); 9480Sstevel@tonic-gate 9490Sstevel@tonic-gate /* LINTED - alignment */ 9500Sstevel@tonic-gate avg = (uint64_t *)(data + rec->dtrd_offset); 9510Sstevel@tonic-gate lsrec->ls_count = (uint32_t)avg[0]; 9520Sstevel@tonic-gate lsrec->ls_time = (uintptr_t)avg[1]; 9530Sstevel@tonic-gate 9540Sstevel@tonic-gate if (g_recsize >= LS_HIST) 9550Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9560Sstevel@tonic-gate 9570Sstevel@tonic-gate out: 9580Sstevel@tonic-gate lsdata->lsd_next = (lsrec_t *)((uintptr_t)lsrec + g_recsize); 9590Sstevel@tonic-gate lsdata->lsd_count++; 9600Sstevel@tonic-gate 9610Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9620Sstevel@tonic-gate } 9630Sstevel@tonic-gate 9640Sstevel@tonic-gate static int 9650Sstevel@tonic-gate process_trace(const dtrace_probedata_t *pdata, void *arg) 9660Sstevel@tonic-gate { 9670Sstevel@tonic-gate lsdata_t *lsdata = arg; 9680Sstevel@tonic-gate lsrec_t *lsrec = lsdata->lsd_next; 9690Sstevel@tonic-gate dtrace_eprobedesc_t *edesc = pdata->dtpda_edesc; 9700Sstevel@tonic-gate caddr_t data = pdata->dtpda_data; 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate if (lsdata->lsd_count >= g_nrecs) 9730Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 9740Sstevel@tonic-gate 9750Sstevel@tonic-gate lsrec_fill(lsrec, edesc->dtepd_rec, edesc->dtepd_nrecs, data); 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate lsdata->lsd_next = (lsrec_t *)((uintptr_t)lsrec + g_recsize); 9780Sstevel@tonic-gate lsdata->lsd_count++; 9790Sstevel@tonic-gate 9800Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 9810Sstevel@tonic-gate } 9820Sstevel@tonic-gate 9830Sstevel@tonic-gate static int 9840Sstevel@tonic-gate process_data(FILE *out, char *data) 9850Sstevel@tonic-gate { 9860Sstevel@tonic-gate lsdata_t lsdata; 9870Sstevel@tonic-gate 9880Sstevel@tonic-gate /* LINTED - alignment */ 9890Sstevel@tonic-gate lsdata.lsd_next = (lsrec_t *)data; 9900Sstevel@tonic-gate lsdata.lsd_count = 0; 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate if (g_tracing) { 9930Sstevel@tonic-gate if (dtrace_consume(g_dtp, out, 9940Sstevel@tonic-gate process_trace, NULL, &lsdata) != 0) 9950Sstevel@tonic-gate dfail("failed to consume buffer"); 9960Sstevel@tonic-gate 9970Sstevel@tonic-gate return (lsdata.lsd_count); 9980Sstevel@tonic-gate } 9990Sstevel@tonic-gate 10000Sstevel@tonic-gate if (dtrace_aggregate_walk_keyvarsorted(g_dtp, 10010Sstevel@tonic-gate process_aggregate, &lsdata) != 0) 10020Sstevel@tonic-gate dfail("failed to walk aggregate"); 10030Sstevel@tonic-gate 10040Sstevel@tonic-gate return (lsdata.lsd_count); 10050Sstevel@tonic-gate } 10060Sstevel@tonic-gate 10070Sstevel@tonic-gate /*ARGSUSED*/ 10080Sstevel@tonic-gate static int 1009457Sbmc drophandler(const dtrace_dropdata_t *data, void *arg) 10100Sstevel@tonic-gate { 10110Sstevel@tonic-gate g_dropped++; 10120Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: warning: %s", data->dtdda_msg); 10130Sstevel@tonic-gate return (DTRACE_HANDLE_OK); 10140Sstevel@tonic-gate } 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate int 10170Sstevel@tonic-gate main(int argc, char **argv) 10180Sstevel@tonic-gate { 10190Sstevel@tonic-gate char *data_buf; 10200Sstevel@tonic-gate lsrec_t *lsp, **current, **first, **sort_buf, **merge_buf; 10210Sstevel@tonic-gate FILE *out = stdout; 10220Sstevel@tonic-gate char c; 10230Sstevel@tonic-gate pid_t child; 10240Sstevel@tonic-gate int status; 10250Sstevel@tonic-gate int i, j; 10260Sstevel@tonic-gate hrtime_t duration; 10270Sstevel@tonic-gate char *addrp, *offp, *sizep, *evp, *lastp, *p; 10280Sstevel@tonic-gate uintptr_t addr; 10290Sstevel@tonic-gate size_t size, off; 10300Sstevel@tonic-gate int events_specified = 0; 10310Sstevel@tonic-gate int exec_errno = 0; 10320Sstevel@tonic-gate uint32_t event; 10330Sstevel@tonic-gate char *filt = NULL, *ifilt = NULL; 10340Sstevel@tonic-gate static uint64_t ev_count[LS_MAX_EVENTS + 1]; 10350Sstevel@tonic-gate static uint64_t ev_time[LS_MAX_EVENTS + 1]; 10360Sstevel@tonic-gate dtrace_optval_t aggsize; 10370Sstevel@tonic-gate char aggstr[10]; 10380Sstevel@tonic-gate long ncpus; 10390Sstevel@tonic-gate int dynvar = 0; 10400Sstevel@tonic-gate int err; 10410Sstevel@tonic-gate 10420Sstevel@tonic-gate if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { 10430Sstevel@tonic-gate fail(0, "cannot open dtrace library: %s", 10440Sstevel@tonic-gate dtrace_errmsg(NULL, err)); 10450Sstevel@tonic-gate } 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate if (dtrace_handle_drop(g_dtp, &drophandler, NULL) == -1) 10480Sstevel@tonic-gate dfail("couldn't establish drop handler"); 10490Sstevel@tonic-gate 10500Sstevel@tonic-gate if (symtab_init() == -1) 10510Sstevel@tonic-gate fail(1, "can't load kernel symbols"); 10520Sstevel@tonic-gate 10530Sstevel@tonic-gate g_nrecs = DEFAULT_NRECS; 10540Sstevel@tonic-gate 10550Sstevel@tonic-gate while ((c = getopt(argc, argv, LOCKSTAT_OPTSTR)) != EOF) { 10560Sstevel@tonic-gate switch (c) { 10570Sstevel@tonic-gate case 'b': 10580Sstevel@tonic-gate g_recsize = LS_BASIC; 10590Sstevel@tonic-gate break; 10600Sstevel@tonic-gate 10610Sstevel@tonic-gate case 't': 10620Sstevel@tonic-gate g_recsize = LS_TIME; 10630Sstevel@tonic-gate break; 10640Sstevel@tonic-gate 10650Sstevel@tonic-gate case 'h': 10660Sstevel@tonic-gate g_recsize = LS_HIST; 10670Sstevel@tonic-gate break; 10680Sstevel@tonic-gate 10690Sstevel@tonic-gate case 's': 10700Sstevel@tonic-gate if (!isdigit(optarg[0])) 10710Sstevel@tonic-gate usage(); 10720Sstevel@tonic-gate g_stkdepth = atoi(optarg); 10730Sstevel@tonic-gate if (g_stkdepth > LS_MAX_STACK_DEPTH) 10740Sstevel@tonic-gate fail(0, "max stack depth is %d", 10750Sstevel@tonic-gate LS_MAX_STACK_DEPTH); 10760Sstevel@tonic-gate g_recsize = LS_STACK(g_stkdepth); 10770Sstevel@tonic-gate break; 10780Sstevel@tonic-gate 10790Sstevel@tonic-gate case 'n': 10800Sstevel@tonic-gate if (!isdigit(optarg[0])) 10810Sstevel@tonic-gate usage(); 10820Sstevel@tonic-gate g_nrecs = atoi(optarg); 10830Sstevel@tonic-gate break; 10840Sstevel@tonic-gate 10850Sstevel@tonic-gate case 'd': 10860Sstevel@tonic-gate if (!isdigit(optarg[0])) 10870Sstevel@tonic-gate usage(); 10880Sstevel@tonic-gate duration = atoll(optarg); 10890Sstevel@tonic-gate 10900Sstevel@tonic-gate /* 10910Sstevel@tonic-gate * XXX -- durations really should be per event 10920Sstevel@tonic-gate * since the units are different, but it's hard 10930Sstevel@tonic-gate * to express this nicely in the interface. 10940Sstevel@tonic-gate * Not clear yet what the cleanest solution is. 10950Sstevel@tonic-gate */ 10960Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 10970Sstevel@tonic-gate if (g_event_info[i].ev_type != 'E') 10980Sstevel@tonic-gate g_min_duration[i] = duration; 10990Sstevel@tonic-gate 11000Sstevel@tonic-gate break; 11010Sstevel@tonic-gate 11020Sstevel@tonic-gate case 'i': 11030Sstevel@tonic-gate if (!isdigit(optarg[0])) 11040Sstevel@tonic-gate usage(); 11050Sstevel@tonic-gate i = atoi(optarg); 11060Sstevel@tonic-gate if (i <= 0) 11070Sstevel@tonic-gate usage(); 11080Sstevel@tonic-gate if (i > MAX_HZ) 11090Sstevel@tonic-gate fail(0, "max interrupt rate is %d Hz", MAX_HZ); 11100Sstevel@tonic-gate 11110Sstevel@tonic-gate for (j = 0; j < LS_MAX_EVENTS; j++) 11120Sstevel@tonic-gate if (strcmp(g_event_info[j].ev_desc, 11130Sstevel@tonic-gate "Profiling interrupt") == 0) 11140Sstevel@tonic-gate break; 11150Sstevel@tonic-gate 11160Sstevel@tonic-gate (void) sprintf(g_event_info[j].ev_name, 11170Sstevel@tonic-gate "profile:::profile-%d", i); 11180Sstevel@tonic-gate break; 11190Sstevel@tonic-gate 11200Sstevel@tonic-gate case 'l': 11210Sstevel@tonic-gate case 'f': 11220Sstevel@tonic-gate addrp = strtok(optarg, ","); 11230Sstevel@tonic-gate sizep = strtok(NULL, ","); 11240Sstevel@tonic-gate addrp = strtok(optarg, ",+"); 11250Sstevel@tonic-gate offp = strtok(NULL, ","); 11260Sstevel@tonic-gate 11270Sstevel@tonic-gate size = sizep ? strtoul(sizep, NULL, 0) : 1; 11280Sstevel@tonic-gate off = offp ? strtoul(offp, NULL, 0) : 0; 11290Sstevel@tonic-gate 11300Sstevel@tonic-gate if (addrp[0] == '0') { 11310Sstevel@tonic-gate addr = strtoul(addrp, NULL, 16) + off; 11320Sstevel@tonic-gate } else { 11330Sstevel@tonic-gate addr = sym_to_addr(addrp) + off; 11340Sstevel@tonic-gate if (sizep == NULL) 11350Sstevel@tonic-gate size = sym_size(addrp) - off; 11360Sstevel@tonic-gate if (addr - off == 0) 11370Sstevel@tonic-gate fail(0, "symbol '%s' not found", addrp); 11380Sstevel@tonic-gate if (size == 0) 11390Sstevel@tonic-gate size = 1; 11400Sstevel@tonic-gate } 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate 11430Sstevel@tonic-gate if (c == 'l') { 11440Sstevel@tonic-gate filter_add(&filt, "arg0", addr, size); 11450Sstevel@tonic-gate } else { 11460Sstevel@tonic-gate filter_add(&filt, "caller", addr, size); 11470Sstevel@tonic-gate filter_add(&ifilt, "arg0", addr, size); 11480Sstevel@tonic-gate } 11490Sstevel@tonic-gate break; 11500Sstevel@tonic-gate 11510Sstevel@tonic-gate case 'e': 11520Sstevel@tonic-gate evp = strtok_r(optarg, ",", &lastp); 11530Sstevel@tonic-gate while (evp) { 11540Sstevel@tonic-gate int ev1, ev2; 11550Sstevel@tonic-gate char *evp2; 11560Sstevel@tonic-gate 11570Sstevel@tonic-gate (void) strtok(evp, "-"); 11580Sstevel@tonic-gate evp2 = strtok(NULL, "-"); 11590Sstevel@tonic-gate ev1 = atoi(evp); 11600Sstevel@tonic-gate ev2 = evp2 ? atoi(evp2) : ev1; 11610Sstevel@tonic-gate if ((uint_t)ev1 >= LS_MAX_EVENTS || 11620Sstevel@tonic-gate (uint_t)ev2 >= LS_MAX_EVENTS || ev1 > ev2) 11630Sstevel@tonic-gate fail(0, "-e events out of range"); 11640Sstevel@tonic-gate for (i = ev1; i <= ev2; i++) 11650Sstevel@tonic-gate g_enabled[i] = 1; 11660Sstevel@tonic-gate evp = strtok_r(NULL, ",", &lastp); 11670Sstevel@tonic-gate } 11680Sstevel@tonic-gate events_specified = 1; 11690Sstevel@tonic-gate break; 11700Sstevel@tonic-gate 11710Sstevel@tonic-gate case 'c': 11720Sstevel@tonic-gate g_cflag = 1; 11730Sstevel@tonic-gate break; 11740Sstevel@tonic-gate 11750Sstevel@tonic-gate case 'k': 11760Sstevel@tonic-gate g_kflag = 1; 11770Sstevel@tonic-gate break; 11780Sstevel@tonic-gate 11790Sstevel@tonic-gate case 'w': 11800Sstevel@tonic-gate g_wflag = 1; 11810Sstevel@tonic-gate break; 11820Sstevel@tonic-gate 11830Sstevel@tonic-gate case 'W': 11840Sstevel@tonic-gate g_Wflag = 1; 11850Sstevel@tonic-gate break; 11860Sstevel@tonic-gate 11870Sstevel@tonic-gate case 'g': 11880Sstevel@tonic-gate g_gflag = 1; 11890Sstevel@tonic-gate break; 11900Sstevel@tonic-gate 11910Sstevel@tonic-gate case 'C': 11920Sstevel@tonic-gate case 'E': 11930Sstevel@tonic-gate case 'H': 11940Sstevel@tonic-gate case 'I': 11950Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 11960Sstevel@tonic-gate if (g_event_info[i].ev_type == c) 11970Sstevel@tonic-gate g_enabled[i] = 1; 11980Sstevel@tonic-gate events_specified = 1; 11990Sstevel@tonic-gate break; 12000Sstevel@tonic-gate 12010Sstevel@tonic-gate case 'A': 12020Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 12030Sstevel@tonic-gate if (strchr("CH", g_event_info[i].ev_type)) 12040Sstevel@tonic-gate g_enabled[i] = 1; 12050Sstevel@tonic-gate events_specified = 1; 12060Sstevel@tonic-gate break; 12070Sstevel@tonic-gate 12080Sstevel@tonic-gate case 'T': 12090Sstevel@tonic-gate g_tracing = 1; 12100Sstevel@tonic-gate break; 12110Sstevel@tonic-gate 12120Sstevel@tonic-gate case 'D': 12130Sstevel@tonic-gate if (!isdigit(optarg[0])) 12140Sstevel@tonic-gate usage(); 12150Sstevel@tonic-gate g_topn = atoi(optarg); 12160Sstevel@tonic-gate break; 12170Sstevel@tonic-gate 12180Sstevel@tonic-gate case 'R': 12190Sstevel@tonic-gate g_rates = 1; 12200Sstevel@tonic-gate break; 12210Sstevel@tonic-gate 12220Sstevel@tonic-gate case 'p': 12230Sstevel@tonic-gate g_pflag = 1; 12240Sstevel@tonic-gate break; 12250Sstevel@tonic-gate 12260Sstevel@tonic-gate case 'P': 12270Sstevel@tonic-gate g_Pflag = 1; 12280Sstevel@tonic-gate break; 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate case 'o': 12310Sstevel@tonic-gate if ((out = fopen(optarg, "w")) == NULL) 12320Sstevel@tonic-gate fail(1, "error opening file"); 12330Sstevel@tonic-gate break; 12340Sstevel@tonic-gate 12350Sstevel@tonic-gate case 'V': 12360Sstevel@tonic-gate g_Vflag = 1; 12370Sstevel@tonic-gate break; 12380Sstevel@tonic-gate 12390Sstevel@tonic-gate default: 12400Sstevel@tonic-gate if (strchr(LOCKSTAT_OPTSTR, c) == NULL) 12410Sstevel@tonic-gate usage(); 12420Sstevel@tonic-gate } 12430Sstevel@tonic-gate } 12440Sstevel@tonic-gate 12450Sstevel@tonic-gate if (filt != NULL) { 12460Sstevel@tonic-gate predicate_add(&g_predicate, filt, NULL, 0); 12470Sstevel@tonic-gate filter_destroy(&filt); 12480Sstevel@tonic-gate } 12490Sstevel@tonic-gate 12500Sstevel@tonic-gate if (ifilt != NULL) { 12510Sstevel@tonic-gate predicate_add(&g_ipredicate, ifilt, NULL, 0); 12520Sstevel@tonic-gate filter_destroy(&ifilt); 12530Sstevel@tonic-gate } 12540Sstevel@tonic-gate 12550Sstevel@tonic-gate if (g_recsize == 0) { 12560Sstevel@tonic-gate if (g_gflag) { 12570Sstevel@tonic-gate g_stkdepth = LS_MAX_STACK_DEPTH; 12580Sstevel@tonic-gate g_recsize = LS_STACK(g_stkdepth); 12590Sstevel@tonic-gate } else { 12600Sstevel@tonic-gate g_recsize = LS_TIME; 12610Sstevel@tonic-gate } 12620Sstevel@tonic-gate } 12630Sstevel@tonic-gate 12640Sstevel@tonic-gate if (g_gflag && g_recsize <= LS_STACK(0)) 12650Sstevel@tonic-gate fail(0, "'-g' requires at least '-s 1' data gathering"); 12660Sstevel@tonic-gate 12670Sstevel@tonic-gate /* 12680Sstevel@tonic-gate * Make sure the alignment is reasonable 12690Sstevel@tonic-gate */ 12700Sstevel@tonic-gate g_recsize = -(-g_recsize & -sizeof (uint64_t)); 12710Sstevel@tonic-gate 12720Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 12730Sstevel@tonic-gate /* 12740Sstevel@tonic-gate * If no events were specified, enable -C. 12750Sstevel@tonic-gate */ 12760Sstevel@tonic-gate if (!events_specified && g_event_info[i].ev_type == 'C') 12770Sstevel@tonic-gate g_enabled[i] = 1; 12780Sstevel@tonic-gate } 12790Sstevel@tonic-gate 12800Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 12810Sstevel@tonic-gate if (!g_enabled[i]) 12820Sstevel@tonic-gate continue; 12830Sstevel@tonic-gate 12840Sstevel@tonic-gate if (g_event_info[i].ev_acquire != NULL) { 12850Sstevel@tonic-gate /* 12860Sstevel@tonic-gate * If we've enabled a hold event, we must explicitly 12870Sstevel@tonic-gate * allocate dynamic variable space. 12880Sstevel@tonic-gate */ 12890Sstevel@tonic-gate dynvar = 1; 12900Sstevel@tonic-gate } 12910Sstevel@tonic-gate 12920Sstevel@tonic-gate dprog_addevent(i); 12930Sstevel@tonic-gate } 12940Sstevel@tonic-gate 12950Sstevel@tonic-gate /* 12960Sstevel@tonic-gate * Make sure there are remaining arguments to specify a child command 12970Sstevel@tonic-gate * to execute. 12980Sstevel@tonic-gate */ 12990Sstevel@tonic-gate if (argc <= optind) 13000Sstevel@tonic-gate usage(); 13010Sstevel@tonic-gate 13020Sstevel@tonic-gate if ((ncpus = sysconf(_SC_NPROCESSORS_ONLN)) == -1) 13030Sstevel@tonic-gate dfail("couldn't determine number of online CPUs"); 13040Sstevel@tonic-gate 13050Sstevel@tonic-gate /* 13060Sstevel@tonic-gate * By default, we set our data buffer size to be the number of records 13070Sstevel@tonic-gate * multiplied by the size of the record, doubled to account for some 13080Sstevel@tonic-gate * DTrace slop and divided by the number of CPUs. We silently clamp 13090Sstevel@tonic-gate * the aggregation size at both a minimum and a maximum to prevent 13100Sstevel@tonic-gate * absurdly low or high values. 13110Sstevel@tonic-gate */ 13120Sstevel@tonic-gate if ((aggsize = (g_nrecs * g_recsize * 2) / ncpus) < MIN_AGGSIZE) 13130Sstevel@tonic-gate aggsize = MIN_AGGSIZE; 13140Sstevel@tonic-gate 13150Sstevel@tonic-gate if (aggsize > MAX_AGGSIZE) 13160Sstevel@tonic-gate aggsize = MAX_AGGSIZE; 13170Sstevel@tonic-gate 13180Sstevel@tonic-gate (void) sprintf(aggstr, "%lld", (long long)aggsize); 13190Sstevel@tonic-gate 13200Sstevel@tonic-gate if (!g_tracing) { 13210Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "bufsize", "4k") == -1) 13220Sstevel@tonic-gate dfail("failed to set 'bufsize'"); 13230Sstevel@tonic-gate 13240Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "aggsize", aggstr) == -1) 13250Sstevel@tonic-gate dfail("failed to set 'aggsize'"); 13260Sstevel@tonic-gate 13270Sstevel@tonic-gate if (dynvar) { 13280Sstevel@tonic-gate /* 13290Sstevel@tonic-gate * If we're using dynamic variables, we set our 13300Sstevel@tonic-gate * dynamic variable size to be one megabyte per CPU, 13310Sstevel@tonic-gate * with a hard-limit of 32 megabytes. This may still 13320Sstevel@tonic-gate * be too small in some cases, but it can be tuned 13330Sstevel@tonic-gate * manually via -x if need be. 13340Sstevel@tonic-gate */ 13350Sstevel@tonic-gate (void) sprintf(aggstr, "%ldm", ncpus < 32 ? ncpus : 32); 13360Sstevel@tonic-gate 13370Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "dynvarsize", aggstr) == -1) 13380Sstevel@tonic-gate dfail("failed to set 'dynvarsize'"); 13390Sstevel@tonic-gate } 13400Sstevel@tonic-gate } else { 13410Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "bufsize", aggstr) == -1) 13420Sstevel@tonic-gate dfail("failed to set 'bufsize'"); 13430Sstevel@tonic-gate } 13440Sstevel@tonic-gate 13450Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "statusrate", "10sec") == -1) 13460Sstevel@tonic-gate dfail("failed to set 'statusrate'"); 13470Sstevel@tonic-gate 13480Sstevel@tonic-gate optind = 1; 13490Sstevel@tonic-gate while ((c = getopt(argc, argv, LOCKSTAT_OPTSTR)) != EOF) { 13500Sstevel@tonic-gate switch (c) { 13510Sstevel@tonic-gate case 'x': 13520Sstevel@tonic-gate if ((p = strchr(optarg, '=')) != NULL) 13530Sstevel@tonic-gate *p++ = '\0'; 13540Sstevel@tonic-gate 13550Sstevel@tonic-gate if (dtrace_setopt(g_dtp, optarg, p) != 0) 13560Sstevel@tonic-gate dfail("failed to set -x %s", optarg); 13570Sstevel@tonic-gate break; 13580Sstevel@tonic-gate } 13590Sstevel@tonic-gate } 13600Sstevel@tonic-gate 13610Sstevel@tonic-gate argc -= optind; 13620Sstevel@tonic-gate argv += optind; 13630Sstevel@tonic-gate 13640Sstevel@tonic-gate dprog_compile(); 13650Sstevel@tonic-gate status_init(); 13660Sstevel@tonic-gate 13670Sstevel@tonic-gate g_elapsed = -gethrtime(); 13680Sstevel@tonic-gate 13690Sstevel@tonic-gate /* 13700Sstevel@tonic-gate * Spawn the specified command and wait for it to complete. 13710Sstevel@tonic-gate */ 13720Sstevel@tonic-gate child = fork(); 13730Sstevel@tonic-gate if (child == -1) 13740Sstevel@tonic-gate fail(1, "cannot fork"); 13750Sstevel@tonic-gate if (child == 0) { 13760Sstevel@tonic-gate (void) dtrace_close(g_dtp); 13770Sstevel@tonic-gate (void) execvp(argv[0], &argv[0]); 13780Sstevel@tonic-gate exec_errno = errno; 13790Sstevel@tonic-gate exit(127); 13800Sstevel@tonic-gate } 13810Sstevel@tonic-gate 13820Sstevel@tonic-gate while (waitpid(child, &status, WEXITED) != child) 13830Sstevel@tonic-gate status_check(); 13840Sstevel@tonic-gate 13850Sstevel@tonic-gate g_elapsed += gethrtime(); 13860Sstevel@tonic-gate 13870Sstevel@tonic-gate if (WIFEXITED(status)) { 13880Sstevel@tonic-gate if (WEXITSTATUS(status) != 0) { 13890Sstevel@tonic-gate if (exec_errno != 0) { 13900Sstevel@tonic-gate errno = exec_errno; 13910Sstevel@tonic-gate fail(1, "could not execute %s", argv[0]); 13920Sstevel@tonic-gate } 13930Sstevel@tonic-gate (void) fprintf(stderr, 13940Sstevel@tonic-gate "lockstat: warning: %s exited with code %d\n", 13950Sstevel@tonic-gate argv[0], WEXITSTATUS(status)); 13960Sstevel@tonic-gate } 13970Sstevel@tonic-gate } else { 13980Sstevel@tonic-gate (void) fprintf(stderr, 13990Sstevel@tonic-gate "lockstat: warning: %s died on signal %d\n", 14000Sstevel@tonic-gate argv[0], WTERMSIG(status)); 14010Sstevel@tonic-gate } 14020Sstevel@tonic-gate 14030Sstevel@tonic-gate if (dtrace_stop(g_dtp) == -1) 14040Sstevel@tonic-gate dfail("failed to stop dtrace"); 14050Sstevel@tonic-gate 14060Sstevel@tonic-gate /* 14070Sstevel@tonic-gate * Before we read out the results, we need to allocate our buffer. 14080Sstevel@tonic-gate * If we're tracing, then we'll just use the precalculated size. If 14090Sstevel@tonic-gate * we're not, then we'll take a snapshot of the aggregate, and walk 14100Sstevel@tonic-gate * it to count the number of records. 14110Sstevel@tonic-gate */ 14120Sstevel@tonic-gate if (!g_tracing) { 14130Sstevel@tonic-gate if (dtrace_aggregate_snap(g_dtp) != 0) 14140Sstevel@tonic-gate dfail("failed to snap aggregate"); 14150Sstevel@tonic-gate 14160Sstevel@tonic-gate g_nrecs = 0; 14170Sstevel@tonic-gate 14180Sstevel@tonic-gate if (dtrace_aggregate_walk(g_dtp, 14190Sstevel@tonic-gate count_aggregate, &g_nrecs) != 0) 14200Sstevel@tonic-gate dfail("failed to walk aggregate"); 14210Sstevel@tonic-gate } 14220Sstevel@tonic-gate 14230Sstevel@tonic-gate if ((data_buf = memalign(sizeof (uint64_t), 14240Sstevel@tonic-gate (g_nrecs + 1) * g_recsize)) == NULL) 14250Sstevel@tonic-gate fail(1, "Memory allocation failed"); 14260Sstevel@tonic-gate 14270Sstevel@tonic-gate /* 14280Sstevel@tonic-gate * Read out the DTrace data. 14290Sstevel@tonic-gate */ 14300Sstevel@tonic-gate g_nrecs_used = process_data(out, data_buf); 14310Sstevel@tonic-gate 14320Sstevel@tonic-gate if (g_nrecs_used > g_nrecs || g_dropped) 14330Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: warning: " 14340Sstevel@tonic-gate "ran out of data records (use -n for more)\n"); 14350Sstevel@tonic-gate 14360Sstevel@tonic-gate /* LINTED - alignment */ 14370Sstevel@tonic-gate for (i = 0, lsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 14380Sstevel@tonic-gate /* LINTED - alignment */ 14390Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + g_recsize)) { 14400Sstevel@tonic-gate ev_count[lsp->ls_event] += lsp->ls_count; 14410Sstevel@tonic-gate ev_time[lsp->ls_event] += lsp->ls_time; 14420Sstevel@tonic-gate } 14430Sstevel@tonic-gate 14440Sstevel@tonic-gate /* 14450Sstevel@tonic-gate * If -g was specified, convert stacks into individual records. 14460Sstevel@tonic-gate */ 14470Sstevel@tonic-gate if (g_gflag) { 14480Sstevel@tonic-gate lsrec_t *newlsp, *oldlsp; 14490Sstevel@tonic-gate 14500Sstevel@tonic-gate newlsp = memalign(sizeof (uint64_t), 14510Sstevel@tonic-gate g_nrecs_used * LS_TIME * (g_stkdepth + 1)); 14520Sstevel@tonic-gate if (newlsp == NULL) 14530Sstevel@tonic-gate fail(1, "Cannot allocate space for -g processing"); 14540Sstevel@tonic-gate lsp = newlsp; 14550Sstevel@tonic-gate /* LINTED - alignment */ 14560Sstevel@tonic-gate for (i = 0, oldlsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 14570Sstevel@tonic-gate /* LINTED - alignment */ 14580Sstevel@tonic-gate oldlsp = (lsrec_t *)((char *)oldlsp + g_recsize)) { 14590Sstevel@tonic-gate int fr; 14600Sstevel@tonic-gate int caller_in_stack = 0; 14610Sstevel@tonic-gate 14620Sstevel@tonic-gate if (oldlsp->ls_count == 0) 14630Sstevel@tonic-gate continue; 14640Sstevel@tonic-gate 14650Sstevel@tonic-gate for (fr = 0; fr < g_stkdepth; fr++) { 14660Sstevel@tonic-gate if (oldlsp->ls_stack[fr] == 0) 14670Sstevel@tonic-gate break; 14680Sstevel@tonic-gate if (oldlsp->ls_stack[fr] == oldlsp->ls_caller) 14690Sstevel@tonic-gate caller_in_stack = 1; 14700Sstevel@tonic-gate bcopy(oldlsp, lsp, LS_TIME); 14710Sstevel@tonic-gate lsp->ls_caller = oldlsp->ls_stack[fr]; 14720Sstevel@tonic-gate /* LINTED - alignment */ 14730Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + LS_TIME); 14740Sstevel@tonic-gate } 14750Sstevel@tonic-gate if (!caller_in_stack) { 14760Sstevel@tonic-gate bcopy(oldlsp, lsp, LS_TIME); 14770Sstevel@tonic-gate /* LINTED - alignment */ 14780Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + LS_TIME); 14790Sstevel@tonic-gate } 14800Sstevel@tonic-gate } 14810Sstevel@tonic-gate g_nrecs = g_nrecs_used = 14820Sstevel@tonic-gate ((uintptr_t)lsp - (uintptr_t)newlsp) / LS_TIME; 14830Sstevel@tonic-gate g_recsize = LS_TIME; 14840Sstevel@tonic-gate g_stkdepth = 0; 14850Sstevel@tonic-gate free(data_buf); 14860Sstevel@tonic-gate data_buf = (char *)newlsp; 14870Sstevel@tonic-gate } 14880Sstevel@tonic-gate 14890Sstevel@tonic-gate if ((sort_buf = calloc(2 * (g_nrecs + 1), 14900Sstevel@tonic-gate sizeof (void *))) == NULL) 14910Sstevel@tonic-gate fail(1, "Sort buffer allocation failed"); 14920Sstevel@tonic-gate merge_buf = sort_buf + (g_nrecs + 1); 14930Sstevel@tonic-gate 14940Sstevel@tonic-gate /* 14950Sstevel@tonic-gate * Build the sort buffer, discarding zero-count records along the way. 14960Sstevel@tonic-gate */ 14970Sstevel@tonic-gate /* LINTED - alignment */ 14980Sstevel@tonic-gate for (i = 0, lsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 14990Sstevel@tonic-gate /* LINTED - alignment */ 15000Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + g_recsize)) { 15010Sstevel@tonic-gate if (lsp->ls_count == 0) 15020Sstevel@tonic-gate lsp->ls_event = LS_MAX_EVENTS; 15030Sstevel@tonic-gate sort_buf[i] = lsp; 15040Sstevel@tonic-gate } 15050Sstevel@tonic-gate 15060Sstevel@tonic-gate if (g_nrecs_used == 0) 15070Sstevel@tonic-gate exit(0); 15080Sstevel@tonic-gate 15090Sstevel@tonic-gate /* 15100Sstevel@tonic-gate * Add a sentinel after the last record 15110Sstevel@tonic-gate */ 15120Sstevel@tonic-gate sort_buf[i] = lsp; 15130Sstevel@tonic-gate lsp->ls_event = LS_MAX_EVENTS; 15140Sstevel@tonic-gate 15150Sstevel@tonic-gate if (g_tracing) { 15160Sstevel@tonic-gate report_trace(out, sort_buf); 15170Sstevel@tonic-gate return (0); 15180Sstevel@tonic-gate } 15190Sstevel@tonic-gate 15200Sstevel@tonic-gate /* 15210Sstevel@tonic-gate * Application of -g may have resulted in multiple records 15220Sstevel@tonic-gate * with the same signature; coalesce them. 15230Sstevel@tonic-gate */ 15240Sstevel@tonic-gate if (g_gflag) { 15250Sstevel@tonic-gate mergesort(lockcmp, sort_buf, merge_buf, g_nrecs_used); 15260Sstevel@tonic-gate coalesce(lockcmp, sort_buf, g_nrecs_used); 15270Sstevel@tonic-gate } 15280Sstevel@tonic-gate 15290Sstevel@tonic-gate /* 15300Sstevel@tonic-gate * Coalesce locks within the same symbol if -c option specified. 15310Sstevel@tonic-gate * Coalesce PCs within the same function if -k option specified. 15320Sstevel@tonic-gate */ 15330Sstevel@tonic-gate if (g_cflag || g_kflag) { 15340Sstevel@tonic-gate for (i = 0; i < g_nrecs_used; i++) { 15350Sstevel@tonic-gate int fr; 15360Sstevel@tonic-gate lsp = sort_buf[i]; 15370Sstevel@tonic-gate if (g_cflag) 15380Sstevel@tonic-gate coalesce_symbol(&lsp->ls_lock); 15390Sstevel@tonic-gate if (g_kflag) { 15400Sstevel@tonic-gate for (fr = 0; fr < g_stkdepth; fr++) 15410Sstevel@tonic-gate coalesce_symbol(&lsp->ls_stack[fr]); 15420Sstevel@tonic-gate coalesce_symbol(&lsp->ls_caller); 15430Sstevel@tonic-gate } 15440Sstevel@tonic-gate } 15450Sstevel@tonic-gate mergesort(lockcmp, sort_buf, merge_buf, g_nrecs_used); 15460Sstevel@tonic-gate coalesce(lockcmp, sort_buf, g_nrecs_used); 15470Sstevel@tonic-gate } 15480Sstevel@tonic-gate 15490Sstevel@tonic-gate /* 15500Sstevel@tonic-gate * Coalesce callers if -w option specified 15510Sstevel@tonic-gate */ 15520Sstevel@tonic-gate if (g_wflag) { 15530Sstevel@tonic-gate mergesort(lock_and_count_cmp_anywhere, 15540Sstevel@tonic-gate sort_buf, merge_buf, g_nrecs_used); 15550Sstevel@tonic-gate coalesce(lockcmp_anywhere, sort_buf, g_nrecs_used); 15560Sstevel@tonic-gate } 15570Sstevel@tonic-gate 15580Sstevel@tonic-gate /* 15590Sstevel@tonic-gate * Coalesce locks if -W option specified 15600Sstevel@tonic-gate */ 15610Sstevel@tonic-gate if (g_Wflag) { 15620Sstevel@tonic-gate mergesort(site_and_count_cmp_anylock, 15630Sstevel@tonic-gate sort_buf, merge_buf, g_nrecs_used); 15640Sstevel@tonic-gate coalesce(sitecmp_anylock, sort_buf, g_nrecs_used); 15650Sstevel@tonic-gate } 15660Sstevel@tonic-gate 15670Sstevel@tonic-gate /* 15680Sstevel@tonic-gate * Sort data by contention count (ls_count) or total time (ls_time), 15690Sstevel@tonic-gate * depending on g_Pflag. Override g_Pflag if time wasn't measured. 15700Sstevel@tonic-gate */ 15710Sstevel@tonic-gate if (g_recsize < LS_TIME) 15720Sstevel@tonic-gate g_Pflag = 0; 15730Sstevel@tonic-gate 15740Sstevel@tonic-gate if (g_Pflag) 15750Sstevel@tonic-gate mergesort(timecmp, sort_buf, merge_buf, g_nrecs_used); 15760Sstevel@tonic-gate else 15770Sstevel@tonic-gate mergesort(countcmp, sort_buf, merge_buf, g_nrecs_used); 15780Sstevel@tonic-gate 15790Sstevel@tonic-gate /* 15800Sstevel@tonic-gate * Display data by event type 15810Sstevel@tonic-gate */ 15820Sstevel@tonic-gate first = &sort_buf[0]; 15830Sstevel@tonic-gate while ((event = (*first)->ls_event) < LS_MAX_EVENTS) { 15840Sstevel@tonic-gate current = first; 15850Sstevel@tonic-gate while ((lsp = *current)->ls_event == event) 15860Sstevel@tonic-gate current++; 15870Sstevel@tonic-gate report_stats(out, first, current - first, ev_count[event], 15880Sstevel@tonic-gate ev_time[event]); 15890Sstevel@tonic-gate first = current; 15900Sstevel@tonic-gate } 15910Sstevel@tonic-gate 15920Sstevel@tonic-gate return (0); 15930Sstevel@tonic-gate } 15940Sstevel@tonic-gate 15950Sstevel@tonic-gate static char * 15960Sstevel@tonic-gate format_symbol(char *buf, uintptr_t addr, int show_size) 15970Sstevel@tonic-gate { 15980Sstevel@tonic-gate uintptr_t symoff; 15990Sstevel@tonic-gate char *symname; 16000Sstevel@tonic-gate size_t symsize; 16010Sstevel@tonic-gate 16020Sstevel@tonic-gate symname = addr_to_sym(addr, &symoff, &symsize); 16030Sstevel@tonic-gate 16040Sstevel@tonic-gate if (show_size && symoff == 0) 16050Sstevel@tonic-gate (void) sprintf(buf, "%s[%ld]", symname, (long)symsize); 16060Sstevel@tonic-gate else if (symoff == 0) 16070Sstevel@tonic-gate (void) sprintf(buf, "%s", symname); 16080Sstevel@tonic-gate else if (symoff < 16 && bcmp(symname, "cpu[", 4) == 0) /* CPU+PIL */ 16090Sstevel@tonic-gate (void) sprintf(buf, "%s+%ld", symname, (long)symoff); 16100Sstevel@tonic-gate else if (symoff <= symsize || (symoff < 256 && addr != symoff)) 16110Sstevel@tonic-gate (void) sprintf(buf, "%s+0x%llx", symname, 16120Sstevel@tonic-gate (unsigned long long)symoff); 16130Sstevel@tonic-gate else 16140Sstevel@tonic-gate (void) sprintf(buf, "0x%llx", (unsigned long long)addr); 16150Sstevel@tonic-gate return (buf); 16160Sstevel@tonic-gate } 16170Sstevel@tonic-gate 16180Sstevel@tonic-gate static void 16190Sstevel@tonic-gate report_stats(FILE *out, lsrec_t **sort_buf, size_t nrecs, uint64_t total_count, 16200Sstevel@tonic-gate uint64_t total_time) 16210Sstevel@tonic-gate { 16220Sstevel@tonic-gate uint32_t event = sort_buf[0]->ls_event; 16230Sstevel@tonic-gate lsrec_t *lsp; 16240Sstevel@tonic-gate double ptotal = 0.0; 16250Sstevel@tonic-gate double percent; 16260Sstevel@tonic-gate int i, j, fr; 16270Sstevel@tonic-gate int displayed; 16280Sstevel@tonic-gate int first_bin, last_bin, max_bin_count, total_bin_count; 16290Sstevel@tonic-gate int rectype; 16300Sstevel@tonic-gate char buf[256]; 16310Sstevel@tonic-gate char lhdr[80], chdr[80]; 16320Sstevel@tonic-gate 16330Sstevel@tonic-gate rectype = g_recsize; 16340Sstevel@tonic-gate 16350Sstevel@tonic-gate if (g_topn == 0) { 16360Sstevel@tonic-gate (void) fprintf(out, "%20llu %s\n", 16370Sstevel@tonic-gate g_rates == 0 ? total_count : 16380Sstevel@tonic-gate ((unsigned long long)total_count * NANOSEC) / g_elapsed, 16390Sstevel@tonic-gate g_event_info[event].ev_desc); 16400Sstevel@tonic-gate return; 16410Sstevel@tonic-gate } 16420Sstevel@tonic-gate 16430Sstevel@tonic-gate (void) sprintf(lhdr, "%s%s", 16440Sstevel@tonic-gate g_Wflag ? "Hottest " : "", g_event_info[event].ev_lhdr); 16450Sstevel@tonic-gate (void) sprintf(chdr, "%s%s", 16460Sstevel@tonic-gate g_wflag ? "Hottest " : "", "Caller"); 16470Sstevel@tonic-gate 16480Sstevel@tonic-gate if (!g_pflag) 16490Sstevel@tonic-gate (void) fprintf(out, 16500Sstevel@tonic-gate "\n%s: %.0f events in %.3f seconds (%.0f events/sec)\n\n", 16510Sstevel@tonic-gate g_event_info[event].ev_desc, (double)total_count, 16520Sstevel@tonic-gate (double)g_elapsed / NANOSEC, 16530Sstevel@tonic-gate (double)total_count * NANOSEC / g_elapsed); 16540Sstevel@tonic-gate 16550Sstevel@tonic-gate if (!g_pflag && rectype < LS_HIST) { 16560Sstevel@tonic-gate (void) sprintf(buf, "%s", g_event_info[event].ev_units); 16570Sstevel@tonic-gate (void) fprintf(out, "%5s %4s %4s %4s %8s %-22s %-24s\n", 16580Sstevel@tonic-gate g_rates ? "ops/s" : "Count", 16590Sstevel@tonic-gate g_gflag ? "genr" : "indv", 16600Sstevel@tonic-gate "cuml", "rcnt", rectype >= LS_TIME ? buf : "", lhdr, chdr); 16610Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 16620Sstevel@tonic-gate "----------------------------------------------\n"); 16630Sstevel@tonic-gate } 16640Sstevel@tonic-gate 16650Sstevel@tonic-gate displayed = 0; 16660Sstevel@tonic-gate for (i = 0; i < nrecs; i++) { 16670Sstevel@tonic-gate lsp = sort_buf[i]; 16680Sstevel@tonic-gate 16690Sstevel@tonic-gate if (displayed++ >= g_topn) 16700Sstevel@tonic-gate break; 16710Sstevel@tonic-gate 16720Sstevel@tonic-gate if (g_pflag) { 16730Sstevel@tonic-gate int j; 16740Sstevel@tonic-gate 16750Sstevel@tonic-gate (void) fprintf(out, "%u %u", 16760Sstevel@tonic-gate lsp->ls_event, lsp->ls_count); 16770Sstevel@tonic-gate (void) fprintf(out, " %s", 16780Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, g_cflag)); 16790Sstevel@tonic-gate (void) fprintf(out, " %s", 16800Sstevel@tonic-gate format_symbol(buf, lsp->ls_caller, 0)); 16810Sstevel@tonic-gate (void) fprintf(out, " %f", 16820Sstevel@tonic-gate (double)lsp->ls_refcnt / lsp->ls_count); 16830Sstevel@tonic-gate if (rectype >= LS_TIME) 16840Sstevel@tonic-gate (void) fprintf(out, " %llu", 16850Sstevel@tonic-gate (unsigned long long)lsp->ls_time); 16860Sstevel@tonic-gate if (rectype >= LS_HIST) { 16870Sstevel@tonic-gate for (j = 0; j < 64; j++) 16880Sstevel@tonic-gate (void) fprintf(out, " %u", 16890Sstevel@tonic-gate lsp->ls_hist[j]); 16900Sstevel@tonic-gate } 16910Sstevel@tonic-gate for (j = 0; j < LS_MAX_STACK_DEPTH; j++) { 16920Sstevel@tonic-gate if (rectype <= LS_STACK(j) || 16930Sstevel@tonic-gate lsp->ls_stack[j] == 0) 16940Sstevel@tonic-gate break; 16950Sstevel@tonic-gate (void) fprintf(out, " %s", 16960Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[j], 0)); 16970Sstevel@tonic-gate } 16980Sstevel@tonic-gate (void) fprintf(out, "\n"); 16990Sstevel@tonic-gate continue; 17000Sstevel@tonic-gate } 17010Sstevel@tonic-gate 17020Sstevel@tonic-gate if (rectype >= LS_HIST) { 17030Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 17040Sstevel@tonic-gate "----------------------------------------------\n"); 17050Sstevel@tonic-gate (void) sprintf(buf, "%s", 17060Sstevel@tonic-gate g_event_info[event].ev_units); 17070Sstevel@tonic-gate (void) fprintf(out, "%5s %4s %4s %4s %8s %-22s %-24s\n", 17080Sstevel@tonic-gate g_rates ? "ops/s" : "Count", 17090Sstevel@tonic-gate g_gflag ? "genr" : "indv", 17100Sstevel@tonic-gate "cuml", "rcnt", buf, lhdr, chdr); 17110Sstevel@tonic-gate } 17120Sstevel@tonic-gate 17130Sstevel@tonic-gate if (g_Pflag && total_time != 0) 17140Sstevel@tonic-gate percent = (lsp->ls_time * 100.00) / total_time; 17150Sstevel@tonic-gate else 17160Sstevel@tonic-gate percent = (lsp->ls_count * 100.00) / total_count; 17170Sstevel@tonic-gate 17180Sstevel@tonic-gate ptotal += percent; 17190Sstevel@tonic-gate 17200Sstevel@tonic-gate if (rectype >= LS_TIME) 17210Sstevel@tonic-gate (void) sprintf(buf, "%llu", 17220Sstevel@tonic-gate (unsigned long long)(lsp->ls_time / lsp->ls_count)); 17230Sstevel@tonic-gate else 17240Sstevel@tonic-gate buf[0] = '\0'; 17250Sstevel@tonic-gate 17260Sstevel@tonic-gate (void) fprintf(out, "%5llu ", 17270Sstevel@tonic-gate g_rates == 0 ? lsp->ls_count : 17280Sstevel@tonic-gate ((uint64_t)lsp->ls_count * NANOSEC) / g_elapsed); 17290Sstevel@tonic-gate 17300Sstevel@tonic-gate (void) fprintf(out, "%3.0f%% ", percent); 17310Sstevel@tonic-gate 17320Sstevel@tonic-gate if (g_gflag) 17330Sstevel@tonic-gate (void) fprintf(out, "---- "); 17340Sstevel@tonic-gate else 17350Sstevel@tonic-gate (void) fprintf(out, "%3.0f%% ", ptotal); 17360Sstevel@tonic-gate 17370Sstevel@tonic-gate (void) fprintf(out, "%4.2f %8s ", 17380Sstevel@tonic-gate (double)lsp->ls_refcnt / lsp->ls_count, buf); 17390Sstevel@tonic-gate 17400Sstevel@tonic-gate (void) fprintf(out, "%-22s ", 17410Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, g_cflag)); 17420Sstevel@tonic-gate 17430Sstevel@tonic-gate (void) fprintf(out, "%-24s\n", 17440Sstevel@tonic-gate format_symbol(buf, lsp->ls_caller, 0)); 17450Sstevel@tonic-gate 17460Sstevel@tonic-gate if (rectype < LS_HIST) 17470Sstevel@tonic-gate continue; 17480Sstevel@tonic-gate 17490Sstevel@tonic-gate (void) fprintf(out, "\n"); 17500Sstevel@tonic-gate (void) fprintf(out, "%10s %31s %-9s %-24s\n", 17510Sstevel@tonic-gate g_event_info[event].ev_units, 17520Sstevel@tonic-gate "------ Time Distribution ------", 17530Sstevel@tonic-gate g_rates ? "ops/s" : "count", 17540Sstevel@tonic-gate rectype > LS_STACK(0) ? "Stack" : ""); 17550Sstevel@tonic-gate 17560Sstevel@tonic-gate first_bin = 0; 17570Sstevel@tonic-gate while (lsp->ls_hist[first_bin] == 0) 17580Sstevel@tonic-gate first_bin++; 17590Sstevel@tonic-gate 17600Sstevel@tonic-gate last_bin = 63; 17610Sstevel@tonic-gate while (lsp->ls_hist[last_bin] == 0) 17620Sstevel@tonic-gate last_bin--; 17630Sstevel@tonic-gate 17640Sstevel@tonic-gate max_bin_count = 0; 17650Sstevel@tonic-gate total_bin_count = 0; 17660Sstevel@tonic-gate for (j = first_bin; j <= last_bin; j++) { 17670Sstevel@tonic-gate total_bin_count += lsp->ls_hist[j]; 17680Sstevel@tonic-gate if (lsp->ls_hist[j] > max_bin_count) 17690Sstevel@tonic-gate max_bin_count = lsp->ls_hist[j]; 17700Sstevel@tonic-gate } 17710Sstevel@tonic-gate 17720Sstevel@tonic-gate /* 17730Sstevel@tonic-gate * If we went a few frames below the caller, ignore them 17740Sstevel@tonic-gate */ 17750Sstevel@tonic-gate for (fr = 3; fr > 0; fr--) 17760Sstevel@tonic-gate if (lsp->ls_stack[fr] == lsp->ls_caller) 17770Sstevel@tonic-gate break; 17780Sstevel@tonic-gate 17790Sstevel@tonic-gate for (j = first_bin; j <= last_bin; j++) { 17800Sstevel@tonic-gate uint_t depth = (lsp->ls_hist[j] * 30) / total_bin_count; 17810Sstevel@tonic-gate (void) fprintf(out, "%10llu |%s%s %-9u ", 17820Sstevel@tonic-gate 1ULL << j, 17830Sstevel@tonic-gate "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + 30 - depth, 17840Sstevel@tonic-gate " " + depth, 17850Sstevel@tonic-gate g_rates == 0 ? lsp->ls_hist[j] : 17860Sstevel@tonic-gate (uint_t)(((uint64_t)lsp->ls_hist[j] * NANOSEC) / 17870Sstevel@tonic-gate g_elapsed)); 17880Sstevel@tonic-gate if (rectype <= LS_STACK(fr) || lsp->ls_stack[fr] == 0) { 17890Sstevel@tonic-gate (void) fprintf(out, "\n"); 17900Sstevel@tonic-gate continue; 17910Sstevel@tonic-gate } 17920Sstevel@tonic-gate (void) fprintf(out, "%-24s\n", 17930Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 17940Sstevel@tonic-gate fr++; 17950Sstevel@tonic-gate } 17960Sstevel@tonic-gate while (rectype > LS_STACK(fr) && lsp->ls_stack[fr] != 0) { 17970Sstevel@tonic-gate (void) fprintf(out, "%15s %-36s %-24s\n", "", "", 17980Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 17990Sstevel@tonic-gate fr++; 18000Sstevel@tonic-gate } 18010Sstevel@tonic-gate } 18020Sstevel@tonic-gate 18030Sstevel@tonic-gate if (!g_pflag) 18040Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 18050Sstevel@tonic-gate "----------------------------------------------\n"); 18060Sstevel@tonic-gate 18070Sstevel@tonic-gate (void) fflush(out); 18080Sstevel@tonic-gate } 18090Sstevel@tonic-gate 18100Sstevel@tonic-gate static void 18110Sstevel@tonic-gate report_trace(FILE *out, lsrec_t **sort_buf) 18120Sstevel@tonic-gate { 18130Sstevel@tonic-gate lsrec_t *lsp; 18140Sstevel@tonic-gate int i, fr; 18150Sstevel@tonic-gate int rectype; 18160Sstevel@tonic-gate char buf[256], buf2[256]; 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate rectype = g_recsize; 18190Sstevel@tonic-gate 18200Sstevel@tonic-gate if (!g_pflag) { 18210Sstevel@tonic-gate (void) fprintf(out, "%5s %7s %11s %-24s %-24s\n", 18220Sstevel@tonic-gate "Event", "Time", "Owner", "Lock", "Caller"); 18230Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 18240Sstevel@tonic-gate "----------------------------------------------\n"); 18250Sstevel@tonic-gate } 18260Sstevel@tonic-gate 18270Sstevel@tonic-gate for (i = 0; i < g_nrecs_used; i++) { 18280Sstevel@tonic-gate 18290Sstevel@tonic-gate lsp = sort_buf[i]; 18300Sstevel@tonic-gate 18310Sstevel@tonic-gate if (lsp->ls_event >= LS_MAX_EVENTS || lsp->ls_count == 0) 18320Sstevel@tonic-gate continue; 18330Sstevel@tonic-gate 18340Sstevel@tonic-gate (void) fprintf(out, "%2d %10llu %11p %-24s %-24s\n", 18350Sstevel@tonic-gate lsp->ls_event, (unsigned long long)lsp->ls_time, 18360Sstevel@tonic-gate (void *)lsp->ls_next, 18370Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, 0), 18380Sstevel@tonic-gate format_symbol(buf2, lsp->ls_caller, 0)); 18390Sstevel@tonic-gate 18400Sstevel@tonic-gate if (rectype <= LS_STACK(0)) 18410Sstevel@tonic-gate continue; 18420Sstevel@tonic-gate 18430Sstevel@tonic-gate /* 18440Sstevel@tonic-gate * If we went a few frames below the caller, ignore them 18450Sstevel@tonic-gate */ 18460Sstevel@tonic-gate for (fr = 3; fr > 0; fr--) 18470Sstevel@tonic-gate if (lsp->ls_stack[fr] == lsp->ls_caller) 18480Sstevel@tonic-gate break; 18490Sstevel@tonic-gate 18500Sstevel@tonic-gate while (rectype > LS_STACK(fr) && lsp->ls_stack[fr] != 0) { 18510Sstevel@tonic-gate (void) fprintf(out, "%53s %-24s\n", "", 18520Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 18530Sstevel@tonic-gate fr++; 18540Sstevel@tonic-gate } 18550Sstevel@tonic-gate (void) fprintf(out, "\n"); 18560Sstevel@tonic-gate } 18570Sstevel@tonic-gate 18580Sstevel@tonic-gate (void) fflush(out); 18590Sstevel@tonic-gate } 1860