[PATCH 2/3] libdw: Rewrite the memory handler to be thread-safe.

Mark Wielaard mark@klomp.org
Thu Aug 29 13:16:00 GMT 2019


From: Jonathon Anderson <jma14@rice.edu>

Signed-off-by: Jonathon Anderson <jma14@rice.edu>
---
 libdw/ChangeLog         |  8 ++++++
 libdw/Makefile.am       |  4 +--
 libdw/dwarf_begin_elf.c | 12 ++++-----
 libdw/dwarf_end.c       |  7 ++---
 libdw/libdwP.h          | 59 ++++++++++++++++++++++++++---------------
 libdw/libdw_alloc.c     |  5 ++--
 6 files changed, 59 insertions(+), 36 deletions(-)

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 1d3586f0..ec809070 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,11 @@
+2019-08-26  Jonathon Anderson  <jma14@rice.edu>
+
+	* libdw_alloc.c (__libdw_allocate): Added thread-safe stack allocator.
+	* libdwP.h (Dwarf): Likewise.
+	* dwarf_begin_elf.c (dwarf_begin_elf): Support for above.
+	* dwarf_end.c (dwarf_end): Likewise.
+	* Makefile.am: Use -pthread to provide rwlocks.
+
 2019-08-25  Jonathon Anderson  <jma14@rice.edu>
 
 	* dwarf_getcfi.c (dwarf_getcfi): Set default_same_value to false.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 7a3d5322..ba5745f3 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -31,7 +31,7 @@ include $(top_srcdir)/config/eu.am
 if BUILD_STATIC
 AM_CFLAGS += $(fpic_CFLAGS)
 endif
-AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdwelf
+AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdwelf -pthread
 VERSION = 1
 
 lib_LIBRARIES = libdw.a
@@ -108,7 +108,7 @@ am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os)
 libdw_so_LIBS = libdw_pic.a ../libdwelf/libdwelf_pic.a \
 	  ../libdwfl/libdwfl_pic.a ../libebl/libebl.a
 libdw_so_DEPS = ../lib/libeu.a ../libelf/libelf.so
-libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(zip_LIBS)
+libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(zip_LIBS) -pthread
 libdw_so_SOURCES =
 libdw.so$(EXEEXT): $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS)
 # The rpath is necessary for libebl because its $ORIGIN use will
diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
index 38c8f5c6..f865f69c 100644
--- a/libdw/dwarf_begin_elf.c
+++ b/libdw/dwarf_begin_elf.c
@@ -397,7 +397,7 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
   assert (sizeof (struct Dwarf) < mem_default_size);
 
   /* Allocate the data structure.  */
