This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

ping [PATCH v1] Add threadsafe variant of malloc hooks.


ping
On Fri, Nov 08, 2013 at 02:38:18PM +0100, OndÅej BÃlka wrote:
> As mentioned previously on adding thread safe equivalents for malloc
> extensions I created a wrapper which will replace malloc hooks, mcheck
> and mtrace.
> 
> In current state hook presence slows down production code 
> with little benefit, so we should recommand moving to new interface.
> 
> With some work this could be done portably, one needs a working
> malloc/free and way to redirect malloc (which can be done by #define in
> worst case.)
> 
> I wrote this for flexibility, this adds around 16 bytes per allocation.
> 
> 
> I simplified a hook writing, you need to write only a malloc/free hook
> pair and install it by add_hooks function. Realloc will get transformed
> to malloc/free pair.
> 
> As frequent question in free is its size we pass it as argument of hook.
> 
> As hooks need to save additional data which is problem with aligned
> alloc I added additional overhead field, implemenatations should write
> their data at address size+overhead and appropriately adjust overhead.
> 
> Here are two possibilities, a safer one is to add a generation count and
> invoke only free hooks for which a malloc hook was called, second is
> make user responsible for installing hooks before allocation. A first
> one has overhead so I want to know what to do.
> 
> A caller should be handled at user level by calling backtrace.
> 
> 
> Comments?
> 
> void *
> malloc_hook(size_t size, size_t alignment,
>             size_t overhead, struct mallochook *hook)
> {
>   /* do stuff */
>   char *ret = hook->hook(size, alignment, overhead + 1, hook->next);
>   ret[size + overhead] = 42;
>   return ret;
> }
> 
> void
> free_hook(void *ptr, size_t size, size_t overhead, struct freehook *hook)
> {
>   /* do stuff */
>   hook->hook(size, overhead + 1, hook->next);
> }
> 
> 
> init()
> {
>   add_hooks(malloc_hook,free_hook);
> }
> 
> 	* malloc/mallochook.c: New file.
> 	* malloc/mallochook.h: Likewise.
> 	* malloc/Makefile: Add libmallochook logic.
> 
> 
> 
> ---
>  malloc/Makefile     |   9 +-
>  malloc/mallochook.c | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  malloc/mallochook.h |  23 ++++
>  4 files changed, 340 insertions(+), 2 deletions(-)
>  create mode 100644 malloc/mallochook.c
>  create mode 100644 malloc/mallochook.h
> 
> diff --git a/malloc/Makefile b/malloc/Makefile
> index 84339d1..a29d9ae 100644
> --- a/malloc/Makefile
> +++ b/malloc/Makefile
> @@ -36,12 +36,17 @@ install-lib := libmcheck.a
>  non-lib.a := libmcheck.a
>  
>  # Additional library.
> -extra-libs = libmemusage
> +extra-libs = libmemusage libmallochook
>  extra-libs-others = $(extra-libs)
>  
>  libmemusage-routines = memusage
>  libmemusage-inhibit-o = $(filter-out .os,$(object-suffixes))
>  
> +libmallochook-routines = mallochook
> +libmallochook-inhibit-o = $(filter-out .os,$(object-suffixes))
> +
> +
> +
>  # These should be removed by `make clean'.
>  extra-objs = mcheck-init.o libmcheck.a
>  
> @@ -144,6 +149,8 @@ $(objpfx)memusage: memusage.sh
>  
>  # The implementation uses `dlsym'
>  $(objpfx)libmemusage.so: $(common-objpfx)dlfcn/libdl.so
> +$(objpfx)libmallochook.so: $(common-objpfx)dlfcn/libdl.so $(common-objpfx)nptl/libpthread.so
> +
>  
>  # Extra dependencies
>  $(foreach o,$(all-object-suffixes),$(objpfx)malloc$(o)): arena.c hooks.c
> diff --git a/malloc/mallochook.c b/malloc/mallochook.c
> new file mode 100644
> index 0000000..525b5d3
> --- /dev/null
> +++ b/malloc/mallochook.c
> @@ -0,0 +1,304 @@
> +/* A thread-safe variant of malloc hooks.
> +   Copyright (C) 2013 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <dlfcn.h>
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <stdint.h>
> +#include <unistd.h>
> +#include <pthread.h>
> +#include <sys/mman.h>
> +#include <mallochook.h>
> +#include <string.h>
> +
> +static pthread_mutex_t mutex;
> +static size_t page_size;
> +
> +struct hook
> +{
> +  void *hook;
> +  struct hook *next;
> +};
> +static int
> +addhook (struct hook **old, void *fn)
> +{
> +  if (fn == NULL)
> +    return 0;
> +
> +  struct hook *new = mmap (NULL, page_size, PROT_READ | PROT_WRITE,
> +			   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> +  if (!new)
> +    return -1;
> +
> +  new->hook = fn;
> +  new->next = *old;
> +  *old = new;
> +  return 0;
> +}
> +static int
> +deletehook (struct hook **old, void *fn)
> +{
> +  if (fn == NULL)
> +    return 0;
> +
> +  struct hook *h = *old;
> +  while (h->next)
> +    {
> +      if (h->next->hook == fn)
> +	{
> +	  void *unmap = h->next;
> +	  h->next = h->next->next;
> +	  munmap (unmap, page_size);
> +	}
> +      else
> +	h = h->next;
> +    }
> +  return 0;
> +}
> +
> +static struct mallochook *mallochook;
> +static struct mallochook mallochookinit;
> +static void *(*mallocp) (size_t);
> +static struct freehook *freehook;
> +static struct freehook freehookinit;
> +static void (*freep) (void *);
> +
> +
> +
> +
> +
> +int
> +addhooks (void *(*mhook)(size_t, size_t, size_t, struct mallochook *),
> +	  void (*fhook)(void *, size_t, size_t, struct freehook *))
> +{
> +  if (pthread_mutex_lock (&mutex))
> +    return -1;
> +
> +  if (addhook ((struct hook **) &mallochook, (void *) mhook))
> +    return -1;
> +
> +  if (addhook ((struct hook **) &freehook, (void *) fhook))
> +    return -1;
> +
> +  if (pthread_mutex_unlock (&mutex))
> +    return -1;
> +
> +  return 0;
> +}
> +int
> +deletehooks (void *(*mhook)(size_t, size_t, size_t, struct mallochook *),
> +	     void (*fhook)(void *, size_t, size_t, struct freehook *))
> +{
> +  if (pthread_mutex_lock (&mutex))
> +    return -1;
> +
> +  if (deletehook ((struct hook **) &mallochook, (void *) mhook))
> +    return -1;
> +
> +  if (deletehook ((struct hook **) &freehook, (void *) fhook))
> +    return -1;
> +
> +  if (pthread_mutex_unlock (&mutex))
> +    return -1;
> +
> +  return 0;
> +}
> +
> +
> +struct header
> +{
> +  void *start; /* Needed for aligned allocations.  */
> +  size_t size;
> +};
> +
> +static void *
> +wrap_malloc (size_t size, size_t alignment, size_t overhead,
> +	     struct mallochook *hook)
> +{
> +  char *s = (char *) mallocp (size + sizeof (struct header)
> +			      + alignment + overhead);
> +  char *p = s;
> +
> +  p += sizeof (struct header);
> +
> +  p = (char *) ((((uintptr_t) p) + alignment - 1) & ~(alignment - 1));
> +
> +  struct header *h = (struct header *) p;
> +  h--;
> +  h->start = s;
> +  h->size = size;
> +  return (void *) p;
> +}
> +
> +static void
> +wrap_free (void *ptr, size_t size, size_t overhead, struct freehook *hook)
> +{
> +  struct header *h = (struct header *) ptr;
> +  h--;
> +
> +  freep (h->start);
> +}
> +
> +static int initialized = 0;
> +static void
> +initialize (void)
> +{
> +  initialized = -1;
> +  pthread_mutex_init (&mutex, NULL);
> +
> +  page_size = sysconf (_SC_PAGE_SIZE);
> +
> +  mallocp = (void *(*)(size_t))dlsym (RTLD_NEXT, "malloc");
> +  mallochookinit.hook = wrap_malloc;
> +  mallochookinit.next = NULL;
> +  mallochook = &mallochookinit;
> +
> +  freep = (void (*)(void *))dlsym (RTLD_NEXT, "free");
> +  freehookinit.hook = wrap_free;
> +  freehookinit.next = NULL;
> +  freehook = &freehookinit;
> +
> +  initialized = 1;
> +}
> +
> +
> +
> +void *
> +malloc (size_t len)
> +{
> +  /* Determine real implementation if not already happened.  */
> +  if (__builtin_expect (initialized <= 0, 0))
> +    {
> +      if (initialized == -1)
> +	return NULL;
> +
> +      initialize ();
> +    }
> +
> +  size_t to_align = (16 - len % 16) % 16;
> +  return mallochook->hook (len, 16, to_align, mallochook->next);
> +
> +}
> +
> +void
> +free (void *p)
> +{
> +  if (p == NULL)
> +    return;
> +
> +  /* Determine real implementation if not already happened.  */
> +  if (__builtin_expect (initialized <= 0, 0))
> +    {
> +      if (initialized == -1)
> +	return;
> +
> +      initialize ();
> +    }
> +
> +  struct header *h = (struct header *) p;
> +  h--;
> +
> +  freehook->hook (p, h->size, 0, freehook->next);
> +}
> +
> +
> +
> +void *
> +realloc (void *p, size_t len)
> +{
> +  /* Determine real implementation if not already happened.  */
> +  if (__builtin_expect (initialized <= 0, 0))
> +    {
> +      if (initialized == -1)
> +	return NULL;
> +
> +      initialize ();
> +    }
> +
> +  if (!p)
> +    return malloc (len);
> +
> +
> +  struct header *h = (struct header *) p;
> +  h--;
> +  if (h->size > len)
> +    return p;
> +
> +  void *new = malloc (len);
> +  if (!new)
> +    return NULL;
> +
> +  memcpy (new, p, h->size);
> +  free (p);
> +  return new;
> +}
> +
> +void *
> +memalign (size_t align, size_t len)
> +{
> +  /* Determine real implementation if not already happened.  */
> +  if (__builtin_expect (initialized <= 0, 0))
> +    {
> +      if (initialized == -1)
> +	return NULL;
> +
> +      initialize ();
> +    }
> +
> +  if (align < 16)
> +    align = 16;
> +
> +  size_t to_align = (16 - len % 16) % 16;
> +  return mallochook->hook (len, align, to_align, mallochook->next);
> +}
> +
> +int
> +posix_memalign (void **mem, size_t alignment, size_t size)
> +{
> +  *mem = memalign (alignment, size);
> +  return *mem ? 0 : errno;
> +}
> +
> +void *
> +valloc (size_t len)
> +{
> +  return memalign (page_size, len);
> +}
> +
> +void *
> +pvalloc (size_t len)
> +{
> +  if (len + page_size < len)
> +    return NULL;
> +
> +  return memalign (page_size, len + page_size - 1);
> +}
> +
> +
> +void *
> +calloc (size_t n, size_t s)
> +{
> +  if (s && SIZE_MAX / s < n)
> +    return NULL;
> +
> +  void *ret = malloc (n * s);
> +  if (ret)
> +    return memset (ret, 0, n * s);
> +
> +  return ret;
> +}
> diff --git a/malloc/mallochook.h b/malloc/mallochook.h
> new file mode 100644
> index 0000000..3a70c7a
> --- /dev/null
> +++ b/malloc/mallochook.h
> @@ -0,0 +1,23 @@
> +
> +/* Called on malloc, calloc, realloc, memalign,
> +   aligned_alloc, posix_memalign, valloc, pvalloc.  */
> +
> +struct mallochook
> +{
> +  void *(*hook)(size_t, size_t, size_t, struct mallochook *);
> +  struct mallochook *next;
> +};
> +
> +struct freehook
> +{
> +  void (*hook)(void *, size_t, size_t, struct freehook *);
> +  struct freehook *next;
> +};
> +
> +int
> +addhooks (void *(*mhook)(size_t, size_t, size_t, struct mallochook *),
> +	  void (*fhook)(void *, size_t, size_t, struct freehook *));
> +
> +int
> +deletehooks (void *(*mhook)(size_t, size_t, size_t, struct mallochook *),
> +	     void (*fhook)(void *, size_t, size_t, struct freehook *));
> -- 
> 1.8.4.rc3

-- 

Hot Java has gone cold


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]