#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * lockstat.c * * Utility to display kernel lock contention statistics. * Usage: * lockstat [all, spin, mutex, rw, ] {} {abs} * * Argument 1 specifies the type of lock to display contention statistics * for; alternatively, a lock group (a logically grouped set of locks, * which can encompass multiple types of locks) can be specified by name. * When argument 1 is "all", statistics are displayed for all lock groups * which have statistics enabled. * Lock types include mutexes, reader-writer locks and spin locks. * Note that support for gathering contention statistics may not be present * for all types of locks on all platforms. * * Argument 2 specifies a periodic interval. The program will display an * updated list of statistics every seconds. This * argument is optional. The updates display the deltas from the previous * set of statistics, unless "abs" is specified as argument 3. * * Argument 3, if "abs", causes the periodically refreshed lock statistics * to be displayed as absolute values rather than deltas from the previous * display. * * Types of statistics: * Acquisitions: These can include both normal acquisitions, as well * as acquisition attempts. These are listed in the first column. * Examples include calls to lck_mtx_lock and lck_mtx_try_lock * Misses: Incremented if a lock acquisition attempt failed, due to * contention. * Waits (Meaningful only for lock types that can block): Incremented * if a lock acquisition attempt proceeded to block. * * Direct Waits (currently implemented only on i386/x86_64): For adaptive * locks, such as mutexes, incremented if the owner of the mutex * wasn't active on another processor at the time of the lock * attempt. This indicates that no adaptive spin occurred. */ /* * HISTORY * 2005: Bernard Semeria * Created. * 2006: Derek Kumar * Display i386 specific stats, fix incremental display, add * explanatory block comment. */ void usage(void); void print_spin_hdr(void); void print_spin(int requested, lockgroup_info_t *lockgroup); void print_all_spin(lockgroup_info_t *lockgroup); void print_mutex_hdr(void); void print_mutex(int requested, lockgroup_info_t *lockgroup); void print_all_mutex(lockgroup_info_t *lockgroup); void print_rw_hdr(void); void print_rw(int requested, lockgroup_info_t *lockgroup); void print_all_rw(lockgroup_info_t *lockgroup); void prime_lockgroup_deltas(void); void get_lockgroup_deltas(void); char *pgmname; mach_port_t host_control; lockgroup_info_t *lockgroup_info, *lockgroup_start, *lockgroup_deltas; unsigned int count; unsigned int gDebug = 1; int main(int argc, char **argv) { kern_return_t kr; int arg2; unsigned int i; int found; setlinebuf(stdout); pgmname = argv[0]; gDebug = (NULL != strstr(argv[0], "debug")); host_control = mach_host_self(); kr = host_lockgroup_info(host_control, &lockgroup_info, &count); if (kr != KERN_SUCCESS) { mach_error("host_statistics", kr); exit (EXIT_FAILURE); } if (gDebug) { printf("count = %d\n", count); for (i = 0; i < count; i++) { printf("%s\n",lockgroup_info[i].lockgroup_name); } } switch (argc) { case 2: if (strcmp(argv[1], "all") == 0) { print_spin_hdr(); print_all_spin(lockgroup_info); print_mutex_hdr(); print_all_mutex(lockgroup_info); print_rw_hdr(); print_all_rw(lockgroup_info); } else if (strcmp(argv[1], "spin") == 0) { print_spin_hdr(); print_all_spin(lockgroup_info); } else if (strcmp(argv[1], "mutex") == 0) { print_mutex_hdr(); print_all_mutex(lockgroup_info); } else if (strcmp(argv[1], "rw") == 0) { print_rw_hdr(); print_all_rw(lockgroup_info); } else { found = 0; for (i = 0;i < count;i++) { if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) { found = 1; print_spin_hdr(); print_spin(i, lockgroup_info); print_mutex_hdr(); print_mutex(i, lockgroup_info); print_rw_hdr(); print_rw(i, lockgroup_info); break; } } if (found == 0) { usage(); } } break; case 3: if (sscanf(argv[2], "%d", &arg2) != 1) { usage(); } if (arg2 < 0) { usage(); } prime_lockgroup_deltas(); if (strcmp(argv[1], "all") == 0) { while (1) { sleep(arg2); get_lockgroup_deltas(); print_spin_hdr(); print_all_spin(lockgroup_deltas); print_mutex_hdr(); print_all_mutex(lockgroup_deltas); print_rw_hdr(); print_all_rw(lockgroup_deltas); } } else if (strcmp(argv[1], "spin") == 0) { while (1) { sleep(arg2); get_lockgroup_deltas(); print_spin_hdr(); print_all_spin(lockgroup_deltas); } } else if (strcmp(argv[1], "mutex") == 0) { while (1) { sleep(arg2); get_lockgroup_deltas(); print_mutex_hdr(); print_all_mutex(lockgroup_deltas); } } else if (strcmp(argv[1], "rw") == 0) { while (1) { sleep(arg2); get_lockgroup_deltas(); print_rw_hdr(); print_all_rw(lockgroup_deltas); } } else { found = 0; for (i = 0;i < count;i++) { if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) { found = 1; while (1) { sleep(arg2); get_lockgroup_deltas(); print_spin_hdr(); print_spin(i, lockgroup_deltas); print_mutex_hdr(); print_mutex(i, lockgroup_deltas); print_rw_hdr(); print_rw(i, lockgroup_deltas); } } } if (found == 0) { usage(); } } break; case 4: if (strcmp(argv[3], "abs") != 0) { usage(); } if (sscanf(argv[2], "%d", &arg2) != 1) { usage(); } if (strcmp(argv[1], "all") == 0) { while (1) { print_spin_hdr(); print_all_spin(lockgroup_info); print_mutex_hdr(); print_all_mutex(lockgroup_info); print_rw_hdr(); print_all_rw(lockgroup_info); sleep(arg2); } } else if (strcmp(argv[1], "spin") == 0) { while (1) {print_all_spin(lockgroup_info); sleep(arg2); } } else if (strcmp(argv[1], "mutex") == 0) { print_mutex_hdr(); while (1) {print_all_mutex(lockgroup_info); sleep(arg2); } } else if (strcmp(argv[1], "rw") == 0) { print_rw_hdr(); while (1) {print_all_rw(lockgroup_info); sleep(arg2); } } else { found = 0; for (i = 0;i < count;i++) { if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) { found = 1; while (1) { print_spin_hdr(); print_spin(i, lockgroup_info); print_mutex_hdr(); print_mutex(i, lockgroup_info); print_rw_hdr(); print_rw(i, lockgroup_info); sleep(arg2); } } } if (found == 0) { usage(); } } break; default: usage(); break; } exit(0); } void usage() { fprintf(stderr, "Usage: %s [all, spin, mutex, rw, ] {} {abs}\n", pgmname); exit(EXIT_FAILURE); } void print_spin_hdr(void) { printf(" Spinlock acquires misses Name\n"); } void print_spin(int requested, lockgroup_info_t *lockgroup) { lockgroup_info_t *curptr = &lockgroup[requested]; if (curptr->lock_spin_cnt != 0 && curptr->lock_spin_util_cnt != 0) { printf("%16lld ", curptr->lock_spin_util_cnt); printf("%16lld ", curptr->lock_spin_miss_cnt); printf("%-14s\n", curptr->lockgroup_name); } } void print_all_spin(lockgroup_info_t *lockgroup) { unsigned int i; for (i = 0;i < count;i++) print_spin(i, lockgroup); printf("\n"); } void print_mutex_hdr(void) { #if defined(__i386__) || defined(__x86_64__) printf("Mutex lock attempts Misses Waits Direct Waits Name\n"); #else printf(" mutex locks misses waits name\n"); #endif } void print_mutex(int requested, lockgroup_info_t *lockgroup) { lockgroup_info_t *curptr = &lockgroup[requested]; if (curptr->lock_mtx_cnt != 0 && curptr->lock_mtx_util_cnt != 0) { printf("%16lld ", curptr->lock_mtx_util_cnt); #if defined(__i386__) || defined(__x86_64__) printf("%10lld %10lld %10lld ", curptr->lock_mtx_miss_cnt, curptr->lock_mtx_wait_cnt, curptr->lock_mtx_held_cnt); #else printf("%16lld %16lld ", curptr->lock_mtx_miss_cnt, curptr->lock_mtx_wait_cnt); #endif printf("%-14s\n", curptr->lockgroup_name); } } void print_all_mutex(lockgroup_info_t *lockgroup) { unsigned int i; for (i = 0;i < count;i++) print_mutex(i, lockgroup); printf("\n"); } void print_rw_hdr(void) { printf(" RW locks Misses Waits Name\n"); } void print_rw(int requested, lockgroup_info_t *lockgroup) { lockgroup_info_t *curptr = &lockgroup[requested]; if (curptr->lock_rw_cnt != 0 && curptr->lock_rw_util_cnt != 0) { printf("%16lld ", curptr->lock_rw_util_cnt); printf("%16lld %16lld ", curptr->lock_rw_miss_cnt, curptr->lock_rw_wait_cnt); printf("%-14s\n", curptr->lockgroup_name); } } void print_all_rw(lockgroup_info_t *lockgroup) { unsigned int i; for (i = 0;i < count;i++) print_rw(i, lockgroup); printf("\n"); } void prime_lockgroup_deltas(void) { lockgroup_start = calloc(count, sizeof(lockgroup_info_t)); if (lockgroup_start == NULL) { fprintf(stderr, "Can't allocate memory for lockgroup info\n"); exit (EXIT_FAILURE); } memcpy(lockgroup_start, lockgroup_info, count * sizeof(lockgroup_info_t)); lockgroup_deltas = calloc(count, sizeof(lockgroup_info_t)); if (lockgroup_deltas == NULL) { fprintf(stderr, "Can't allocate memory for lockgroup info\n"); exit (EXIT_FAILURE); } } void get_lockgroup_deltas(void) { kern_return_t kr; unsigned int i; kr = host_lockgroup_info(host_control, &lockgroup_info, &count); if (kr != KERN_SUCCESS) { mach_error("host_statistics", kr); exit (EXIT_FAILURE); } memcpy(lockgroup_deltas, lockgroup_info, count * sizeof(lockgroup_info_t)); for (i = 0; i < count; i++) { lockgroup_deltas[i].lock_spin_util_cnt = lockgroup_info[i].lock_spin_util_cnt - lockgroup_start[i].lock_spin_util_cnt; lockgroup_deltas[i].lock_spin_miss_cnt = lockgroup_info[i].lock_spin_miss_cnt - lockgroup_start[i].lock_spin_miss_cnt; lockgroup_deltas[i].lock_mtx_util_cnt = lockgroup_info[i].lock_mtx_util_cnt - lockgroup_start[i].lock_mtx_util_cnt; lockgroup_deltas[i].lock_mtx_miss_cnt = lockgroup_info[i].lock_mtx_miss_cnt - lockgroup_start[i].lock_mtx_miss_cnt; lockgroup_deltas[i].lock_mtx_wait_cnt = lockgroup_info[i].lock_mtx_wait_cnt - lockgroup_start[i].lock_mtx_wait_cnt; lockgroup_deltas[i].lock_mtx_held_cnt = lockgroup_info[i].lock_mtx_held_cnt - lockgroup_start[i].lock_mtx_held_cnt; lockgroup_deltas[i].lock_rw_util_cnt = lockgroup_info[i].lock_rw_util_cnt - lockgroup_start[i].lock_rw_util_cnt; lockgroup_deltas[i].lock_rw_miss_cnt = lockgroup_info[i].lock_rw_miss_cnt - lockgroup_start[i].lock_rw_miss_cnt; lockgroup_deltas[i].lock_rw_wait_cnt = lockgroup_info[i].lock_rw_wait_cnt - lockgroup_start[i].lock_rw_wait_cnt; } memcpy(lockgroup_start, lockgroup_info, count * sizeof(lockgroup_info_t)); }