📄 lockstat.c
字号:
/* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only. * See the file usr/src/LICENSING.NOTICE in this distribution or * http://www.opensolaris.org/license/ for details. */#pragma ident "@(#)lockstat.c 1.12 04/11/17 SMI"#include <stdio.h>#include <stddef.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <strings.h>#include <ctype.h>#include <fcntl.h>#include <unistd.h>#include <errno.h>#include <limits.h>#include <sys/types.h>#include <sys/modctl.h>#include <sys/stat.h>#include <sys/wait.h>#include <dtrace.h>#include <sys/lockstat.h>#include <alloca.h>#include <signal.h>#define LS_MAX_STACK_DEPTH 50#define LS_MAX_EVENTS 64typedef struct lsrec { struct lsrec *ls_next; /* next in hash chain */ uintptr_t ls_lock; /* lock address */ uintptr_t ls_caller; /* caller address */ uint32_t ls_count; /* cumulative event count */ uint32_t ls_event; /* type of event */ uintptr_t ls_refcnt; /* cumulative reference count */ uint64_t ls_time; /* cumulative event duration */ uint32_t ls_hist[64]; /* log2(duration) histogram */ uintptr_t ls_stack[LS_MAX_STACK_DEPTH];} lsrec_t;typedef struct lsdata { struct lsrec *lsd_next; /* next available */ int lsd_count; /* number of records */} lsdata_t;/* * Definitions for the types of experiments which can be run. They are * listed in increasing order of memory cost and processing time cost. * The numerical value of each type is the number of bytes needed per record. */#define LS_BASIC offsetof(lsrec_t, ls_time)#define LS_TIME offsetof(lsrec_t, ls_hist[0])#define LS_HIST offsetof(lsrec_t, ls_stack[0])#define LS_STACK(depth) offsetof(lsrec_t, ls_stack[depth])static void report_stats(FILE *, lsrec_t **, size_t, uint64_t, uint64_t);static void report_trace(FILE *, lsrec_t **);extern int symtab_init(void);extern char *addr_to_sym(uintptr_t, uintptr_t *, size_t *);extern uintptr_t sym_to_addr(char *name);extern size_t sym_size(char *name);extern char *strtok_r(char *, const char *, char **);#define DEFAULT_NRECS 10000#define DEFAULT_HZ 97#define MAX_HZ 1000static int g_stkdepth;static int g_topn = INT_MAX;static hrtime_t g_elapsed;static int g_rates = 0;static int g_pflag = 0;static int g_Pflag = 0;static int g_wflag = 0;static int g_Wflag = 0;static int g_cflag = 0;static int g_kflag = 0;static int g_gflag = 0;static int g_Vflag = 0;static int g_tracing = 0;static size_t g_recsize;static size_t g_nrecs;static int g_nrecs_used;static uchar_t g_enabled[LS_MAX_EVENTS];static hrtime_t g_min_duration[LS_MAX_EVENTS];static dtrace_hdl_t *g_dtp;static char *g_predicate;static char *g_ipredicate;static char *g_prog;static int g_proglen;static int g_dropped;typedef struct ls_event_info { char ev_type; char ev_lhdr[20]; char ev_desc[80]; char ev_units[10]; char ev_name[DTRACE_NAMELEN]; char *ev_predicate; char *ev_acquire;} ls_event_info_t;static ls_event_info_t g_event_info[LS_MAX_EVENTS] = { { 'C', "Lock", "Adaptive mutex spin", "spin", "lockstat:::adaptive-spin" }, { 'C', "Lock", "Adaptive mutex block", "nsec", "lockstat:::adaptive-block" }, { 'C', "Lock", "Spin lock spin", "spin", "lockstat:::spin-spin" }, { 'C', "Lock", "Thread lock spin", "spin", "lockstat:::thread-spin" }, { 'C', "Lock", "R/W writer blocked by writer", "nsec", "lockstat:::rw-block", "arg2 == 0 && arg3 == 1" }, { 'C', "Lock", "R/W writer blocked by readers", "nsec", "lockstat:::rw-block", "arg2 == 0 && arg3 == 0 && arg4" }, { 'C', "Lock", "R/W reader blocked by writer", "nsec", "lockstat:::rw-block", "arg2 != 0 && arg3 == 1" }, { 'C', "Lock", "R/W reader blocked by write wanted", "nsec", "lockstat:::rw-block", "arg2 != 0 && arg3 == 0 && arg4" }, { 'C', "Lock", "Unknown event (type 8)", "units" }, { 'C', "Lock", "Unknown event (type 9)", "units" }, { 'C', "Lock", "Unknown event (type 10)", "units" }, { 'C', "Lock", "Unknown event (type 11)", "units" }, { 'C', "Lock", "Unknown event (type 12)", "units" }, { 'C', "Lock", "Unknown event (type 13)", "units" }, { 'C', "Lock", "Unknown event (type 14)", "units" }, { 'C', "Lock", "Unknown event (type 15)", "units" }, { 'C', "Lock", "Unknown event (type 16)", "units" }, { 'C', "Lock", "Unknown event (type 17)", "units" }, { 'C', "Lock", "Unknown event (type 18)", "units" }, { 'C', "Lock", "Unknown event (type 19)", "units" }, { 'C', "Lock", "Unknown event (type 20)", "units" }, { 'C', "Lock", "Unknown event (type 21)", "units" }, { 'C', "Lock", "Unknown event (type 22)", "units" }, { 'C', "Lock", "Unknown event (type 23)", "units" }, { 'C', "Lock", "Unknown event (type 24)", "units" }, { 'C', "Lock", "Unknown event (type 25)", "units" }, { 'C', "Lock", "Unknown event (type 26)", "units" }, { 'C', "Lock", "Unknown event (type 27)", "units" }, { 'C', "Lock", "Unknown event (type 28)", "units" }, { 'C', "Lock", "Unknown event (type 29)", "units" }, { 'C', "Lock", "Unknown event (type 30)", "units" }, { 'C', "Lock", "Unknown event (type 31)", "units" }, { 'H', "Lock", "Adaptive mutex hold", "nsec", "lockstat:::adaptive-release", NULL, "lockstat:::adaptive-acquire" }, { 'H', "Lock", "Spin lock hold", "nsec", "lockstat:::spin-release", NULL, "lockstat:::spin-acquire" }, { 'H', "Lock", "R/W writer hold", "nsec", "lockstat:::rw-release", "arg1 == 0", "lockstat:::rw-acquire" }, { 'H', "Lock", "R/W reader hold", "nsec", "lockstat:::rw-release", "arg1 != 0", "lockstat:::rw-acquire" }, { 'H', "Lock", "Unknown event (type 36)", "units" }, { 'H', "Lock", "Unknown event (type 37)", "units" }, { 'H', "Lock", "Unknown event (type 38)", "units" }, { 'H', "Lock", "Unknown event (type 39)", "units" }, { 'H', "Lock", "Unknown event (type 40)", "units" }, { 'H', "Lock", "Unknown event (type 41)", "units" }, { 'H', "Lock", "Unknown event (type 42)", "units" }, { 'H', "Lock", "Unknown event (type 43)", "units" }, { 'H', "Lock", "Unknown event (type 44)", "units" }, { 'H', "Lock", "Unknown event (type 45)", "units" }, { 'H', "Lock", "Unknown event (type 46)", "units" }, { 'H', "Lock", "Unknown event (type 47)", "units" }, { 'H', "Lock", "Unknown event (type 48)", "units" }, { 'H', "Lock", "Unknown event (type 49)", "units" }, { 'H', "Lock", "Unknown event (type 50)", "units" }, { 'H', "Lock", "Unknown event (type 51)", "units" }, { 'H', "Lock", "Unknown event (type 52)", "units" }, { 'H', "Lock", "Unknown event (type 53)", "units" }, { 'H', "Lock", "Unknown event (type 54)", "units" }, { 'H', "Lock", "Unknown event (type 55)", "units" }, { 'I', "CPU+PIL", "Profiling interrupt", "nsec", "profile:::profile-97", NULL }, { 'I', "Lock", "Unknown event (type 57)", "units" }, { 'I', "Lock", "Unknown event (type 58)", "units" }, { 'I', "Lock", "Unknown event (type 59)", "units" }, { 'E', "Lock", "Recursive lock entry detected", "(N/A)", "lockstat:::rw-release", NULL, "lockstat:::rw-acquire" }, { 'E', "Lock", "Lockstat enter failure", "(N/A)" }, { 'E', "Lock", "Lockstat exit failure", "nsec" }, { 'E', "Lock", "Lockstat record failure", "(N/A)" },};static voidfail(int do_perror, const char *message, ...){ va_list args; int save_errno = errno; va_start(args, message); (void) fprintf(stderr, "lockstat: "); (void) vfprintf(stderr, message, args); va_end(args); if (do_perror) (void) fprintf(stderr, ": %s", strerror(save_errno)); (void) fprintf(stderr, "\n"); exit(2);}static voiddfail(const char *message, ...){ va_list args; va_start(args, message); (void) fprintf(stderr, "lockstat: "); (void) vfprintf(stderr, message, args); va_end(args); (void) fprintf(stderr, ": %s\n", dtrace_errmsg(g_dtp, dtrace_errno(g_dtp))); exit(2);}static voidshow_events(char event_type, char *desc){ int i, first = -1, last; for (i = 0; i < LS_MAX_EVENTS; i++) { ls_event_info_t *evp = &g_event_info[i]; if (evp->ev_type != event_type || strncmp(evp->ev_desc, "Unknown event", 13) == 0) continue; if (first == -1) first = i; last = i; } (void) fprintf(stderr, "\n%s events (lockstat -%c or lockstat -e %d-%d):\n\n", desc, event_type, first, last); for (i = first; i <= last; i++) (void) fprintf(stderr, "%4d = %s\n", i, g_event_info[i].ev_desc);}static voidusage(void){ (void) fprintf(stderr, "Usage: lockstat [options] command [args]\n" "\nEvent selection options:\n\n" " -C watch contention events [on by default]\n" " -E watch error events [off by default]\n" " -H watch hold events [off by default]\n" " -I watch interrupt events [off by default]\n" " -A watch all lock events [equivalent to -CH]\n" " -e event_list only watch the specified events (shown below);\n" " <event_list> is a comma-separated list of\n" " events or ranges of events, e.g. 1,4-7,35\n" " -i rate interrupt rate for -I [default: %d Hz]\n" "\nData gathering options:\n\n" " -b basic statistics (lock, caller, event count)\n" " -t timing for all events [default]\n" " -h histograms for event times\n" " -s depth stack traces <depth> deep\n" "\nData filtering options:\n\n" " -n nrecords maximum number of data records [default: %d]\n" " -l lock[,size] only watch <lock>, which can be specified as a\n" " symbolic name or hex address; <size> defaults\n" " to the ELF symbol size if available, 1 if not\n" " -f func[,size] only watch events generated by <func>\n" " -d duration only watch events longer than <duration>\n" " -T trace (rather than sample) events\n" "\nData reporting options:\n\n" " -c coalesce lock data for arrays like pse_mutex[]\n" " -k coalesce PCs within functions\n" " -g show total events generated by function\n" " -w wherever: don't distinguish events by caller\n" " -W whichever: don't distinguish events by lock\n" " -R display rates rather than counts\n" " -p parsable output format (awk(1)-friendly)\n" " -P sort lock data by (count * avg_time) product\n" " -D n only display top <n> events of each type\n" " -o filename send output to <filename>\n", DEFAULT_HZ, DEFAULT_NRECS); show_events('C', "Contention"); show_events('H', "Hold-time"); show_events('I', "Interrupt"); show_events('E', "Error"); (void) fprintf(stderr, "\n"); exit(1);}static intlockcmp(lsrec_t *a, lsrec_t *b){ int i; if (a->ls_event < b->ls_event) return (-1); if (a->ls_event > b->ls_event) return (1); for (i = g_stkdepth - 1; i >= 0; i--) { if (a->ls_stack[i] < b->ls_stack[i]) return (-1); if (a->ls_stack[i] > b->ls_stack[i]) return (1); } if (a->ls_caller < b->ls_caller) return (-1); if (a->ls_caller > b->ls_caller) return (1); if (a->ls_lock < b->ls_lock) return (-1); if (a->ls_lock > b->ls_lock) return (1); return (0);}static intcountcmp(lsrec_t *a, lsrec_t *b){ if (a->ls_event < b->ls_event) return (-1); if (a->ls_event > b->ls_event) return (1); return (b->ls_count - a->ls_count);}static inttimecmp(lsrec_t *a, lsrec_t *b){ if (a->ls_event < b->ls_event) return (-1); if (a->ls_event > b->ls_event) return (1); if (a->ls_time < b->ls_time) return (1); if (a->ls_time > b->ls_time) return (-1); return (0);}static intlockcmp_anywhere(lsrec_t *a, lsrec_t *b){ if (a->ls_event < b->ls_event) return (-1); if (a->ls_event > b->ls_event) return (1); if (a->ls_lock < b->ls_lock) return (-1); if (a->ls_lock > b->ls_lock) return (1); return (0);}static intlock_and_count_cmp_anywhere(lsrec_t *a, lsrec_t *b){ if (a->ls_event < b->ls_event) return (-1); if (a->ls_event > b->ls_event) return (1); if (a->ls_lock < b->ls_lock) return (-1); if (a->ls_lock > b->ls_lock) return (1); return (b->ls_count - a->ls_count);}static intsitecmp_anylock(lsrec_t *a, lsrec_t *b){ int i; if (a->ls_event < b->ls_event) return (-1); if (a->ls_event > b->ls_event) return (1); for (i = g_stkdepth - 1; i >= 0; i--) { if (a->ls_stack[i] < b->ls_stack[i]) return (-1); if (a->ls_stack[i] > b->ls_stack[i]) return (1); } if (a->ls_caller < b->ls_caller) return (-1); if (a->ls_caller > b->ls_caller) return (1); return (0);}static intsite_and_count_cmp_anylock(lsrec_t *a, lsrec_t *b){ int i; if (a->ls_event < b->ls_event) return (-1); if (a->ls_event > b->ls_event) return (1); for (i = g_stkdepth - 1; i >= 0; i--) { if (a->ls_stack[i] < b->ls_stack[i]) return (-1); if (a->ls_stack[i] > b->ls_stack[i]) return (1); } if (a->ls_caller < b->ls_caller) return (-1); if (a->ls_caller > b->ls_caller) return (1); return (b->ls_count - a->ls_count);}static voidmergesort(int (*cmp)(lsrec_t *, lsrec_t *), lsrec_t **a, lsrec_t **b, int n){ int m = n / 2; int i, j; if (m > 1) mergesort(cmp, a, b, m); if (n - m > 1) mergesort(cmp, a + m, b + m, n - m); for (i = m; i > 0; i--) b[i - 1] = a[i - 1]; for (j = m - 1; j < n - 1; j++) b[n + m - j - 2] = a[j + 1]; while (i < j) *a++ = cmp(b[i], b[j]) < 0 ? b[i++] : b[j--]; *a = b[i];}static voidcoalesce(int (*cmp)(lsrec_t *, lsrec_t *), lsrec_t **lock, int n){ int i, j; lsrec_t *target, *current; target = lock[0]; for (i = 1; i < n; i++) { current = lock[i]; if (cmp(current, target) != 0) { target = current; continue; } current->ls_event = LS_MAX_EVENTS; target->ls_count += current->ls_count; target->ls_refcnt += current->ls_refcnt; if (g_recsize < LS_TIME) continue; target->ls_time += current->ls_time; if (g_recsize < LS_HIST) continue; for (j = 0; j < 64; j++) target->ls_hist[j] += current->ls_hist[j]; }}static voidcoalesce_symbol(uintptr_t *addrp){ uintptr_t symoff; size_t symsize; if (addr_to_sym(*addrp, &symoff, &symsize) != NULL && symoff < symsize) *addrp -= symoff;}static voidpredicate_add(char **pred, char *what, char *cmp, uintptr_t value){ char *new; int len, newlen; if (what == NULL) return; if (*pred == NULL) { *pred = malloc(1); *pred[0] = '\0'; } len = strlen(*pred); newlen = len + strlen(what) + 32 + strlen("( && )"); new = malloc(newlen); if (*pred[0] != '\0') { if (cmp != NULL) { (void) sprintf(new, "(%s) && (%s %s 0x%p)", *pred, what, cmp, (void *)value); } else { (void) sprintf(new, "(%s) && (%s)", *pred, what); } } else { if (cmp != NULL) { (void) sprintf(new, "%s %s 0x%p", what, cmp, (void *)value); } else { (void) sprintf(new, "%s", what); } } free(*pred); *pred = new;}static voidpredicate_destroy(char **pred){ free(*pred); *pred = NULL;}static voidfilter_add(char **filt, char *what, uintptr_t base, uintptr_t size){ char buf[256], *c = buf, *new; int len, newlen; if (*filt == NULL) { *filt = malloc(1); *filt[0] = '\0'; } (void) sprintf(c, "%s(%s >= 0x%p && %s < 0x%p)", *filt[0] != '\0' ? " || " : "", what, (void *)base, what, (void *)(base + size)); newlen = (len = strlen(*filt) + 1) + strlen(c); new = malloc(newlen); bcopy(*filt, new, len); (void) strcat(new, c); free(*filt); *filt = new;}static voidfilter_destroy(char **filt){ free(*filt); *filt = NULL;}static voiddprog_add(const char *fmt, ...){ va_list args; int size, offs; char c; va_start(args, fmt); size = vsnprintf(&c, 1, fmt, args) + 1; if (g_proglen == 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -