1/*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
43 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
49 *  School of Computer Science
50 *  Carnegie Mellon University
51 *  Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie the
54 * rights to redistribute these changes.
55 */
56/*
57 *	zprint.c
58 *
59 *	utility for printing out zone structures
60 *
61 *	With no arguments, prints information on all zone structures.
62 *	With an argument, prints information only on those zones for
63 *	which the given name is a substring of the zone's name.
64 *	With a "-w" flag, calculates how much much space is allocated
65 *	to zones but not currently in use.
66 */
67
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <mach/mach.h>
72#include <mach_debug/mach_debug.h>
73#include <mach/mach_error.h>
74#include <libutil.h>
75#include <errno.h>
76
77#define streql(a, b)		(strcmp((a), (b)) == 0)
78#define strneql(a, b, n)	(strncmp((a), (b), (n)) == 0)
79
80static void usage(void);
81static void printzone(mach_zone_name_t *, task_zone_info_t *);
82static void colprintzone(mach_zone_name_t *, task_zone_info_t *);
83static int  find_deltas(mach_zone_name_t *, task_zone_info_t *, task_zone_info_t *, char *, int, int);
84static void colprintzoneheader(void);
85static boolean_t substr(const char *a, int alen, const char *b, int blen);
86
87static char *program;
88
89static pid_t pid = 0;
90static task_t task = TASK_NULL;
91static boolean_t ShowPid = FALSE;
92
93static boolean_t ShowDeltas = FALSE;
94static boolean_t ShowWasted = FALSE;
95static boolean_t ShowTotal = FALSE;
96static boolean_t SortZones = FALSE;
97static boolean_t ColFormat = TRUE;
98static boolean_t PrintHeader = TRUE;
99
100static unsigned long long totalsize = 0;
101static unsigned long long totalused = 0;
102static unsigned long long totalsum = 0;
103static unsigned long long pidsum = 0;
104
105static int last_time = 0;
106
107static	char	*zname = NULL;
108static	int	znamelen = 0;
109
110static void
111sigintr(__unused int signum)
112{
113	last_time = 1;
114}
115
116static void
117usage(void)
118{
119	fprintf(stderr, "usage: %s [-w] [-s] [-c] [-h] [-t] [-d] [-p <pid>] [name]\n", program);
120	exit(1);
121}
122
123int
124main(int argc, char **argv)
125{
126	mach_zone_name_t *name = NULL;
127	unsigned int nameCnt = 0;
128	task_zone_info_t *info = NULL;
129	unsigned int infoCnt = 0;
130
131	task_zone_info_t *max_info = NULL;
132	char		*deltas = NULL;
133
134	kern_return_t	kr;
135	int		i, j;
136	int		first_time = 1;
137	int             must_print = 1;
138	int		interval = 1;
139
140	signal(SIGINT, sigintr);
141
142	program = strrchr(argv[0], '/');
143	if (program == NULL)
144		program = argv[0];
145	else
146		program++;
147
148	for (i = 1; i < argc; i++) {
149		if (streql(argv[i], "-d"))
150			ShowDeltas = TRUE;
151		else if (streql(argv[i], "-t"))
152			ShowTotal = TRUE;
153		else if (streql(argv[i], "-T"))
154			ShowTotal = FALSE;
155		else if (streql(argv[i], "-w"))
156			ShowWasted = TRUE;
157		else if (streql(argv[i], "-W"))
158			ShowWasted = FALSE;
159		else if (streql(argv[i], "-s"))
160			SortZones = TRUE;
161		else if (streql(argv[i], "-S"))
162			SortZones = FALSE;
163		else if (streql(argv[i], "-c"))
164			ColFormat = TRUE;
165		else if (streql(argv[i], "-C"))
166			ColFormat = FALSE;
167		else if (streql(argv[i], "-H"))
168			PrintHeader = FALSE;
169		else if (streql(argv[i], "-p")) {
170			ShowPid = TRUE;
171			if (i < argc - 1) {
172				pid = atoi(argv[i+1]);
173				i++;
174			} else
175				usage();
176		} else if (streql(argv[i], "--")) {
177			i++;
178			break;
179		} else if (argv[i][0] == '-')
180			usage();
181		else
182			break;
183	}
184
185	switch (argc - i) {
186	    case 0:
187		zname = "";
188		znamelen = 0;
189		break;
190
191	    case 1:
192		zname = argv[i];
193		znamelen = strlen(zname);
194		break;
195
196	    default:
197		usage();
198	}
199
200	if (ShowDeltas) {
201		SortZones = FALSE;
202		ColFormat = TRUE;
203		PrintHeader = TRUE;
204	}
205
206	if (ShowPid) {
207		kr = task_for_pid(mach_task_self(), pid, &task);
208		if (kr != KERN_SUCCESS) {
209			fprintf(stderr, "%s: task_for_pid(%d) failed: %s (try running as root)\n",
210				program, pid, mach_error_string(kr));
211			exit(1);
212		}
213	}
214
215    for (;;) {
216        if (ShowPid) {
217	    kr = task_zone_info(task, &name, &nameCnt, &info, &infoCnt);
218	    if (kr != KERN_SUCCESS) {
219		fprintf(stderr, "%s: task_zone_info: %s\n",
220			program, mach_error_string(kr));
221		exit(1);
222	    }
223	} else {
224	    mach_zone_info_t *zinfo = NULL;
225
226	    kr = mach_zone_info(mach_host_self(),
227				&name, &nameCnt, &zinfo, &infoCnt);
228	    if (kr != KERN_SUCCESS) {
229	        fprintf(stderr, "%s: mach_zone_info: %s\n",
230			program, mach_error_string(kr));
231		exit(1);
232	    }
233	    kr = vm_allocate(mach_task_self(), (vm_address_t *)&info,
234			     infoCnt * sizeof *info, VM_FLAGS_ANYWHERE);
235	    if (kr != KERN_SUCCESS) {
236		    fprintf(stderr, "%s vm_allocate: %s\n",
237			    program, mach_error_string(kr));
238		    exit(1);
239	    }
240	    for (i = 0; i < infoCnt; i++) {
241		    *(mach_zone_info_t *)(info + i) = zinfo[i];
242		    info[i].tzi_caller_acct = 0;
243		    info[i].tzi_task_alloc = 0;
244		    info[i].tzi_task_free = 0;
245	    }
246	    kr = vm_deallocate(mach_task_self(), (vm_address_t) zinfo,
247			       (vm_size_t) (infoCnt * sizeof *zinfo));
248	    if (kr != KERN_SUCCESS) {
249		    fprintf(stderr, "%s: vm_deallocate: %s\n",
250			    program, mach_error_string(kr));
251		    exit(1);
252	    }
253	}
254
255	if (nameCnt != infoCnt) {
256		fprintf(stderr, "%s: mach/task_zone_info: counts not equal?\n",
257			program);
258		exit(1);
259	}
260
261	if (first_time) {
262		deltas = (char *)malloc(infoCnt);
263		max_info = (task_zone_info_t *)malloc((infoCnt * sizeof *info));
264	}
265
266	if (SortZones) {
267		for (i = 0; i < nameCnt-1; i++)
268			for (j = i+1; j < nameCnt; j++) {
269				int wastei, wastej;
270
271				wastei = (info[i].tzi_cur_size -
272					  (info[i].tzi_elem_size *
273					   info[i].tzi_count));
274				wastej = (info[j].tzi_cur_size -
275					  (info[j].tzi_elem_size *
276					   info[j].tzi_count));
277
278				if (wastej > wastei) {
279					task_zone_info_t tinfo;
280					mach_zone_name_t tname;
281
282					tinfo = info[i];
283					info[i] = info[j];
284					info[j] = tinfo;
285
286					tname = name[i];
287					name[i] = name[j];
288					name[j] = tname;
289				}
290			}
291	}
292
293	must_print = find_deltas(name, info, max_info, deltas, infoCnt, first_time);
294	if (must_print) {
295		if (ColFormat) {
296			if (!first_time)
297				printf("\n");
298			colprintzoneheader();
299		}
300		for (i = 0; i < nameCnt; i++) {
301			if (deltas[i]) {
302				if (ColFormat)
303					colprintzone(&name[i], &info[i]);
304				else
305					printzone(&name[i], &info[i]);
306			}
307		}
308	}
309
310	first_time = 0;
311
312	if ((name != NULL) && (nameCnt != 0)) {
313		kr = vm_deallocate(mach_task_self(), (vm_address_t) name,
314				   (vm_size_t) (nameCnt * sizeof *name));
315		if (kr != KERN_SUCCESS) {
316			fprintf(stderr, "%s: vm_deallocate: %s\n",
317			     program, mach_error_string(kr));
318			exit(1);
319		}
320	}
321
322	if ((info != NULL) && (infoCnt != 0)) {
323		kr = vm_deallocate(mach_task_self(), (vm_address_t) info,
324				   (vm_size_t) (infoCnt * sizeof *info));
325		if (kr != KERN_SUCCESS) {
326			fprintf(stderr, "%s: vm_deallocate: %s\n",
327			     program, mach_error_string(kr));
328			exit(1);
329		}
330	}
331
332	if ((ShowWasted||ShowTotal) && PrintHeader && !ShowDeltas) {
333		printf("TOTAL SIZE   = %llu\n", totalsize);
334		printf("TOTAL USED   = %llu\n", totalused);
335		if (ShowWasted)
336			printf("TOTAL WASTED = %llu\n", totalsize - totalused);
337		if (ShowTotal)
338			printf("TOTAL ALLOCS = %llu\n", totalsum);
339	}
340
341	if (ShowDeltas == FALSE || last_time)
342	        break;
343
344	sleep(interval);
345    }
346    exit(0);
347}
348
349static boolean_t
350substr(const char *a, int alen, const char *b, int blen)
351{
352	int i;
353
354	for (i = 0; i <= blen - alen; i++)
355		if (strneql(a, b+i, alen))
356			return TRUE;
357
358	return FALSE;
359}
360
361static void
362printzone(mach_zone_name_t *name, task_zone_info_t *info)
363{
364	unsigned long long used, size;
365
366	printf("%.*s zone:\n", (int)sizeof name->mzn_name, name->mzn_name);
367	printf("\tcur_size:    %lluK bytes (%llu elements)\n",
368	       info->tzi_cur_size/1024,
369	       (info->tzi_elem_size == 0) ? 0 :
370	       info->tzi_cur_size/info->tzi_elem_size);
371	printf("\tmax_size:    %lluK bytes (%llu elements)\n",
372	       info->tzi_max_size/1024,
373	       (info->tzi_elem_size == 0) ? 0 :
374	       info->tzi_max_size/info->tzi_elem_size);
375	printf("\telem_size:   %llu bytes\n",
376	       info->tzi_elem_size);
377	printf("\t# of elems:  %llu\n",
378	       info->tzi_count);
379	printf("\talloc_size:  %lluK bytes (%llu elements)\n",
380	       info->tzi_alloc_size/1024,
381	       (info->tzi_elem_size == 0) ? 0 :
382	       info->tzi_alloc_size/info->tzi_elem_size);
383	if (info->tzi_exhaustible)
384		printf("\tEXHAUSTIBLE\n");
385	if (info->tzi_collectable)
386		printf("\tCOLLECTABLE\n");
387	if (ShowPid && info->tzi_caller_acct)
388		printf("\tCALLER ACCOUNTED\n");
389	if (ShowPid) {
390		pidsum += info->tzi_task_alloc - info->tzi_task_free;
391		printf("\tproc_alloc_size: %8dK bytes (%llu elements)\n",
392		       (int)((info->tzi_task_alloc - info->tzi_task_free)/1024),
393		       (info->tzi_elem_size == 0) ? 0 :
394		       (info->tzi_task_alloc - info->tzi_task_free)/info->tzi_elem_size);
395	}
396	if (ShowWasted) {
397		totalused += used = info->tzi_elem_size * info->tzi_count;
398		totalsize += size = info->tzi_cur_size;
399		printf("\t\t\t\t\tWASTED: %llu\n", size - used);
400	}
401	if (ShowTotal) {
402		totalsum += info->tzi_sum_size;
403		printf("\t\t\t\t\tTOTAL: %llu\n", totalsum);
404		if (ShowPid)
405			printf("\t\t\t\t\tPID TOTAL: %llu\n", pidsum);
406	}
407}
408
409#define	PRINTK(fmt, value)	\
410	printf(fmt "K", (value) / 1024 )	/* ick */
411
412static void
413colprintzone(mach_zone_name_t *zone_name, task_zone_info_t *info)
414{
415	char *name = zone_name->mzn_name;
416	int j, namewidth;
417	unsigned long long used, size;
418
419	namewidth = 25;
420	if (ShowWasted || ShowTotal) {
421		namewidth -= 7;
422	}
423	for (j = 0; j < namewidth - 1 && name[j]; j++) {
424		if (name[j] == ' ') {
425			putchar('.');
426		} else {
427			putchar(name[j]);
428		}
429	}
430	if (j == namewidth - 1) {
431		if (name[j]) {
432			putchar('$');
433		} else {
434			putchar(' ');
435		}
436	} else {
437		for (; j < namewidth; j++) {
438			putchar(' ');
439		}
440	}
441	printf(" %6llu", info->tzi_elem_size);
442	PRINTK(" %10llu", info->tzi_cur_size);
443	if (info->tzi_max_size / 1024 > 9999999) {
444		printf("    --------");
445	} else {
446		PRINTK(" %10llu", info->tzi_max_size);
447	}
448	printf(" %10llu", info->tzi_cur_size / info->tzi_elem_size);
449	if (info->tzi_max_size / 1024 >= 999999999) {
450		printf("  ----------");
451	} else {
452		printf(" %11llu", info->tzi_max_size / info->tzi_elem_size);
453	}
454	printf(" %11llu", info->tzi_count);
455	PRINTK(" %5llu", info->tzi_alloc_size);
456	printf(" %6llu", info->tzi_alloc_size / info->tzi_elem_size);
457
458	totalused += used = info->tzi_elem_size * info->tzi_count;
459	totalsize += size = info->tzi_cur_size;
460	totalsum += info->tzi_sum_size;
461
462	printf(" %c%c%c",
463	       (info->tzi_exhaustible ? 'X' : ' '),
464	       (info->tzi_caller_acct ? 'A' : ' '),
465	       (info->tzi_collectable ? 'C' : ' '));
466	if (ShowWasted) {
467		PRINTK(" %8llu", size - used);
468	}
469	if (ShowPid) {
470		printf("%8dK", (int)((info->tzi_task_alloc - info->tzi_task_free)/1024));
471	}
472	if (ShowTotal) {
473		if (info->tzi_sum_size < 1024)
474			printf(" %16lluB", info->tzi_sum_size);
475		else
476			PRINTK(" %16llu", info->tzi_sum_size);
477	}
478	printf("\n");
479}
480
481static void
482colprintzoneheader(void)
483{
484	if (! PrintHeader) {
485		return;
486	}
487	printf("%s                     elem         cur         max        cur         max"
488	       "         cur  alloc  alloc    %s%s\n",
489	       (ShowWasted||ShowTotal)? "" : "       ",
490	       (ShowWasted)? "          ":"",
491	       (ShowPid) ? "      PID" : "" );
492	printf("zone name%s           size        size        size      #elts       #elts"
493	       "       inuse   size  count    ", (ShowWasted||ShowTotal)? " " : "        " );
494	if (ShowWasted)
495		printf("    wasted");
496	if (ShowPid)
497		printf("   Allocs");
498	if (ShowTotal)
499		printf("      Total Allocs");
500	printf("\n%s-------------------------------------------------------"
501	       "-----------------------------------------------",
502	       (ShowWasted||ShowTotal)? "" : "-------");
503	if (ShowWasted)
504		printf("----------");
505	if (ShowPid)
506		printf("---------");
507	if (ShowTotal)
508		printf("------------------");
509	printf("\n");
510}
511
512int
513find_deltas(mach_zone_name_t *name, task_zone_info_t *info, task_zone_info_t *max_info,
514	    char *deltas, int cnt, int first_time)
515{
516       int i;
517       int  found_one = 0;
518
519       for (i = 0; i < cnt; i++) {
520	       deltas[i] = 0;
521	       if (substr(zname, znamelen, name[i].mzn_name,
522			  strnlen(name[i].mzn_name, sizeof name[i].mzn_name))) {
523		       if (first_time || info->tzi_cur_size > max_info->tzi_cur_size ||
524			   (ShowTotal && ((info->tzi_sum_size >> 1) > max_info->tzi_sum_size))) {
525			       max_info->tzi_cur_size = info->tzi_cur_size;
526			       max_info->tzi_sum_size = info->tzi_sum_size;
527			       deltas[i] = 1;
528			       found_one = 1;
529		       }
530	       }
531	       info++;
532	       max_info++;
533       }
534       return(found_one);
535}
536