17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*9d68b18eSck142721 * Common Development and Distribution License (the "License"). 6*9d68b18eSck142721 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*9d68b18eSck142721 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #include <stdio.h> 297c478bd9Sstevel@tonic-gate #include <stddef.h> 307c478bd9Sstevel@tonic-gate #include <stdlib.h> 317c478bd9Sstevel@tonic-gate #include <stdarg.h> 327c478bd9Sstevel@tonic-gate #include <string.h> 337c478bd9Sstevel@tonic-gate #include <strings.h> 347c478bd9Sstevel@tonic-gate #include <ctype.h> 357c478bd9Sstevel@tonic-gate #include <fcntl.h> 367c478bd9Sstevel@tonic-gate #include <unistd.h> 377c478bd9Sstevel@tonic-gate #include <errno.h> 387c478bd9Sstevel@tonic-gate #include <limits.h> 397c478bd9Sstevel@tonic-gate #include <sys/types.h> 407c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 417c478bd9Sstevel@tonic-gate #include <sys/stat.h> 427c478bd9Sstevel@tonic-gate #include <sys/wait.h> 437c478bd9Sstevel@tonic-gate #include <dtrace.h> 447c478bd9Sstevel@tonic-gate #include <sys/lockstat.h> 457c478bd9Sstevel@tonic-gate #include <alloca.h> 467c478bd9Sstevel@tonic-gate #include <signal.h> 477c478bd9Sstevel@tonic-gate #include <assert.h> 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate #define LOCKSTAT_OPTSTR "x:bths:n:d:i:l:f:e:ckwWgCHEATID:RpPo:V" 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate #define LS_MAX_STACK_DEPTH 50 527c478bd9Sstevel@tonic-gate #define LS_MAX_EVENTS 64 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate typedef struct lsrec { 557c478bd9Sstevel@tonic-gate struct lsrec *ls_next; /* next in hash chain */ 567c478bd9Sstevel@tonic-gate uintptr_t ls_lock; /* lock address */ 577c478bd9Sstevel@tonic-gate uintptr_t ls_caller; /* caller address */ 587c478bd9Sstevel@tonic-gate uint32_t ls_count; /* cumulative event count */ 597c478bd9Sstevel@tonic-gate uint32_t ls_event; /* type of event */ 607c478bd9Sstevel@tonic-gate uintptr_t ls_refcnt; /* cumulative reference count */ 617c478bd9Sstevel@tonic-gate uint64_t ls_time; /* cumulative event duration */ 627c478bd9Sstevel@tonic-gate uint32_t ls_hist[64]; /* log2(duration) histogram */ 637c478bd9Sstevel@tonic-gate uintptr_t ls_stack[LS_MAX_STACK_DEPTH]; 647c478bd9Sstevel@tonic-gate } lsrec_t; 657c478bd9Sstevel@tonic-gate 667c478bd9Sstevel@tonic-gate typedef struct lsdata { 677c478bd9Sstevel@tonic-gate struct lsrec *lsd_next; /* next available */ 687c478bd9Sstevel@tonic-gate int lsd_count; /* number of records */ 697c478bd9Sstevel@tonic-gate } lsdata_t; 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* 727c478bd9Sstevel@tonic-gate * Definitions for the types of experiments which can be run. They are 737c478bd9Sstevel@tonic-gate * listed in increasing order of memory cost and processing time cost. 747c478bd9Sstevel@tonic-gate * The numerical value of each type is the number of bytes needed per record. 757c478bd9Sstevel@tonic-gate */ 767c478bd9Sstevel@tonic-gate #define LS_BASIC offsetof(lsrec_t, ls_time) 777c478bd9Sstevel@tonic-gate #define LS_TIME offsetof(lsrec_t, ls_hist[0]) 787c478bd9Sstevel@tonic-gate #define LS_HIST offsetof(lsrec_t, ls_stack[0]) 797c478bd9Sstevel@tonic-gate #define LS_STACK(depth) offsetof(lsrec_t, ls_stack[depth]) 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate static void report_stats(FILE *, lsrec_t **, size_t, uint64_t, uint64_t); 827c478bd9Sstevel@tonic-gate static void report_trace(FILE *, lsrec_t **); 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate extern int symtab_init(void); 857c478bd9Sstevel@tonic-gate extern char *addr_to_sym(uintptr_t, uintptr_t *, size_t *); 867c478bd9Sstevel@tonic-gate extern uintptr_t sym_to_addr(char *name); 877c478bd9Sstevel@tonic-gate extern size_t sym_size(char *name); 887c478bd9Sstevel@tonic-gate extern char *strtok_r(char *, const char *, char **); 897c478bd9Sstevel@tonic-gate 907c478bd9Sstevel@tonic-gate #define DEFAULT_NRECS 10000 917c478bd9Sstevel@tonic-gate #define DEFAULT_HZ 97 927c478bd9Sstevel@tonic-gate #define MAX_HZ 1000 937c478bd9Sstevel@tonic-gate #define MIN_AGGSIZE (16 * 1024) 947c478bd9Sstevel@tonic-gate #define MAX_AGGSIZE (32 * 1024 * 1024) 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate static int g_stkdepth; 977c478bd9Sstevel@tonic-gate static int g_topn = INT_MAX; 987c478bd9Sstevel@tonic-gate static hrtime_t g_elapsed; 997c478bd9Sstevel@tonic-gate static int g_rates = 0; 1007c478bd9Sstevel@tonic-gate static int g_pflag = 0; 1017c478bd9Sstevel@tonic-gate static int g_Pflag = 0; 1027c478bd9Sstevel@tonic-gate static int g_wflag = 0; 1037c478bd9Sstevel@tonic-gate static int g_Wflag = 0; 1047c478bd9Sstevel@tonic-gate static int g_cflag = 0; 1057c478bd9Sstevel@tonic-gate static int g_kflag = 0; 1067c478bd9Sstevel@tonic-gate static int g_gflag = 0; 1077c478bd9Sstevel@tonic-gate static int g_Vflag = 0; 1087c478bd9Sstevel@tonic-gate static int g_tracing = 0; 1097c478bd9Sstevel@tonic-gate static size_t g_recsize; 1107c478bd9Sstevel@tonic-gate static size_t g_nrecs; 1117c478bd9Sstevel@tonic-gate static int g_nrecs_used; 1127c478bd9Sstevel@tonic-gate static uchar_t g_enabled[LS_MAX_EVENTS]; 1137c478bd9Sstevel@tonic-gate static hrtime_t g_min_duration[LS_MAX_EVENTS]; 1147c478bd9Sstevel@tonic-gate static dtrace_hdl_t *g_dtp; 1157c478bd9Sstevel@tonic-gate static char *g_predicate; 1167c478bd9Sstevel@tonic-gate static char *g_ipredicate; 1177c478bd9Sstevel@tonic-gate static char *g_prog; 1187c478bd9Sstevel@tonic-gate static int g_proglen; 1197c478bd9Sstevel@tonic-gate static int g_dropped; 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate typedef struct ls_event_info { 1227c478bd9Sstevel@tonic-gate char ev_type; 1237c478bd9Sstevel@tonic-gate char ev_lhdr[20]; 1247c478bd9Sstevel@tonic-gate char ev_desc[80]; 1257c478bd9Sstevel@tonic-gate char ev_units[10]; 1267c478bd9Sstevel@tonic-gate char ev_name[DTRACE_NAMELEN]; 1277c478bd9Sstevel@tonic-gate char *ev_predicate; 1287c478bd9Sstevel@tonic-gate char *ev_acquire; 1297c478bd9Sstevel@tonic-gate } ls_event_info_t; 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate static ls_event_info_t g_event_info[LS_MAX_EVENTS] = { 132*9d68b18eSck142721 { 'C', "Lock", "Adaptive mutex spin", "nsec", 1337c478bd9Sstevel@tonic-gate "lockstat:::adaptive-spin" }, 1347c478bd9Sstevel@tonic-gate { 'C', "Lock", "Adaptive mutex block", "nsec", 1357c478bd9Sstevel@tonic-gate "lockstat:::adaptive-block" }, 136*9d68b18eSck142721 { 'C', "Lock", "Spin lock spin", "nsec", 1377c478bd9Sstevel@tonic-gate "lockstat:::spin-spin" }, 138*9d68b18eSck142721 { 'C', "Lock", "Thread lock spin", "nsec", 1397c478bd9Sstevel@tonic-gate "lockstat:::thread-spin" }, 1407c478bd9Sstevel@tonic-gate { 'C', "Lock", "R/W writer blocked by writer", "nsec", 1417c478bd9Sstevel@tonic-gate "lockstat:::rw-block", "arg2 == 0 && arg3 == 1" }, 1427c478bd9Sstevel@tonic-gate { 'C', "Lock", "R/W writer blocked by readers", "nsec", 1437c478bd9Sstevel@tonic-gate "lockstat:::rw-block", "arg2 == 0 && arg3 == 0 && arg4" }, 1447c478bd9Sstevel@tonic-gate { 'C', "Lock", "R/W reader blocked by writer", "nsec", 1457c478bd9Sstevel@tonic-gate "lockstat:::rw-block", "arg2 != 0 && arg3 == 1" }, 1467c478bd9Sstevel@tonic-gate { 'C', "Lock", "R/W reader blocked by write wanted", "nsec", 1477c478bd9Sstevel@tonic-gate "lockstat:::rw-block", "arg2 != 0 && arg3 == 0 && arg4" }, 1487c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 8)", "units" }, 1497c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 9)", "units" }, 1507c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 10)", "units" }, 1517c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 11)", "units" }, 1527c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 12)", "units" }, 1537c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 13)", "units" }, 1547c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 14)", "units" }, 1557c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 15)", "units" }, 1567c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 16)", "units" }, 1577c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 17)", "units" }, 1587c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 18)", "units" }, 1597c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 19)", "units" }, 1607c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 20)", "units" }, 1617c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 21)", "units" }, 1627c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 22)", "units" }, 1637c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 23)", "units" }, 1647c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 24)", "units" }, 1657c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 25)", "units" }, 1667c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 26)", "units" }, 1677c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 27)", "units" }, 1687c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 28)", "units" }, 1697c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 29)", "units" }, 1707c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 30)", "units" }, 1717c478bd9Sstevel@tonic-gate { 'C', "Lock", "Unknown event (type 31)", "units" }, 1727c478bd9Sstevel@tonic-gate { 'H', "Lock", "Adaptive mutex hold", "nsec", 1737c478bd9Sstevel@tonic-gate "lockstat:::adaptive-release", NULL, 1747c478bd9Sstevel@tonic-gate "lockstat:::adaptive-acquire" }, 1757c478bd9Sstevel@tonic-gate { 'H', "Lock", "Spin lock hold", "nsec", 1767c478bd9Sstevel@tonic-gate "lockstat:::spin-release", NULL, 1777c478bd9Sstevel@tonic-gate "lockstat:::spin-acquire" }, 1787c478bd9Sstevel@tonic-gate { 'H', "Lock", "R/W writer hold", "nsec", 1797c478bd9Sstevel@tonic-gate "lockstat:::rw-release", "arg1 == 0", 1807c478bd9Sstevel@tonic-gate "lockstat:::rw-acquire" }, 1817c478bd9Sstevel@tonic-gate { 'H', "Lock", "R/W reader hold", "nsec", 1827c478bd9Sstevel@tonic-gate "lockstat:::rw-release", "arg1 != 0", 1837c478bd9Sstevel@tonic-gate "lockstat:::rw-acquire" }, 1847c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 36)", "units" }, 1857c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 37)", "units" }, 1867c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 38)", "units" }, 1877c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 39)", "units" }, 1887c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 40)", "units" }, 1897c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 41)", "units" }, 1907c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 42)", "units" }, 1917c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 43)", "units" }, 1927c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 44)", "units" }, 1937c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 45)", "units" }, 1947c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 46)", "units" }, 1957c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 47)", "units" }, 1967c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 48)", "units" }, 1977c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 49)", "units" }, 1987c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 50)", "units" }, 1997c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 51)", "units" }, 2007c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 52)", "units" }, 2017c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 53)", "units" }, 2027c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 54)", "units" }, 2037c478bd9Sstevel@tonic-gate { 'H', "Lock", "Unknown event (type 55)", "units" }, 2047c478bd9Sstevel@tonic-gate { 'I', "CPU+PIL", "Profiling interrupt", "nsec", 2057c478bd9Sstevel@tonic-gate "profile:::profile-97", NULL }, 2067c478bd9Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 57)", "units" }, 2077c478bd9Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 58)", "units" }, 2087c478bd9Sstevel@tonic-gate { 'I', "Lock", "Unknown event (type 59)", "units" }, 2097c478bd9Sstevel@tonic-gate { 'E', "Lock", "Recursive lock entry detected", "(N/A)", 2107c478bd9Sstevel@tonic-gate "lockstat:::rw-release", NULL, "lockstat:::rw-acquire" }, 2117c478bd9Sstevel@tonic-gate { 'E', "Lock", "Lockstat enter failure", "(N/A)" }, 2127c478bd9Sstevel@tonic-gate { 'E', "Lock", "Lockstat exit failure", "nsec" }, 2137c478bd9Sstevel@tonic-gate { 'E', "Lock", "Lockstat record failure", "(N/A)" }, 2147c478bd9Sstevel@tonic-gate }; 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate static void 2177c478bd9Sstevel@tonic-gate fail(int do_perror, const char *message, ...) 2187c478bd9Sstevel@tonic-gate { 2197c478bd9Sstevel@tonic-gate va_list args; 2207c478bd9Sstevel@tonic-gate int save_errno = errno; 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate va_start(args, message); 2237c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: "); 2247c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, message, args); 2257c478bd9Sstevel@tonic-gate va_end(args); 2267c478bd9Sstevel@tonic-gate if (do_perror) 2277c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s", strerror(save_errno)); 2287c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 2297c478bd9Sstevel@tonic-gate exit(2); 2307c478bd9Sstevel@tonic-gate } 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate static void 2337c478bd9Sstevel@tonic-gate dfail(const char *message, ...) 2347c478bd9Sstevel@tonic-gate { 2357c478bd9Sstevel@tonic-gate va_list args; 2367c478bd9Sstevel@tonic-gate 2377c478bd9Sstevel@tonic-gate va_start(args, message); 2387c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: "); 2397c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, message, args); 2407c478bd9Sstevel@tonic-gate va_end(args); 2417c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", 2427c478bd9Sstevel@tonic-gate dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate exit(2); 2457c478bd9Sstevel@tonic-gate } 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate static void 2487c478bd9Sstevel@tonic-gate show_events(char event_type, char *desc) 2497c478bd9Sstevel@tonic-gate { 2507c478bd9Sstevel@tonic-gate int i, first = -1, last; 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 2537c478bd9Sstevel@tonic-gate ls_event_info_t *evp = &g_event_info[i]; 2547c478bd9Sstevel@tonic-gate if (evp->ev_type != event_type || 2557c478bd9Sstevel@tonic-gate strncmp(evp->ev_desc, "Unknown event", 13) == 0) 2567c478bd9Sstevel@tonic-gate continue; 2577c478bd9Sstevel@tonic-gate if (first == -1) 2587c478bd9Sstevel@tonic-gate first = i; 2597c478bd9Sstevel@tonic-gate last = i; 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2637c478bd9Sstevel@tonic-gate "\n%s events (lockstat -%c or lockstat -e %d-%d):\n\n", 2647c478bd9Sstevel@tonic-gate desc, event_type, first, last); 2657c478bd9Sstevel@tonic-gate 2667c478bd9Sstevel@tonic-gate for (i = first; i <= last; i++) 2677c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2687c478bd9Sstevel@tonic-gate "%4d = %s\n", i, g_event_info[i].ev_desc); 2697c478bd9Sstevel@tonic-gate } 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate static void 2727c478bd9Sstevel@tonic-gate usage(void) 2737c478bd9Sstevel@tonic-gate { 2747c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 2757c478bd9Sstevel@tonic-gate "Usage: lockstat [options] command [args]\n" 2767c478bd9Sstevel@tonic-gate "\nEvent selection options:\n\n" 2777c478bd9Sstevel@tonic-gate " -C watch contention events [on by default]\n" 2787c478bd9Sstevel@tonic-gate " -E watch error events [off by default]\n" 2797c478bd9Sstevel@tonic-gate " -H watch hold events [off by default]\n" 2807c478bd9Sstevel@tonic-gate " -I watch interrupt events [off by default]\n" 2817c478bd9Sstevel@tonic-gate " -A watch all lock events [equivalent to -CH]\n" 2827c478bd9Sstevel@tonic-gate " -e event_list only watch the specified events (shown below);\n" 2837c478bd9Sstevel@tonic-gate " <event_list> is a comma-separated list of\n" 2847c478bd9Sstevel@tonic-gate " events or ranges of events, e.g. 1,4-7,35\n" 2857c478bd9Sstevel@tonic-gate " -i rate interrupt rate for -I [default: %d Hz]\n" 2867c478bd9Sstevel@tonic-gate "\nData gathering options:\n\n" 2877c478bd9Sstevel@tonic-gate " -b basic statistics (lock, caller, event count)\n" 2887c478bd9Sstevel@tonic-gate " -t timing for all events [default]\n" 2897c478bd9Sstevel@tonic-gate " -h histograms for event times\n" 2907c478bd9Sstevel@tonic-gate " -s depth stack traces <depth> deep\n" 2917c478bd9Sstevel@tonic-gate " -x opt[=val] enable or modify DTrace options\n" 2927c478bd9Sstevel@tonic-gate "\nData filtering options:\n\n" 2937c478bd9Sstevel@tonic-gate " -n nrecords maximum number of data records [default: %d]\n" 2947c478bd9Sstevel@tonic-gate " -l lock[,size] only watch <lock>, which can be specified as a\n" 2957c478bd9Sstevel@tonic-gate " symbolic name or hex address; <size> defaults\n" 2967c478bd9Sstevel@tonic-gate " to the ELF symbol size if available, 1 if not\n" 2977c478bd9Sstevel@tonic-gate " -f func[,size] only watch events generated by <func>\n" 2987c478bd9Sstevel@tonic-gate " -d duration only watch events longer than <duration>\n" 2997c478bd9Sstevel@tonic-gate " -T trace (rather than sample) events\n" 3007c478bd9Sstevel@tonic-gate "\nData reporting options:\n\n" 3017c478bd9Sstevel@tonic-gate " -c coalesce lock data for arrays like pse_mutex[]\n" 3027c478bd9Sstevel@tonic-gate " -k coalesce PCs within functions\n" 3037c478bd9Sstevel@tonic-gate " -g show total events generated by function\n" 3047c478bd9Sstevel@tonic-gate " -w wherever: don't distinguish events by caller\n" 3057c478bd9Sstevel@tonic-gate " -W whichever: don't distinguish events by lock\n" 3067c478bd9Sstevel@tonic-gate " -R display rates rather than counts\n" 3077c478bd9Sstevel@tonic-gate " -p parsable output format (awk(1)-friendly)\n" 3087c478bd9Sstevel@tonic-gate " -P sort lock data by (count * avg_time) product\n" 3097c478bd9Sstevel@tonic-gate " -D n only display top <n> events of each type\n" 3107c478bd9Sstevel@tonic-gate " -o filename send output to <filename>\n", 3117c478bd9Sstevel@tonic-gate DEFAULT_HZ, DEFAULT_NRECS); 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate show_events('C', "Contention"); 3147c478bd9Sstevel@tonic-gate show_events('H', "Hold-time"); 3157c478bd9Sstevel@tonic-gate show_events('I', "Interrupt"); 3167c478bd9Sstevel@tonic-gate show_events('E', "Error"); 3177c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate exit(1); 3207c478bd9Sstevel@tonic-gate } 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate static int 3237c478bd9Sstevel@tonic-gate lockcmp(lsrec_t *a, lsrec_t *b) 3247c478bd9Sstevel@tonic-gate { 3257c478bd9Sstevel@tonic-gate int i; 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3287c478bd9Sstevel@tonic-gate return (-1); 3297c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 3307c478bd9Sstevel@tonic-gate return (1); 3317c478bd9Sstevel@tonic-gate 3327c478bd9Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 3337c478bd9Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 3347c478bd9Sstevel@tonic-gate return (-1); 3357c478bd9Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 3367c478bd9Sstevel@tonic-gate return (1); 3377c478bd9Sstevel@tonic-gate } 3387c478bd9Sstevel@tonic-gate 3397c478bd9Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 3407c478bd9Sstevel@tonic-gate return (-1); 3417c478bd9Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 3427c478bd9Sstevel@tonic-gate return (1); 3437c478bd9Sstevel@tonic-gate 3447c478bd9Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 3457c478bd9Sstevel@tonic-gate return (-1); 3467c478bd9Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 3477c478bd9Sstevel@tonic-gate return (1); 3487c478bd9Sstevel@tonic-gate 3497c478bd9Sstevel@tonic-gate return (0); 3507c478bd9Sstevel@tonic-gate } 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate static int 3537c478bd9Sstevel@tonic-gate countcmp(lsrec_t *a, lsrec_t *b) 3547c478bd9Sstevel@tonic-gate { 3557c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3567c478bd9Sstevel@tonic-gate return (-1); 3577c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 3587c478bd9Sstevel@tonic-gate return (1); 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate return (b->ls_count - a->ls_count); 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate static int 3647c478bd9Sstevel@tonic-gate timecmp(lsrec_t *a, lsrec_t *b) 3657c478bd9Sstevel@tonic-gate { 3667c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3677c478bd9Sstevel@tonic-gate return (-1); 3687c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 3697c478bd9Sstevel@tonic-gate return (1); 3707c478bd9Sstevel@tonic-gate 3717c478bd9Sstevel@tonic-gate if (a->ls_time < b->ls_time) 3727c478bd9Sstevel@tonic-gate return (1); 3737c478bd9Sstevel@tonic-gate if (a->ls_time > b->ls_time) 3747c478bd9Sstevel@tonic-gate return (-1); 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate return (0); 3777c478bd9Sstevel@tonic-gate } 3787c478bd9Sstevel@tonic-gate 3797c478bd9Sstevel@tonic-gate static int 3807c478bd9Sstevel@tonic-gate lockcmp_anywhere(lsrec_t *a, lsrec_t *b) 3817c478bd9Sstevel@tonic-gate { 3827c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3837c478bd9Sstevel@tonic-gate return (-1); 3847c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 3857c478bd9Sstevel@tonic-gate return (1); 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 3887c478bd9Sstevel@tonic-gate return (-1); 3897c478bd9Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 3907c478bd9Sstevel@tonic-gate return (1); 3917c478bd9Sstevel@tonic-gate 3927c478bd9Sstevel@tonic-gate return (0); 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate static int 3967c478bd9Sstevel@tonic-gate lock_and_count_cmp_anywhere(lsrec_t *a, lsrec_t *b) 3977c478bd9Sstevel@tonic-gate { 3987c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 3997c478bd9Sstevel@tonic-gate return (-1); 4007c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 4017c478bd9Sstevel@tonic-gate return (1); 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate if (a->ls_lock < b->ls_lock) 4047c478bd9Sstevel@tonic-gate return (-1); 4057c478bd9Sstevel@tonic-gate if (a->ls_lock > b->ls_lock) 4067c478bd9Sstevel@tonic-gate return (1); 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate return (b->ls_count - a->ls_count); 4097c478bd9Sstevel@tonic-gate } 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate static int 4127c478bd9Sstevel@tonic-gate sitecmp_anylock(lsrec_t *a, lsrec_t *b) 4137c478bd9Sstevel@tonic-gate { 4147c478bd9Sstevel@tonic-gate int i; 4157c478bd9Sstevel@tonic-gate 4167c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 4177c478bd9Sstevel@tonic-gate return (-1); 4187c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 4197c478bd9Sstevel@tonic-gate return (1); 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 4227c478bd9Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 4237c478bd9Sstevel@tonic-gate return (-1); 4247c478bd9Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 4257c478bd9Sstevel@tonic-gate return (1); 4267c478bd9Sstevel@tonic-gate } 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 4297c478bd9Sstevel@tonic-gate return (-1); 4307c478bd9Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 4317c478bd9Sstevel@tonic-gate return (1); 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate return (0); 4347c478bd9Sstevel@tonic-gate } 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate static int 4377c478bd9Sstevel@tonic-gate site_and_count_cmp_anylock(lsrec_t *a, lsrec_t *b) 4387c478bd9Sstevel@tonic-gate { 4397c478bd9Sstevel@tonic-gate int i; 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate if (a->ls_event < b->ls_event) 4427c478bd9Sstevel@tonic-gate return (-1); 4437c478bd9Sstevel@tonic-gate if (a->ls_event > b->ls_event) 4447c478bd9Sstevel@tonic-gate return (1); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate for (i = g_stkdepth - 1; i >= 0; i--) { 4477c478bd9Sstevel@tonic-gate if (a->ls_stack[i] < b->ls_stack[i]) 4487c478bd9Sstevel@tonic-gate return (-1); 4497c478bd9Sstevel@tonic-gate if (a->ls_stack[i] > b->ls_stack[i]) 4507c478bd9Sstevel@tonic-gate return (1); 4517c478bd9Sstevel@tonic-gate } 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate if (a->ls_caller < b->ls_caller) 4547c478bd9Sstevel@tonic-gate return (-1); 4557c478bd9Sstevel@tonic-gate if (a->ls_caller > b->ls_caller) 4567c478bd9Sstevel@tonic-gate return (1); 4577c478bd9Sstevel@tonic-gate 4587c478bd9Sstevel@tonic-gate return (b->ls_count - a->ls_count); 4597c478bd9Sstevel@tonic-gate } 4607c478bd9Sstevel@tonic-gate 4617c478bd9Sstevel@tonic-gate static void 4627c478bd9Sstevel@tonic-gate mergesort(int (*cmp)(lsrec_t *, lsrec_t *), lsrec_t **a, lsrec_t **b, int n) 4637c478bd9Sstevel@tonic-gate { 4647c478bd9Sstevel@tonic-gate int m = n / 2; 4657c478bd9Sstevel@tonic-gate int i, j; 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate if (m > 1) 4687c478bd9Sstevel@tonic-gate mergesort(cmp, a, b, m); 4697c478bd9Sstevel@tonic-gate if (n - m > 1) 4707c478bd9Sstevel@tonic-gate mergesort(cmp, a + m, b + m, n - m); 4717c478bd9Sstevel@tonic-gate for (i = m; i > 0; i--) 4727c478bd9Sstevel@tonic-gate b[i - 1] = a[i - 1]; 4737c478bd9Sstevel@tonic-gate for (j = m - 1; j < n - 1; j++) 4747c478bd9Sstevel@tonic-gate b[n + m - j - 2] = a[j + 1]; 4757c478bd9Sstevel@tonic-gate while (i < j) 4767c478bd9Sstevel@tonic-gate *a++ = cmp(b[i], b[j]) < 0 ? b[i++] : b[j--]; 4777c478bd9Sstevel@tonic-gate *a = b[i]; 4787c478bd9Sstevel@tonic-gate } 4797c478bd9Sstevel@tonic-gate 4807c478bd9Sstevel@tonic-gate static void 4817c478bd9Sstevel@tonic-gate coalesce(int (*cmp)(lsrec_t *, lsrec_t *), lsrec_t **lock, int n) 4827c478bd9Sstevel@tonic-gate { 4837c478bd9Sstevel@tonic-gate int i, j; 4847c478bd9Sstevel@tonic-gate lsrec_t *target, *current; 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate target = lock[0]; 4877c478bd9Sstevel@tonic-gate 4887c478bd9Sstevel@tonic-gate for (i = 1; i < n; i++) { 4897c478bd9Sstevel@tonic-gate current = lock[i]; 4907c478bd9Sstevel@tonic-gate if (cmp(current, target) != 0) { 4917c478bd9Sstevel@tonic-gate target = current; 4927c478bd9Sstevel@tonic-gate continue; 4937c478bd9Sstevel@tonic-gate } 4947c478bd9Sstevel@tonic-gate current->ls_event = LS_MAX_EVENTS; 4957c478bd9Sstevel@tonic-gate target->ls_count += current->ls_count; 4967c478bd9Sstevel@tonic-gate target->ls_refcnt += current->ls_refcnt; 4977c478bd9Sstevel@tonic-gate if (g_recsize < LS_TIME) 4987c478bd9Sstevel@tonic-gate continue; 4997c478bd9Sstevel@tonic-gate target->ls_time += current->ls_time; 5007c478bd9Sstevel@tonic-gate if (g_recsize < LS_HIST) 5017c478bd9Sstevel@tonic-gate continue; 5027c478bd9Sstevel@tonic-gate for (j = 0; j < 64; j++) 5037c478bd9Sstevel@tonic-gate target->ls_hist[j] += current->ls_hist[j]; 5047c478bd9Sstevel@tonic-gate } 5057c478bd9Sstevel@tonic-gate } 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate static void 5087c478bd9Sstevel@tonic-gate coalesce_symbol(uintptr_t *addrp) 5097c478bd9Sstevel@tonic-gate { 5107c478bd9Sstevel@tonic-gate uintptr_t symoff; 5117c478bd9Sstevel@tonic-gate size_t symsize; 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate if (addr_to_sym(*addrp, &symoff, &symsize) != NULL && symoff < symsize) 5147c478bd9Sstevel@tonic-gate *addrp -= symoff; 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate static void 5187c478bd9Sstevel@tonic-gate predicate_add(char **pred, char *what, char *cmp, uintptr_t value) 5197c478bd9Sstevel@tonic-gate { 5207c478bd9Sstevel@tonic-gate char *new; 5217c478bd9Sstevel@tonic-gate int len, newlen; 5227c478bd9Sstevel@tonic-gate 5237c478bd9Sstevel@tonic-gate if (what == NULL) 5247c478bd9Sstevel@tonic-gate return; 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate if (*pred == NULL) { 5277c478bd9Sstevel@tonic-gate *pred = malloc(1); 5287c478bd9Sstevel@tonic-gate *pred[0] = '\0'; 5297c478bd9Sstevel@tonic-gate } 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate len = strlen(*pred); 5327c478bd9Sstevel@tonic-gate newlen = len + strlen(what) + 32 + strlen("( && )"); 5337c478bd9Sstevel@tonic-gate new = malloc(newlen); 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate if (*pred[0] != '\0') { 5367c478bd9Sstevel@tonic-gate if (cmp != NULL) { 5377c478bd9Sstevel@tonic-gate (void) sprintf(new, "(%s) && (%s %s 0x%p)", 5387c478bd9Sstevel@tonic-gate *pred, what, cmp, (void *)value); 5397c478bd9Sstevel@tonic-gate } else { 5407c478bd9Sstevel@tonic-gate (void) sprintf(new, "(%s) && (%s)", *pred, what); 5417c478bd9Sstevel@tonic-gate } 5427c478bd9Sstevel@tonic-gate } else { 5437c478bd9Sstevel@tonic-gate if (cmp != NULL) { 5447c478bd9Sstevel@tonic-gate (void) sprintf(new, "%s %s 0x%p", 5457c478bd9Sstevel@tonic-gate what, cmp, (void *)value); 5467c478bd9Sstevel@tonic-gate } else { 5477c478bd9Sstevel@tonic-gate (void) sprintf(new, "%s", what); 5487c478bd9Sstevel@tonic-gate } 5497c478bd9Sstevel@tonic-gate } 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate free(*pred); 5527c478bd9Sstevel@tonic-gate *pred = new; 5537c478bd9Sstevel@tonic-gate } 5547c478bd9Sstevel@tonic-gate 5557c478bd9Sstevel@tonic-gate static void 5567c478bd9Sstevel@tonic-gate predicate_destroy(char **pred) 5577c478bd9Sstevel@tonic-gate { 5587c478bd9Sstevel@tonic-gate free(*pred); 5597c478bd9Sstevel@tonic-gate *pred = NULL; 5607c478bd9Sstevel@tonic-gate } 5617c478bd9Sstevel@tonic-gate 5627c478bd9Sstevel@tonic-gate static void 5637c478bd9Sstevel@tonic-gate filter_add(char **filt, char *what, uintptr_t base, uintptr_t size) 5647c478bd9Sstevel@tonic-gate { 5657c478bd9Sstevel@tonic-gate char buf[256], *c = buf, *new; 5667c478bd9Sstevel@tonic-gate int len, newlen; 5677c478bd9Sstevel@tonic-gate 5687c478bd9Sstevel@tonic-gate if (*filt == NULL) { 5697c478bd9Sstevel@tonic-gate *filt = malloc(1); 5707c478bd9Sstevel@tonic-gate *filt[0] = '\0'; 5717c478bd9Sstevel@tonic-gate } 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate (void) sprintf(c, "%s(%s >= 0x%p && %s < 0x%p)", *filt[0] != '\0' ? 5747c478bd9Sstevel@tonic-gate " || " : "", what, (void *)base, what, (void *)(base + size)); 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate newlen = (len = strlen(*filt) + 1) + strlen(c); 5777c478bd9Sstevel@tonic-gate new = malloc(newlen); 5787c478bd9Sstevel@tonic-gate bcopy(*filt, new, len); 5797c478bd9Sstevel@tonic-gate (void) strcat(new, c); 5807c478bd9Sstevel@tonic-gate free(*filt); 5817c478bd9Sstevel@tonic-gate *filt = new; 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate static void 5857c478bd9Sstevel@tonic-gate filter_destroy(char **filt) 5867c478bd9Sstevel@tonic-gate { 5877c478bd9Sstevel@tonic-gate free(*filt); 5887c478bd9Sstevel@tonic-gate *filt = NULL; 5897c478bd9Sstevel@tonic-gate } 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate static void 5927c478bd9Sstevel@tonic-gate dprog_add(const char *fmt, ...) 5937c478bd9Sstevel@tonic-gate { 5947c478bd9Sstevel@tonic-gate va_list args; 5957c478bd9Sstevel@tonic-gate int size, offs; 5967c478bd9Sstevel@tonic-gate char c; 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate va_start(args, fmt); 5997c478bd9Sstevel@tonic-gate size = vsnprintf(&c, 1, fmt, args) + 1; 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate if (g_proglen == 0) { 6027c478bd9Sstevel@tonic-gate offs = 0; 6037c478bd9Sstevel@tonic-gate } else { 6047c478bd9Sstevel@tonic-gate offs = g_proglen - 1; 6057c478bd9Sstevel@tonic-gate } 6067c478bd9Sstevel@tonic-gate 6077c478bd9Sstevel@tonic-gate g_proglen = offs + size; 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate if ((g_prog = realloc(g_prog, g_proglen)) == NULL) 6107c478bd9Sstevel@tonic-gate fail(1, "failed to reallocate program text"); 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate (void) vsnprintf(&g_prog[offs], size, fmt, args); 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate /* 6167c478bd9Sstevel@tonic-gate * This function may read like an open sewer, but keep in mind that programs 6177c478bd9Sstevel@tonic-gate * that generate other programs are rarely pretty. If one has the unenviable 6187c478bd9Sstevel@tonic-gate * task of maintaining or -- worse -- extending this code, use the -V option 6197c478bd9Sstevel@tonic-gate * to examine the D program as generated by this function. 6207c478bd9Sstevel@tonic-gate */ 6217c478bd9Sstevel@tonic-gate static void 6227c478bd9Sstevel@tonic-gate dprog_addevent(int event) 6237c478bd9Sstevel@tonic-gate { 6247c478bd9Sstevel@tonic-gate ls_event_info_t *info = &g_event_info[event]; 6257c478bd9Sstevel@tonic-gate char *pred = NULL; 6267c478bd9Sstevel@tonic-gate char stack[20]; 6277c478bd9Sstevel@tonic-gate const char *arg0, *caller; 6287c478bd9Sstevel@tonic-gate char *arg1 = "arg1"; 6297c478bd9Sstevel@tonic-gate char buf[80]; 6307c478bd9Sstevel@tonic-gate hrtime_t dur; 6317c478bd9Sstevel@tonic-gate int depth; 6327c478bd9Sstevel@tonic-gate 6337c478bd9Sstevel@tonic-gate if (info->ev_name[0] == '\0') 6347c478bd9Sstevel@tonic-gate return; 6357c478bd9Sstevel@tonic-gate 6367c478bd9Sstevel@tonic-gate if (info->ev_type == 'I') { 6377c478bd9Sstevel@tonic-gate /* 6387c478bd9Sstevel@tonic-gate * For interrupt events, arg0 (normally the lock pointer) is 6397c478bd9Sstevel@tonic-gate * the CPU address plus the current pil, and arg1 (normally 6407c478bd9Sstevel@tonic-gate * the number of nanoseconds) is the number of nanoseconds 6417c478bd9Sstevel@tonic-gate * late -- and it's stored in arg2. 6427c478bd9Sstevel@tonic-gate */ 6437c478bd9Sstevel@tonic-gate arg0 = "(uintptr_t)curthread->t_cpu + \n" 6447c478bd9Sstevel@tonic-gate "\t curthread->t_cpu->cpu_profile_pil"; 6457c478bd9Sstevel@tonic-gate caller = "(uintptr_t)arg0"; 6467c478bd9Sstevel@tonic-gate arg1 = "arg2"; 6477c478bd9Sstevel@tonic-gate } else { 6487c478bd9Sstevel@tonic-gate arg0 = "(uintptr_t)arg0"; 6497c478bd9Sstevel@tonic-gate caller = "caller"; 6507c478bd9Sstevel@tonic-gate } 6517c478bd9Sstevel@tonic-gate 6527c478bd9Sstevel@tonic-gate if (g_recsize > LS_HIST) { 6537c478bd9Sstevel@tonic-gate for (depth = 0; g_recsize > LS_STACK(depth); depth++) 6547c478bd9Sstevel@tonic-gate continue; 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate if (g_tracing) { 6577c478bd9Sstevel@tonic-gate (void) sprintf(stack, "\tstack(%d);\n", depth); 6587c478bd9Sstevel@tonic-gate } else { 6597c478bd9Sstevel@tonic-gate (void) sprintf(stack, ", stack(%d)", depth); 6607c478bd9Sstevel@tonic-gate } 6617c478bd9Sstevel@tonic-gate } else { 6627c478bd9Sstevel@tonic-gate (void) sprintf(stack, ""); 6637c478bd9Sstevel@tonic-gate } 6647c478bd9Sstevel@tonic-gate 6657c478bd9Sstevel@tonic-gate if (info->ev_acquire != NULL) { 6667c478bd9Sstevel@tonic-gate /* 6677c478bd9Sstevel@tonic-gate * If this is a hold event, we need to generate an additional 6687c478bd9Sstevel@tonic-gate * clause for the acquire; the clause for the release will be 6697c478bd9Sstevel@tonic-gate * generated with the aggregating statement, below. 6707c478bd9Sstevel@tonic-gate */ 6717c478bd9Sstevel@tonic-gate dprog_add("%s\n", info->ev_acquire); 6727c478bd9Sstevel@tonic-gate predicate_add(&pred, info->ev_predicate, NULL, 0); 6737c478bd9Sstevel@tonic-gate predicate_add(&pred, g_predicate, NULL, 0); 6747c478bd9Sstevel@tonic-gate if (pred != NULL) 6757c478bd9Sstevel@tonic-gate dprog_add("/%s/\n", pred); 6767c478bd9Sstevel@tonic-gate 6777c478bd9Sstevel@tonic-gate dprog_add("{\n"); 6787c478bd9Sstevel@tonic-gate (void) sprintf(buf, "self->ev%d[(uintptr_t)arg0]", event); 6797c478bd9Sstevel@tonic-gate 6807c478bd9Sstevel@tonic-gate if (info->ev_type == 'H') { 6817c478bd9Sstevel@tonic-gate dprog_add("\t%s = timestamp;\n", buf); 6827c478bd9Sstevel@tonic-gate } else { 6837c478bd9Sstevel@tonic-gate /* 6847c478bd9Sstevel@tonic-gate * If this isn't a hold event, it's the recursive 6857c478bd9Sstevel@tonic-gate * error event. For this, we simply bump the 6867c478bd9Sstevel@tonic-gate * thread-local, per-lock count. 6877c478bd9Sstevel@tonic-gate */ 6887c478bd9Sstevel@tonic-gate dprog_add("\t%s++;\n", buf); 6897c478bd9Sstevel@tonic-gate } 6907c478bd9Sstevel@tonic-gate 6917c478bd9Sstevel@tonic-gate dprog_add("}\n\n"); 6927c478bd9Sstevel@tonic-gate predicate_destroy(&pred); 6937c478bd9Sstevel@tonic-gate pred = NULL; 6947c478bd9Sstevel@tonic-gate 6957c478bd9Sstevel@tonic-gate if (info->ev_type == 'E') { 6967c478bd9Sstevel@tonic-gate /* 6977c478bd9Sstevel@tonic-gate * If this is the recursive lock error event, we need 6987c478bd9Sstevel@tonic-gate * to generate an additional clause to decrement the 6997c478bd9Sstevel@tonic-gate * thread-local, per-lock count. This assures that we 7007c478bd9Sstevel@tonic-gate * only execute the aggregating clause if we have 7017c478bd9Sstevel@tonic-gate * recursive entry. 7027c478bd9Sstevel@tonic-gate */ 7037c478bd9Sstevel@tonic-gate dprog_add("%s\n", info->ev_name); 7047c478bd9Sstevel@tonic-gate dprog_add("/%s/\n{\n\t%s--;\n}\n\n", buf, buf); 7057c478bd9Sstevel@tonic-gate } 7067c478bd9Sstevel@tonic-gate 7077c478bd9Sstevel@tonic-gate predicate_add(&pred, buf, NULL, 0); 7087c478bd9Sstevel@tonic-gate 7097c478bd9Sstevel@tonic-gate if (info->ev_type == 'H') { 7107c478bd9Sstevel@tonic-gate (void) sprintf(buf, "timestamp -\n\t " 7117c478bd9Sstevel@tonic-gate "self->ev%d[(uintptr_t)arg0]", event); 7127c478bd9Sstevel@tonic-gate } 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate arg1 = buf; 7157c478bd9Sstevel@tonic-gate } else { 7167c478bd9Sstevel@tonic-gate predicate_add(&pred, info->ev_predicate, NULL, 0); 7177c478bd9Sstevel@tonic-gate if (info->ev_type != 'I') 7187c478bd9Sstevel@tonic-gate predicate_add(&pred, g_predicate, NULL, 0); 7197c478bd9Sstevel@tonic-gate else 7207c478bd9Sstevel@tonic-gate predicate_add(&pred, g_ipredicate, NULL, 0); 7217c478bd9Sstevel@tonic-gate } 7227c478bd9Sstevel@tonic-gate 7237c478bd9Sstevel@tonic-gate if ((dur = g_min_duration[event]) != 0) 7247c478bd9Sstevel@tonic-gate predicate_add(&pred, arg1, ">=", dur); 7257c478bd9Sstevel@tonic-gate 7267c478bd9Sstevel@tonic-gate dprog_add("%s\n", info->ev_name); 7277c478bd9Sstevel@tonic-gate 7287c478bd9Sstevel@tonic-gate if (pred != NULL) 7297c478bd9Sstevel@tonic-gate dprog_add("/%s/\n", pred); 7307c478bd9Sstevel@tonic-gate predicate_destroy(&pred); 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate dprog_add("{\n"); 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate if (g_tracing) { 7357c478bd9Sstevel@tonic-gate dprog_add("\ttrace(%dULL);\n", event); 7367c478bd9Sstevel@tonic-gate dprog_add("\ttrace(%s);\n", arg0); 7377c478bd9Sstevel@tonic-gate dprog_add("\ttrace(%s);\n", caller); 7387c478bd9Sstevel@tonic-gate dprog_add(stack); 7397c478bd9Sstevel@tonic-gate } else { 74002198c0bSbmc /* 74102198c0bSbmc * The ordering here is important: when we process the 74202198c0bSbmc * aggregate, we count on the fact that @avg appears before 74302198c0bSbmc * @hist in program order to assure that @avg is assigned the 74402198c0bSbmc * first aggregation variable ID and @hist assigned the 74502198c0bSbmc * second; see the comment in process_aggregate() for details. 74602198c0bSbmc */ 7477c478bd9Sstevel@tonic-gate dprog_add("\t@avg[%dULL, %s, %s%s] = avg(%s);\n", 7487c478bd9Sstevel@tonic-gate event, arg0, caller, stack, arg1); 7497c478bd9Sstevel@tonic-gate 7507c478bd9Sstevel@tonic-gate if (g_recsize >= LS_HIST) { 7517c478bd9Sstevel@tonic-gate dprog_add("\t@hist[%dULL, %s, %s%s] = quantize" 7527c478bd9Sstevel@tonic-gate "(%s);\n", event, arg0, caller, stack, arg1); 7537c478bd9Sstevel@tonic-gate } 7547c478bd9Sstevel@tonic-gate } 7557c478bd9Sstevel@tonic-gate 7567c478bd9Sstevel@tonic-gate if (info->ev_acquire != NULL) 7577c478bd9Sstevel@tonic-gate dprog_add("\tself->ev%d[arg0] = 0;\n", event); 7587c478bd9Sstevel@tonic-gate 7597c478bd9Sstevel@tonic-gate dprog_add("}\n\n"); 7607c478bd9Sstevel@tonic-gate } 7617c478bd9Sstevel@tonic-gate 7627c478bd9Sstevel@tonic-gate static void 7637c478bd9Sstevel@tonic-gate dprog_compile() 7647c478bd9Sstevel@tonic-gate { 7657c478bd9Sstevel@tonic-gate dtrace_prog_t *prog; 7667c478bd9Sstevel@tonic-gate dtrace_proginfo_t info; 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate if (g_Vflag) { 7697c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: vvvv D program vvvv\n"); 7707c478bd9Sstevel@tonic-gate (void) fputs(g_prog, stderr); 7717c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: ^^^^ D program ^^^^\n"); 7727c478bd9Sstevel@tonic-gate } 7737c478bd9Sstevel@tonic-gate 7747c478bd9Sstevel@tonic-gate if ((prog = dtrace_program_strcompile(g_dtp, g_prog, 7757c478bd9Sstevel@tonic-gate DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) 7767c478bd9Sstevel@tonic-gate dfail("failed to compile program"); 7777c478bd9Sstevel@tonic-gate 7787c478bd9Sstevel@tonic-gate if (dtrace_program_exec(g_dtp, prog, &info) == -1) 7797c478bd9Sstevel@tonic-gate dfail("failed to enable probes"); 7807c478bd9Sstevel@tonic-gate 7817c478bd9Sstevel@tonic-gate if (dtrace_go(g_dtp) != 0) 7827c478bd9Sstevel@tonic-gate dfail("couldn't start tracing"); 7837c478bd9Sstevel@tonic-gate } 7847c478bd9Sstevel@tonic-gate 7857c478bd9Sstevel@tonic-gate static void 7867c478bd9Sstevel@tonic-gate status_fire(void) 7877c478bd9Sstevel@tonic-gate {} 7887c478bd9Sstevel@tonic-gate 7897c478bd9Sstevel@tonic-gate static void 7907c478bd9Sstevel@tonic-gate status_init(void) 7917c478bd9Sstevel@tonic-gate { 7927c478bd9Sstevel@tonic-gate dtrace_optval_t val, status, agg; 7937c478bd9Sstevel@tonic-gate struct sigaction act; 7947c478bd9Sstevel@tonic-gate struct itimerspec ts; 7957c478bd9Sstevel@tonic-gate struct sigevent ev; 7967c478bd9Sstevel@tonic-gate timer_t tid; 7977c478bd9Sstevel@tonic-gate 7987c478bd9Sstevel@tonic-gate if (dtrace_getopt(g_dtp, "statusrate", &status) == -1) 7997c478bd9Sstevel@tonic-gate dfail("failed to get 'statusrate'"); 8007c478bd9Sstevel@tonic-gate 8017c478bd9Sstevel@tonic-gate if (dtrace_getopt(g_dtp, "aggrate", &agg) == -1) 8027c478bd9Sstevel@tonic-gate dfail("failed to get 'statusrate'"); 8037c478bd9Sstevel@tonic-gate 8047c478bd9Sstevel@tonic-gate /* 8057c478bd9Sstevel@tonic-gate * We would want to awaken at a rate that is the GCD of the statusrate 8067c478bd9Sstevel@tonic-gate * and the aggrate -- but that seems a bit absurd. Instead, we'll 8077c478bd9Sstevel@tonic-gate * simply awaken at a rate that is the more frequent of the two, which 8087c478bd9Sstevel@tonic-gate * assures that we're never later than the interval implied by the 8097c478bd9Sstevel@tonic-gate * more frequent rate. 8107c478bd9Sstevel@tonic-gate */ 8117c478bd9Sstevel@tonic-gate val = status < agg ? status : agg; 8127c478bd9Sstevel@tonic-gate 8137c478bd9Sstevel@tonic-gate (void) sigemptyset(&act.sa_mask); 8147c478bd9Sstevel@tonic-gate act.sa_flags = 0; 8157c478bd9Sstevel@tonic-gate act.sa_handler = status_fire; 8167c478bd9Sstevel@tonic-gate (void) sigaction(SIGUSR1, &act, NULL); 8177c478bd9Sstevel@tonic-gate 8187c478bd9Sstevel@tonic-gate ev.sigev_notify = SIGEV_SIGNAL; 8197c478bd9Sstevel@tonic-gate ev.sigev_signo = SIGUSR1; 8207c478bd9Sstevel@tonic-gate 8217c478bd9Sstevel@tonic-gate if (timer_create(CLOCK_REALTIME, &ev, &tid) == -1) 8227c478bd9Sstevel@tonic-gate dfail("cannot create CLOCK_REALTIME timer"); 8237c478bd9Sstevel@tonic-gate 8247c478bd9Sstevel@tonic-gate ts.it_value.tv_sec = val / NANOSEC; 8257c478bd9Sstevel@tonic-gate ts.it_value.tv_nsec = val % NANOSEC; 8267c478bd9Sstevel@tonic-gate ts.it_interval = ts.it_value; 8277c478bd9Sstevel@tonic-gate 8287c478bd9Sstevel@tonic-gate if (timer_settime(tid, TIMER_RELTIME, &ts, NULL) == -1) 8297c478bd9Sstevel@tonic-gate dfail("cannot set time on CLOCK_REALTIME timer"); 8307c478bd9Sstevel@tonic-gate } 8317c478bd9Sstevel@tonic-gate 8327c478bd9Sstevel@tonic-gate static void 8337c478bd9Sstevel@tonic-gate status_check(void) 8347c478bd9Sstevel@tonic-gate { 8357c478bd9Sstevel@tonic-gate if (!g_tracing && dtrace_aggregate_snap(g_dtp) != 0) 8367c478bd9Sstevel@tonic-gate dfail("failed to snap aggregate"); 8377c478bd9Sstevel@tonic-gate 8387c478bd9Sstevel@tonic-gate if (dtrace_status(g_dtp) == -1) 8397c478bd9Sstevel@tonic-gate dfail("dtrace_status()"); 8407c478bd9Sstevel@tonic-gate } 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate static void 843a1b5e537Sbmc lsrec_fill(lsrec_t *lsrec, const dtrace_recdesc_t *rec, int nrecs, caddr_t data) 8447c478bd9Sstevel@tonic-gate { 8457c478bd9Sstevel@tonic-gate bzero(lsrec, g_recsize); 8467c478bd9Sstevel@tonic-gate lsrec->ls_count = 1; 8477c478bd9Sstevel@tonic-gate 8487c478bd9Sstevel@tonic-gate if ((g_recsize > LS_HIST && nrecs < 4) || (nrecs < 3)) 8497c478bd9Sstevel@tonic-gate fail(0, "truncated DTrace record"); 8507c478bd9Sstevel@tonic-gate 8517c478bd9Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uint64_t)) 8527c478bd9Sstevel@tonic-gate fail(0, "bad event size in first record"); 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 8557c478bd9Sstevel@tonic-gate lsrec->ls_event = (uint32_t)*((uint64_t *)(data + rec->dtrd_offset)); 8567c478bd9Sstevel@tonic-gate rec++; 8577c478bd9Sstevel@tonic-gate 8587c478bd9Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uintptr_t)) 8597c478bd9Sstevel@tonic-gate fail(0, "bad lock address size in second record"); 8607c478bd9Sstevel@tonic-gate 8617c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 8627c478bd9Sstevel@tonic-gate lsrec->ls_lock = *((uintptr_t *)(data + rec->dtrd_offset)); 8637c478bd9Sstevel@tonic-gate rec++; 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate if (rec->dtrd_size != sizeof (uintptr_t)) 8667c478bd9Sstevel@tonic-gate fail(0, "bad caller size in third record"); 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 8697c478bd9Sstevel@tonic-gate lsrec->ls_caller = *((uintptr_t *)(data + rec->dtrd_offset)); 8707c478bd9Sstevel@tonic-gate rec++; 8717c478bd9Sstevel@tonic-gate 8727c478bd9Sstevel@tonic-gate if (g_recsize > LS_HIST) { 8737c478bd9Sstevel@tonic-gate int frames, i; 8747c478bd9Sstevel@tonic-gate pc_t *stack; 8757c478bd9Sstevel@tonic-gate 8767c478bd9Sstevel@tonic-gate frames = rec->dtrd_size / sizeof (pc_t); 8777c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 8787c478bd9Sstevel@tonic-gate stack = (pc_t *)(data + rec->dtrd_offset); 8797c478bd9Sstevel@tonic-gate 8807c478bd9Sstevel@tonic-gate for (i = 1; i < frames; i++) 8817c478bd9Sstevel@tonic-gate lsrec->ls_stack[i - 1] = stack[i]; 8827c478bd9Sstevel@tonic-gate } 8837c478bd9Sstevel@tonic-gate } 8847c478bd9Sstevel@tonic-gate 8857c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 8867c478bd9Sstevel@tonic-gate static int 887a1b5e537Sbmc count_aggregate(const dtrace_aggdata_t *agg, void *arg) 8887c478bd9Sstevel@tonic-gate { 8897c478bd9Sstevel@tonic-gate *((size_t *)arg) += 1; 8907c478bd9Sstevel@tonic-gate 8917c478bd9Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 8927c478bd9Sstevel@tonic-gate } 8937c478bd9Sstevel@tonic-gate 8947c478bd9Sstevel@tonic-gate static int 895a1b5e537Sbmc process_aggregate(const dtrace_aggdata_t *agg, void *arg) 8967c478bd9Sstevel@tonic-gate { 897a1b5e537Sbmc const dtrace_aggdesc_t *aggdesc = agg->dtada_desc; 8987c478bd9Sstevel@tonic-gate caddr_t data = agg->dtada_data; 8997c478bd9Sstevel@tonic-gate lsdata_t *lsdata = arg; 9007c478bd9Sstevel@tonic-gate lsrec_t *lsrec = lsdata->lsd_next; 901a1b5e537Sbmc const dtrace_recdesc_t *rec; 9027c478bd9Sstevel@tonic-gate uint64_t *avg, *quantized; 9037c478bd9Sstevel@tonic-gate int i, j; 9047c478bd9Sstevel@tonic-gate 9057c478bd9Sstevel@tonic-gate assert(lsdata->lsd_count < g_nrecs); 9067c478bd9Sstevel@tonic-gate 9077c478bd9Sstevel@tonic-gate /* 90802198c0bSbmc * Aggregation variable IDs are guaranteed to be generated in program 90902198c0bSbmc * order, and they are guaranteed to start from DTRACE_AGGVARIDNONE 91002198c0bSbmc * plus one. As "avg" appears before "hist" in program order, we know 91102198c0bSbmc * that "avg" will be allocated the first aggregation variable ID, and 91202198c0bSbmc * "hist" will be allocated the second aggregation variable ID -- and 91302198c0bSbmc * we therefore use the aggregation variable ID to differentiate the 91402198c0bSbmc * cases. 91502198c0bSbmc */ 91602198c0bSbmc if (aggdesc->dtagd_varid > DTRACE_AGGVARIDNONE + 1) { 91702198c0bSbmc /* 91802198c0bSbmc * If this is the histogram entry. We'll copy the quantized 91902198c0bSbmc * data into lc_hist, and jump over the rest. 9207c478bd9Sstevel@tonic-gate */ 9217c478bd9Sstevel@tonic-gate rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; 9227c478bd9Sstevel@tonic-gate 92302198c0bSbmc if (aggdesc->dtagd_varid != DTRACE_AGGVARIDNONE + 2) 92402198c0bSbmc fail(0, "bad variable ID in aggregation record"); 92502198c0bSbmc 9267c478bd9Sstevel@tonic-gate if (rec->dtrd_size != 9277c478bd9Sstevel@tonic-gate DTRACE_QUANTIZE_NBUCKETS * sizeof (uint64_t)) 9287c478bd9Sstevel@tonic-gate fail(0, "bad quantize size in aggregation record"); 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 9317c478bd9Sstevel@tonic-gate quantized = (uint64_t *)(data + rec->dtrd_offset); 9327c478bd9Sstevel@tonic-gate 9337c478bd9Sstevel@tonic-gate for (i = DTRACE_QUANTIZE_ZEROBUCKET, j = 0; 9347c478bd9Sstevel@tonic-gate i < DTRACE_QUANTIZE_NBUCKETS; i++, j++) 9357c478bd9Sstevel@tonic-gate lsrec->ls_hist[j] = quantized[i]; 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate goto out; 9387c478bd9Sstevel@tonic-gate } 9397c478bd9Sstevel@tonic-gate 9407c478bd9Sstevel@tonic-gate lsrec_fill(lsrec, &aggdesc->dtagd_rec[1], 9417c478bd9Sstevel@tonic-gate aggdesc->dtagd_nrecs - 1, data); 9427c478bd9Sstevel@tonic-gate 9437c478bd9Sstevel@tonic-gate rec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; 9447c478bd9Sstevel@tonic-gate 9457c478bd9Sstevel@tonic-gate if (rec->dtrd_size != 2 * sizeof (uint64_t)) 9467c478bd9Sstevel@tonic-gate fail(0, "bad avg size in aggregation record"); 9477c478bd9Sstevel@tonic-gate 9487c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 9497c478bd9Sstevel@tonic-gate avg = (uint64_t *)(data + rec->dtrd_offset); 9507c478bd9Sstevel@tonic-gate lsrec->ls_count = (uint32_t)avg[0]; 9517c478bd9Sstevel@tonic-gate lsrec->ls_time = (uintptr_t)avg[1]; 9527c478bd9Sstevel@tonic-gate 9537c478bd9Sstevel@tonic-gate if (g_recsize >= LS_HIST) 9547c478bd9Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9557c478bd9Sstevel@tonic-gate 9567c478bd9Sstevel@tonic-gate out: 9577c478bd9Sstevel@tonic-gate lsdata->lsd_next = (lsrec_t *)((uintptr_t)lsrec + g_recsize); 9587c478bd9Sstevel@tonic-gate lsdata->lsd_count++; 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate return (DTRACE_AGGWALK_NEXT); 9617c478bd9Sstevel@tonic-gate } 9627c478bd9Sstevel@tonic-gate 9637c478bd9Sstevel@tonic-gate static int 9647c478bd9Sstevel@tonic-gate process_trace(const dtrace_probedata_t *pdata, void *arg) 9657c478bd9Sstevel@tonic-gate { 9667c478bd9Sstevel@tonic-gate lsdata_t *lsdata = arg; 9677c478bd9Sstevel@tonic-gate lsrec_t *lsrec = lsdata->lsd_next; 9687c478bd9Sstevel@tonic-gate dtrace_eprobedesc_t *edesc = pdata->dtpda_edesc; 9697c478bd9Sstevel@tonic-gate caddr_t data = pdata->dtpda_data; 9707c478bd9Sstevel@tonic-gate 9717c478bd9Sstevel@tonic-gate if (lsdata->lsd_count >= g_nrecs) 9727c478bd9Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 9737c478bd9Sstevel@tonic-gate 9747c478bd9Sstevel@tonic-gate lsrec_fill(lsrec, edesc->dtepd_rec, edesc->dtepd_nrecs, data); 9757c478bd9Sstevel@tonic-gate 9767c478bd9Sstevel@tonic-gate lsdata->lsd_next = (lsrec_t *)((uintptr_t)lsrec + g_recsize); 9777c478bd9Sstevel@tonic-gate lsdata->lsd_count++; 9787c478bd9Sstevel@tonic-gate 9797c478bd9Sstevel@tonic-gate return (DTRACE_CONSUME_NEXT); 9807c478bd9Sstevel@tonic-gate } 9817c478bd9Sstevel@tonic-gate 9827c478bd9Sstevel@tonic-gate static int 9837c478bd9Sstevel@tonic-gate process_data(FILE *out, char *data) 9847c478bd9Sstevel@tonic-gate { 9857c478bd9Sstevel@tonic-gate lsdata_t lsdata; 9867c478bd9Sstevel@tonic-gate 9877c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 9887c478bd9Sstevel@tonic-gate lsdata.lsd_next = (lsrec_t *)data; 9897c478bd9Sstevel@tonic-gate lsdata.lsd_count = 0; 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate if (g_tracing) { 9927c478bd9Sstevel@tonic-gate if (dtrace_consume(g_dtp, out, 9937c478bd9Sstevel@tonic-gate process_trace, NULL, &lsdata) != 0) 9947c478bd9Sstevel@tonic-gate dfail("failed to consume buffer"); 9957c478bd9Sstevel@tonic-gate 9967c478bd9Sstevel@tonic-gate return (lsdata.lsd_count); 9977c478bd9Sstevel@tonic-gate } 9987c478bd9Sstevel@tonic-gate 9997c478bd9Sstevel@tonic-gate if (dtrace_aggregate_walk_keyvarsorted(g_dtp, 10007c478bd9Sstevel@tonic-gate process_aggregate, &lsdata) != 0) 10017c478bd9Sstevel@tonic-gate dfail("failed to walk aggregate"); 10027c478bd9Sstevel@tonic-gate 10037c478bd9Sstevel@tonic-gate return (lsdata.lsd_count); 10047c478bd9Sstevel@tonic-gate } 10057c478bd9Sstevel@tonic-gate 10067c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 10077c478bd9Sstevel@tonic-gate static int 1008a1b5e537Sbmc drophandler(const dtrace_dropdata_t *data, void *arg) 10097c478bd9Sstevel@tonic-gate { 10107c478bd9Sstevel@tonic-gate g_dropped++; 10117c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: warning: %s", data->dtdda_msg); 10127c478bd9Sstevel@tonic-gate return (DTRACE_HANDLE_OK); 10137c478bd9Sstevel@tonic-gate } 10147c478bd9Sstevel@tonic-gate 10157c478bd9Sstevel@tonic-gate int 10167c478bd9Sstevel@tonic-gate main(int argc, char **argv) 10177c478bd9Sstevel@tonic-gate { 10187c478bd9Sstevel@tonic-gate char *data_buf; 10197c478bd9Sstevel@tonic-gate lsrec_t *lsp, **current, **first, **sort_buf, **merge_buf; 10207c478bd9Sstevel@tonic-gate FILE *out = stdout; 10217c478bd9Sstevel@tonic-gate char c; 10227c478bd9Sstevel@tonic-gate pid_t child; 10237c478bd9Sstevel@tonic-gate int status; 10247c478bd9Sstevel@tonic-gate int i, j; 10257c478bd9Sstevel@tonic-gate hrtime_t duration; 10267c478bd9Sstevel@tonic-gate char *addrp, *offp, *sizep, *evp, *lastp, *p; 10277c478bd9Sstevel@tonic-gate uintptr_t addr; 10287c478bd9Sstevel@tonic-gate size_t size, off; 10297c478bd9Sstevel@tonic-gate int events_specified = 0; 10307c478bd9Sstevel@tonic-gate int exec_errno = 0; 10317c478bd9Sstevel@tonic-gate uint32_t event; 10327c478bd9Sstevel@tonic-gate char *filt = NULL, *ifilt = NULL; 10337c478bd9Sstevel@tonic-gate static uint64_t ev_count[LS_MAX_EVENTS + 1]; 10347c478bd9Sstevel@tonic-gate static uint64_t ev_time[LS_MAX_EVENTS + 1]; 10357c478bd9Sstevel@tonic-gate dtrace_optval_t aggsize; 10367c478bd9Sstevel@tonic-gate char aggstr[10]; 10377c478bd9Sstevel@tonic-gate long ncpus; 10387c478bd9Sstevel@tonic-gate int dynvar = 0; 10397c478bd9Sstevel@tonic-gate int err; 10407c478bd9Sstevel@tonic-gate 10417c478bd9Sstevel@tonic-gate if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { 10427c478bd9Sstevel@tonic-gate fail(0, "cannot open dtrace library: %s", 10437c478bd9Sstevel@tonic-gate dtrace_errmsg(NULL, err)); 10447c478bd9Sstevel@tonic-gate } 10457c478bd9Sstevel@tonic-gate 10467c478bd9Sstevel@tonic-gate if (dtrace_handle_drop(g_dtp, &drophandler, NULL) == -1) 10477c478bd9Sstevel@tonic-gate dfail("couldn't establish drop handler"); 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate if (symtab_init() == -1) 10507c478bd9Sstevel@tonic-gate fail(1, "can't load kernel symbols"); 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate g_nrecs = DEFAULT_NRECS; 10537c478bd9Sstevel@tonic-gate 10547c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, LOCKSTAT_OPTSTR)) != EOF) { 10557c478bd9Sstevel@tonic-gate switch (c) { 10567c478bd9Sstevel@tonic-gate case 'b': 10577c478bd9Sstevel@tonic-gate g_recsize = LS_BASIC; 10587c478bd9Sstevel@tonic-gate break; 10597c478bd9Sstevel@tonic-gate 10607c478bd9Sstevel@tonic-gate case 't': 10617c478bd9Sstevel@tonic-gate g_recsize = LS_TIME; 10627c478bd9Sstevel@tonic-gate break; 10637c478bd9Sstevel@tonic-gate 10647c478bd9Sstevel@tonic-gate case 'h': 10657c478bd9Sstevel@tonic-gate g_recsize = LS_HIST; 10667c478bd9Sstevel@tonic-gate break; 10677c478bd9Sstevel@tonic-gate 10687c478bd9Sstevel@tonic-gate case 's': 10697c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 10707c478bd9Sstevel@tonic-gate usage(); 10717c478bd9Sstevel@tonic-gate g_stkdepth = atoi(optarg); 10727c478bd9Sstevel@tonic-gate if (g_stkdepth > LS_MAX_STACK_DEPTH) 10737c478bd9Sstevel@tonic-gate fail(0, "max stack depth is %d", 10747c478bd9Sstevel@tonic-gate LS_MAX_STACK_DEPTH); 10757c478bd9Sstevel@tonic-gate g_recsize = LS_STACK(g_stkdepth); 10767c478bd9Sstevel@tonic-gate break; 10777c478bd9Sstevel@tonic-gate 10787c478bd9Sstevel@tonic-gate case 'n': 10797c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 10807c478bd9Sstevel@tonic-gate usage(); 10817c478bd9Sstevel@tonic-gate g_nrecs = atoi(optarg); 10827c478bd9Sstevel@tonic-gate break; 10837c478bd9Sstevel@tonic-gate 10847c478bd9Sstevel@tonic-gate case 'd': 10857c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 10867c478bd9Sstevel@tonic-gate usage(); 10877c478bd9Sstevel@tonic-gate duration = atoll(optarg); 10887c478bd9Sstevel@tonic-gate 10897c478bd9Sstevel@tonic-gate /* 10907c478bd9Sstevel@tonic-gate * XXX -- durations really should be per event 10917c478bd9Sstevel@tonic-gate * since the units are different, but it's hard 10927c478bd9Sstevel@tonic-gate * to express this nicely in the interface. 10937c478bd9Sstevel@tonic-gate * Not clear yet what the cleanest solution is. 10947c478bd9Sstevel@tonic-gate */ 10957c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 10967c478bd9Sstevel@tonic-gate if (g_event_info[i].ev_type != 'E') 10977c478bd9Sstevel@tonic-gate g_min_duration[i] = duration; 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate break; 11007c478bd9Sstevel@tonic-gate 11017c478bd9Sstevel@tonic-gate case 'i': 11027c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 11037c478bd9Sstevel@tonic-gate usage(); 11047c478bd9Sstevel@tonic-gate i = atoi(optarg); 11057c478bd9Sstevel@tonic-gate if (i <= 0) 11067c478bd9Sstevel@tonic-gate usage(); 11077c478bd9Sstevel@tonic-gate if (i > MAX_HZ) 11087c478bd9Sstevel@tonic-gate fail(0, "max interrupt rate is %d Hz", MAX_HZ); 11097c478bd9Sstevel@tonic-gate 11107c478bd9Sstevel@tonic-gate for (j = 0; j < LS_MAX_EVENTS; j++) 11117c478bd9Sstevel@tonic-gate if (strcmp(g_event_info[j].ev_desc, 11127c478bd9Sstevel@tonic-gate "Profiling interrupt") == 0) 11137c478bd9Sstevel@tonic-gate break; 11147c478bd9Sstevel@tonic-gate 11157c478bd9Sstevel@tonic-gate (void) sprintf(g_event_info[j].ev_name, 11167c478bd9Sstevel@tonic-gate "profile:::profile-%d", i); 11177c478bd9Sstevel@tonic-gate break; 11187c478bd9Sstevel@tonic-gate 11197c478bd9Sstevel@tonic-gate case 'l': 11207c478bd9Sstevel@tonic-gate case 'f': 11217c478bd9Sstevel@tonic-gate addrp = strtok(optarg, ","); 11227c478bd9Sstevel@tonic-gate sizep = strtok(NULL, ","); 11237c478bd9Sstevel@tonic-gate addrp = strtok(optarg, ",+"); 11247c478bd9Sstevel@tonic-gate offp = strtok(NULL, ","); 11257c478bd9Sstevel@tonic-gate 11267c478bd9Sstevel@tonic-gate size = sizep ? strtoul(sizep, NULL, 0) : 1; 11277c478bd9Sstevel@tonic-gate off = offp ? strtoul(offp, NULL, 0) : 0; 11287c478bd9Sstevel@tonic-gate 11297c478bd9Sstevel@tonic-gate if (addrp[0] == '0') { 11307c478bd9Sstevel@tonic-gate addr = strtoul(addrp, NULL, 16) + off; 11317c478bd9Sstevel@tonic-gate } else { 11327c478bd9Sstevel@tonic-gate addr = sym_to_addr(addrp) + off; 11337c478bd9Sstevel@tonic-gate if (sizep == NULL) 11347c478bd9Sstevel@tonic-gate size = sym_size(addrp) - off; 11357c478bd9Sstevel@tonic-gate if (addr - off == 0) 11367c478bd9Sstevel@tonic-gate fail(0, "symbol '%s' not found", addrp); 11377c478bd9Sstevel@tonic-gate if (size == 0) 11387c478bd9Sstevel@tonic-gate size = 1; 11397c478bd9Sstevel@tonic-gate } 11407c478bd9Sstevel@tonic-gate 11417c478bd9Sstevel@tonic-gate 11427c478bd9Sstevel@tonic-gate if (c == 'l') { 11437c478bd9Sstevel@tonic-gate filter_add(&filt, "arg0", addr, size); 11447c478bd9Sstevel@tonic-gate } else { 11457c478bd9Sstevel@tonic-gate filter_add(&filt, "caller", addr, size); 11467c478bd9Sstevel@tonic-gate filter_add(&ifilt, "arg0", addr, size); 11477c478bd9Sstevel@tonic-gate } 11487c478bd9Sstevel@tonic-gate break; 11497c478bd9Sstevel@tonic-gate 11507c478bd9Sstevel@tonic-gate case 'e': 11517c478bd9Sstevel@tonic-gate evp = strtok_r(optarg, ",", &lastp); 11527c478bd9Sstevel@tonic-gate while (evp) { 11537c478bd9Sstevel@tonic-gate int ev1, ev2; 11547c478bd9Sstevel@tonic-gate char *evp2; 11557c478bd9Sstevel@tonic-gate 11567c478bd9Sstevel@tonic-gate (void) strtok(evp, "-"); 11577c478bd9Sstevel@tonic-gate evp2 = strtok(NULL, "-"); 11587c478bd9Sstevel@tonic-gate ev1 = atoi(evp); 11597c478bd9Sstevel@tonic-gate ev2 = evp2 ? atoi(evp2) : ev1; 11607c478bd9Sstevel@tonic-gate if ((uint_t)ev1 >= LS_MAX_EVENTS || 11617c478bd9Sstevel@tonic-gate (uint_t)ev2 >= LS_MAX_EVENTS || ev1 > ev2) 11627c478bd9Sstevel@tonic-gate fail(0, "-e events out of range"); 11637c478bd9Sstevel@tonic-gate for (i = ev1; i <= ev2; i++) 11647c478bd9Sstevel@tonic-gate g_enabled[i] = 1; 11657c478bd9Sstevel@tonic-gate evp = strtok_r(NULL, ",", &lastp); 11667c478bd9Sstevel@tonic-gate } 11677c478bd9Sstevel@tonic-gate events_specified = 1; 11687c478bd9Sstevel@tonic-gate break; 11697c478bd9Sstevel@tonic-gate 11707c478bd9Sstevel@tonic-gate case 'c': 11717c478bd9Sstevel@tonic-gate g_cflag = 1; 11727c478bd9Sstevel@tonic-gate break; 11737c478bd9Sstevel@tonic-gate 11747c478bd9Sstevel@tonic-gate case 'k': 11757c478bd9Sstevel@tonic-gate g_kflag = 1; 11767c478bd9Sstevel@tonic-gate break; 11777c478bd9Sstevel@tonic-gate 11787c478bd9Sstevel@tonic-gate case 'w': 11797c478bd9Sstevel@tonic-gate g_wflag = 1; 11807c478bd9Sstevel@tonic-gate break; 11817c478bd9Sstevel@tonic-gate 11827c478bd9Sstevel@tonic-gate case 'W': 11837c478bd9Sstevel@tonic-gate g_Wflag = 1; 11847c478bd9Sstevel@tonic-gate break; 11857c478bd9Sstevel@tonic-gate 11867c478bd9Sstevel@tonic-gate case 'g': 11877c478bd9Sstevel@tonic-gate g_gflag = 1; 11887c478bd9Sstevel@tonic-gate break; 11897c478bd9Sstevel@tonic-gate 11907c478bd9Sstevel@tonic-gate case 'C': 11917c478bd9Sstevel@tonic-gate case 'E': 11927c478bd9Sstevel@tonic-gate case 'H': 11937c478bd9Sstevel@tonic-gate case 'I': 11947c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 11957c478bd9Sstevel@tonic-gate if (g_event_info[i].ev_type == c) 11967c478bd9Sstevel@tonic-gate g_enabled[i] = 1; 11977c478bd9Sstevel@tonic-gate events_specified = 1; 11987c478bd9Sstevel@tonic-gate break; 11997c478bd9Sstevel@tonic-gate 12007c478bd9Sstevel@tonic-gate case 'A': 12017c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) 12027c478bd9Sstevel@tonic-gate if (strchr("CH", g_event_info[i].ev_type)) 12037c478bd9Sstevel@tonic-gate g_enabled[i] = 1; 12047c478bd9Sstevel@tonic-gate events_specified = 1; 12057c478bd9Sstevel@tonic-gate break; 12067c478bd9Sstevel@tonic-gate 12077c478bd9Sstevel@tonic-gate case 'T': 12087c478bd9Sstevel@tonic-gate g_tracing = 1; 12097c478bd9Sstevel@tonic-gate break; 12107c478bd9Sstevel@tonic-gate 12117c478bd9Sstevel@tonic-gate case 'D': 12127c478bd9Sstevel@tonic-gate if (!isdigit(optarg[0])) 12137c478bd9Sstevel@tonic-gate usage(); 12147c478bd9Sstevel@tonic-gate g_topn = atoi(optarg); 12157c478bd9Sstevel@tonic-gate break; 12167c478bd9Sstevel@tonic-gate 12177c478bd9Sstevel@tonic-gate case 'R': 12187c478bd9Sstevel@tonic-gate g_rates = 1; 12197c478bd9Sstevel@tonic-gate break; 12207c478bd9Sstevel@tonic-gate 12217c478bd9Sstevel@tonic-gate case 'p': 12227c478bd9Sstevel@tonic-gate g_pflag = 1; 12237c478bd9Sstevel@tonic-gate break; 12247c478bd9Sstevel@tonic-gate 12257c478bd9Sstevel@tonic-gate case 'P': 12267c478bd9Sstevel@tonic-gate g_Pflag = 1; 12277c478bd9Sstevel@tonic-gate break; 12287c478bd9Sstevel@tonic-gate 12297c478bd9Sstevel@tonic-gate case 'o': 12307c478bd9Sstevel@tonic-gate if ((out = fopen(optarg, "w")) == NULL) 12317c478bd9Sstevel@tonic-gate fail(1, "error opening file"); 12327c478bd9Sstevel@tonic-gate break; 12337c478bd9Sstevel@tonic-gate 12347c478bd9Sstevel@tonic-gate case 'V': 12357c478bd9Sstevel@tonic-gate g_Vflag = 1; 12367c478bd9Sstevel@tonic-gate break; 12377c478bd9Sstevel@tonic-gate 12387c478bd9Sstevel@tonic-gate default: 12397c478bd9Sstevel@tonic-gate if (strchr(LOCKSTAT_OPTSTR, c) == NULL) 12407c478bd9Sstevel@tonic-gate usage(); 12417c478bd9Sstevel@tonic-gate } 12427c478bd9Sstevel@tonic-gate } 12437c478bd9Sstevel@tonic-gate 12447c478bd9Sstevel@tonic-gate if (filt != NULL) { 12457c478bd9Sstevel@tonic-gate predicate_add(&g_predicate, filt, NULL, 0); 12467c478bd9Sstevel@tonic-gate filter_destroy(&filt); 12477c478bd9Sstevel@tonic-gate } 12487c478bd9Sstevel@tonic-gate 12497c478bd9Sstevel@tonic-gate if (ifilt != NULL) { 12507c478bd9Sstevel@tonic-gate predicate_add(&g_ipredicate, ifilt, NULL, 0); 12517c478bd9Sstevel@tonic-gate filter_destroy(&ifilt); 12527c478bd9Sstevel@tonic-gate } 12537c478bd9Sstevel@tonic-gate 12547c478bd9Sstevel@tonic-gate if (g_recsize == 0) { 12557c478bd9Sstevel@tonic-gate if (g_gflag) { 12567c478bd9Sstevel@tonic-gate g_stkdepth = LS_MAX_STACK_DEPTH; 12577c478bd9Sstevel@tonic-gate g_recsize = LS_STACK(g_stkdepth); 12587c478bd9Sstevel@tonic-gate } else { 12597c478bd9Sstevel@tonic-gate g_recsize = LS_TIME; 12607c478bd9Sstevel@tonic-gate } 12617c478bd9Sstevel@tonic-gate } 12627c478bd9Sstevel@tonic-gate 12637c478bd9Sstevel@tonic-gate if (g_gflag && g_recsize <= LS_STACK(0)) 12647c478bd9Sstevel@tonic-gate fail(0, "'-g' requires at least '-s 1' data gathering"); 12657c478bd9Sstevel@tonic-gate 12667c478bd9Sstevel@tonic-gate /* 12677c478bd9Sstevel@tonic-gate * Make sure the alignment is reasonable 12687c478bd9Sstevel@tonic-gate */ 12697c478bd9Sstevel@tonic-gate g_recsize = -(-g_recsize & -sizeof (uint64_t)); 12707c478bd9Sstevel@tonic-gate 12717c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 12727c478bd9Sstevel@tonic-gate /* 12737c478bd9Sstevel@tonic-gate * If no events were specified, enable -C. 12747c478bd9Sstevel@tonic-gate */ 12757c478bd9Sstevel@tonic-gate if (!events_specified && g_event_info[i].ev_type == 'C') 12767c478bd9Sstevel@tonic-gate g_enabled[i] = 1; 12777c478bd9Sstevel@tonic-gate } 12787c478bd9Sstevel@tonic-gate 12797c478bd9Sstevel@tonic-gate for (i = 0; i < LS_MAX_EVENTS; i++) { 12807c478bd9Sstevel@tonic-gate if (!g_enabled[i]) 12817c478bd9Sstevel@tonic-gate continue; 12827c478bd9Sstevel@tonic-gate 12837c478bd9Sstevel@tonic-gate if (g_event_info[i].ev_acquire != NULL) { 12847c478bd9Sstevel@tonic-gate /* 12857c478bd9Sstevel@tonic-gate * If we've enabled a hold event, we must explicitly 12867c478bd9Sstevel@tonic-gate * allocate dynamic variable space. 12877c478bd9Sstevel@tonic-gate */ 12887c478bd9Sstevel@tonic-gate dynvar = 1; 12897c478bd9Sstevel@tonic-gate } 12907c478bd9Sstevel@tonic-gate 12917c478bd9Sstevel@tonic-gate dprog_addevent(i); 12927c478bd9Sstevel@tonic-gate } 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate /* 12957c478bd9Sstevel@tonic-gate * Make sure there are remaining arguments to specify a child command 12967c478bd9Sstevel@tonic-gate * to execute. 12977c478bd9Sstevel@tonic-gate */ 12987c478bd9Sstevel@tonic-gate if (argc <= optind) 12997c478bd9Sstevel@tonic-gate usage(); 13007c478bd9Sstevel@tonic-gate 13017c478bd9Sstevel@tonic-gate if ((ncpus = sysconf(_SC_NPROCESSORS_ONLN)) == -1) 13027c478bd9Sstevel@tonic-gate dfail("couldn't determine number of online CPUs"); 13037c478bd9Sstevel@tonic-gate 13047c478bd9Sstevel@tonic-gate /* 13057c478bd9Sstevel@tonic-gate * By default, we set our data buffer size to be the number of records 13067c478bd9Sstevel@tonic-gate * multiplied by the size of the record, doubled to account for some 13077c478bd9Sstevel@tonic-gate * DTrace slop and divided by the number of CPUs. We silently clamp 13087c478bd9Sstevel@tonic-gate * the aggregation size at both a minimum and a maximum to prevent 13097c478bd9Sstevel@tonic-gate * absurdly low or high values. 13107c478bd9Sstevel@tonic-gate */ 13117c478bd9Sstevel@tonic-gate if ((aggsize = (g_nrecs * g_recsize * 2) / ncpus) < MIN_AGGSIZE) 13127c478bd9Sstevel@tonic-gate aggsize = MIN_AGGSIZE; 13137c478bd9Sstevel@tonic-gate 13147c478bd9Sstevel@tonic-gate if (aggsize > MAX_AGGSIZE) 13157c478bd9Sstevel@tonic-gate aggsize = MAX_AGGSIZE; 13167c478bd9Sstevel@tonic-gate 13177c478bd9Sstevel@tonic-gate (void) sprintf(aggstr, "%lld", (long long)aggsize); 13187c478bd9Sstevel@tonic-gate 13197c478bd9Sstevel@tonic-gate if (!g_tracing) { 13207c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "bufsize", "4k") == -1) 13217c478bd9Sstevel@tonic-gate dfail("failed to set 'bufsize'"); 13227c478bd9Sstevel@tonic-gate 13237c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "aggsize", aggstr) == -1) 13247c478bd9Sstevel@tonic-gate dfail("failed to set 'aggsize'"); 13257c478bd9Sstevel@tonic-gate 13267c478bd9Sstevel@tonic-gate if (dynvar) { 13277c478bd9Sstevel@tonic-gate /* 13287c478bd9Sstevel@tonic-gate * If we're using dynamic variables, we set our 13297c478bd9Sstevel@tonic-gate * dynamic variable size to be one megabyte per CPU, 13307c478bd9Sstevel@tonic-gate * with a hard-limit of 32 megabytes. This may still 13317c478bd9Sstevel@tonic-gate * be too small in some cases, but it can be tuned 13327c478bd9Sstevel@tonic-gate * manually via -x if need be. 13337c478bd9Sstevel@tonic-gate */ 13347c478bd9Sstevel@tonic-gate (void) sprintf(aggstr, "%ldm", ncpus < 32 ? ncpus : 32); 13357c478bd9Sstevel@tonic-gate 13367c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "dynvarsize", aggstr) == -1) 13377c478bd9Sstevel@tonic-gate dfail("failed to set 'dynvarsize'"); 13387c478bd9Sstevel@tonic-gate } 13397c478bd9Sstevel@tonic-gate } else { 13407c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "bufsize", aggstr) == -1) 13417c478bd9Sstevel@tonic-gate dfail("failed to set 'bufsize'"); 13427c478bd9Sstevel@tonic-gate } 13437c478bd9Sstevel@tonic-gate 13447c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, "statusrate", "10sec") == -1) 13457c478bd9Sstevel@tonic-gate dfail("failed to set 'statusrate'"); 13467c478bd9Sstevel@tonic-gate 13477c478bd9Sstevel@tonic-gate optind = 1; 13487c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, LOCKSTAT_OPTSTR)) != EOF) { 13497c478bd9Sstevel@tonic-gate switch (c) { 13507c478bd9Sstevel@tonic-gate case 'x': 13517c478bd9Sstevel@tonic-gate if ((p = strchr(optarg, '=')) != NULL) 13527c478bd9Sstevel@tonic-gate *p++ = '\0'; 13537c478bd9Sstevel@tonic-gate 13547c478bd9Sstevel@tonic-gate if (dtrace_setopt(g_dtp, optarg, p) != 0) 13557c478bd9Sstevel@tonic-gate dfail("failed to set -x %s", optarg); 13567c478bd9Sstevel@tonic-gate break; 13577c478bd9Sstevel@tonic-gate } 13587c478bd9Sstevel@tonic-gate } 13597c478bd9Sstevel@tonic-gate 13607c478bd9Sstevel@tonic-gate argc -= optind; 13617c478bd9Sstevel@tonic-gate argv += optind; 13627c478bd9Sstevel@tonic-gate 13637c478bd9Sstevel@tonic-gate dprog_compile(); 13647c478bd9Sstevel@tonic-gate status_init(); 13657c478bd9Sstevel@tonic-gate 13667c478bd9Sstevel@tonic-gate g_elapsed = -gethrtime(); 13677c478bd9Sstevel@tonic-gate 13687c478bd9Sstevel@tonic-gate /* 13697c478bd9Sstevel@tonic-gate * Spawn the specified command and wait for it to complete. 13707c478bd9Sstevel@tonic-gate */ 13717c478bd9Sstevel@tonic-gate child = fork(); 13727c478bd9Sstevel@tonic-gate if (child == -1) 13737c478bd9Sstevel@tonic-gate fail(1, "cannot fork"); 13747c478bd9Sstevel@tonic-gate if (child == 0) { 13757c478bd9Sstevel@tonic-gate (void) dtrace_close(g_dtp); 13767c478bd9Sstevel@tonic-gate (void) execvp(argv[0], &argv[0]); 13777c478bd9Sstevel@tonic-gate exec_errno = errno; 13787c478bd9Sstevel@tonic-gate exit(127); 13797c478bd9Sstevel@tonic-gate } 13807c478bd9Sstevel@tonic-gate 13817c478bd9Sstevel@tonic-gate while (waitpid(child, &status, WEXITED) != child) 13827c478bd9Sstevel@tonic-gate status_check(); 13837c478bd9Sstevel@tonic-gate 13847c478bd9Sstevel@tonic-gate g_elapsed += gethrtime(); 13857c478bd9Sstevel@tonic-gate 13867c478bd9Sstevel@tonic-gate if (WIFEXITED(status)) { 13877c478bd9Sstevel@tonic-gate if (WEXITSTATUS(status) != 0) { 13887c478bd9Sstevel@tonic-gate if (exec_errno != 0) { 13897c478bd9Sstevel@tonic-gate errno = exec_errno; 13907c478bd9Sstevel@tonic-gate fail(1, "could not execute %s", argv[0]); 13917c478bd9Sstevel@tonic-gate } 13927c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 13937c478bd9Sstevel@tonic-gate "lockstat: warning: %s exited with code %d\n", 13947c478bd9Sstevel@tonic-gate argv[0], WEXITSTATUS(status)); 13957c478bd9Sstevel@tonic-gate } 13967c478bd9Sstevel@tonic-gate } else { 13977c478bd9Sstevel@tonic-gate (void) fprintf(stderr, 13987c478bd9Sstevel@tonic-gate "lockstat: warning: %s died on signal %d\n", 13997c478bd9Sstevel@tonic-gate argv[0], WTERMSIG(status)); 14007c478bd9Sstevel@tonic-gate } 14017c478bd9Sstevel@tonic-gate 14027c478bd9Sstevel@tonic-gate if (dtrace_stop(g_dtp) == -1) 14037c478bd9Sstevel@tonic-gate dfail("failed to stop dtrace"); 14047c478bd9Sstevel@tonic-gate 14057c478bd9Sstevel@tonic-gate /* 14067c478bd9Sstevel@tonic-gate * Before we read out the results, we need to allocate our buffer. 14077c478bd9Sstevel@tonic-gate * If we're tracing, then we'll just use the precalculated size. If 14087c478bd9Sstevel@tonic-gate * we're not, then we'll take a snapshot of the aggregate, and walk 14097c478bd9Sstevel@tonic-gate * it to count the number of records. 14107c478bd9Sstevel@tonic-gate */ 14117c478bd9Sstevel@tonic-gate if (!g_tracing) { 14127c478bd9Sstevel@tonic-gate if (dtrace_aggregate_snap(g_dtp) != 0) 14137c478bd9Sstevel@tonic-gate dfail("failed to snap aggregate"); 14147c478bd9Sstevel@tonic-gate 14157c478bd9Sstevel@tonic-gate g_nrecs = 0; 14167c478bd9Sstevel@tonic-gate 14177c478bd9Sstevel@tonic-gate if (dtrace_aggregate_walk(g_dtp, 14187c478bd9Sstevel@tonic-gate count_aggregate, &g_nrecs) != 0) 14197c478bd9Sstevel@tonic-gate dfail("failed to walk aggregate"); 14207c478bd9Sstevel@tonic-gate } 14217c478bd9Sstevel@tonic-gate 14227c478bd9Sstevel@tonic-gate if ((data_buf = memalign(sizeof (uint64_t), 14237c478bd9Sstevel@tonic-gate (g_nrecs + 1) * g_recsize)) == NULL) 14247c478bd9Sstevel@tonic-gate fail(1, "Memory allocation failed"); 14257c478bd9Sstevel@tonic-gate 14267c478bd9Sstevel@tonic-gate /* 14277c478bd9Sstevel@tonic-gate * Read out the DTrace data. 14287c478bd9Sstevel@tonic-gate */ 14297c478bd9Sstevel@tonic-gate g_nrecs_used = process_data(out, data_buf); 14307c478bd9Sstevel@tonic-gate 14317c478bd9Sstevel@tonic-gate if (g_nrecs_used > g_nrecs || g_dropped) 14327c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "lockstat: warning: " 14337c478bd9Sstevel@tonic-gate "ran out of data records (use -n for more)\n"); 14347c478bd9Sstevel@tonic-gate 14357c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 14367c478bd9Sstevel@tonic-gate for (i = 0, lsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 14377c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 14387c478bd9Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + g_recsize)) { 14397c478bd9Sstevel@tonic-gate ev_count[lsp->ls_event] += lsp->ls_count; 14407c478bd9Sstevel@tonic-gate ev_time[lsp->ls_event] += lsp->ls_time; 14417c478bd9Sstevel@tonic-gate } 14427c478bd9Sstevel@tonic-gate 14437c478bd9Sstevel@tonic-gate /* 14447c478bd9Sstevel@tonic-gate * If -g was specified, convert stacks into individual records. 14457c478bd9Sstevel@tonic-gate */ 14467c478bd9Sstevel@tonic-gate if (g_gflag) { 14477c478bd9Sstevel@tonic-gate lsrec_t *newlsp, *oldlsp; 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate newlsp = memalign(sizeof (uint64_t), 14507c478bd9Sstevel@tonic-gate g_nrecs_used * LS_TIME * (g_stkdepth + 1)); 14517c478bd9Sstevel@tonic-gate if (newlsp == NULL) 14527c478bd9Sstevel@tonic-gate fail(1, "Cannot allocate space for -g processing"); 14537c478bd9Sstevel@tonic-gate lsp = newlsp; 14547c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 14557c478bd9Sstevel@tonic-gate for (i = 0, oldlsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 14567c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 14577c478bd9Sstevel@tonic-gate oldlsp = (lsrec_t *)((char *)oldlsp + g_recsize)) { 14587c478bd9Sstevel@tonic-gate int fr; 14597c478bd9Sstevel@tonic-gate int caller_in_stack = 0; 14607c478bd9Sstevel@tonic-gate 14617c478bd9Sstevel@tonic-gate if (oldlsp->ls_count == 0) 14627c478bd9Sstevel@tonic-gate continue; 14637c478bd9Sstevel@tonic-gate 14647c478bd9Sstevel@tonic-gate for (fr = 0; fr < g_stkdepth; fr++) { 14657c478bd9Sstevel@tonic-gate if (oldlsp->ls_stack[fr] == 0) 14667c478bd9Sstevel@tonic-gate break; 14677c478bd9Sstevel@tonic-gate if (oldlsp->ls_stack[fr] == oldlsp->ls_caller) 14687c478bd9Sstevel@tonic-gate caller_in_stack = 1; 14697c478bd9Sstevel@tonic-gate bcopy(oldlsp, lsp, LS_TIME); 14707c478bd9Sstevel@tonic-gate lsp->ls_caller = oldlsp->ls_stack[fr]; 14717c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 14727c478bd9Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + LS_TIME); 14737c478bd9Sstevel@tonic-gate } 14747c478bd9Sstevel@tonic-gate if (!caller_in_stack) { 14757c478bd9Sstevel@tonic-gate bcopy(oldlsp, lsp, LS_TIME); 14767c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 14777c478bd9Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + LS_TIME); 14787c478bd9Sstevel@tonic-gate } 14797c478bd9Sstevel@tonic-gate } 14807c478bd9Sstevel@tonic-gate g_nrecs = g_nrecs_used = 14817c478bd9Sstevel@tonic-gate ((uintptr_t)lsp - (uintptr_t)newlsp) / LS_TIME; 14827c478bd9Sstevel@tonic-gate g_recsize = LS_TIME; 14837c478bd9Sstevel@tonic-gate g_stkdepth = 0; 14847c478bd9Sstevel@tonic-gate free(data_buf); 14857c478bd9Sstevel@tonic-gate data_buf = (char *)newlsp; 14867c478bd9Sstevel@tonic-gate } 14877c478bd9Sstevel@tonic-gate 14887c478bd9Sstevel@tonic-gate if ((sort_buf = calloc(2 * (g_nrecs + 1), 14897c478bd9Sstevel@tonic-gate sizeof (void *))) == NULL) 14907c478bd9Sstevel@tonic-gate fail(1, "Sort buffer allocation failed"); 14917c478bd9Sstevel@tonic-gate merge_buf = sort_buf + (g_nrecs + 1); 14927c478bd9Sstevel@tonic-gate 14937c478bd9Sstevel@tonic-gate /* 14947c478bd9Sstevel@tonic-gate * Build the sort buffer, discarding zero-count records along the way. 14957c478bd9Sstevel@tonic-gate */ 14967c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 14977c478bd9Sstevel@tonic-gate for (i = 0, lsp = (lsrec_t *)data_buf; i < g_nrecs_used; i++, 14987c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 14997c478bd9Sstevel@tonic-gate lsp = (lsrec_t *)((char *)lsp + g_recsize)) { 15007c478bd9Sstevel@tonic-gate if (lsp->ls_count == 0) 15017c478bd9Sstevel@tonic-gate lsp->ls_event = LS_MAX_EVENTS; 15027c478bd9Sstevel@tonic-gate sort_buf[i] = lsp; 15037c478bd9Sstevel@tonic-gate } 15047c478bd9Sstevel@tonic-gate 15057c478bd9Sstevel@tonic-gate if (g_nrecs_used == 0) 15067c478bd9Sstevel@tonic-gate exit(0); 15077c478bd9Sstevel@tonic-gate 15087c478bd9Sstevel@tonic-gate /* 15097c478bd9Sstevel@tonic-gate * Add a sentinel after the last record 15107c478bd9Sstevel@tonic-gate */ 15117c478bd9Sstevel@tonic-gate sort_buf[i] = lsp; 15127c478bd9Sstevel@tonic-gate lsp->ls_event = LS_MAX_EVENTS; 15137c478bd9Sstevel@tonic-gate 15147c478bd9Sstevel@tonic-gate if (g_tracing) { 15157c478bd9Sstevel@tonic-gate report_trace(out, sort_buf); 15167c478bd9Sstevel@tonic-gate return (0); 15177c478bd9Sstevel@tonic-gate } 15187c478bd9Sstevel@tonic-gate 15197c478bd9Sstevel@tonic-gate /* 15207c478bd9Sstevel@tonic-gate * Application of -g may have resulted in multiple records 15217c478bd9Sstevel@tonic-gate * with the same signature; coalesce them. 15227c478bd9Sstevel@tonic-gate */ 15237c478bd9Sstevel@tonic-gate if (g_gflag) { 15247c478bd9Sstevel@tonic-gate mergesort(lockcmp, sort_buf, merge_buf, g_nrecs_used); 15257c478bd9Sstevel@tonic-gate coalesce(lockcmp, sort_buf, g_nrecs_used); 15267c478bd9Sstevel@tonic-gate } 15277c478bd9Sstevel@tonic-gate 15287c478bd9Sstevel@tonic-gate /* 15297c478bd9Sstevel@tonic-gate * Coalesce locks within the same symbol if -c option specified. 15307c478bd9Sstevel@tonic-gate * Coalesce PCs within the same function if -k option specified. 15317c478bd9Sstevel@tonic-gate */ 15327c478bd9Sstevel@tonic-gate if (g_cflag || g_kflag) { 15337c478bd9Sstevel@tonic-gate for (i = 0; i < g_nrecs_used; i++) { 15347c478bd9Sstevel@tonic-gate int fr; 15357c478bd9Sstevel@tonic-gate lsp = sort_buf[i]; 15367c478bd9Sstevel@tonic-gate if (g_cflag) 15377c478bd9Sstevel@tonic-gate coalesce_symbol(&lsp->ls_lock); 15387c478bd9Sstevel@tonic-gate if (g_kflag) { 15397c478bd9Sstevel@tonic-gate for (fr = 0; fr < g_stkdepth; fr++) 15407c478bd9Sstevel@tonic-gate coalesce_symbol(&lsp->ls_stack[fr]); 15417c478bd9Sstevel@tonic-gate coalesce_symbol(&lsp->ls_caller); 15427c478bd9Sstevel@tonic-gate } 15437c478bd9Sstevel@tonic-gate } 15447c478bd9Sstevel@tonic-gate mergesort(lockcmp, sort_buf, merge_buf, g_nrecs_used); 15457c478bd9Sstevel@tonic-gate coalesce(lockcmp, sort_buf, g_nrecs_used); 15467c478bd9Sstevel@tonic-gate } 15477c478bd9Sstevel@tonic-gate 15487c478bd9Sstevel@tonic-gate /* 15497c478bd9Sstevel@tonic-gate * Coalesce callers if -w option specified 15507c478bd9Sstevel@tonic-gate */ 15517c478bd9Sstevel@tonic-gate if (g_wflag) { 15527c478bd9Sstevel@tonic-gate mergesort(lock_and_count_cmp_anywhere, 15537c478bd9Sstevel@tonic-gate sort_buf, merge_buf, g_nrecs_used); 15547c478bd9Sstevel@tonic-gate coalesce(lockcmp_anywhere, sort_buf, g_nrecs_used); 15557c478bd9Sstevel@tonic-gate } 15567c478bd9Sstevel@tonic-gate 15577c478bd9Sstevel@tonic-gate /* 15587c478bd9Sstevel@tonic-gate * Coalesce locks if -W option specified 15597c478bd9Sstevel@tonic-gate */ 15607c478bd9Sstevel@tonic-gate if (g_Wflag) { 15617c478bd9Sstevel@tonic-gate mergesort(site_and_count_cmp_anylock, 15627c478bd9Sstevel@tonic-gate sort_buf, merge_buf, g_nrecs_used); 15637c478bd9Sstevel@tonic-gate coalesce(sitecmp_anylock, sort_buf, g_nrecs_used); 15647c478bd9Sstevel@tonic-gate } 15657c478bd9Sstevel@tonic-gate 15667c478bd9Sstevel@tonic-gate /* 15677c478bd9Sstevel@tonic-gate * Sort data by contention count (ls_count) or total time (ls_time), 15687c478bd9Sstevel@tonic-gate * depending on g_Pflag. Override g_Pflag if time wasn't measured. 15697c478bd9Sstevel@tonic-gate */ 15707c478bd9Sstevel@tonic-gate if (g_recsize < LS_TIME) 15717c478bd9Sstevel@tonic-gate g_Pflag = 0; 15727c478bd9Sstevel@tonic-gate 15737c478bd9Sstevel@tonic-gate if (g_Pflag) 15747c478bd9Sstevel@tonic-gate mergesort(timecmp, sort_buf, merge_buf, g_nrecs_used); 15757c478bd9Sstevel@tonic-gate else 15767c478bd9Sstevel@tonic-gate mergesort(countcmp, sort_buf, merge_buf, g_nrecs_used); 15777c478bd9Sstevel@tonic-gate 15787c478bd9Sstevel@tonic-gate /* 15797c478bd9Sstevel@tonic-gate * Display data by event type 15807c478bd9Sstevel@tonic-gate */ 15817c478bd9Sstevel@tonic-gate first = &sort_buf[0]; 15827c478bd9Sstevel@tonic-gate while ((event = (*first)->ls_event) < LS_MAX_EVENTS) { 15837c478bd9Sstevel@tonic-gate current = first; 15847c478bd9Sstevel@tonic-gate while ((lsp = *current)->ls_event == event) 15857c478bd9Sstevel@tonic-gate current++; 15867c478bd9Sstevel@tonic-gate report_stats(out, first, current - first, ev_count[event], 15877c478bd9Sstevel@tonic-gate ev_time[event]); 15887c478bd9Sstevel@tonic-gate first = current; 15897c478bd9Sstevel@tonic-gate } 15907c478bd9Sstevel@tonic-gate 15917c478bd9Sstevel@tonic-gate return (0); 15927c478bd9Sstevel@tonic-gate } 15937c478bd9Sstevel@tonic-gate 15947c478bd9Sstevel@tonic-gate static char * 15957c478bd9Sstevel@tonic-gate format_symbol(char *buf, uintptr_t addr, int show_size) 15967c478bd9Sstevel@tonic-gate { 15977c478bd9Sstevel@tonic-gate uintptr_t symoff; 15987c478bd9Sstevel@tonic-gate char *symname; 15997c478bd9Sstevel@tonic-gate size_t symsize; 16007c478bd9Sstevel@tonic-gate 16017c478bd9Sstevel@tonic-gate symname = addr_to_sym(addr, &symoff, &symsize); 16027c478bd9Sstevel@tonic-gate 16037c478bd9Sstevel@tonic-gate if (show_size && symoff == 0) 16047c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s[%ld]", symname, (long)symsize); 16057c478bd9Sstevel@tonic-gate else if (symoff == 0) 16067c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s", symname); 16077c478bd9Sstevel@tonic-gate else if (symoff < 16 && bcmp(symname, "cpu[", 4) == 0) /* CPU+PIL */ 16087c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s+%ld", symname, (long)symoff); 16097c478bd9Sstevel@tonic-gate else if (symoff <= symsize || (symoff < 256 && addr != symoff)) 16107c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s+0x%llx", symname, 16117c478bd9Sstevel@tonic-gate (unsigned long long)symoff); 16127c478bd9Sstevel@tonic-gate else 16137c478bd9Sstevel@tonic-gate (void) sprintf(buf, "0x%llx", (unsigned long long)addr); 16147c478bd9Sstevel@tonic-gate return (buf); 16157c478bd9Sstevel@tonic-gate } 16167c478bd9Sstevel@tonic-gate 16177c478bd9Sstevel@tonic-gate static void 16187c478bd9Sstevel@tonic-gate report_stats(FILE *out, lsrec_t **sort_buf, size_t nrecs, uint64_t total_count, 16197c478bd9Sstevel@tonic-gate uint64_t total_time) 16207c478bd9Sstevel@tonic-gate { 16217c478bd9Sstevel@tonic-gate uint32_t event = sort_buf[0]->ls_event; 16227c478bd9Sstevel@tonic-gate lsrec_t *lsp; 16237c478bd9Sstevel@tonic-gate double ptotal = 0.0; 16247c478bd9Sstevel@tonic-gate double percent; 16257c478bd9Sstevel@tonic-gate int i, j, fr; 16267c478bd9Sstevel@tonic-gate int displayed; 16277c478bd9Sstevel@tonic-gate int first_bin, last_bin, max_bin_count, total_bin_count; 16287c478bd9Sstevel@tonic-gate int rectype; 16297c478bd9Sstevel@tonic-gate char buf[256]; 16307c478bd9Sstevel@tonic-gate char lhdr[80], chdr[80]; 16317c478bd9Sstevel@tonic-gate 16327c478bd9Sstevel@tonic-gate rectype = g_recsize; 16337c478bd9Sstevel@tonic-gate 16347c478bd9Sstevel@tonic-gate if (g_topn == 0) { 16357c478bd9Sstevel@tonic-gate (void) fprintf(out, "%20llu %s\n", 16367c478bd9Sstevel@tonic-gate g_rates == 0 ? total_count : 16377c478bd9Sstevel@tonic-gate ((unsigned long long)total_count * NANOSEC) / g_elapsed, 16387c478bd9Sstevel@tonic-gate g_event_info[event].ev_desc); 16397c478bd9Sstevel@tonic-gate return; 16407c478bd9Sstevel@tonic-gate } 16417c478bd9Sstevel@tonic-gate 16427c478bd9Sstevel@tonic-gate (void) sprintf(lhdr, "%s%s", 16437c478bd9Sstevel@tonic-gate g_Wflag ? "Hottest " : "", g_event_info[event].ev_lhdr); 16447c478bd9Sstevel@tonic-gate (void) sprintf(chdr, "%s%s", 16457c478bd9Sstevel@tonic-gate g_wflag ? "Hottest " : "", "Caller"); 16467c478bd9Sstevel@tonic-gate 16477c478bd9Sstevel@tonic-gate if (!g_pflag) 16487c478bd9Sstevel@tonic-gate (void) fprintf(out, 16497c478bd9Sstevel@tonic-gate "\n%s: %.0f events in %.3f seconds (%.0f events/sec)\n\n", 16507c478bd9Sstevel@tonic-gate g_event_info[event].ev_desc, (double)total_count, 16517c478bd9Sstevel@tonic-gate (double)g_elapsed / NANOSEC, 16527c478bd9Sstevel@tonic-gate (double)total_count * NANOSEC / g_elapsed); 16537c478bd9Sstevel@tonic-gate 16547c478bd9Sstevel@tonic-gate if (!g_pflag && rectype < LS_HIST) { 16557c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s", g_event_info[event].ev_units); 16567c478bd9Sstevel@tonic-gate (void) fprintf(out, "%5s %4s %4s %4s %8s %-22s %-24s\n", 16577c478bd9Sstevel@tonic-gate g_rates ? "ops/s" : "Count", 16587c478bd9Sstevel@tonic-gate g_gflag ? "genr" : "indv", 16597c478bd9Sstevel@tonic-gate "cuml", "rcnt", rectype >= LS_TIME ? buf : "", lhdr, chdr); 16607c478bd9Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 16617c478bd9Sstevel@tonic-gate "----------------------------------------------\n"); 16627c478bd9Sstevel@tonic-gate } 16637c478bd9Sstevel@tonic-gate 16647c478bd9Sstevel@tonic-gate displayed = 0; 16657c478bd9Sstevel@tonic-gate for (i = 0; i < nrecs; i++) { 16667c478bd9Sstevel@tonic-gate lsp = sort_buf[i]; 16677c478bd9Sstevel@tonic-gate 16687c478bd9Sstevel@tonic-gate if (displayed++ >= g_topn) 16697c478bd9Sstevel@tonic-gate break; 16707c478bd9Sstevel@tonic-gate 16717c478bd9Sstevel@tonic-gate if (g_pflag) { 16727c478bd9Sstevel@tonic-gate int j; 16737c478bd9Sstevel@tonic-gate 16747c478bd9Sstevel@tonic-gate (void) fprintf(out, "%u %u", 16757c478bd9Sstevel@tonic-gate lsp->ls_event, lsp->ls_count); 16767c478bd9Sstevel@tonic-gate (void) fprintf(out, " %s", 16777c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, g_cflag)); 16787c478bd9Sstevel@tonic-gate (void) fprintf(out, " %s", 16797c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_caller, 0)); 16807c478bd9Sstevel@tonic-gate (void) fprintf(out, " %f", 16817c478bd9Sstevel@tonic-gate (double)lsp->ls_refcnt / lsp->ls_count); 16827c478bd9Sstevel@tonic-gate if (rectype >= LS_TIME) 16837c478bd9Sstevel@tonic-gate (void) fprintf(out, " %llu", 16847c478bd9Sstevel@tonic-gate (unsigned long long)lsp->ls_time); 16857c478bd9Sstevel@tonic-gate if (rectype >= LS_HIST) { 16867c478bd9Sstevel@tonic-gate for (j = 0; j < 64; j++) 16877c478bd9Sstevel@tonic-gate (void) fprintf(out, " %u", 16887c478bd9Sstevel@tonic-gate lsp->ls_hist[j]); 16897c478bd9Sstevel@tonic-gate } 16907c478bd9Sstevel@tonic-gate for (j = 0; j < LS_MAX_STACK_DEPTH; j++) { 16917c478bd9Sstevel@tonic-gate if (rectype <= LS_STACK(j) || 16927c478bd9Sstevel@tonic-gate lsp->ls_stack[j] == 0) 16937c478bd9Sstevel@tonic-gate break; 16947c478bd9Sstevel@tonic-gate (void) fprintf(out, " %s", 16957c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[j], 0)); 16967c478bd9Sstevel@tonic-gate } 16977c478bd9Sstevel@tonic-gate (void) fprintf(out, "\n"); 16987c478bd9Sstevel@tonic-gate continue; 16997c478bd9Sstevel@tonic-gate } 17007c478bd9Sstevel@tonic-gate 17017c478bd9Sstevel@tonic-gate if (rectype >= LS_HIST) { 17027c478bd9Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 17037c478bd9Sstevel@tonic-gate "----------------------------------------------\n"); 17047c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%s", 17057c478bd9Sstevel@tonic-gate g_event_info[event].ev_units); 17067c478bd9Sstevel@tonic-gate (void) fprintf(out, "%5s %4s %4s %4s %8s %-22s %-24s\n", 17077c478bd9Sstevel@tonic-gate g_rates ? "ops/s" : "Count", 17087c478bd9Sstevel@tonic-gate g_gflag ? "genr" : "indv", 17097c478bd9Sstevel@tonic-gate "cuml", "rcnt", buf, lhdr, chdr); 17107c478bd9Sstevel@tonic-gate } 17117c478bd9Sstevel@tonic-gate 17127c478bd9Sstevel@tonic-gate if (g_Pflag && total_time != 0) 17137c478bd9Sstevel@tonic-gate percent = (lsp->ls_time * 100.00) / total_time; 17147c478bd9Sstevel@tonic-gate else 17157c478bd9Sstevel@tonic-gate percent = (lsp->ls_count * 100.00) / total_count; 17167c478bd9Sstevel@tonic-gate 17177c478bd9Sstevel@tonic-gate ptotal += percent; 17187c478bd9Sstevel@tonic-gate 17197c478bd9Sstevel@tonic-gate if (rectype >= LS_TIME) 17207c478bd9Sstevel@tonic-gate (void) sprintf(buf, "%llu", 17217c478bd9Sstevel@tonic-gate (unsigned long long)(lsp->ls_time / lsp->ls_count)); 17227c478bd9Sstevel@tonic-gate else 17237c478bd9Sstevel@tonic-gate buf[0] = '\0'; 17247c478bd9Sstevel@tonic-gate 17257c478bd9Sstevel@tonic-gate (void) fprintf(out, "%5llu ", 17267c478bd9Sstevel@tonic-gate g_rates == 0 ? lsp->ls_count : 17277c478bd9Sstevel@tonic-gate ((uint64_t)lsp->ls_count * NANOSEC) / g_elapsed); 17287c478bd9Sstevel@tonic-gate 17297c478bd9Sstevel@tonic-gate (void) fprintf(out, "%3.0f%% ", percent); 17307c478bd9Sstevel@tonic-gate 17317c478bd9Sstevel@tonic-gate if (g_gflag) 17327c478bd9Sstevel@tonic-gate (void) fprintf(out, "---- "); 17337c478bd9Sstevel@tonic-gate else 17347c478bd9Sstevel@tonic-gate (void) fprintf(out, "%3.0f%% ", ptotal); 17357c478bd9Sstevel@tonic-gate 17367c478bd9Sstevel@tonic-gate (void) fprintf(out, "%4.2f %8s ", 17377c478bd9Sstevel@tonic-gate (double)lsp->ls_refcnt / lsp->ls_count, buf); 17387c478bd9Sstevel@tonic-gate 17397c478bd9Sstevel@tonic-gate (void) fprintf(out, "%-22s ", 17407c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, g_cflag)); 17417c478bd9Sstevel@tonic-gate 17427c478bd9Sstevel@tonic-gate (void) fprintf(out, "%-24s\n", 17437c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_caller, 0)); 17447c478bd9Sstevel@tonic-gate 17457c478bd9Sstevel@tonic-gate if (rectype < LS_HIST) 17467c478bd9Sstevel@tonic-gate continue; 17477c478bd9Sstevel@tonic-gate 17487c478bd9Sstevel@tonic-gate (void) fprintf(out, "\n"); 17497c478bd9Sstevel@tonic-gate (void) fprintf(out, "%10s %31s %-9s %-24s\n", 17507c478bd9Sstevel@tonic-gate g_event_info[event].ev_units, 17517c478bd9Sstevel@tonic-gate "------ Time Distribution ------", 17527c478bd9Sstevel@tonic-gate g_rates ? "ops/s" : "count", 17537c478bd9Sstevel@tonic-gate rectype > LS_STACK(0) ? "Stack" : ""); 17547c478bd9Sstevel@tonic-gate 17557c478bd9Sstevel@tonic-gate first_bin = 0; 17567c478bd9Sstevel@tonic-gate while (lsp->ls_hist[first_bin] == 0) 17577c478bd9Sstevel@tonic-gate first_bin++; 17587c478bd9Sstevel@tonic-gate 17597c478bd9Sstevel@tonic-gate last_bin = 63; 17607c478bd9Sstevel@tonic-gate while (lsp->ls_hist[last_bin] == 0) 17617c478bd9Sstevel@tonic-gate last_bin--; 17627c478bd9Sstevel@tonic-gate 17637c478bd9Sstevel@tonic-gate max_bin_count = 0; 17647c478bd9Sstevel@tonic-gate total_bin_count = 0; 17657c478bd9Sstevel@tonic-gate for (j = first_bin; j <= last_bin; j++) { 17667c478bd9Sstevel@tonic-gate total_bin_count += lsp->ls_hist[j]; 17677c478bd9Sstevel@tonic-gate if (lsp->ls_hist[j] > max_bin_count) 17687c478bd9Sstevel@tonic-gate max_bin_count = lsp->ls_hist[j]; 17697c478bd9Sstevel@tonic-gate } 17707c478bd9Sstevel@tonic-gate 17717c478bd9Sstevel@tonic-gate /* 17727c478bd9Sstevel@tonic-gate * If we went a few frames below the caller, ignore them 17737c478bd9Sstevel@tonic-gate */ 17747c478bd9Sstevel@tonic-gate for (fr = 3; fr > 0; fr--) 17757c478bd9Sstevel@tonic-gate if (lsp->ls_stack[fr] == lsp->ls_caller) 17767c478bd9Sstevel@tonic-gate break; 17777c478bd9Sstevel@tonic-gate 17787c478bd9Sstevel@tonic-gate for (j = first_bin; j <= last_bin; j++) { 17797c478bd9Sstevel@tonic-gate uint_t depth = (lsp->ls_hist[j] * 30) / total_bin_count; 17807c478bd9Sstevel@tonic-gate (void) fprintf(out, "%10llu |%s%s %-9u ", 17817c478bd9Sstevel@tonic-gate 1ULL << j, 17827c478bd9Sstevel@tonic-gate "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + 30 - depth, 17837c478bd9Sstevel@tonic-gate " " + depth, 17847c478bd9Sstevel@tonic-gate g_rates == 0 ? lsp->ls_hist[j] : 17857c478bd9Sstevel@tonic-gate (uint_t)(((uint64_t)lsp->ls_hist[j] * NANOSEC) / 17867c478bd9Sstevel@tonic-gate g_elapsed)); 17877c478bd9Sstevel@tonic-gate if (rectype <= LS_STACK(fr) || lsp->ls_stack[fr] == 0) { 17887c478bd9Sstevel@tonic-gate (void) fprintf(out, "\n"); 17897c478bd9Sstevel@tonic-gate continue; 17907c478bd9Sstevel@tonic-gate } 17917c478bd9Sstevel@tonic-gate (void) fprintf(out, "%-24s\n", 17927c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 17937c478bd9Sstevel@tonic-gate fr++; 17947c478bd9Sstevel@tonic-gate } 17957c478bd9Sstevel@tonic-gate while (rectype > LS_STACK(fr) && lsp->ls_stack[fr] != 0) { 17967c478bd9Sstevel@tonic-gate (void) fprintf(out, "%15s %-36s %-24s\n", "", "", 17977c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 17987c478bd9Sstevel@tonic-gate fr++; 17997c478bd9Sstevel@tonic-gate } 18007c478bd9Sstevel@tonic-gate } 18017c478bd9Sstevel@tonic-gate 18027c478bd9Sstevel@tonic-gate if (!g_pflag) 18037c478bd9Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 18047c478bd9Sstevel@tonic-gate "----------------------------------------------\n"); 18057c478bd9Sstevel@tonic-gate 18067c478bd9Sstevel@tonic-gate (void) fflush(out); 18077c478bd9Sstevel@tonic-gate } 18087c478bd9Sstevel@tonic-gate 18097c478bd9Sstevel@tonic-gate static void 18107c478bd9Sstevel@tonic-gate report_trace(FILE *out, lsrec_t **sort_buf) 18117c478bd9Sstevel@tonic-gate { 18127c478bd9Sstevel@tonic-gate lsrec_t *lsp; 18137c478bd9Sstevel@tonic-gate int i, fr; 18147c478bd9Sstevel@tonic-gate int rectype; 18157c478bd9Sstevel@tonic-gate char buf[256], buf2[256]; 18167c478bd9Sstevel@tonic-gate 18177c478bd9Sstevel@tonic-gate rectype = g_recsize; 18187c478bd9Sstevel@tonic-gate 18197c478bd9Sstevel@tonic-gate if (!g_pflag) { 18207c478bd9Sstevel@tonic-gate (void) fprintf(out, "%5s %7s %11s %-24s %-24s\n", 18217c478bd9Sstevel@tonic-gate "Event", "Time", "Owner", "Lock", "Caller"); 18227c478bd9Sstevel@tonic-gate (void) fprintf(out, "---------------------------------" 18237c478bd9Sstevel@tonic-gate "----------------------------------------------\n"); 18247c478bd9Sstevel@tonic-gate } 18257c478bd9Sstevel@tonic-gate 18267c478bd9Sstevel@tonic-gate for (i = 0; i < g_nrecs_used; i++) { 18277c478bd9Sstevel@tonic-gate 18287c478bd9Sstevel@tonic-gate lsp = sort_buf[i]; 18297c478bd9Sstevel@tonic-gate 18307c478bd9Sstevel@tonic-gate if (lsp->ls_event >= LS_MAX_EVENTS || lsp->ls_count == 0) 18317c478bd9Sstevel@tonic-gate continue; 18327c478bd9Sstevel@tonic-gate 18337c478bd9Sstevel@tonic-gate (void) fprintf(out, "%2d %10llu %11p %-24s %-24s\n", 18347c478bd9Sstevel@tonic-gate lsp->ls_event, (unsigned long long)lsp->ls_time, 18357c478bd9Sstevel@tonic-gate (void *)lsp->ls_next, 18367c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_lock, 0), 18377c478bd9Sstevel@tonic-gate format_symbol(buf2, lsp->ls_caller, 0)); 18387c478bd9Sstevel@tonic-gate 18397c478bd9Sstevel@tonic-gate if (rectype <= LS_STACK(0)) 18407c478bd9Sstevel@tonic-gate continue; 18417c478bd9Sstevel@tonic-gate 18427c478bd9Sstevel@tonic-gate /* 18437c478bd9Sstevel@tonic-gate * If we went a few frames below the caller, ignore them 18447c478bd9Sstevel@tonic-gate */ 18457c478bd9Sstevel@tonic-gate for (fr = 3; fr > 0; fr--) 18467c478bd9Sstevel@tonic-gate if (lsp->ls_stack[fr] == lsp->ls_caller) 18477c478bd9Sstevel@tonic-gate break; 18487c478bd9Sstevel@tonic-gate 18497c478bd9Sstevel@tonic-gate while (rectype > LS_STACK(fr) && lsp->ls_stack[fr] != 0) { 18507c478bd9Sstevel@tonic-gate (void) fprintf(out, "%53s %-24s\n", "", 18517c478bd9Sstevel@tonic-gate format_symbol(buf, lsp->ls_stack[fr], 0)); 18527c478bd9Sstevel@tonic-gate fr++; 18537c478bd9Sstevel@tonic-gate } 18547c478bd9Sstevel@tonic-gate (void) fprintf(out, "\n"); 18557c478bd9Sstevel@tonic-gate } 18567c478bd9Sstevel@tonic-gate 18577c478bd9Sstevel@tonic-gate (void) fflush(out); 18587c478bd9Sstevel@tonic-gate } 1859