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; 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 334static const char * 335mbpr_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 359static const char * 360mbpr_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 376static int 377mbpr_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 454skip: 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 462done: 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} 475