]> git.saurik.com Git - apple/network_cmds.git/blobdiff - netstat.tproj/mbuf.c
network_cmds-596.100.2.tar.gz
[apple/network_cmds.git] / netstat.tproj / mbuf.c
index 3ec5d285e008c7e74702ae87c3fa8cc477ec10b0..38645e92dc422e292a84c4d5d81ae5ea91975050 100644 (file)
@@ -1,15 +1,20 @@
 /*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
  * 
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -18,7 +23,7 @@
  * Please see the License for the specific language governing rights and
  * limitations under the License.
  * 
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*
  * Copyright (c) 1983, 1988, 1993
 #include <sys/sysctl.h>
 
 #include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
 #include "netstat.h"
-#include <netat/sysglue.h>    /* To get Appletalk message/mbuf types */
 
 #define        YES     1
 typedef int bool;
@@ -86,44 +93,146 @@ static struct mbtypes {
        { MT_SOOPTS,    "socket options" },
        { MT_RIGHTS,    "access rights" },
        { MT_IFADDR,    "interface addresses" },                /* XXX */
-       { MSG_DATA,     "Appletalk data blocks"},
-       { MSG_PROTO,    "Appletalk internal msgs"},
-       { MSG_IOCTL,    "Appletalk ioctl requests"},
-       { MSG_ERROR,    "Appletalk error indicators"},
-       { MSG_HANGUP,   "Appletalk termination requests"},
-       { MSG_IOCACK,   "Appletalk ioctl acks"},
-       { MSG_IOCNAK,   "Appletalk ioctl failure indicators"},
-       { MSG_CTL,      "Appletalk control msgs"},
+       { MT_TAG,               "packet tags" },                /* XXX */
        { 0, 0 }
 };
 
 int nmbtypes = sizeof(mbstat.m_mtypes) / sizeof(short);
 bool seen[256];                        /* "have we seen this type yet?" */
 
+mb_stat_t *mb_stat;
+unsigned int njcl, njclbytes;
+mleak_stat_t *mleak_stat;
+struct mleak_table table;
+
+#define        KERN_IPC_MB_STAT        "kern.ipc.mb_stat"
+#define        KERN_IPC_NJCL           "kern.ipc.njcl"
+#define        KERN_IPC_NJCL_BYTES     "kern.ipc.njclbytes"
+#define        KERN_IPC_MLEAK_TABLE    "kern.ipc.mleak_table"
+#define        KERN_IPC_MLEAK_TOP_TRACE "kern.ipc.mleak_top_trace"
+
+#define        MB_STAT_HDR1 "\
+class        buf   active   ctotal    total cache   cached uncached    memory\n\
+name        size     bufs     bufs     bufs state     bufs     bufs     usage\n\
+---------- ----- -------- -------- -------- ----- -------- -------- ---------\n\
+"
+
+#define        MB_STAT_HDR2 "\n\
+class        waiter   notify    purge   wretry  nwretry  failure\n\
+name          count    count    count    count    count    count\n\
+---------- -------- -------- -------- -------- -------- --------\n\
+"
+
+#define MB_LEAK_HDR "\n\
+    calltrace [1]       calltrace [2]       calltrace [3]       calltrace [4]       calltrace [5]      \n\
+    ------------------  ------------------  ------------------  ------------------  ------------------ \n\
+"
+
+#define MB_LEAK_SPACING "                    "
+static const char *mbpr_state(int);
+static const char *mbpr_mem(u_int32_t);
+static int mbpr_getdata(void);
+
 /*
  * Print mbuf statistics.
  */
 void
 mbpr(void)
 {
-       int totmem, totfree, totmbufs;
+       unsigned long totmem = 0, totfree = 0, totmbufs, totused, totreturned = 0;
+       double totpct;
+       u_int32_t m_msize, m_mbufs = 0, m_clfree = 0, m_bigclfree = 0;
+       u_int32_t m_mbufclfree = 0, m_mbufbigclfree = 0;
+       u_int32_t m_16kclusters = 0, m_16kclfree = 0, m_mbuf16kclfree = 0;
        int i;
        struct mbtypes *mp;
-       size_t len;
+       mb_class_stat_t *cp;
 
-       if (nmbtypes != 256) {
-               fprintf(stderr,
-                   "netstat: unexpected change to mbstat; check source\n");
+       if (mbpr_getdata() != 0)
                return;
+
+       m_msize = mbstat.m_msize;
+       cp = &mb_stat->mbs_class[0];
+       for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) {
+               if (cp->mbcl_size == m_msize) {
+                       m_mbufs = cp->mbcl_active;
+               } else if (cp->mbcl_size == mbstat.m_mclbytes) {
+                       m_clfree = cp->mbcl_total - cp->mbcl_active;
+               } else if (cp->mbcl_size == mbstat.m_bigmclbytes) {
+                       m_bigclfree = cp->mbcl_total - cp->mbcl_active;
+               } else if (njcl > 0 && cp->mbcl_size == njclbytes) {
+                       m_16kclfree = cp->mbcl_total - cp->mbcl_active;
+                       m_16kclusters = cp->mbcl_total;
+               } else if (cp->mbcl_size == (m_msize + mbstat.m_mclbytes)) {
+                       m_mbufclfree = cp->mbcl_total - cp->mbcl_active;
+               } else if (cp->mbcl_size == (m_msize + mbstat.m_bigmclbytes)) {
+                       m_mbufbigclfree = cp->mbcl_total - cp->mbcl_active;
+               } else if (njcl > 0 && cp->mbcl_size == (m_msize + njclbytes)) {
+                       m_mbuf16kclfree = cp->mbcl_total - cp->mbcl_active;
+               }
+       }
+
+       /* adjust free counts to include composite caches */
+       m_clfree += m_mbufclfree;
+       m_bigclfree += m_mbufbigclfree;
+       m_16kclfree += m_mbuf16kclfree;
+
+       cp = &mb_stat->mbs_class[0];
+       for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) {
+               u_int32_t mem;
+
+               mem = cp->mbcl_ctotal * cp->mbcl_size;
+               totmem += mem;
+               totreturned += cp->mbcl_release_cnt;
+               totfree += (cp->mbcl_mc_cached + cp->mbcl_infree) *
+                   cp->mbcl_size;
+               if (mflag > 1) {
+                       if (i == 0)
+                               printf(MB_STAT_HDR1);
+
+                       if (njcl == 0 &&
+                           cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes))
+                               continue;
+
+                       printf("%-10s %5u %8u %8u %8u %5s %8u %8u %9s\n",
+                           cp->mbcl_cname, cp->mbcl_size, cp->mbcl_active,
+                           cp->mbcl_ctotal, cp->mbcl_total,
+                           mbpr_state(cp->mbcl_mc_state), cp->mbcl_mc_cached,
+                           cp->mbcl_infree, mbpr_mem(mem));
+               }
        }
