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]

[PATCH v1] Add threadsafe variant of malloc hooks.


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


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