From: David Lamparter <equi...@opensourcerouting.org>

This rewrites Quagga's memory per-type allocation counting, without
using a fixed global list of types.  Instead, source files can declare
memory types which get handled through constructor functions called by
the dynamic linker during startup.

Signed-off-by: David Lamparter <equi...@opensourcerouting.org>
Signed-off-by: Philippe Guibert <philippe.guib...@6wind.com>
---
 lib/Makefile.am  |   1 +
 lib/log.c        |   8 +++
 lib/memory.c     | 102 +++++++++++++++++++++++++++++++++++
 lib/memory.h     | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/memory_vty.c |  19 +++++++
 lib/memory_vty.h |   2 -
 6 files changed, 288 insertions(+), 3 deletions(-)
 create mode 100644 lib/memory.c

diff --git a/lib/Makefile.am b/lib/Makefile.am
index e1e79caebc41..d67c4e72fd3b 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -14,6 +14,7 @@ libzebra_la_SOURCES = \
        filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
        zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c 
privs.c \
        sigevent.c pqueue.c jhash.c memtypes.c workqueue.c vrf.c \
+       memory.c \
        memory_vty.c
 
 BUILT_SOURCES = memtypes.h route_types.h gitversion.h
diff --git a/lib/log.c b/lib/log.c
index 0914bf8405b6..41de03a8e3ed 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -669,6 +669,14 @@ _zlog_assert_failed (const char *assertion, const char 
*file,
   abort();
 }
 
+void
+memory_oom (size_t size, const char *name)
+{
+       zlog_err("out of memory: failed to allocate %zu bytes for %s"
+                "object", size, name);
+       zlog_backtrace(LOG_ERR);
+       abort();
+}
 
 /* Open log stream */
 struct zlog *
