1#include <mach/mach.h>
2#include <stdlib.h>
3#include <stdio.h>
4#include <signal.h>
5#include <unistd.h>
6#include <sys/time.h>
7#include <time.h>
8#include <mach/error.h>
9#include <mach/mach_error.h>
10#include <mach/mig_errors.h>
11#include <mach/machine.h>
12#include <mach/processor_info.h>
13#include <assert.h>
14#include <nlist.h>
15#include <fcntl.h>
16#include <string.h>
17#include <mach/mach.h>
18#include <mach/host_info.h>
19
20/*
21 *	lockstat.c
22 *
23 *	Utility to display kernel lock contention statistics.
24 *	Usage:
25 *	lockstat [all, spin, mutex, rw, <lock group name>] {<repeat interval>} {abs}
26 *
27 *	Argument 1 specifies the type of lock to display contention statistics
28 *	for; alternatively, a lock group (a logically grouped set of locks,
29 *	which can encompass multiple types of locks) can be specified by name.
30 *	When argument 1 is "all", statistics are displayed for all lock groups
31 *	which have statistics enabled.
32 *	Lock types include mutexes, reader-writer locks and spin locks.
33 *	Note that support for gathering contention statistics may not be present
34 *	for all types of locks on all platforms.
35 *
36 *	Argument 2 specifies a periodic interval. The program will display an
37 *	updated list of statistics every <repeat interval> seconds. This
38 *	argument is optional. The updates display the deltas from the previous
39 *	set of statistics, unless "abs" is specified as argument 3.
40 *
41 *	Argument 3, if "abs", causes the periodically refreshed lock statistics
42 *	to be displayed as absolute values rather than deltas from the previous
43 *	display.
44 *
45 *	Types of statistics:
46 *	Acquisitions: These can include both normal acquisitions, as well
47 *	as acquisition attempts. These are listed in the first column.
48 *	Examples include calls to lck_mtx_lock and lck_mtx_try_lock
49 *	Misses: Incremented if  a lock acquisition attempt failed, due to
50 *	contention.
51 *	Waits (Meaningful only for lock types that can block): Incremented
52 *	if a lock acquisition attempt proceeded to block.
53 *
54 *	Direct Waits (currently implemented only on i386/x86_64): For adaptive
55 *	locks, such as mutexes, incremented if the owner of the mutex
56 *	wasn't active on another processor at the time of the lock
57 *	attempt. This indicates that no adaptive spin occurred.
58 */
59
60/*
61 * HISTORY
62 * 2005: Bernard Semeria
63 *		Created.
64 * 2006: Derek Kumar
65 *		Display i386 specific stats, fix incremental display, add
66 *		explanatory block comment.
67 */
68void usage(void);
69void print_spin_hdr(void);
70void print_spin(int requested, lockgroup_info_t *lockgroup);
71void print_all_spin(lockgroup_info_t *lockgroup);
72void print_mutex_hdr(void);
73void print_mutex(int requested, lockgroup_info_t *lockgroup);
74void print_all_mutex(lockgroup_info_t *lockgroup);
75void print_rw_hdr(void);
76void print_rw(int requested, lockgroup_info_t *lockgroup);
77void print_all_rw(lockgroup_info_t *lockgroup);
78void prime_lockgroup_deltas(void);
79void get_lockgroup_deltas(void);
80
81char *pgmname;
82mach_port_t host_control;
83
84lockgroup_info_t	*lockgroup_info, *lockgroup_start, *lockgroup_deltas;
85unsigned int		count;
86
87unsigned int		gDebug = 1;
88
89int
90main(int argc, char **argv)
91{
92	kern_return_t		kr;
93	int 			arg2;
94	unsigned int 		i;
95	int 			found;
96
97	setlinebuf(stdout);
98
99	pgmname = argv[0];
100	gDebug = (NULL != strstr(argv[0], "debug"));
101
102	host_control = mach_host_self();
103
104	kr = host_lockgroup_info(host_control, &lockgroup_info, &count);
105
106	if (kr != KERN_SUCCESS)
107	{
108		mach_error("host_statistics", kr);
109		exit (EXIT_FAILURE);
110	}
111	if (gDebug) {
112		printf("count = %d\n", count);
113		for (i = 0; i < count; i++) {
114			printf("%s\n",lockgroup_info[i].lockgroup_name);
115		}
116	}
117
118	switch (argc) {
119	case 2:
120		if (strcmp(argv[1], "all") == 0) {
121			print_spin_hdr();
122			print_all_spin(lockgroup_info);
123			print_mutex_hdr();
124			print_all_mutex(lockgroup_info);
125			print_rw_hdr();
126			print_all_rw(lockgroup_info);
127		}
128		else if (strcmp(argv[1], "spin") == 0) {
129			print_spin_hdr();
130			print_all_spin(lockgroup_info);
131		}
132		else if (strcmp(argv[1], "mutex") == 0) {
133			print_mutex_hdr();
134			print_all_mutex(lockgroup_info);
135		}
136		else if (strcmp(argv[1], "rw") == 0) {
137			print_rw_hdr();
138			print_all_rw(lockgroup_info);
139		}
140		else {
141			found = 0;
142			for (i = 0;i < count;i++) {
143				if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) {
144					found = 1;
145					print_spin_hdr();
146					print_spin(i, lockgroup_info);
147					print_mutex_hdr();
148					print_mutex(i, lockgroup_info);
149					print_rw_hdr();
150					print_rw(i, lockgroup_info);
151					break;
152				}
153			}
154			if (found == 0)
155			{ usage(); }
156		}
157		break;
158	case 3:
159		if (sscanf(argv[2], "%d", &arg2) != 1) {
160			usage();
161		}
162		if (arg2 < 0) {
163			usage();
164		}
165		prime_lockgroup_deltas();
166		if (strcmp(argv[1], "all") == 0) {
167
168			while (1) {
169				sleep(arg2);
170				get_lockgroup_deltas();
171				print_spin_hdr();
172				print_all_spin(lockgroup_deltas);
173				print_mutex_hdr();
174				print_all_mutex(lockgroup_deltas);
175				print_rw_hdr();
176				print_all_rw(lockgroup_deltas);
177			}
178		}
179		else if (strcmp(argv[1], "spin") == 0) {
180
181			while (1) {
182				sleep(arg2);
183				get_lockgroup_deltas();
184				print_spin_hdr();
185				print_all_spin(lockgroup_deltas);
186			}
187		}
188		else if (strcmp(argv[1], "mutex") == 0) {
189
190			while (1) {
191				sleep(arg2);
192				get_lockgroup_deltas();
193				print_mutex_hdr();
194				print_all_mutex(lockgroup_deltas);
195			}
196		}
197		else if (strcmp(argv[1], "rw") == 0) {
198
199			while (1) {
200				sleep(arg2);
201				get_lockgroup_deltas();
202				print_rw_hdr();
203				print_all_rw(lockgroup_deltas);
204			}
205		}
206		else {
207
208			found = 0;
209			for (i = 0;i < count;i++) {
210				if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) {
211					found = 1;
212					while (1) {
213						sleep(arg2);
214						get_lockgroup_deltas();
215						print_spin_hdr();
216						print_spin(i, lockgroup_deltas);
217						print_mutex_hdr();
218						print_mutex(i, lockgroup_deltas);
219						print_rw_hdr();
220						print_rw(i, lockgroup_deltas);
221					}
222				}
223			}
224			if (found == 0)
225			{ usage(); }
226		}
227		break;
228	case 4:
229		if (strcmp(argv[3], "abs") != 0)
230		{ usage(); }
231		if (sscanf(argv[2], "%d", &arg2) != 1)
232		{ usage(); }
233		if (strcmp(argv[1], "all") == 0) {
234			while (1)
235			{
236				print_spin_hdr();
237				print_all_spin(lockgroup_info);
238				print_mutex_hdr();
239				print_all_mutex(lockgroup_info);
240				print_rw_hdr();
241				print_all_rw(lockgroup_info);
242				sleep(arg2);
243			}
244		}
245		else if (strcmp(argv[1], "spin") == 0) {
246			while (1)
247			{print_all_spin(lockgroup_info);
248				sleep(arg2);
249			}
250		}
251		else if (strcmp(argv[1], "mutex") == 0) {
252			print_mutex_hdr();
253			while (1)
254			{print_all_mutex(lockgroup_info);
255				sleep(arg2);
256			}
257		}
258		else if (strcmp(argv[1], "rw") == 0) {
259			print_rw_hdr();
260			while (1)
261			{print_all_rw(lockgroup_info);
262				sleep(arg2);
263			}
264		}
265		else {
266			found = 0;
267			for (i = 0;i < count;i++) {
268				if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) {
269					found = 1;
270					while (1)
271					{
272						print_spin_hdr();
273						print_spin(i, lockgroup_info);
274						print_mutex_hdr();
275						print_mutex(i, lockgroup_info);
276						print_rw_hdr();
277						print_rw(i, lockgroup_info);
278						sleep(arg2);
279					}
280				}
281			}
282			if (found == 0)
283			{ usage(); }
284		}
285		break;
286	default:
287		usage();
288		break;
289	}
290
291	exit(0);
292}
293
294void
295usage()
296{
297	fprintf(stderr, "Usage: %s [all, spin, mutex, rw, <lock group name>] {<repeat interval>} {abs}\n", pgmname);
298	exit(EXIT_FAILURE);
299}
300
301void
302print_spin_hdr(void)
303{
304	printf("    Spinlock acquires           misses   Name\n");
305}
306
307void
308print_spin(int requested, lockgroup_info_t *lockgroup)
309{
310	lockgroup_info_t	*curptr = &lockgroup[requested];
311
312	if (curptr->lock_spin_cnt != 0 && curptr->lock_spin_util_cnt != 0) {
313		printf("%16lld ", curptr->lock_spin_util_cnt);
314		printf("%16lld   ", curptr->lock_spin_miss_cnt);
315		printf("%-14s\n", curptr->lockgroup_name);
316	}
317}
318
319void
320print_all_spin(lockgroup_info_t *lockgroup)
321{
322	unsigned int		i;
323
324	for (i = 0;i < count;i++)
325		print_spin(i, lockgroup);
326        printf("\n");
327}
328
329void
330print_mutex_hdr(void)
331{
332#if defined(__i386__) || defined(__x86_64__)
333	printf("Mutex lock attempts  Misses      Waits Direct Waits Name\n");
334#else
335        printf("     mutex locks           misses            waits   name\n");
336#endif
337}
338
339void
340print_mutex(int requested, lockgroup_info_t *lockgroup)
341{
342	lockgroup_info_t	*curptr = &lockgroup[requested];
343
344	if (curptr->lock_mtx_cnt != 0 && curptr->lock_mtx_util_cnt != 0) {
345		printf("%16lld ", curptr->lock_mtx_util_cnt);
346#if defined(__i386__) || defined(__x86_64__)
347		printf("%10lld %10lld %10lld   ", curptr->lock_mtx_miss_cnt,  curptr->lock_mtx_wait_cnt, curptr->lock_mtx_held_cnt);
348#else
349		printf("%16lld %16lld   ", curptr->lock_mtx_miss_cnt,  curptr->lock_mtx_wait_cnt);
350#endif
351		printf("%-14s\n", curptr->lockgroup_name);
352	}
353}
354
355void
356print_all_mutex(lockgroup_info_t *lockgroup)
357{
358	unsigned int		i;
359
360	for (i = 0;i < count;i++)
361		print_mutex(i, lockgroup);
362        printf("\n");
363
364}
365
366void
367print_rw_hdr(void)
368{
369	printf("        RW locks           Misses            Waits   Name\n");
370}
371
372void
373print_rw(int requested, lockgroup_info_t *lockgroup)
374{
375	lockgroup_info_t	*curptr = &lockgroup[requested];
376
377	if (curptr->lock_rw_cnt != 0 && curptr->lock_rw_util_cnt != 0) {
378		printf("%16lld ", curptr->lock_rw_util_cnt);
379		printf("%16lld %16lld   ", curptr->lock_rw_miss_cnt,  curptr->lock_rw_wait_cnt);
380		printf("%-14s\n", curptr->lockgroup_name);
381	}
382}
383
384void
385print_all_rw(lockgroup_info_t *lockgroup)
386{
387	unsigned int		i;
388
389	for (i = 0;i < count;i++)
390		print_rw(i, lockgroup);
391        printf("\n");
392
393}
394
395void
396prime_lockgroup_deltas(void)
397{
398	lockgroup_start = calloc(count, sizeof(lockgroup_info_t));
399	if (lockgroup_start == NULL) {
400		fprintf(stderr, "Can't allocate memory for lockgroup info\n");
401		exit (EXIT_FAILURE);
402	}
403	memcpy(lockgroup_start, lockgroup_info, count * sizeof(lockgroup_info_t));
404
405	lockgroup_deltas = calloc(count,  sizeof(lockgroup_info_t));
406	if (lockgroup_deltas == NULL) {
407		fprintf(stderr, "Can't allocate memory for lockgroup info\n");
408		exit (EXIT_FAILURE);
409	}
410}
411
412void
413get_lockgroup_deltas(void)
414{
415	kern_return_t 			kr;
416	unsigned int			i;
417
418	kr = host_lockgroup_info(host_control, &lockgroup_info, &count);
419
420	if (kr != KERN_SUCCESS)
421	{
422		mach_error("host_statistics", kr);
423		exit (EXIT_FAILURE);
424	}
425
426	memcpy(lockgroup_deltas, lockgroup_info, count * sizeof(lockgroup_info_t));
427	for (i = 0; i < count; i++) {
428		lockgroup_deltas[i].lock_spin_util_cnt =
429		    lockgroup_info[i].lock_spin_util_cnt -
430		    lockgroup_start[i].lock_spin_util_cnt;
431		lockgroup_deltas[i].lock_spin_miss_cnt =
432		    lockgroup_info[i].lock_spin_miss_cnt -
433		    lockgroup_start[i].lock_spin_miss_cnt;
434		lockgroup_deltas[i].lock_mtx_util_cnt =
435		    lockgroup_info[i].lock_mtx_util_cnt -
436		    lockgroup_start[i].lock_mtx_util_cnt;
437		lockgroup_deltas[i].lock_mtx_miss_cnt =
438		    lockgroup_info[i].lock_mtx_miss_cnt -
439		    lockgroup_start[i].lock_mtx_miss_cnt;
440		lockgroup_deltas[i].lock_mtx_wait_cnt =
441		    lockgroup_info[i].lock_mtx_wait_cnt -
442		    lockgroup_start[i].lock_mtx_wait_cnt;
443		lockgroup_deltas[i].lock_mtx_held_cnt =
444		    lockgroup_info[i].lock_mtx_held_cnt -
445		    lockgroup_start[i].lock_mtx_held_cnt;
446		lockgroup_deltas[i].lock_rw_util_cnt =
447		    lockgroup_info[i].lock_rw_util_cnt -
448		    lockgroup_start[i].lock_rw_util_cnt;
449		lockgroup_deltas[i].lock_rw_miss_cnt =
450		    lockgroup_info[i].lock_rw_miss_cnt -
451		    lockgroup_start[i].lock_rw_miss_cnt;
452		lockgroup_deltas[i].lock_rw_wait_cnt =
453		    lockgroup_info[i].lock_rw_wait_cnt -
454		    lockgroup_start[i].lock_rw_wait_cnt;
455	}
456	memcpy(lockgroup_start, lockgroup_info, count * sizeof(lockgroup_info_t));
457}
458