From f15cc2be5c6f2d1a2c669c00c5b88f73cf2ad6ed Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Sat, 13 Aug 2016 22:50:59 -0400 Subject: [PATCH] New custom memory allocators. This version doesn't require any mutex and is somewhat faster for lots of allocations. Valgrind now shows a whole lot of "possibly lost" warnings but they disappear when --enable-mem-debug is passed to configure. I noticed that when using the old allocators that Valgrind has trouble detecting some errors that are discoverable with --enable-mem-debug. So we'll just do --enable-mem-debug to check for errors. This was a suggestion by Justus Winter [1]. 1. https://lists.gnupg.org/pipermail/gnupg-devel/2016-August/031406.html --- src/mem.c | 437 ++++++++++++++++++-------------------------------------- src/mem.h | 21 +-- src/pwmd-dump.c | 3 - src/pwmd.c | 6 - 4 files changed, 144 insertions(+), 323 deletions(-) rewrite src/mem.c (72%) diff --git a/src/mem.c b/src/mem.c dissimilarity index 72% index 1ff73a69..73de4f69 100644 --- a/src/mem.c +++ b/src/mem.c @@ -1,300 +1,137 @@ -/* - Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, - 2016 - Ben Kibbey - - This file is part of pwmd. - - Pwmd is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - Pwmd is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Pwmd. If not, see . -*/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include "mem.h" - -void * -xrealloc_gpgrt (void *p, size_t n) -{ - if (!n) - { - xfree (p); - return NULL; - } - - if (!p) - return xmalloc (n); - - return xrealloc (p, n); -} - -#ifndef MEM_DEBUG -#ifndef _ -#include "gettext.h" -#define _(msgid) gettext(msgid) -#endif - -#ifdef DEBUG -#ifdef HAVE_BACKTRACE -#include - -#ifndef BACKTRACE -#define BACKTRACE(fn) { \ - int n, nptrs; \ - char **strings; \ - void *buffer[20]; \ - nptrs = backtrace(buffer, 20); \ - strings = backtrace_symbols(buffer, nptrs); \ - for (n = 0; n < nptrs; n++) \ - fprintf(stderr, "BACKTRACE (%s) %i: %s\n", fn, n, strings[n]); \ - fprintf(stderr, "\n"); \ - fflush(stderr); \ - } -#endif -#endif -#else -#define BACKTRACE(fn) -#endif - -#ifndef _ -#define _ gettext(msgid) -#endif - -struct memlist_s -{ - void *ptr; - size_t size; - struct memlist_s *next; -}; - -static struct memlist_s *memlist; -static pthread_mutex_t mem_mutex; -#ifdef DEBUG -static size_t allocations, deallocations; -#endif - -void -xmem_init () -{ - static int init; - - if (!init) - pthread_mutex_init (&mem_mutex, NULL); - - init = 1; -} - -static int -memlist_remove (void *ptr, const char *func) -{ - struct memlist_s *m, *last = NULL, *p; - - pthread_mutex_lock (&mem_mutex); - - for (m = memlist; m; m = m->next) - { - if (m->ptr == ptr) - { -#ifdef DEBUG - fprintf (stderr, "%s: %p %zu\n", func, ptr, m->size); -#endif - wipememory (m->ptr, 0, m->size); - free (m->ptr); - - p = m->next; - free (m); -#ifdef DEBUG - deallocations++; -#endif - - if (last) - last->next = p; - else - memlist = p; - - pthread_mutex_unlock (&mem_mutex); - return 1; - } - - last = m; - } - - pthread_mutex_unlock (&mem_mutex); - return 0; -} - -static void -memlist_prepend (struct memlist_s *new) -{ - pthread_mutex_lock (&mem_mutex); -#ifdef DEBUG - allocations++; -#endif - new->next = memlist; - memlist = new; - pthread_mutex_unlock (&mem_mutex); -} - -void -xfree (void *ptr) -{ - if (!ptr) - return; - - if (!memlist_remove (ptr, __FUNCTION__)) - { - warnx (_("%s: %p not found"), __FUNCTION__, ptr); - assert (0); - } -} - -void * -xmalloc (size_t size) -{ - void *p; - struct memlist_s *m; - - if ((m = (struct memlist_s *) malloc (sizeof (struct memlist_s))) == NULL) - return NULL; - - if ((p = (void *) malloc (size)) == NULL) - { - free (m); - return NULL; - } - - m->ptr = p; - m->size = size; - memlist_prepend (m); -#ifdef DEBUG - fprintf (stderr, "%s: %p %zu\n", __FUNCTION__, p, size); - BACKTRACE (__FUNCTION__); -#endif - return m->ptr; -} - -void * -xcalloc (size_t nmemb, size_t size) -{ - void *p; - struct memlist_s *m; - - if ((m = (struct memlist_s *) malloc (sizeof (struct memlist_s))) == NULL) - return NULL; - - if ((p = calloc (nmemb, size)) == NULL) - { - free (m); - return NULL; - } - - m->ptr = p; - m->size = nmemb * size; - memlist_prepend (m); -#ifdef DEBUG - fprintf (stderr, "%s: %p %zu\n", __FUNCTION__, p, nmemb * size); - BACKTRACE (__FUNCTION__); -#endif - return m->ptr; -} - -void * -xrealloc (void *ptr, size_t size) -{ - void *p; - struct memlist_s *m; - - if (!size && ptr) - { - xfree (ptr); - return NULL; - } - - if (!ptr) - return xmalloc (size); - - pthread_mutex_lock (&mem_mutex); - - for (m = memlist; m; m = m->next) - { - if (m->ptr == ptr) - { - if ((p = (void *) malloc (size)) == NULL) - { - pthread_mutex_unlock (&mem_mutex); - return NULL; - } - - memcpy (p, m->ptr, size < m->size ? size : m->size); - wipememory (m->ptr, 0, m->size); - free (m->ptr); - m->ptr = p; - m->size = size; -#ifdef DEBUG - fprintf (stderr, "%s: %p %zu\n", __FUNCTION__, p, size); - BACKTRACE (__FUNCTION__); -#endif - pthread_mutex_unlock (&mem_mutex); - return m->ptr; - } - } - - warnx (_("%s: %p not found"), __FUNCTION__, ptr); - pthread_mutex_unlock (&mem_mutex); - assert (0); - return NULL; -} - -void -xpanic (void) -{ - struct memlist_s *m; - - pthread_mutex_lock (&mem_mutex); - - for (m = memlist; m; m = memlist) - xfree (m->ptr); - - pthread_mutex_unlock (&mem_mutex); -} - -#ifdef DEBUG -void -xdump (void) -{ - struct memlist_s *m; - size_t total = 0; - - pthread_mutex_lock (&mem_mutex); - - for (m = memlist; m; m = m->next) - { - fprintf (stderr, "%s: %p %zu\n", __FUNCTION__, m->ptr, m->size); - total += m->size; - } - - fprintf (stderr, - "Total unfreed: %zu bytes, allocations: %zu, deallocations: %zu\n", - total, allocations, deallocations); - pthread_mutex_unlock (&mem_mutex); -} -#endif -#endif +/* + Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, + 2016 + Ben Kibbey + + This file is part of pwmd. + + Pwmd is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Pwmd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Pwmd. If not, see . +*/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "mem.h" + +void * +xrealloc_gpgrt (void *p, size_t n) +{ + if (!n) + { + xfree (p); + return NULL; + } + + if (!p) + return xmalloc (n); + + return xrealloc (p, n); +} + +#ifndef MEM_DEBUG +struct memchunk_s +{ + size_t size; + char data[1]; +}; + +void +xfree (void *ptr) +{ + struct memchunk_s *m; + void *p; + + if (!ptr) + return; + + m = (struct memchunk_s *)((void *)ptr-(offsetof (struct memchunk_s, data))); + p = (void *)m+(offsetof (struct memchunk_s, data)); + wipememory (p, 0, m->size); + free (m); +} + +void * +xmalloc (size_t size) +{ + struct memchunk_s *m; + + if (!size) + return NULL; + + m = malloc (sizeof (struct memchunk_s)+size); + if (!m) + return NULL; + + m->size = size; + return (void *)m+(offsetof (struct memchunk_s, data)); +} + +void * +xcalloc (size_t nmemb, size_t size) +{ + void *p; + struct memchunk_s *m; + + m = malloc (sizeof (struct memchunk_s)+(nmemb*size)); + if (!m) + return NULL; + + m->size = nmemb*size; + p = (void *)m+(offsetof (struct memchunk_s, data)); + memset (p, 0, m->size); + return p; +} + +void * +xrealloc (void *ptr, size_t size) +{ + void *p, *np; + struct memchunk_s *m, *mp; + size_t n; + + if (!size && ptr) + { + m = (struct memchunk_s *)((void *)ptr-(offsetof (struct memchunk_s, data))); + p = (void *)m+(offsetof (struct memchunk_s, data)); + wipememory (p, 0, m->size); + free (m); + return NULL; + } + else if (!ptr) + return xmalloc (size); + + m = malloc (sizeof (struct memchunk_s)+size); + if (!m) + return NULL; + + m->size = size; + np = (void *)m+(offsetof (struct memchunk_s, data)); + + mp = (struct memchunk_s *)((void *)ptr-(offsetof (struct memchunk_s, data))); + p = (void *)mp+(offsetof (struct memchunk_s, data)); + + n = size > mp->size ? mp->size : size; + memcpy (np, p, n); + wipememory (p, 0, mp->size); + + free (mp); + return (void *)m+(offsetof (struct memchunk_s, data)); +} +#endif diff --git a/src/mem.h b/src/mem.h index ed289687..43ba0814 100644 --- a/src/mem.h +++ b/src/mem.h @@ -35,7 +35,7 @@ extern "C" #include /* To avoid that a compiler optimizes certain memset calls away, these - macros may be used instead. */ + macros may be used instead. Thanks g10Code. */ #define wipememory(_ptr,_set,_len) do { \ volatile char *_vptr=(volatile char *)(_ptr); \ size_t _vlen=(_len); \ @@ -47,20 +47,13 @@ extern "C" #define xmalloc malloc #define xrealloc realloc #define xcalloc calloc - void *xrealloc_gpgrt (void *, size_t) __attribute__ ((visibility ("hidden"))); + void *xrealloc_gpgrt (void *, size_t); #else - void xmem_init (void) __attribute__ ((visibility ("hidden"))); - void xfree (void *ptr) __attribute__ ((visibility ("hidden"))); - void *xmalloc (size_t size) __attribute__ ((visibility ("hidden"))); - void *xrealloc (void *ptr, size_t size) - __attribute__ ((visibility ("hidden"))); - void *xcalloc (size_t nmemb, size_t size) - __attribute__ ((visibility ("hidden"))); - void xpanic (void) __attribute__ ((visibility ("hidden"))); - void *xrealloc_gpgrt (void *, size_t) __attribute__ ((visibility ("hidden"))); -#ifdef DEBUG - void xdump (void) __attribute__ ((visibility ("hidden"))); -#endif + void xfree (void *ptr); + void *xmalloc (size_t size); + void *xrealloc (void *ptr, size_t size); + void *xcalloc (size_t nmemb, size_t size); + void *xrealloc_gpgrt (void *, size_t); #endif #ifdef __cplusplus diff --git a/src/pwmd-dump.c b/src/pwmd-dump.c index 50319801..6b5d088b 100644 --- a/src/pwmd-dump.c +++ b/src/pwmd-dump.c @@ -449,9 +449,6 @@ main(int argc, char **argv) #endif #endif -#ifndef MEM_DEBUG - xmem_init (); -#endif gpg_err_init (); if (!gcry_check_version (GCRYPT_VERSION)) diff --git a/src/pwmd.c b/src/pwmd.c index c29cfe04..fd0e572d 100644 --- a/src/pwmd.c +++ b/src/pwmd.c @@ -2585,9 +2585,6 @@ main (int argc, char *argv[]) textdomain ("pwmd"); #endif -#ifndef MEM_DEBUG - xmem_init (); -#endif gpgrt_init (); gpgrt_set_alloc_func (xrealloc_gpgrt); @@ -3131,9 +3128,6 @@ do_exit: pthread_key_delete (thread_name_key); closelog (); -#if defined(DEBUG) && !defined(MEM_DEBUG) - xdump (); -#endif if (log_fd != -1) close (log_fd); -- 2.11.4.GIT