diff --git a/lib/memory.c b/lib/memory.c
new file mode 100644
index 000000000000..c30d41dda4e9
--- /dev/null
+++ b/lib/memory.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2015-16  David Lamparter
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <zebra.h>
+
+#include <stdlib.h>
+
+#include "memory.h"
+
+static struct memgroup *mg_first = NULL;
+struct memgroup **mg_insert = &mg_first;
+
+static inline void mt_count_alloc(struct memtype *mt, size_t size)
+{
+       mt->n_alloc++;
+
+       if (mt->size == 0)
+               mt->size = size;
+       else if (mt->size != size)
+               mt->size = SIZE_VAR;
+}
+
+static inline void mt_count_free(struct memtype *mt)
+{
+       mt->n_alloc--;
+}
+
+static inline void *mt_checkalloc(struct memtype *mt, void *ptr, size_t size)
+{
+       if (__builtin_expect(ptr == NULL, 0)) {
+               memory_oom(size, mt->name);
+               return NULL;
+       }
+       mt_count_alloc (mt, size);
+       return ptr;
+}
+
+void *qmalloc (struct memtype *mt, size_t size)
+{
+       return mt_checkalloc(mt, malloc (size), size);
+}
+
+void *qcalloc (struct memtype *mt, size_t size)
+{
+       return mt_checkalloc(mt, calloc (size, 1), size);
+}
+
+void *qrealloc (struct memtype *mt, void *ptr, size_t size)
+{
+       if (ptr)
+               mt_count_free (mt);
+       return mt_checkalloc(mt,
+                       ptr ? realloc(ptr, size) : malloc(size),
+                       size);
+}
+
+void *qstrdup (struct memtype *mt, const char *str)
+{
+       return mt_checkalloc(mt, strdup (str), strlen (str) + 1);
+}
+
+void qfree (struct memtype *mt, void *ptr)
+{
+       if (ptr)
+               mt_count_free (mt);
+       free (ptr);
+}
+
+int qmem_walk (qmem_walk_fn *func, void *arg)
+{
+       struct memgroup *mg;
+       struct memtype *mt;
+       int rv;
+
+       for (mg = mg_first; mg; mg = mg->next) {
+               if ((rv = func (arg, mg, NULL)))
+                       return rv;
+               for (mt = mg->types; mt; mt = mt->next)
+                       if ((rv = func (arg, mg, mt)))
+                               return rv;
+       }
+       return 0;
+}
diff --git a/lib/memory.h b/lib/memory.h
index 15bf280199c8..0c4a1ef56ccc 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -1,2 +1,159 @@
-/* temporary */
+/*
+ * Copyright (c) 2015-16  David Lamparter
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _QUAGGA_MEMORY_H
+#define _QUAGGA_MEMORY_H
+
+#include <stdlib.h>
+
+#define array_size(ar) (sizeof(ar) / sizeof(ar[0]))
+
+#define SIZE_VAR ~0UL
+struct memtype {
+       struct memtype *next;
+       const char *name;
+       size_t n_alloc;
+       size_t size;
+};
+
+struct memgroup {
+       struct memgroup *next;
+       struct memtype *types, **insert;
+       const char *name;
+};
+
+#if defined(__clang__)
+# if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 5)
+#  define _RET_NONNULL  , returns_nonnull
+# endif
+# define _CONSTRUCTOR(x) constructor(x)
+#elif defined(__GNUC__)
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
+#  define _RET_NONNULL  , returns_nonnull
+# endif
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
+#  define _CONSTRUCTOR(x) constructor(x)
+#  define _ALLOC_SIZE(x)  alloc_size(x)
+# endif
+#endif
+
+#ifndef _RET_NONNULL
+# define _RET_NONNULL
+#endif
+#ifndef _CONSTRUCTOR
+# define _CONSTRUCTOR(x) constructor
+#endif
+#ifndef _ALLOC_SIZE
+# define _ALLOC_SIZE(x)
+#endif
+
+/* macro usage:
+ *
+ *  mydaemon.h
+ *    DECLARE_MGROUP(MYDAEMON)
+ *    DECLARE_MTYPE(MYDAEMON_COMMON)
+ *
+ *  mydaemon.c
+ *    DEFINE_MGROUP(MYDAEMON, "my daemon memory")
+ *    DEFINE_MTYPE(MYDAEMON, MYDAEMON_COMMON,
+ *                   "this mtype is used in multiple files in mydaemon")
+ *    foo = qmalloc (MTYPE_MYDAEMON_COMMON, sizeof (*foo))
+ *
+ *  mydaemon_io.c
+ *    bar = qmalloc (MTYPE_MYDAEMON_COMMON, sizeof (*bar))
+ *
+ *    DEFINE_MTYPE_STATIC(MYDAEMON, MYDAEMON_IO,
+ *                          "this mtype is used only in this file")
+ *    baz = qmalloc (MTYPE_MYDAEMON_IO, sizeof (*baz))
+ *
+ *  Note:  Naming conventions (MGROUP_ and MTYPE_ prefixes are enforced
+ *         by not having these as part of the macro arguments)
+ *  Note:  MTYPE_* are symbols to the compiler (of type struct memtype *),
+ *         but MGROUP_* aren't.
+ */
+
+#define DECLARE_MGROUP(name) \
+       extern struct memgroup _mg_##name;
+#define DEFINE_MGROUP(mname, desc) \
+       struct memgroup _mg_##mname \
+       __attribute__ ((section (".data.mgroups"))) = { \
+               .name = desc, \
+               .types = NULL, .next = NULL, .insert = NULL, \
+       }; \
+       static void _mginit_##mname (void) \
+         __attribute__ ((_CONSTRUCTOR (1000))); \
+       static void _mginit_##mname (void) \
+       {       extern struct memgroup **mg_insert; \
+               *mg_insert = &_mg_##mname; \
+               mg_insert =  &_mg_##mname.next; }
+
+
+#define DECLARE_MTYPE(name) \
+       extern struct memtype _mt_##name; \
+       static struct memtype * const MTYPE_ ## name = &_mt_##name;
+
+#define DEFINE_MTYPE_ATTR(group, mname, attr, desc) \
+       attr struct memtype _mt_##mname \
+       __attribute__ ((section (".data.mtypes"))) = { \
+               .name = desc, \
+               .next = NULL, .n_alloc = 0, .size = 0, \
+       }; \
+       static void _mtinit_##mname (void) \
+         __attribute__ ((_CONSTRUCTOR (1001))); \
+       static void _mtinit_##mname (void) \
+       {       if (_mg_##group.insert == NULL) \
+                       _mg_##group.insert = &_mg_##group.types; \
+               *_mg_##group.insert = &_mt_##mname; \
+               _mg_##group.insert =  &_mt_##mname.next; }
+
+#define DEFINE_MTYPE(group, name, desc) \
+       DEFINE_MTYPE_ATTR(group, name, , desc)
+#define DEFINE_MTYPE_STATIC(group, name, desc) \
+       DEFINE_MTYPE_ATTR(group, name, static, desc) \
+       static struct memtype * const MTYPE_ ## name = &_mt_##name;
+
+
+extern void *qmalloc (struct memtype *mt, size_t size)
+       __attribute__ ((malloc, _ALLOC_SIZE(2), nonnull (1) _RET_NONNULL));
+extern void *qcalloc (struct memtype *mt, size_t size)
+       __attribute__ ((malloc, _ALLOC_SIZE(2), nonnull (1) _RET_NONNULL));
+extern void *qrealloc (struct memtype *mt, void *ptr, size_t size)
+       __attribute__ ((_ALLOC_SIZE(3), nonnull (1) _RET_NONNULL));
+extern void *qstrdup (struct memtype *mt, const char *str)
+       __attribute__ ((malloc, nonnull (1) _RET_NONNULL));
+extern void qfree (struct memtype *mt, void *ptr);
+
+/* still here until next commit */
 #include "memory_vty.h"
+
+/* NB: calls are ordered by memgroup; and there is a call with mt == NULL for
+ * each memgroup (so that a header can be printed, and empty memgroups show)
+ *
+ * return value: 0: continue, !0: abort walk.  qmem_walk will return the
+ * last value from qmem_walk_fn. */
+typedef int qmem_walk_fn (void *arg, struct memgroup *mg, struct memtype *mt);
+extern int qmem_walk (qmem_walk_fn *func, void *arg);
+
+extern void memory_oom (size_t size, const char *name);
+
+#endif /* _QUAGGA_MEMORY_H */
+
diff --git a/lib/memory_vty.c b/lib/memory_vty.c
index 269520d5a435..763f2383e50b 100644
--- a/lib/memory_vty.c
+++ b/lib/memory_vty.c
@@ -395,6 +395,24 @@ show_memory_mallinfo (struct vty *vty)
 }
 #endif /* HAVE_MALLINFO */
 
+static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt)
+{
+       struct vty *vty = arg;
+       if (!mt)
+               vty_out (vty, "--- qmem %s ---%s", mg->name, VTY_NEWLINE);
+       else {
+               char size[32];
+               snprintf(size, sizeof(size), "%6ld", mt->size);
+               vty_out (vty, "%-30s: %10ld  %s%s",
+                       mt->name, mt->n_alloc,
+                       mt->size == 0 ? "" :
+                       mt->size == SIZE_VAR ? "(variably sized)" :
+                       size, VTY_NEWLINE);
+       }
+       return 0;
+}
+
+
 DEFUN (show_memory,
        show_memory_cmd,
        "show memory",
@@ -415,6 +433,7 @@ DEFUN (show_memory,
       needsep = show_memory_vty (vty, ml->list);
     }
 
+  qmem_walk(qmem_walker, vty);
   return CMD_SUCCESS;
 }
 
diff --git a/lib/memory_vty.h b/lib/memory_vty.h
index 4d7601607a59..d691b9b2b8ab 100644
--- a/lib/memory_vty.h
+++ b/lib/memory_vty.h
@@ -21,8 +21,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, 
Boston, MA
 #ifndef _ZEBRA_MEMORY_VTY_H
 #define _ZEBRA_MEMORY_VTY_H
 
-#define array_size(ar) (sizeof(ar) / sizeof(ar[0]))
-
 /* For pretty printing of memory allocate information. */
 struct memory_list
 {
-- 
2.1.4


_______________________________________________
Quagga-dev mailing list
Quagga-dev@lists.quagga.net
https://lists.quagga.net/mailman/listinfo/quagga-dev

Reply via email to