-  Dwarf *result = (Dwarf *) calloc (1, sizeof (Dwarf) + mem_default_size);
+  Dwarf *result = (Dwarf *) calloc (1, sizeof (Dwarf));
   if (unlikely (result == NULL)
       || unlikely (Dwarf_Sig8_Hash_init (&result->sig8_hash, 11) < 0))
     {
@@ -414,14 +414,12 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
   result->elf = elf;
   result->alt_fd = -1;
 
-  /* Initialize the memory handling.  */
+  /* Initialize the memory handling.  Initial blocks are allocated on first
+     actual allocation.  */
   result->mem_default_size = mem_default_size;
   result->oom_handler = __libdw_oom;
-  result->mem_tail = (struct libdw_memblock *) (result + 1);
-  result->mem_tail->size = (result->mem_default_size
-			    - offsetof (struct libdw_memblock, mem));
-  result->mem_tail->remaining = result->mem_tail->size;
-  result->mem_tail->prev = NULL;
+  pthread_key_create (&result->mem_key, NULL);
+  atomic_init (&result->mem_tail, (uintptr_t)NULL);
 
   if (cmd == DWARF_C_READ || cmd == DWARF_C_RDWR)
     {
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 29795c10..fc573cb3 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -94,14 +94,15 @@ dwarf_end (Dwarf *dwarf)
       /* And the split Dwarf.  */
       tdestroy (dwarf->split_tree, noop_free);
 
-      struct libdw_memblock *memp = dwarf->mem_tail;
-      /* The first block is allocated together with the Dwarf object.  */
-      while (memp->prev != NULL)
+      /* Free the internally allocated memory.  */
+      struct libdw_memblock *memp = (struct libdw_memblock *)dwarf->mem_tail;
+      while (memp != NULL)
 	{
 	  struct libdw_memblock *prevp = memp->prev;
 	  free (memp);
 	  memp = prevp;
 	}
+      pthread_key_delete (dwarf->mem_key);
 
       /* Free the pubnames helper structure.  */
       free (dwarf->pubnames_sets);
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index eebb7d12..ad2599eb 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -31,9 +31,11 @@
 
 #include <libintl.h>
 #include <stdbool.h>
+#include <pthread.h>
 
 #include <libdw.h>
 #include <dwarf.h>
+#include "atomics.h"
 
 
 /* gettext helper macros.  */
@@ -147,6 +149,17 @@ enum
 
 #include "dwarf_sig8_hash.h"
 
+/* Structure for internal memory handling.  This is basically a simplified
+   reimplementation of obstacks.  Unfortunately the standard obstack
+   implementation is not usable in libraries.  */
+struct libdw_memblock
+{
+  size_t size;
+  size_t remaining;
+  struct libdw_memblock *prev;
+  char mem[0];
+};
+
 /* This is the structure representing the debugging state.  */
 struct Dwarf
 {
@@ -218,16 +231,11 @@ struct Dwarf
   /* Similar for addrx/constx, which will come from .debug_addr section.  */
   struct Dwarf_CU *fake_addr_cu;
 
-  /* Internal memory handling.  This is basically a simplified
-     reimplementation of obstacks.  Unfortunately the standard obstack
-     implementation is not usable in libraries.  */
-  struct libdw_memblock
-  {
-    size_t size;
-    size_t remaining;
-    struct libdw_memblock *prev;
-    char mem[0];
-  } *mem_tail;
+  /* Internal memory handling.  Each thread allocates separately and only
+     allocates from its own blocks, while all the blocks are pushed atomically
+     onto a unified stack for easy deallocation.  */
+  pthread_key_t mem_key;
+  atomic_uintptr_t mem_tail;
 
   /* Default size of allocated memory blocks.  */
   size_t mem_default_size;
@@ -570,21 +578,28 @@ libdw_valid_user_form (int form)
 extern void __libdw_seterrno (int value) internal_function;
 
 
-/* Memory handling, the easy parts.  This macro does not do any locking.  */
+/* Memory handling, the easy parts.  This macro does not do nor need to do any
+   locking for proper concurrent operation.  */
 #define libdw_alloc(dbg, type, tsize, cnt) \
-  ({ struct libdw_memblock *_tail = (dbg)->mem_tail;			      \
-     size_t _required = (tsize) * (cnt);				      \
-     type *_result = (type *) (_tail->mem + (_tail->size - _tail->remaining));\
-     size_t _padding = ((__alignof (type)				      \
-			 - ((uintptr_t) _result & (__alignof (type) - 1)))    \
-			& (__alignof (type) - 1));			      \
-     if (unlikely (_tail->remaining < _required + _padding))		      \
-       _result = (type *) __libdw_allocate (dbg, _required, __alignof (type));\
+  ({ struct libdw_memblock *_tail = pthread_getspecific (dbg->mem_key);       \
+     size_t _req = (tsize) * (cnt);					      \
+     type *_result;							      \
+     if (unlikely (_tail == NULL))					      \
+       _result = (type *) __libdw_allocate (dbg, _req, __alignof (type));     \
      else								      \
        {								      \
-	 _required += _padding;						      \
-	 _result = (type *) ((char *) _result + _padding);		      \
-	 _tail->remaining -= _required;					      \
+	 _result = (type *) (_tail->mem + (_tail->size - _tail->remaining));  \
+	 size_t _padding = ((__alignof (type)				      \
+			    - ((uintptr_t) _result & (__alignof (type) - 1))) \
+			       & (__alignof (type) - 1));		      \
+	 if (unlikely (_tail->remaining < _req + _padding))		      \
+	   _result = (type *) __libdw_allocate (dbg, _req, __alignof (type)); \
+	 else								      \
+	   {								      \
+	     _req += _padding;						      \
+	     _result = (type *) ((char *) _result + _padding);		      \
+	     _tail->remaining -= _req;					      \
+	   }								      \
        }								      \
      _result; })
 
diff --git a/libdw/libdw_alloc.c b/libdw/libdw_alloc.c
index f1e08714..78977e54 100644
--- a/libdw/libdw_alloc.c
+++ b/libdw/libdw_alloc.c
@@ -52,8 +52,9 @@ __libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
   newp->size = size - offsetof (struct libdw_memblock, mem);
   newp->remaining = (uintptr_t) newp + size - (result + minsize);
 
-  newp->prev = dbg->mem_tail;
-  dbg->mem_tail = newp;
+  newp->prev = (struct libdw_memblock*)atomic_exchange_explicit(
+      &dbg->mem_tail, (uintptr_t)newp, memory_order_relaxed);
+  pthread_setspecific(dbg->mem_key, newp);
 
   return (void *) result;
 }
-- 
2.18.1



More information about the Elfutils-devel mailing list