[lib/memory] Add mallinfo support

2006-02-15 Paul Jakma <paul.jakma@sun.com>

	* configure.ac: Check for mallinfo, being careful to link test
	  so we can detect things like umem being used (which doesn't
	  provide a mallinfo).
	* lib/memory.c: (mtype_memstr) new helper function to
	  return human friendly string for a byte count.
	  (mtype_stats_alloc) new function, for users to retrieve
	  number of objects allocated.
	  (show_memory_mallinfo) New function, show mallinfo statistics
	  if available.
	  (show_memory_all_cmd) Call show_memory_mallinfo, if mallinfo
	  is available.
	* lib/memory.h: Export mtype_memstr and mtype_stats_alloc.
	  Provide a define for a reasonable buffer size for
	  mtype_memstr.
diff --git a/ChangeLog b/ChangeLog
index 33d1a02..98a0a8e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2006-02-15 Paul Jakma <paul.jakma@sun.com>
+
+	* configure.ac: Check for mallinfo, being careful to link test
+	  so we can detect things like umem being used (which doesn't
+	  provide a mallinfo).
+
 2006-01-31 Paul Jakma <paul.jakma@sun.com>
 
 	* configure.ac: Cleanup the hideous {net,ucd}-snmp section
diff --git a/configure.ac b/configure.ac
index 8727e5d..841786f 100755
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@
 ##  Copyright (c) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
 ##  Portions Copyright (c) 2003 Paul Jakma <paul@dishone.st>
 ##
-## $Id: configure.ac,v 1.118 2006/01/31 10:09:27 paul Exp $
+## $Id: configure.ac,v 1.119 2006/03/30 13:53:59 paul Exp $
 AC_PREREQ(2.53)
 
 AC_INIT(Quagga, 0.99.3, [http://bugzilla.quagga.net])
@@ -1236,6 +1236,24 @@
   )
 fi
 
+dnl -----------------------------------------
+dnl check for malloc mallinfo struct and call
+dnl this must try and link using LIBS, in
+dnl order to check no alternative allocator
+dnl has been specified, which might not provide
+dnl mallinfo, e.g. such as Umem on Solaris.
+dnl -----------------------------------------
+AC_CHECK_HEADERS(malloc.h,
+ [AC_MSG_CHECKING(whether mallinfo is available)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <malloc.h>]],
+                        [[struct mallinfo ac_x; ac_x = mallinfo ();]])],
+      [AC_MSG_RESULT(yes)
+       AC_DEFINE(HAVE_MALLINFO,,mallinfo)],
+       AC_MSG_RESULT(no)
+  )
+ ]
+)
+
 dnl ----------
 dnl configure date
 dnl ----------
diff --git a/lib/ChangeLog b/lib/ChangeLog
index 8794d69..de0f352 100644
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,3 +1,17 @@
+2006-03-15 Paul Jakma <paul.jakma@sun.com>
+
+	* memory.c: (mtype_memstr) new helper function to 
+	  return human friendly string for a byte count.
+	  (mtype_stats_alloc) new function, for users to retrieve
+	  number of objects allocated.
+	  (show_memory_mallinfo) New function, show mallinfo statistics
+	  if available.
+	  (show_memory_all_cmd) Call show_memory_mallinfo, if mallinfo
+	  is available.
+	* memory.h: Export mtype_memstr and mtype_stats_alloc.
+	  Provide a define for a reasonable buffer size for
+	  mtype_memstr.
+	  
 2006-03-14 Paul Jakma <paul.jakma@sun.com>
 
 	* privs.c: (zprivs_caps_init) Change user IDs before lowering
diff --git a/lib/memory.c b/lib/memory.c
index dae2b9a..802c07f 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -21,6 +21,7 @@
  */
 
 #include <zebra.h>
+#include <malloc.h>
 
 #include "log.h"
 #include "memory.h"
@@ -278,6 +279,47 @@
   return needsep;
 }
 