-       len = sizeof(mbstat);
-       if (sysctlbyname("kern.ipc.mbstat", &mbstat, &len, 0, 0) == -1)
-               return;
+
+       cp = &mb_stat->mbs_class[0];
+       for (i = 0; i < mb_stat->mbs_cnt; i++, cp++) {
+               if (mflag > 2) {
+                       if (i == 0)
+                               printf(MB_STAT_HDR2);
+
+                       if (njcl == 0 &&
+                           cp->mbcl_size > (m_msize + mbstat.m_bigmclbytes))
+                               continue;
+
+                       printf("%-10s %8u %8llu %8llu %8u %8u %8llu\n",
+                           cp->mbcl_cname, cp->mbcl_mc_waiter_cnt,
+                           cp->mbcl_notified, cp->mbcl_purge_cnt,
+                           cp->mbcl_mc_wretry_cnt, cp->mbcl_mc_nwretry_cnt,
+                           cp->mbcl_fail_cnt);
+               }
+       }
+
+       if (mflag > 1)
+               printf("\n");
 
        totmbufs = 0;
        for (mp = mbtypes; mp->mt_name; mp++)
                totmbufs += mbstat.m_mtypes[mp->mt_type];
-       printf("%u mbufs in use:\n", totmbufs);
+       /*
+        * These stats are not updated atomically in the kernel;
+        * adjust the total as neeeded.
+        */
+       if (totmbufs > m_mbufs)
+               totmbufs = m_mbufs;
+       printf("%lu/%u mbufs in use:\n", totmbufs, m_mbufs);
        for (mp = mbtypes; mp->mt_name; mp++)
                if (mbstat.m_mtypes[mp->mt_type]) {
                        seen[mp->mt_type] = YES;
@@ -136,19 +245,232 @@ mbpr(void)
                        printf("\t%u mbufs allocated to <mbuf type %d>\n",
                            mbstat.m_mtypes[i], i);
                }
