]>
Commit | Line | Data |
---|---|---|
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 | |
74 | typedef int bool; | |
75 | ||
76 | struct mbstat mbstat; | |
77 | ||
78 | static 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 | ||
100 | int nmbtypes = sizeof(mbstat.m_mtypes) / sizeof(short); | |
101 | bool seen[256]; /* "have we seen this type yet?" */ | |
102 | ||
103 | mb_stat_t *mb_stat; | |
104 | unsigned int njcl, njclbytes; | |
105 | mleak_stat_t *mleak_stat; | |
106 | struct 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 "\ | |
115 | class buf active ctotal total cache cached uncached memory\n\ | |
116 | name size bufs bufs bufs state bufs bufs usage\n\ | |
117 | ---------- ----- -------- -------- -------- ----- -------- -------- ---------\n\ | |
118 | " | |
119 | ||
120 | #define MB_STAT_HDR2 "\n\ | |
121 | class waiter notify purge wretry nwretry failure\n\ | |
122 | name 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 " " | |
132 | static const char *mbpr_state(int); | |
133 | static const char *mbpr_mem(u_int32_t); | |
134 | static int mbpr_getdata(void); | |
135 | ||
136 | /* | |
137 | * Print mbuf statistics. | |
138 | */ | |
139 | void | |
140 | mbpr(void) | |
141 | { | |
142 | unsigned long totmem = 0, totfree = 0, totmbufs, totused; | |
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 | totfree += (cp->mbcl_mc_cached + cp->mbcl_infree) * | |
187 | cp->mbcl_size; | |
188 | if (mflag > 1) { | |
189 | if (i == 0) | |
190 | printf(MB_STAT_HDR1); | |
191 | ||
192 | if (njcl == 0 && | |
193 | cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes)) | |
194 | continue; | |
195 | ||
196 | printf("%-10s %5u %8u %8u %8u %5s %8u %8u %9s\n", | |
197 | cp->mbcl_cname, cp->mbcl_size, cp->mbcl_active, | |
198 | cp->mbcl_ctotal, cp->mbcl_total, | |
199 | mbpr_state(cp->mbcl_mc_state), cp->mbcl_mc_cached, | |
200 | cp->mbcl_infree, mbpr_mem(mem)); | |
201 | } | |
202 | } | |
203 | ||
204 | cp = &mb_stat->mbs_class[0]; | |
205 | for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) { | |
206 | if (mflag > 2) { | |
207 | if (i == 0) | |
208 | printf(MB_STAT_HDR2); | |
209 | ||
210 | if (njcl == 0 && | |
211 | cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes)) | |
212 | continue; | |
213 | ||
214 | printf("%-10s %8u %8llu %8llu %8u %8u %8llu\n", | |
215 | cp->mbcl_cname, cp->mbcl_mc_waiter_cnt, | |
216 | cp->mbcl_notified, cp->mbcl_purge_cnt, | |
217 | cp->mbcl_mc_wretry_cnt, cp->mbcl_mc_nwretry_cnt, | |
218 | cp->mbcl_fail_cnt); | |
219 | } | |
220 | } | |
221 | ||
222 | if (mflag > 1) | |
223 | printf("\n"); | |
224 | ||
225 | totmbufs = 0; | |
226 | for (mp = mbtypes; mp->mt_name; mp++) | |
227 | totmbufs += mbstat.m_mtypes[mp->mt_type]; | |
228 | /* | |
229 | * These stats are not updated atomically in the kernel; | |
230 | * adjust the total as neeeded. | |
231 | */ | |
232 | if (totmbufs > m_mbufs) | |
233 | totmbufs = m_mbufs; | |
234 | printf("%lu/%u mbufs in use:\n", totmbufs, m_mbufs); | |
235 | for (mp = mbtypes; mp->mt_name; mp++) | |
236 | if (mbstat.m_mtypes[mp->mt_type]) { | |
237 | seen[mp->mt_type] = YES; | |
238 | printf("\t%u mbufs allocated to %s\n", | |
239 | mbstat.m_mtypes[mp->mt_type], mp->mt_name); | |
240 | } | |
241 | seen[MT_FREE] = YES; | |
242 | for (i = 0; i < nmbtypes; i++) | |
243 | if (!seen[i] && mbstat.m_mtypes[i]) { | |
244 | printf("\t%u mbufs allocated to <mbuf type %d>\n", | |
245 | mbstat.m_mtypes[i], i); | |
246 | } | |
247 | if ((m_mbufs - totmbufs) > 0) | |
248 | printf("\t%lu mbufs allocated to caches\n", | |
249 | m_mbufs - totmbufs); | |
250 | printf("%u/%u mbuf 2KB clusters in use\n", | |
251 | (unsigned int)(mbstat.m_clusters - m_clfree), | |
252 | (unsigned int)mbstat.m_clusters); | |
253 | printf("%u/%u mbuf 4KB clusters in use\n", | |
254 | (unsigned int)(mbstat.m_bigclusters - m_bigclfree), | |
255 | (unsigned int)mbstat.m_bigclusters); | |
256 | if (njcl > 0) { | |
257 | printf("%u/%u mbuf %uKB clusters in use\n", | |
258 | m_16kclusters - m_16kclfree, m_16kclusters, | |
259 | njclbytes/1024); | |
260 | } | |
261 | totused = totmem - totfree; | |
262 | if (totmem == 0) | |
263 | totpct = 0; | |
264 | else if (totused < (ULONG_MAX/100)) | |
265 | totpct = (totused * 100)/(double)totmem; | |
266 | else { | |
267 | u_long totmem1 = totmem/100; | |
268 | u_long totused1 = totused/100; | |
269 | totpct = (totused1 * 100)/(double)totmem1; | |
270 | } | |
271 | printf("%lu KB allocated to network (%.1f%% in use)\n", | |
272 | totmem / 1024, totpct); | |
273 | ||
274 | printf("%u requests for memory denied\n", (unsigned int)mbstat.m_drops); | |
275 | printf("%u requests for memory delayed\n", (unsigned int)mbstat.m_wait); | |
276 | printf("%u calls to drain routines\n", (unsigned int)mbstat.m_drain); | |
277 | ||
278 | free(mb_stat); | |
279 | mb_stat = NULL; | |
280 | ||
281 | if (mleak_stat != NULL) { | |
282 | mleak_trace_stat_t *mltr; | |
283 | ||
284 | printf("\nmbuf leak detection table:\n"); | |
285 | printf("\ttotal captured: %u (one per %u)\n" | |
286 | "\ttotal allocs outstanding: %llu\n" | |
287 | "\tnew hash recorded: %llu allocs, %llu traces\n" | |
288 | "\thash collisions: %llu allocs, %llu traces\n" | |
289 | "\toverwrites: %llu allocs, %llu traces\n" | |
290 | "\tlock conflicts: %llu\n\n", | |
291 | table.mleak_capture / table.mleak_sample_factor, | |
292 | table.mleak_sample_factor, | |
293 | table.outstanding_allocs, | |
294 | table.alloc_recorded, table.trace_recorded, | |
295 | table.alloc_collisions, table.trace_collisions, | |
296 | table.alloc_overwrites, table.trace_overwrites, | |
297 | table.total_conflicts); | |
298 | ||
299 | printf("top %d outstanding traces:\n", mleak_stat->ml_cnt); | |
300 | for (i = 0; i < mleak_stat->ml_cnt; i++) { | |
301 | mltr = &mleak_stat->ml_trace[i]; | |
302 | printf("[%d] %llu outstanding alloc(s), " | |
303 | "%llu hit(s), %llu collision(s)\n", (i + 1), | |
304 | mltr->mltr_allocs, mltr->mltr_hitcount, | |
305 | mltr->mltr_collisions); | |
306 | } | |
307 | ||
308 | printf(MB_LEAK_HDR); | |
309 | for (i = 0; i < MLEAK_STACK_DEPTH; i++) { | |
310 | int j; | |
311 | ||
312 | printf("%2d: ", (i + 1)); | |
313 | for (j = 0; j < mleak_stat->ml_cnt; j++) { | |
314 | mltr = &mleak_stat->ml_trace[j]; | |
315 | if (i < mltr->mltr_depth) { | |
316 | if (mleak_stat->ml_isaddr64) { | |
317 | printf("0x%0llx ", | |
318 | mltr->mltr_addr[i]); | |
319 | } else { | |
320 | printf("0x%08x ", | |
321 | (u_int32_t)mltr->mltr_addr[i]); | |
322 | } | |
323 | } else { | |
324 | printf(MB_LEAK_SPACING); | |
325 | } | |
326 | } | |
327 | printf("\n"); | |
328 | } | |
329 | free(mleak_stat); | |
330 | mleak_stat = NULL; | |
331 | } | |
332 | } | |
333 | ||
334 | static const char * | |
335 | mbpr_state(int state) | |
336 | { | |
337 | char *msg = "?"; | |
338 | ||
339 | switch (state) { | |
340 | case MCS_DISABLED: | |
341 | msg = "dis"; | |
342 | break; | |
343 | ||
344 | case MCS_ONLINE: | |
345 | msg = "on"; | |
346 | break; | |
347 | ||
348 | case MCS_PURGING: | |
349 | msg = "purge"; | |
350 | break; | |
351 | ||
352 | case MCS_OFFLINE: | |
353 | msg = "off"; | |
354 | break; | |
355 | } | |
356 | return (msg); | |
357 | } | |
358 | ||
359 | static const char * | |
360 | mbpr_mem(u_int32_t bytes) | |
361 | { | |
362 | static char buf[33]; | |
363 | double mem = bytes; | |
364 | ||
365 | if (mem < 1024) { | |
366 | (void) snprintf(buf, sizeof (buf), "%d", (int)mem); | |
367 | } else if ((mem /= 1024) < 1024) { | |
368 | (void) snprintf(buf, sizeof (buf), "%.1f KB", mem); | |
369 | } else { | |
370 | mem /= 1024; | |
371 | (void) snprintf(buf, sizeof (buf), "%.1f MB", mem); | |
372 | } | |
373 | return (buf); | |
374 | } | |
375 | ||
376 | static int | |
377 | mbpr_getdata(void) | |
378 | { | |
379 | size_t len; | |
380 | int error = -1; | |
381 | ||
382 | if (nmbtypes != 256) { | |
383 | (void) fprintf(stderr, | |
384 | "netstat: unexpected change to mbstat; check source\n"); | |
385 | goto done; | |
386 | } | |
387 | ||
388 | len = sizeof(mbstat); | |
389 | if (sysctlbyname("kern.ipc.mbstat", &mbstat, &len, 0, 0) == -1) | |
390 | goto done; | |
391 | ||
392 | if (sysctlbyname(KERN_IPC_MB_STAT, NULL, &len, 0, 0) == -1) { | |
393 | (void) fprintf(stderr, | |
394 | "Error retrieving length for %s\n", KERN_IPC_MB_STAT); | |
395 | goto done; | |
396 | } | |
397 | ||
398 | mb_stat = calloc(1, len); | |
399 | if (mb_stat == NULL) { | |
400 | (void) fprintf(stderr, | |
401 | "Error allocating %lu bytes for sysctl data\n", len); | |
402 | goto done; | |
403 | } | |
404 | ||
405 | if (sysctlbyname(KERN_IPC_MB_STAT, mb_stat, &len, 0, 0) == -1) { | |
406 | (void) fprintf(stderr, | |
407 | "Error %d getting %s\n", errno, KERN_IPC_MB_STAT); | |
408 | goto done; | |
409 | } | |
410 | ||
411 | if (mb_stat->mbs_cnt == 0) { | |
412 | (void) fprintf(stderr, | |
413 | "Invalid mbuf class count (%d)\n", mb_stat->mbs_cnt); | |
414 | goto done; | |
415 | } | |
416 | ||
417 | /* mbuf leak detection! */ | |
418 | if (mflag > 3) { | |
419 | errno = 0; | |
420 | len = sizeof (table); | |
421 | if (sysctlbyname(KERN_IPC_MLEAK_TABLE, &table, &len, 0, 0) == | |
422 | -1 && errno != ENXIO) { | |
423 | (void) fprintf(stderr, "error %d getting %s\n", errno, | |
424 | KERN_IPC_MLEAK_TABLE); | |
425 | goto done; | |
426 | } else if (errno == ENXIO) { | |
427 | (void) fprintf(stderr, "mbuf leak detection is not " | |
428 | "enabled in the kernel.\n"); | |
429 | goto skip; | |
430 | } | |
431 | ||
432 | if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, NULL, &len, | |
433 | 0, 0) == -1) { | |
434 | (void) fprintf(stderr, "Error retrieving length for " | |
435 | "%s: %d\n", KERN_IPC_MB_STAT, errno); | |
436 | goto done; | |
437 | } | |
438 | ||
439 | mleak_stat = calloc(1, len); | |
440 | if (mleak_stat == NULL) { | |
441 | (void) fprintf(stderr, "Error allocating %lu bytes " | |
442 | "for sysctl data\n", len); | |
443 | goto done; | |
444 | } | |
445 | ||
446 | if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, mleak_stat, &len, | |
447 | 0, 0) == -1) { | |
448 | (void) fprintf(stderr, "error %d getting %s\n", errno, | |
449 | KERN_IPC_MLEAK_TOP_TRACE); | |
450 | goto done; | |
451 | } | |
452 | } | |
453 | ||
454 | skip: | |
455 | len = sizeof (njcl); | |
456 | (void) sysctlbyname(KERN_IPC_NJCL, &njcl, &len, 0, 0); | |
457 | len = sizeof (njclbytes); | |
458 | (void) sysctlbyname(KERN_IPC_NJCL_BYTES, &njclbytes, &len, 0, 0); | |
459 | ||
460 | error = 0; | |
461 | ||
462 | done: | |
463 | if (error != 0 && mb_stat != NULL) { | |
464 | free(mb_stat); | |
465 | mb_stat = NULL; | |
466 | } | |
467 | ||
468 | if (error != 0 && mleak_stat != NULL) { | |
469 | free(mleak_stat); | |
470 | mleak_stat = NULL; | |
471 | } | |
472 | ||
473 | return (error); | |
474 | } |