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