-       printf("%u/%u mbuf clusters in use\n",
-              (unsigned int)(mbstat.m_clusters - mbstat.m_clfree),
+       if ((m_mbufs - totmbufs) > 0)
+               printf("\t%lu mbufs allocated to caches\n",
+                   m_mbufs - totmbufs);
+       printf("%u/%u mbuf 2KB clusters in use\n",
+              (unsigned int)(mbstat.m_clusters - m_clfree),
               (unsigned int)mbstat.m_clusters);
        printf("%u/%u mbuf 4KB clusters in use\n",
-              (unsigned int)(mbstat.m_bigclusters - mbstat.m_bigclfree),
+              (unsigned int)(mbstat.m_bigclusters - m_bigclfree),
               (unsigned int)mbstat.m_bigclusters);
-       totmem = totmbufs * MSIZE + mbstat.m_clusters * MCLBYTES + mbstat.m_bigclusters * mbstat.m_bigmclbytes;
-       totfree = mbstat.m_clfree * MCLBYTES + mbstat.m_bigclfree * mbstat.m_bigmclbytes;
-       printf("%u Kbytes allocated to network (%d%% in use)\n",
-               totmem / 1024, (totmem - totfree) * 100 / totmem);
-       printf("%u requests for memory denied\n",
-              (unsigned int)mbstat.m_drops);
+       if (njcl > 0) {
+               printf("%u/%u mbuf %uKB clusters in use\n",
+                   m_16kclusters - m_16kclfree, m_16kclusters,
+                   njclbytes/1024);
+       }
+       totused = totmem - totfree;
+       if (totmem == 0)
+               totpct = 0;
+       else if (totused < (ULONG_MAX/100))
+               totpct = (totused * 100)/(double)totmem;
+       else {
+               u_long totmem1 = totmem/100;
+               u_long totused1 = totused/100;
+               totpct = (totused1 * 100)/(double)totmem1;
+       }
+       printf("%lu KB allocated to network (%.1f%% in use)\n",
+               totmem / 1024, totpct);
+       printf("%lu KB returned to the system\n", totreturned / 1024);
+
+       printf("%u requests for memory denied\n", (unsigned int)mbstat.m_drops);
        printf("%u requests for memory delayed\n", (unsigned int)mbstat.m_wait);
-       printf("%u calls to protocol drain routines\n",
-              (unsigned int)mbstat.m_drain);
+       printf("%u calls to drain routines\n", (unsigned int)mbstat.m_drain);
+
+       free(mb_stat);
+       mb_stat = NULL;
+
+       if (mleak_stat != NULL) {
+               mleak_trace_stat_t *mltr;
+
+               printf("\nmbuf leak detection table:\n");
+               printf("\ttotal captured: %u (one per %u)\n"
+                   "\ttotal allocs outstanding: %llu\n"
+                   "\tnew hash recorded: %llu allocs, %llu traces\n"
+                   "\thash collisions: %llu allocs, %llu traces\n"
+                   "\toverwrites: %llu allocs, %llu traces\n"
+                   "\tlock conflicts: %llu\n\n",
+                   table.mleak_capture / table.mleak_sample_factor,
+                   table.mleak_sample_factor,
+                   table.outstanding_allocs,
+                   table.alloc_recorded, table.trace_recorded,
+                   table.alloc_collisions, table.trace_collisions,
+                   table.alloc_overwrites, table.trace_overwrites,
+                   table.total_conflicts);
+
+               printf("top %d outstanding traces:\n", mleak_stat->ml_cnt);
+               for (i = 0; i < mleak_stat->ml_cnt; i++) {
+                       mltr = &mleak_stat->ml_trace[i];
+                       printf("[%d] %llu outstanding alloc(s), "
+                           "%llu hit(s), %llu collision(s)\n", (i + 1),
+                           mltr->mltr_allocs, mltr->mltr_hitcount,
+                           mltr->mltr_collisions);
+               }
+
+               printf(MB_LEAK_HDR);
+               for (i = 0; i < MLEAK_STACK_DEPTH; i++) {
+                       int j;
+
+                       printf("%2d: ", (i + 1));
+                       for (j = 0; j < mleak_stat->ml_cnt; j++) {
+                               mltr = &mleak_stat->ml_trace[j];
+                               if (i < mltr->mltr_depth) {
+                                       if (mleak_stat->ml_isaddr64) {
+                                               printf("0x%0llx  ",
+                                                   mltr->mltr_addr[i]);
+                                       } else {
+                                               printf("0x%08x          ",
+                                                   (u_int32_t)mltr->mltr_addr[i]);
+                                       }
+                               } else {
+                                       printf(MB_LEAK_SPACING);
+                               }
+                       }
+                       printf("\n");
+               }
+               free(mleak_stat);
+               mleak_stat = NULL;
+       }
+}
+
+static const char *
+mbpr_state(int state)
+{
+       char *msg = "?";
+
+       switch (state) {
+       case MCS_DISABLED:
+               msg = "dis";
+               break;
+
+       case MCS_ONLINE:
+               msg = "on";
+               break;
+
+       case MCS_PURGING:
+               msg = "purge";
+               break;
+
+       case MCS_OFFLINE:
+               msg = "off";
+               break;
+       }
+       return (msg);
+}
+
+static const char *
+mbpr_mem(u_int32_t bytes)
+{
+       static char buf[33];
+       double mem = bytes;
+
+       if (mem < 1024) {
+               (void) snprintf(buf, sizeof (buf), "%d", (int)mem);
+       } else if ((mem /= 1024) < 1024) {
+               (void) snprintf(buf, sizeof (buf), "%.1f KB", mem);
+       } else {
+               mem /= 1024;
+               (void) snprintf(buf, sizeof (buf), "%.1f MB", mem);
+       }
+       return (buf);
+}
+
+static int
+mbpr_getdata(void)
+{
+       size_t len;
+       int error = -1;
+
+       if (nmbtypes != 256) {
+               (void) fprintf(stderr,
+                   "netstat: unexpected change to mbstat; check source\n");
+               goto done;
+       }
+
+       len = sizeof(mbstat);
+       if (sysctlbyname("kern.ipc.mbstat", &mbstat, &len, 0, 0) == -1)
+               goto done;
+
+       if (sysctlbyname(KERN_IPC_MB_STAT, NULL, &len, 0, 0) == -1) {
+               (void) fprintf(stderr,
+                   "Error retrieving length for %s\n", KERN_IPC_MB_STAT);
+               goto done;
+       }
+
+       mb_stat = calloc(1, len);
+       if (mb_stat == NULL) {
+               (void) fprintf(stderr,
+                   "Error allocating %lu bytes for sysctl data\n", len);
+               goto done;
+       }
+
+       if (sysctlbyname(KERN_IPC_MB_STAT, mb_stat, &len, 0, 0) == -1) {
+               (void) fprintf(stderr,
+                    "Error %d getting %s\n", errno, KERN_IPC_MB_STAT);
+               goto done;
+       }
+
+       if (mb_stat->mbs_cnt == 0) {
+               (void) fprintf(stderr,
+                   "Invalid mbuf class count (%d)\n", mb_stat->mbs_cnt);
+               goto done;
+       }
+
+       /* mbuf leak detection! */
+       if (mflag > 3) {
+               errno = 0;
+               len = sizeof (table);
+               if (sysctlbyname(KERN_IPC_MLEAK_TABLE, &table, &len, 0, 0) ==
+                   -1 && errno != ENXIO) {
+                       (void) fprintf(stderr, "error %d getting %s\n", errno,
+                           KERN_IPC_MLEAK_TABLE);
+                       goto done;
+               } else if (errno == ENXIO) {
+                       (void) fprintf(stderr, "mbuf leak detection is not "
+                           "enabled in the kernel.\n");
+                       goto skip;
+               }
+
+               if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, NULL, &len,
+                   0, 0) == -1) {
+                       (void) fprintf(stderr, "Error retrieving length for "
+                           "%s: %d\n", KERN_IPC_MB_STAT, errno);
+                       goto done;
+               }
+
+               mleak_stat = calloc(1, len);
+               if (mleak_stat == NULL) {
+                       (void) fprintf(stderr, "Error allocating %lu bytes "
+                           "for sysctl data\n", len);
+                       goto done;
+               }
+
+               if (sysctlbyname(KERN_IPC_MLEAK_TOP_TRACE, mleak_stat, &len,
+                   0, 0) == -1) {
+                       (void) fprintf(stderr, "error %d getting %s\n", errno,
+                            KERN_IPC_MLEAK_TOP_TRACE);
+                       goto done;
+               }
+       }
+
+skip:
+       len = sizeof (njcl);
+       (void) sysctlbyname(KERN_IPC_NJCL, &njcl, &len, 0, 0);
+       len = sizeof (njclbytes);
+       (void) sysctlbyname(KERN_IPC_NJCL_BYTES, &njclbytes, &len, 0, 0);
+
+       error = 0;
+
+done:
+       if (error != 0  && mb_stat != NULL) {
+               free(mb_stat);
+               mb_stat = NULL;
+       }
+
+       if (error != 0 && mleak_stat != NULL) {
+               free(mleak_stat);
+               mleak_stat = NULL;
+       }
+
+       return (error);
 }