+#ifdef HAVE_MALLINFO
+static int
+show_memory_mallinfo (struct vty *vty)
+{
+  struct mallinfo minfo = mallinfo();
+  char buf[MTYPE_MEMSTR_LEN];
+  
+  vty_out (vty, "System allocator statistics:%s", VTY_NEWLINE);
+  vty_out (vty, "  Total heap allocated:  %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.arena),
+           VTY_NEWLINE);
+  vty_out (vty, "  Holding block headers: %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.hblkhd),
+           VTY_NEWLINE);
+  vty_out (vty, "  Used small blocks:     %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.usmblks),
+           VTY_NEWLINE);
+  vty_out (vty, "  Used ordinary blocks:  %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.uordblks),
+           VTY_NEWLINE);
+  vty_out (vty, "  Free small blocks:     %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fsmblks),
+           VTY_NEWLINE);
+  vty_out (vty, "  Free ordinary blocks:  %s%s",
+           mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fordblks),
+           VTY_NEWLINE);
+  vty_out (vty, "  Ordinary blocks:       %ld%s",
+           (unsigned long)minfo.ordblks,
+           VTY_NEWLINE);
+  vty_out (vty, "  Small blocks:          %ld%s",
+           (unsigned long)minfo.smblks,
+           VTY_NEWLINE);
+  vty_out (vty, "  Holding blocks:        %ld%s",
+           (unsigned long)minfo.hblks,
+           VTY_NEWLINE);
+  vty_out (vty, "(see system documentation for 'mallinfo' for meaning)%s",
+           VTY_NEWLINE);
+  return 1;
+}
+#endif /* HAVE_MALLINFO */
+
 DEFUN (show_memory_all,
        show_memory_all_cmd,
        "show memory all",
@@ -287,7 +329,11 @@
 {
   struct mlist *ml;
   int needsep = 0;
-
+  
+#ifdef HAVE_MALLINFO
+  needsep = show_memory_mallinfo (vty);
+#endif /* HAVE_MALLINFO */
+  
   for (ml = mlists; ml->list; ml++)
     {
       if (needsep)
@@ -416,3 +462,72 @@
   install_element (ENABLE_NODE, &show_memory_ospf6_cmd);
   install_element (ENABLE_NODE, &show_memory_isis_cmd);
 }
+
+/* Stats querying from users */
+/* Return a pointer to a human friendly string describing
+ * the byte count passed in. E.g:
+ * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
+ * Up to 4 significant figures will be given.
+ * The pointer returned may be NULL (indicating an error)
+ * or point to the given buffer, or point to static storage.
+ */
+const char *
+mtype_memstr (char *buf, size_t len, unsigned long bytes)
+{
+  unsigned int t, g, m, k;
+  
+  /* easy cases */
+  if (!bytes)
+    return "0 bytes";
+  if (bytes == 1)
+    return "1 byte";
+    
+  if (sizeof (unsigned long) >= 8)
+    /* Hacked to make it not warn on ILP32 machines
+     * Shift will always be 40 at runtime. See below too */
+    t = bytes >> (sizeof (unsigned long) >= 8 ? 40 : 0);
+  else
+    t = 0;
+  g = bytes >> 30;
+  m = bytes >> 20;
+  k = bytes >> 10;
+  
+  if (t > 10)
+    {
+      /* The shift will always be 39 at runtime.
+       * Just hacked to make it not warn on 'smaller' machines. 
+       * Static compiler analysis should mean no extra code
+       */
+      if (bytes & (1 << (sizeof (unsigned long) >= 8 ? 39 : 0)))
+        t++;
+      snprintf (buf, len, "%4d TiB", t);
+    }
+  else if (g > 10)
+    {
+      if (bytes & (1 << 29))
+        g++;
+      snprintf (buf, len, "%d GiB", g);
+    }
+  else if (m > 10)
+    {
+      if (bytes & (1 << 19))
+        m++;
+      snprintf (buf, len, "%d MiB", m);
+    }
+  else if (k > 10)
+    {
+      if (bytes & (1 << 9))
+        k++;
+      snprintf (buf, len, "%d KiB", k);
+    }
+  else
+    snprintf (buf, len, "%ld bytes", bytes);
+  
+  return buf;
+}
+
+unsigned long
+mtype_stats_alloc (int type)
+{
+  return mstat[type].alloc;
+}
diff --git a/lib/memory.h b/lib/memory.h
index ef20b8c..071f394 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -79,4 +79,10 @@
 		            const char *str);
 extern void memory_init (void);
 
+/* return number of allocations outstanding for the type */
+extern unsigned long mtype_stats_alloc (int);
+
+/* Human friendly string for given byte count */
+#define MTYPE_MEMSTR_LEN 20
+extern const char *mtype_memstr (char *, size_t, unsigned long);
 #endif /* _ZEBRA_MEMORY_H */