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> --- lib/Makefile.am | 1 + lib/log.c | 8 +++ lib/memory.c | 102 ++++++++++++++++++++++++++++ lib/memory.h | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- lib/memory_vty.c | 19 ++++++ lib/memory_vty.h | 2 - 6 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 lib/memory.c diff --git a/lib/Makefile.am b/lib/Makefile.am index 2093f30..f77b5a7 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 0914bf8..41de03a 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 0000000..592b683 --- /dev/null +++ b/lib/memory.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. + * + * 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 15bf280..1ef411f 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -1,2 +1,201 @@ -/* temporary */ +/* + * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. + * + * 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 + +#ifdef __sun +/* Solaris doesn't do constructor priorities due to linker restrictions */ +# undef _CONSTRUCTOR +#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); + +#if 0 +#define XMALLOC(mtype, size) qmalloc(mtype, size) +#define XCALLOC(mtype, size) qcalloc(mtype, size) +#define XREALLOC(mtype, ptr, size) qrealloc(mtype, ptr, size) +#define XSTRDUP(mtype, str) qstrdup(mtype, str) +#define XFREE(mtype, ptr) do { qfree(mtype, ptr); ptr = NULL; } \ + while (0) + +static inline size_t mtype_stats_alloc(struct memtype *mt) +{ + return mt->n_alloc; +} +#else +/* temporary wrapping macros to allow old/new coexistence */ #include "memory_vty.h" +#undef XMALLOC +#undef XCALLOC +#undef XREALLOC +#undef XSTRDUP +#undef XFREE + +#define XMALLOC(mtype, size) (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof (mtype), struct memtype *), \ + qmalloc, zmalloc)(mtype, size)) +#define XCALLOC(mtype, size) (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof (mtype), struct memtype *), \ + qcalloc, zcalloc)(mtype, size)) +#define XREALLOC(mtype, ptr, size) (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof (mtype), struct memtype *), \ + qrealloc, zrealloc)(mtype, ptr, size)) +#define XSTRDUP(mtype, str) (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof (mtype), struct memtype *), \ + qstrdup, zstrdup)(mtype, str)) +#define XFREE(mtype, ptr) do { \ + (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof (mtype), struct memtype *), \ + qfree, zfree)(mtype, ptr)); \ + ptr = NULL; } \ + while (0) +#endif + +/* 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 269520d..5e0b8af 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), "%6zu", mt->size); + vty_out (vty, "%-30s: %10zu %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 a0c47a9..81af411 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_CLI_H #define _ZEBRA_MEMORY_CLI_H -#define array_size(ar) (sizeof(ar) / sizeof(ar[0])) - /* For pretty printing of memory allocate information. */ struct memory_list { -- 2.3.6 _______________________________________________ Quagga-dev mailing list Quagga-dev@lists.quagga.net https://lists.quagga.net/mailman/listinfo/quagga-dev