1/*
2 * Copyright (c) 2008-2010 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 * Copyright (c) 1983, 1988, 1993
30 *	The Regents of the University of California.  All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 *    notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 *    notice, this list of conditions and the following disclaimer in the
39 *    documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 *    must display the following acknowledgement:
42 *	This product includes software developed by the University of
43 *	California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61
62#include <sys/param.h>
63#include <sys/socket.h>
64#include <sys/mbuf.h>
65#include <sys/sysctl.h>
66
67#include <stdio.h>
68#include <string.h>
69#include <stdlib.h>
70#include <errno.h>
71#include "netstat.h"
72
73#define	YES	1
74typedef int bool;
75
76struct	mbstat mbstat;
77
78static struct mbtypes {
79	int	mt_type;
80	char	*mt_name;
81} mbtypes[] = {
82	{ MT_DATA,	"data" },
83	{ MT_OOBDATA,	"oob data" },
84	{ MT_CONTROL,	"ancillary data" },
85	{ MT_HEADER,	"packet headers" },
86	{ MT_SOCKET,	"socket structures" },			/* XXX */
87	{ MT_PCB,	"protocol control blocks" },		/* XXX */
88	{ MT_RTABLE,	"routing table entries" },		/* XXX */
89	{ MT_HTABLE,	"IMP host table entries" },		/* XXX */
90	{ MT_ATABLE,	"address resolution tables" },
91	{ MT_FTABLE,	"fragment reassembly queue headers" },	/* XXX */
92	{ MT_SONAME,	"socket names and addresses" },
93	{ MT_SOOPTS,	"socket options" },
94	{ MT_RIGHTS,	"access rights" },
95	{ MT_IFADDR,	"interface addresses" },		/* XXX */
96	{ MT_TAG,		"packet tags" },		/* XXX */
97	{ 0, 0 }
98};
99
100int nmbtypes = sizeof(mbstat.m_mtypes) / sizeof(short);
101bool seen[256];			/* "have we seen this type yet?" */
102
103mb_stat_t *mb_stat;
104unsigned int njcl, njclbytes;
105mleak_stat_t *mleak_stat;
106struct mleak_table table;
107
108#define	KERN_IPC_MB_STAT	"kern.ipc.mb_stat"
109#define	KERN_IPC_NJCL		"kern.ipc.njcl"
110#define	KERN_IPC_NJCL_BYTES	"kern.ipc.njclbytes"
111#define	KERN_IPC_MLEAK_TABLE	"kern.ipc.mleak_table"
112#define	KERN_IPC_MLEAK_TOP_TRACE "kern.ipc.mleak_top_trace"
113
114#define	MB_STAT_HDR1 "\
115class        buf   active   ctotal    total cache   cached uncached    memory\n\
116name        size     bufs     bufs     bufs state     bufs     bufs     usage\n\
117---------- ----- -------- -------- -------- ----- -------- -------- ---------\n\
118"
119
120#define	MB_STAT_HDR2 "\n\
121class        waiter   notify    purge   wretry  nwretry  failure\n\
122name          count    count    count    count    count    count\n\
123---------- -------- -------- -------- -------- -------- --------\n\
124"
125
126#define MB_LEAK_HDR "\n\
127    calltrace [1]       calltrace [2]       calltrace [3]       calltrace [4]       calltrace [5]      \n\
128    ------------------  ------------------  ------------------  ------------------  ------------------ \n\
129"
130
131#define MB_LEAK_SPACING "                    "
132static const char *mbpr_state(int);
133static const char *mbpr_mem(u_int32_t);
134static int mbpr_getdata(void);
135
136/*
137 * Print mbuf statistics.
138 */
139void
140mbpr(void)
141{
142	unsigned long totmem = 0, totfree = 0, totmbufs, totused, totreturned = 0;
143	double totpct;
144	u_int32_t m_msize, m_mbufs = 0, m_clfree = 0, m_bigclfree = 0;
145	u_int32_t m_mbufclfree = 0, m_mbufbigclfree = 0;
146	u_int32_t m_16kclusters = 0, m_16kclfree = 0, m_mbuf16kclfree = 0;
147	int i;
148	struct mbtypes *mp;
149	mb_class_stat_t *cp;
150
151	if (mbpr_getdata() != 0)
152		return;
153
154	m_msize = mbstat.m_msize;
155	cp = &mb_stat->mbs_class[0];
156	for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) {
157		if (cp->mbcl_size == m_msize) {
158			m_mbufs = cp->mbcl_active;
159		} else if (cp->mbcl_size == mbstat.m_mclbytes) {
160			m_clfree = cp->mbcl_total - cp->mbcl_active;
161		} else if (cp->mbcl_size == mbstat.m_bigmclbytes) {
162			m_bigclfree = cp->mbcl_total - cp->mbcl_active;
163		} else if (njcl > 0 && cp->mbcl_size == njclbytes) {
164			m_16kclfree = cp->mbcl_total - cp->mbcl_active;
165			m_16kclusters = cp->mbcl_total;
166		} else if (cp->mbcl_size == (m_msize + mbstat.m_mclbytes)) {
167			m_mbufclfree = cp->mbcl_total - cp->mbcl_active;
168		} else if (cp->mbcl_size == (m_msize + mbstat.m_bigmclbytes)) {
169			m_mbufbigclfree = cp->mbcl_total - cp->mbcl_active;
170		} else if (njcl > 0 && cp->mbcl_size == (m_msize + njclbytes)) {
171			m_mbuf16kclfree = cp->mbcl_total - cp->mbcl_active;
172		}
173	}
174
175	/* adjust free counts to include composite caches */
176	m_clfree += m_mbufclfree;
177	m_bigclfree += m_mbufbigclfree;
178	m_16kclfree += m_mbuf16kclfree;
179
180	cp = &mb_stat->mbs_class[0];
181	for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) {
182		u_int32_t mem;
183
184		mem = cp->mbcl_ctotal * cp->mbcl_size;
185		totmem += mem;
186		totreturned += cp->mbcl_release_cnt;
187		totfree += (cp->mbcl_mc_cached + cp->mbcl_infree) *
188		    cp->mbcl_size;
189		if (mflag > 1) {
190			if (i == 0)
191				printf(MB_STAT_HDR1);
192
193			if (njcl == 0 &&
194			    cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes))
195				continue;
196
197			printf("%-10s %5u %8u %8u %8u %5s %8u %8u %9s\n",
198			    cp->mbcl_cname, cp->mbcl_size, cp->mbcl_active,
199			    cp->mbcl_ctotal, cp->mbcl_total,
200			    mbpr_state(cp->mbcl_mc_state), cp->mbcl_mc_cached,
201			    cp->mbcl_infree, mbpr_mem(mem));
202		}
203	}
204
205	cp = &mb_stat->mbs_class[0];
206	for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) {
207		if (mflag > 2) {
208			if (i == 0)
209				printf(MB_STAT_HDR2);
210
211			if (njcl == 0 &&
212			    cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes))
213				continue;
214
215			printf("%-10s %8u %8llu %8llu %8u %8u %8llu\n",
216			    cp->mbcl_cname, cp->mbcl_mc_waiter_cnt,
217			    cp->mbcl_notified, cp->mbcl_purge_cnt,
218			    cp->mbcl_mc_wretry_cnt, cp->mbcl_mc_nwretry_cnt,
219			    cp->mbcl_fail_cnt);
220		}
221	}
222
223	if (mflag > 1)
224		printf("\n");
225
226	totmbufs = 0;
227	for (mp = mbtypes; mp->mt_name; mp++)
228		totmbufs += mbstat.m_mtypes[mp->mt_type];
229	/*
230	 * These stats are not updated atomically in the kernel;
231	 * adjust the total as neeeded.
232	 */
233	if (totmbufs > m_mbufs)
234		totmbufs = m_mbufs;
235	printf("%lu/%u mbufs in use:\n", totmbufs, m_mbufs);
236	for (mp = mbtypes; mp->mt_name; mp++)
237		if (mbstat.m_mtypes[mp->mt_type]) {
238			seen[mp->mt_type] = YES;
239			printf("\t%u mbufs allocated to %s\n",
240			    mbstat.m_mtypes[mp->mt_type], mp->mt_name);
241		}
242	seen[MT_FREE] = YES;
243	for (i = 0; i < nmbtypes; i++)
244		if (!seen[i] && mbstat.m_mtypes[i]) {
245			printf("\t%u mbufs allocated to <mbuf type %d>\n",
246			    mbstat.m_mtypes[i], i);
247		}
248	if ((m_mbufs - totmbufs) > 0)
249		printf("\t%lu mbufs allocated to caches\n",
250		    m_mbufs - totmbufs);
251	printf("%u/%u mbuf 2KB clusters in use\n",
252	       (unsigned int)(mbstat.m_clusters - m_clfree),
253	       (unsigned int)mbstat.m_clusters);
254	printf("%u/%u mbuf 4KB clusters in use\n",
255	       (unsigned int)(mbstat.m_bigclusters - m_bigclfree),
256	       (unsigned int)mbstat.m_bigclusters);
257	if (njcl > 0) {
258		printf("%u/%u mbuf %uKB clusters in use\n",
259		    m_16kclusters - m_16kclfree, m_16kclusters,
260		    njclbytes/1024);
261	}
262	totused = totmem - totfree;
263	if (totmem == 0)
264		totpct = 0;
265	else if (totused < (ULONG_MAX/100))
266		totpct = (totused * 100)/(double)totmem;
267	else {
268		u_long totmem1 = totmem/100;
269		u_long totused1 = totused/100;
270		totpct = (totused1 * 100)/(double)totmem1;
271	}
272	printf("%lu KB allocated to network (%.1f%% in use)\n",
273		totmem / 1024, totpct);
274	printf("%lu KB returned to the system\n", totreturned / 1024);
275
276	printf("%u requests for memory denied\n", (unsigned int)mbstat.m_drops);
277	printf("%u requests for memory delayed\n", (unsigned int)mbstat.m_wait);
278	printf("%u calls to drain routines\n", (unsigned int)mbstat.m_drain);
279
280	free(mb_stat);
281	mb_stat = NULL;
282
283	if (mleak_stat != NULL) {
284		mleak_trace_stat_t *mltr;
285
286		printf("\nmbuf leak detection table:\n");
287		printf("\ttotal captured: %u (one per %u)\n"
288		    "\ttotal allocs outstanding: %llu\n"
289		    "\tnew hash recorded: %llu allocs, %llu traces\n"
290		    "\thash collisions: %llu allocs, %llu traces\n"
291		    "\toverwrites: %llu allocs, %llu traces\n"
292		    "\tlock conflicts: %llu\n\n",
293		    table.mleak_capture / table.mleak_sample_factor,
294		    table.mleak_sample_factor,
295		    table.outstanding_allocs,
296		    table.alloc_recorded, table.trace_recorded,
297		    table.alloc_collisions, table.trace_collisions,
298		    table.alloc_overwrites, table.trace_overwrites,
299		    table.total_conflicts);
300
301		printf("top %d outstanding traces:\n", mleak_stat->ml_cnt);
302		for (i = 0; i < mleak_stat->ml_cnt; i++) {
303			mltr = &mleak_stat->ml_trace[i];
304			printf("[%d] %llu outstanding alloc(s), "
305			    "%llu hit(s), %llu collision(s)\n", (i + 1),
306			    mltr->mltr_allocs, mltr->mltr_hitcount,
307			    mltr->mltr_collisions);
308		}
309
310		printf(MB_LEAK_HDR);
311		for (i = 0; i < MLEAK_STACK_DEPTH; i++) {
312			int j;
313
314			printf("%2d: ", (i + 1));
315			for (j = 0; j < mleak_stat->ml_cnt; j++) {
316				mltr = &mleak_stat->ml_trace[j];
317				if (i < mltr->mltr_depth) {
318					if (mleak_stat->ml_isaddr64) {
319						printf("0x%0llx  ",
320						    mltr->mltr_addr[i]);
321					} else {
322						printf("0x%08x          ",
323						    (u_int32_t)mltr->mltr_addr[i]);
324					}
325				} else {
326					printf(MB_LEAK_SPACING);
327				}
328			}
329			printf("\n");
330		}
331		free(mleak_stat);
332		mleak_stat = NULL;
333	}
334}
335
336static const char *
337mbpr_state(int state)
338{
339	char *msg = "?";
340
341	switch (state) {
342	case MCS_DISABLED:
343		msg = "dis";
344		break;
345
346	case MCS_ONLINE:
347		msg = "on";
348		break;
349
350	case MCS_PURGING:
351		msg = "purge";
352		break;
353
354	case MCS_OFFLINE:
355		msg = "off";
356		break;
357	}
358	return (msg);
359}
360
361static const char *
362mbpr_mem(u_int32_t bytes)
363{
364	static char buf[33];
365	double mem = bytes;
366
367	if (mem < 1024) {
368		(void) snprintf(buf, sizeof (buf), "%d", (int)mem);
369	} else if ((mem /= 1024) < 1024) {
370		(void) snprintf(buf, sizeof (buf), "%.1f KB", mem);
371	} else {
372		mem /= 1024;
373		(void) snprintf(buf, sizeof (buf), "%.1f MB", mem);
374	}
375	return (buf);
376}
377
378static int
379mbpr_getdata(void)
380{
381	size_t len;
382	int error = -1;
383
384	if (nmbtypes != 256) {
385		(void) fprintf(stderr,
386		    "netstat: unexpected change to mbstat; check source\n");
387		goto done;
388	}
389
390	len = sizeof(mbstat);
391	if (sysctlbyname("kern.ipc.mbstat", &mbstat, &len, 0, 0) == -1)
392		goto done;
393
394	if (sysctlbyname(KERN_IPC_MB_STAT, NULL, &len, 0, 0) == -1) {
395		(void) fprintf(stderr,
396		    "Error retrieving length for %s\n", KERN_IPC_MB_STAT);
397		goto done;
398	}
399
400	mb_stat = calloc(1, len);
401	if (mb_stat == NULL) {
402		(void) fprintf(stderr,
403		    "Error allocating %lu bytes for sysctl data\n", len);
404		goto done;
405	}
406
407	if (sysctlbyname(KERN_IPC_MB_STAT, mb_stat, &len, 0, 0) == -1) {
408		(void) fprintf(stderr,
409		     "Error %d getting %s\n", errno, KERN_IPC_MB_STAT);
410		goto done;
411	}
412
413	if (mb_stat->mbs_cnt == 0) {
414		(void) fprintf(stderr,
415		    "Invalid mbuf class count (%d)\n", mb_stat->mbs_cnt);
416		goto done;
417	}
418
419	/* mbuf leak detection! */
420	if (mflag > 3) {
421		errno = 0;
422		len = sizeof (table);
423		if (sysctlbyname(KERN_IPC_MLEAK_TABLE, &table, &len, 0, 0) ==
424		    -1 && errno != ENXIO) {
425			(void) fprintf(stderr, "error %d getting %s\n", errno,
426			    KERN_IPC_MLEAK_TABLE);
427			goto done;
428		} else if (errno == ENXIO) {
429			(void) fprintf(stderr, "mbuf leak detection is not "
430			    "enabled in the kernel.\n");
431			goto skip;
432		}
433
434		if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, NULL, &len,
435		    0, 0) == -1) {
436			(void) fprintf(stderr, "Error retrieving length for "
437			    "%s: %d\n", KERN_IPC_MB_STAT, errno);
438			goto done;
439		}
440
441		mleak_stat = calloc(1, len);
442		if (mleak_stat == NULL) {
443			(void) fprintf(stderr, "Error allocating %lu bytes "
444			    "for sysctl data\n", len);
445			goto done;
446		}
447
448		if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, mleak_stat, &len,
449		    0, 0) == -1) {
450			(void) fprintf(stderr, "error %d getting %s\n", errno,
451			     KERN_IPC_MLEAK_TOP_TRACE);
452			goto done;
453		}
454	}
455
456skip:
457	len = sizeof (njcl);
458	(void) sysctlbyname(KERN_IPC_NJCL, &njcl, &len, 0, 0);
459	len = sizeof (njclbytes);
460	(void) sysctlbyname(KERN_IPC_NJCL_BYTES, &njclbytes, &len, 0, 0);
461
462	error = 0;
463
464done:
465	if (error != 0  && mb_stat != NULL) {
466		free(mb_stat);
467		mb_stat = NULL;
468	}
469
470	if (error != 0 && mleak_stat != NULL) {
471		free(mleak_stat);
472		mleak_stat = NULL;
473	}
474
475	return (error);
476}
477