xref: /xnu-11215/tools/lockstat/lockstat.c (revision a5e72196)
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  */
68 void usage(void);
69 void print_spin_hdr(void);
70 void print_spin(int requested, lockgroup_info_t *lockgroup);
71 void print_all_spin(lockgroup_info_t *lockgroup);
72 void print_mutex_hdr(void);
73 void print_mutex(int requested, lockgroup_info_t *lockgroup);
74 void print_all_mutex(lockgroup_info_t *lockgroup);
75 void print_rw_hdr(void);
76 void print_rw(int requested, lockgroup_info_t *lockgroup);
77 void print_all_rw(lockgroup_info_t *lockgroup);
78 void prime_lockgroup_deltas(void);
79 void get_lockgroup_deltas(void);
80 
81 char *pgmname;
82 mach_port_t host_control;
83 
84 lockgroup_info_t        *lockgroup_info, *lockgroup_start, *lockgroup_deltas;
85 unsigned int            count;
86 
87 unsigned int            gDebug = 1;
88 
89 int
main(int argc,char ** argv)90 main(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 		mach_error("host_statistics", kr);
108 		exit(EXIT_FAILURE);
109 	}
110 	if (gDebug) {
111 		printf("count = %d\n", count);
112 		for (i = 0; i < count; i++) {
113 			printf("%s\n", lockgroup_info[i].lockgroup_name);
114 		}
115 	}
116 
117 	switch (argc) {
118 	case 2:
119 		if (strcmp(argv[1], "all") == 0) {
120 			print_spin_hdr();
121 			print_all_spin(lockgroup_info);
122 			print_mutex_hdr();
123 			print_all_mutex(lockgroup_info);
124 			print_rw_hdr();
125 			print_all_rw(lockgroup_info);
126 		} else if (strcmp(argv[1], "spin") == 0) {
127 			print_spin_hdr();
128 			print_all_spin(lockgroup_info);
129 		} else if (strcmp(argv[1], "mutex") == 0) {
130 			print_mutex_hdr();
131 			print_all_mutex(lockgroup_info);
132 		} else if (strcmp(argv[1], "rw") == 0) {
133 			print_rw_hdr();
134 			print_all_rw(lockgroup_info);
135 		} else {
136 			found = 0;
137 			for (i = 0; i < count; i++) {
138 				if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) {
139 					found = 1;
140 					print_spin_hdr();
141 					print_spin(i, lockgroup_info);
142 					print_mutex_hdr();
143 					print_mutex(i, lockgroup_info);
144 					print_rw_hdr();
145 					print_rw(i, lockgroup_info);
146 					break;
147 				}
148 			}
149 			if (found == 0) {
150 				usage();
151 			}
152 		}
153 		break;
154 	case 3:
155 		if (sscanf(argv[2], "%d", &arg2) != 1) {
156 			usage();
157 		}
158 		if (arg2 < 0) {
159 			usage();
160 		}
161 		prime_lockgroup_deltas();
162 		if (strcmp(argv[1], "all") == 0) {
163 			while (1) {
164 				sleep(arg2);
165 				get_lockgroup_deltas();
166 				print_spin_hdr();
167 				print_all_spin(lockgroup_deltas);
168 				print_mutex_hdr();
169 				print_all_mutex(lockgroup_deltas);
170 				print_rw_hdr();
171 				print_all_rw(lockgroup_deltas);
172 			}
173 		} else if (strcmp(argv[1], "spin") == 0) {
174 			while (1) {
175 				sleep(arg2);
176 				get_lockgroup_deltas();
177 				print_spin_hdr();
178 				print_all_spin(lockgroup_deltas);
179 			}
180 		} else if (strcmp(argv[1], "mutex") == 0) {
181 			while (1) {
182 				sleep(arg2);
183 				get_lockgroup_deltas();
184 				print_mutex_hdr();
185 				print_all_mutex(lockgroup_deltas);
186 			}
187 		} else if (strcmp(argv[1], "rw") == 0) {
188 			while (1) {
189 				sleep(arg2);
190 				get_lockgroup_deltas();
191 				print_rw_hdr();
192 				print_all_rw(lockgroup_deltas);
193 			}
194 		} else {
195 			found = 0;
196 			for (i = 0; i < count; i++) {
197 				if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) {
198 					found = 1;
199 					while (1) {
200 						sleep(arg2);
201 						get_lockgroup_deltas();
202 						print_spin_hdr();
203 						print_spin(i, lockgroup_deltas);
204 						print_mutex_hdr();
205 						print_mutex(i, lockgroup_deltas);
206 						print_rw_hdr();
207 						print_rw(i, lockgroup_deltas);
208 					}
209 				}
210 			}
211 			if (found == 0) {
212 				usage();
213 			}
214 		}
215 		break;
216 	case 4:
217 		if (strcmp(argv[3], "abs") != 0) {
218 			usage();
219 		}
220 		if (sscanf(argv[2], "%d", &arg2) != 1) {
221 			usage();
222 		}
223 		if (strcmp(argv[1], "all") == 0) {
224 			while (1) {
225 				print_spin_hdr();
226 				print_all_spin(lockgroup_info);
227 				print_mutex_hdr();
228 				print_all_mutex(lockgroup_info);
229 				print_rw_hdr();
230 				print_all_rw(lockgroup_info);
231 				sleep(arg2);
232 			}
233 		} else if (strcmp(argv[1], "spin") == 0) {
234 			while (1) {
235 				print_all_spin(lockgroup_info);
236 				sleep(arg2);
237 			}
238 		} else if (strcmp(argv[1], "mutex") == 0) {
239 			print_mutex_hdr();
240 			while (1) {
241 				print_all_mutex(lockgroup_info);
242 				sleep(arg2);
243 			}
244 		} else if (strcmp(argv[1], "rw") == 0) {
245 			print_rw_hdr();
246 			while (1) {
247 				print_all_rw(lockgroup_info);
248 				sleep(arg2);
249 			}
250 		} else {
251 			found = 0;
252 			for (i = 0; i < count; i++) {
253 				if (strcmp(argv[1], lockgroup_info[i].lockgroup_name) == 0) {
254 					found = 1;
255 					while (1) {
256 						print_spin_hdr();
257 						print_spin(i, lockgroup_info);
258 						print_mutex_hdr();
259 						print_mutex(i, lockgroup_info);
260 						print_rw_hdr();
261 						print_rw(i, lockgroup_info);
262 						sleep(arg2);
263 					}
264 				}
265 			}
266 			if (found == 0) {
267 				usage();
268 			}
269 		}
270 		break;
271 	default:
272 		usage();
273 		break;
274 	}
275 
276 	exit(0);
277 }
278 
279 void
usage()280 usage()
281 {
282 	fprintf(stderr, "Usage: %s [all, spin, mutex, rw, <lock group name>] {<repeat interval>} {abs}\n", pgmname);
283 	exit(EXIT_FAILURE);
284 }
285 
286 void
print_spin_hdr(void)287 print_spin_hdr(void)
288 {
289 	printf("    Spinlock acquires           misses   Name\n");
290 }
291 
292 void
print_spin(int requested,lockgroup_info_t * lockgroup)293 print_spin(int requested, lockgroup_info_t *lockgroup)
294 {
295 	lockgroup_info_t        *curptr = &lockgroup[requested];
296 
297 	if (curptr->lock_spin_cnt != 0 && curptr->lock_spin_util_cnt != 0) {
298 		printf("%16lld ", curptr->lock_spin_util_cnt);
299 		printf("%16lld   ", curptr->lock_spin_miss_cnt);
300 		printf("%-14s\n", curptr->lockgroup_name);
301 	}
302 }
303 
304 void
print_all_spin(lockgroup_info_t * lockgroup)305 print_all_spin(lockgroup_info_t *lockgroup)
306 {
307 	unsigned int            i;
308 
309 	for (i = 0; i < count; i++) {
310 		print_spin(i, lockgroup);
311 	}
312 	printf("\n");
313 }
314 
315 void
print_mutex_hdr(void)316 print_mutex_hdr(void)
317 {
318 #if defined(__i386__) || defined(__x86_64__)
319 	printf("Mutex lock attempts  Misses      Waits Direct Waits Name\n");
320 #else
321 	printf("     mutex locks           misses            waits   name\n");
322 #endif
323 }
324 
325 void
print_mutex(int requested,lockgroup_info_t * lockgroup)326 print_mutex(int requested, lockgroup_info_t *lockgroup)
327 {
328 	lockgroup_info_t        *curptr = &lockgroup[requested];
329 
330 	if (curptr->lock_mtx_cnt != 0 && curptr->lock_mtx_util_cnt != 0) {
331 		printf("%16lld ", curptr->lock_mtx_util_cnt);
332 #if defined(__i386__) || defined(__x86_64__)
333 		printf("%10lld %10lld %10lld   ", curptr->lock_mtx_miss_cnt, curptr->lock_mtx_wait_cnt, curptr->lock_mtx_held_cnt);
334 #else
335 		printf("%16lld %16lld   ", curptr->lock_mtx_miss_cnt, curptr->lock_mtx_wait_cnt);
336 #endif
337 		printf("%-14s\n", curptr->lockgroup_name);
338 	}
339 }
340 
341 void
print_all_mutex(lockgroup_info_t * lockgroup)342 print_all_mutex(lockgroup_info_t *lockgroup)
343 {
344 	unsigned int            i;
345 
346 	for (i = 0; i < count; i++) {
347 		print_mutex(i, lockgroup);
348 	}
349 	printf("\n");
350 }
351 
352 void
print_rw_hdr(void)353 print_rw_hdr(void)
354 {
355 	printf("        RW locks           Misses            Waits   Name\n");
356 }
357 
358 void
print_rw(int requested,lockgroup_info_t * lockgroup)359 print_rw(int requested, lockgroup_info_t *lockgroup)
360 {
361 	lockgroup_info_t        *curptr = &lockgroup[requested];
362 
363 	if (curptr->lock_rw_cnt != 0 && curptr->lock_rw_util_cnt != 0) {
364 		printf("%16lld ", curptr->lock_rw_util_cnt);
365 		printf("%16lld %16lld   ", curptr->lock_rw_miss_cnt, curptr->lock_rw_wait_cnt);
366 		printf("%-14s\n", curptr->lockgroup_name);
367 	}
368 }
369 
370 void
print_all_rw(lockgroup_info_t * lockgroup)371 print_all_rw(lockgroup_info_t *lockgroup)
372 {
373 	unsigned int            i;
374 
375 	for (i = 0; i < count; i++) {
376 		print_rw(i, lockgroup);
377 	}
378 	printf("\n");
379 }
380 
381 void
prime_lockgroup_deltas(void)382 prime_lockgroup_deltas(void)
383 {
384 	lockgroup_start = calloc(count, sizeof(lockgroup_info_t));
385 	if (lockgroup_start == NULL) {
386 		fprintf(stderr, "Can't allocate memory for lockgroup info\n");
387 		exit(EXIT_FAILURE);
388 	}
389 	memcpy(lockgroup_start, lockgroup_info, count * sizeof(lockgroup_info_t));
390 
391 	lockgroup_deltas = calloc(count, sizeof(lockgroup_info_t));
392 	if (lockgroup_deltas == NULL) {
393 		fprintf(stderr, "Can't allocate memory for lockgroup info\n");
394 		exit(EXIT_FAILURE);
395 	}
396 }
397 
398 void
get_lockgroup_deltas(void)399 get_lockgroup_deltas(void)
400 {
401 	kern_return_t                   kr;
402 	unsigned int                    i;
403 
404 	kr = host_lockgroup_info(host_control, &lockgroup_info, &count);
405 
406 	if (kr != KERN_SUCCESS) {
407 		mach_error("host_statistics", kr);
408 		exit(EXIT_FAILURE);
409 	}
410 
411 	memcpy(lockgroup_deltas, lockgroup_info, count * sizeof(lockgroup_info_t));
412 	for (i = 0; i < count; i++) {
413 		lockgroup_deltas[i].lock_spin_util_cnt =
414 		    lockgroup_info[i].lock_spin_util_cnt -
415 		    lockgroup_start[i].lock_spin_util_cnt;
416 		lockgroup_deltas[i].lock_spin_miss_cnt =
417 		    lockgroup_info[i].lock_spin_miss_cnt -
418 		    lockgroup_start[i].lock_spin_miss_cnt;
419 		lockgroup_deltas[i].lock_mtx_util_cnt =
420 		    lockgroup_info[i].lock_mtx_util_cnt -
421 		    lockgroup_start[i].lock_mtx_util_cnt;
422 		lockgroup_deltas[i].lock_mtx_miss_cnt =
423 		    lockgroup_info[i].lock_mtx_miss_cnt -
424 		    lockgroup_start[i].lock_mtx_miss_cnt;
425 		lockgroup_deltas[i].lock_mtx_wait_cnt =
426 		    lockgroup_info[i].lock_mtx_wait_cnt -
427 		    lockgroup_start[i].lock_mtx_wait_cnt;
428 		lockgroup_deltas[i].lock_mtx_held_cnt =
429 		    lockgroup_info[i].lock_mtx_held_cnt -
430 		    lockgroup_start[i].lock_mtx_held_cnt;
431 		lockgroup_deltas[i].lock_rw_util_cnt =
432 		    lockgroup_info[i].lock_rw_util_cnt -
433 		    lockgroup_start[i].lock_rw_util_cnt;
434 		lockgroup_deltas[i].lock_rw_miss_cnt =
435 		    lockgroup_info[i].lock_rw_miss_cnt -
436 		    lockgroup_start[i].lock_rw_miss_cnt;
437 		lockgroup_deltas[i].lock_rw_wait_cnt =
438 		    lockgroup_info[i].lock_rw_wait_cnt -
439 		    lockgroup_start[i].lock_rw_wait_cnt;
440 	}
441 	memcpy(lockgroup_start, lockgroup_info, count * sizeof(lockgroup_info_t));
442 }
443