Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Setup the framework allowing to add backtraces to the malloc meta-data
authorMartin Quinson <martin.quinson@loria.fr>
Wed, 8 Feb 2012 10:47:29 +0000 (11:47 +0100)
committerMartin Quinson <martin.quinson@loria.fr>
Wed, 8 Feb 2012 10:47:29 +0000 (11:47 +0100)
- implement a malloc-clean backtrace() function
- make some room to store the backtraces. Only for big blocks for now,
  the memory consumption seem to be very high when doing so for
  fragments. Possible solutions include:
  - increasing the minimal fragment size to reduce the amount of
    possible fragment per block. It will waste some blocks for very
    small fragments, but it will save metadata that is paid for EVERY
    block, including full blocks, through the union in the metadata
  - Reduce the size of the saved backtraces. For now, we save up to 10
    calls, 5 to 3 levels may be enough if space is scarce.
- use that framework to save the backtraces in one malloc execution
  path. Other malloc execution paths, as well as realloc paths should
  now be changed to store the backtrace too.
- Implement a mmalloc_backtrace_display() function that displays the
  backtrace at which the block where malloc()ed. This is a bit crude
  for now, as we reuse the internals of exceptions that where not
  really done for that, but it works.

include/xbt/ex.h
include/xbt/mmalloc.h
src/xbt/backtrace_linux.c
src/xbt/mmalloc/mm_diff.c
src/xbt/mmalloc/mmalloc.c
src/xbt/mmalloc/mmprivate.h

index f23bd37..446dfc9 100644 (file)
@@ -509,6 +509,8 @@ XBT_PUBLIC(void) xbt_ex_free(xbt_ex_t e);
 
 /** @brief Shows a backtrace of the current location */
 XBT_PUBLIC(void) xbt_backtrace_display_current(void);
 
 /** @brief Shows a backtrace of the current location */
 XBT_PUBLIC(void) xbt_backtrace_display_current(void);
+/** @brief reimplementation of glibc backtrace based directly on gcc library, without implicit malloc  */
+XBT_PUBLIC(int)xbt_backtrace_no_malloc(void**bt, int size);
 /** @brief Captures a backtrace for further use */
 XBT_PUBLIC(void) xbt_backtrace_current(xbt_ex_t * e);
 /** @brief Display a previously captured backtrace */
 /** @brief Captures a backtrace for further use */
 XBT_PUBLIC(void) xbt_backtrace_current(xbt_ex_t * e);
 /** @brief Display a previously captured backtrace */
index a9d33ee..e726950 100644 (file)
@@ -60,5 +60,7 @@ xbt_mheap_t mmalloc_get_current_heap(void);
 
 int mmalloc_compare_heap(xbt_mheap_t mdp1, xbt_mheap_t mdp2, void *std_heap_addr);
 
 
 int mmalloc_compare_heap(xbt_mheap_t mdp1, xbt_mheap_t mdp2, void *std_heap_addr);
 
+void mmalloc_backtrace_display(xbt_mheap_t mdp, void *addr);
+
 
 #endif                          /* MMALLOC_H */
 
 #endif                          /* MMALLOC_H */
index 966070a..ce4d0d3 100644 (file)
@@ -25,6 +25,51 @@ void xbt_backtrace_postexit(void)
 {
 }
 
 {
 }
 
+#include <unwind.h>
+struct trace_arg {
+  void **array;
+  int cnt, size;
+};
+
+static _Unwind_Reason_Code
+backtrace_helper (struct _Unwind_Context *ctx, void *a)
+{
+  struct trace_arg *arg = a;
+
+  /* We are first called with address in the __backtrace function.
+     Skip it.  */
+  if (arg->cnt != -1)
+    {
+      arg->array[arg->cnt] = (void *) _Unwind_GetIP(ctx);
+
+      /* Check whether we make any progress.  */
+      if (arg->cnt > 0 && arg->array[arg->cnt - 1] == arg->array[arg->cnt])
+        return _URC_END_OF_STACK;
+    }
+  if (++arg->cnt == arg->size)
+    return _URC_END_OF_STACK;
+  return _URC_NO_REASON;
+}
+
+/** @brief reimplementation of glibc backtrace based directly on gcc library, without implicit malloc
+ *
+ * See http://webloria.loria.fr/~quinson/blog/2012/0208/system_programming_fun_in_SimGrid/
+ * for the motivation behind this function
+ * */
+
+int xbt_backtrace_no_malloc(void **array, int size) {
+  struct trace_arg arg = { .array = array, .size = size, .cnt = -1 };
+
+  if (size >= 1)
+    _Unwind_Backtrace(backtrace_helper, &arg);
+
+  /* _Unwind_Backtrace on IA-64 seems to put NULL address above
+     _start.  Fix it up here.  */
+  if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL)
+    --arg.cnt;
+  return arg.cnt != -1 ? arg.cnt : 0;
+}
+
 void xbt_backtrace_current(xbt_ex_t * e)
 {
   e->used = backtrace((void **) e->bt, XBT_BACKTRACE_SIZE);
 void xbt_backtrace_current(xbt_ex_t * e)
 {
   e->used = backtrace((void **) e->bt, XBT_BACKTRACE_SIZE);
@@ -37,7 +82,7 @@ void xbt_backtrace_current(xbt_ex_t * e)
 }
 
 
 }
 
 
-void xbt_ex_setup_backtrace(xbt_ex_t * e)
+void xbt_ex_setup_backtrace(xbt_ex_t * e) //FIXME: This code could be greatly improved/simplifyied with http://cairo.sourcearchive.com/documentation/1.9.4/backtrace-symbols_8c-source.html
 {
   int i;
 
 {
   int i;
 
@@ -66,6 +111,8 @@ void xbt_ex_setup_backtrace(xbt_ex_t * e)
               && e->used,
               "Backtrace not setup yet, cannot set it up for display");
 
               && e->used,
               "Backtrace not setup yet, cannot set it up for display");
 
+  e->bt_strings = NULL;
+
   if (!xbt_binary_name) /* no binary name, nothing to do */
     return;
 
   if (!xbt_binary_name) /* no binary name, nothing to do */
     return;
 
@@ -74,7 +121,6 @@ void xbt_ex_setup_backtrace(xbt_ex_t * e)
   e->used--;
   memmove(backtrace_syms, backtrace_syms + 1, sizeof(char *) * e->used);
 
   e->used--;
   memmove(backtrace_syms, backtrace_syms + 1, sizeof(char *) * e->used);
 
-  e->bt_strings = NULL;
 
   /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
   if (!e->used)
 
   /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
   if (!e->used)
index f3b0eed..5a08142 100644 (file)
@@ -5,6 +5,49 @@
 /* This program is free software; you can redistribute it and/or modify it
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
 /* This program is free software; you can redistribute it and/or modify it
  * under the terms of the license (GNU LGPL) which comes with this package. */
 
+#include "xbt/ex_interface.h" /* internals of backtrace setup */
+
+extern char *xbt_binary_name;
+
+void mmalloc_backtrace_display(xbt_mheap_t mdp, void *ptr){
+  size_t block = BLOCK(ptr);
+  int type;
+  xbt_ex_t e;
+
+  if ((char *) ptr < (char *) mdp->heapbase || block > mdp->heapsize) {
+    fprintf(stderr,"Ouch, this pointer is not mine. I cannot display its backtrace. I refuse it to death!!\n");
+    abort();
+  }
+
+  type = mdp->heapinfo[block].type;
+
+  if (type != 0) {
+    //fprintf(stderr,"Only full blocks are backtraced for now. Ignoring your request.\n");
+    return;
+  }
+  if (mdp->heapinfo[block].busy_block.bt_size == 0) {
+    fprintf(stderr,"No backtrace available for that block, sorry.\n");
+    return;
+  }
+
+  memcpy(&e.bt,&(mdp->heapinfo[block].busy_block.bt),sizeof(void*)*XBT_BACKTRACE_SIZE);
+  e.used = mdp->heapinfo[block].busy_block.bt_size;
+
+  xbt_ex_setup_backtrace(&e);
+  if (e.used == 0) {
+    fprintf(stderr, "(backtrace not set)\n");
+  } else if (e.bt_strings == NULL) {
+    fprintf(stderr, "(backtrace not ready to be computed. %s)\n",xbt_binary_name?"Dunno why":"xbt_binary_name not setup yet");
+  } else {
+    int i;
+
+    fprintf(stderr, "Backtrace of where the block %p where malloced (%d frames):\n",ptr,e.used);
+    for (i = 0; i < e.used; i++)       /* no need to display "xbt_backtrace_display" */{
+      fprintf(stderr,"%d",i);fflush(NULL);
+      fprintf(stderr, "---> %s\n", e.bt_strings[i] + 4);
+    }
+  }
+}
 
 int mmalloc_compare_heap(xbt_mheap_t mdp1, xbt_mheap_t mdp2, void *std_heap_addr){
 
 
 int mmalloc_compare_heap(xbt_mheap_t mdp1, xbt_mheap_t mdp2, void *std_heap_addr){
 
index 816f034..1205be3 100644 (file)
@@ -96,6 +96,7 @@ static void *register_morecore(struct mdesc *mdp, size_t size)
 
     newinfo[BLOCK(oldinfo)].busy_block.size = BLOCKIFY(mdp->heapsize * sizeof(malloc_info));
     newinfo[BLOCK(oldinfo)].busy_block.busy_size = size;
 
     newinfo[BLOCK(oldinfo)].busy_block.size = BLOCKIFY(mdp->heapsize * sizeof(malloc_info));
     newinfo[BLOCK(oldinfo)].busy_block.busy_size = size;
+    newinfo[BLOCK(oldinfo)].busy_block.bt_size = 0;// FIXME setup the backtrace
     mfree(mdp, (void *) oldinfo);
     mdp->heapsize = newsize;
   }
     mfree(mdp, (void *) oldinfo);
     mdp->heapsize = newsize;
   }
@@ -229,7 +230,7 @@ void *mmalloc(xbt_mheap_t mdp, size_t size)
                mdp->heapinfo[block+it].type = 0;
         mdp->heapinfo[block].busy_block.size = blocks;
         mdp->heapinfo[block].busy_block.busy_size = requested_size;
                mdp->heapinfo[block+it].type = 0;
         mdp->heapinfo[block].busy_block.size = blocks;
         mdp->heapinfo[block].busy_block.busy_size = requested_size;
-        // FIXME: setup backtrace
+        mdp->heapinfo[block].busy_block.bt_size=xbt_backtrace_no_malloc(mdp->heapinfo[block].busy_block.bt,XBT_BACKTRACE_SIZE);
 
         return result;
       }
 
         return result;
       }
@@ -264,6 +265,7 @@ void *mmalloc(xbt_mheap_t mdp, size_t size)
        mdp->heapinfo[block+it].type = 0;
     mdp->heapinfo[block].busy_block.size = blocks;
     mdp->heapinfo[block].busy_block.busy_size = requested_size;
        mdp->heapinfo[block+it].type = 0;
     mdp->heapinfo[block].busy_block.size = blocks;
     mdp->heapinfo[block].busy_block.busy_size = requested_size;
+    mdp->heapinfo[block].busy_block.bt_size = 0;
   }
   //printf("(%s) Done mallocing. Result is %p\n",xbt_thread_self_name(),result);fflush(stdout);
   return (result);
   }
   //printf("(%s) Done mallocing. Result is %p\n",xbt_thread_self_name(),result);fflush(stdout);
   return (result);
index 24ca7e8..b2dc18f 100644 (file)
@@ -29,7 +29,7 @@
 
 #define MMALLOC_MAGIC          "mmalloc"       /* Mapped file magic number */
 #define MMALLOC_MAGIC_SIZE     8       /* Size of magic number buf */
 
 #define MMALLOC_MAGIC          "mmalloc"       /* Mapped file magic number */
 #define MMALLOC_MAGIC_SIZE     8       /* Size of magic number buf */
-#define MMALLOC_VERSION                1       /* Current mmalloc version */
+#define MMALLOC_VERSION                2       /* Current mmalloc version */
 
 /* The allocator divides the heap into blocks of fixed size; large
    requests receive one or more whole blocks, and small requests
 
 /* The allocator divides the heap into blocks of fixed size; large
    requests receive one or more whole blocks, and small requests
@@ -88,8 +88,6 @@
 
 #define ADDRESS(B) ((void*) (((ADDR2UINT(B)) - 1) * BLOCKSIZE + (char*) mdp -> heapbase))
 
 
 #define ADDRESS(B) ((void*) (((ADDR2UINT(B)) - 1) * BLOCKSIZE + (char*) mdp -> heapbase))
 
-const char *xbt_thread_self_name(void);
-
 /* Doubly linked lists of free fragments.  */
 struct list {
        struct list *next;
 /* Doubly linked lists of free fragments.  */
 struct list {
        struct list *next;
@@ -98,8 +96,8 @@ struct list {
 
 /* Data structure giving per-block information.
  *
 
 /* Data structure giving per-block information.
  *
- * There is one such structure in the mdp->heapinfo array,
- * that is addressed by block number.
+ * There is one such structure in the mdp->heapinfo array per block used in that heap,
+ *    the array index is the block number.
  *
  * There is several types of blocks in memory:
  *  - full busy blocks: used when we are asked to malloc a block which size is > BLOCKSIZE/2
  *
  * There is several types of blocks in memory:
  *  - full busy blocks: used when we are asked to malloc a block which size is > BLOCKSIZE/2
@@ -120,7 +118,6 @@ struct list {
  * You can crawl the array and rely on that value.
  *
  * TODO:
  * You can crawl the array and rely on that value.
  *
  * TODO:
- *  - add an indication of the requested size in each fragment, similarly to busy_block.busy_size
  *  - make room to store the backtrace of where the blocks and fragment were malloced, too.
  */
 typedef struct {
  *  - make room to store the backtrace of where the blocks and fragment were malloced, too.
  */
 typedef struct {
@@ -133,10 +130,13 @@ typedef struct {
                        size_t nfree;           /* Free fragments in a fragmented block.  */
                        size_t first;           /* First free fragment of the block.  */
                        unsigned short frag_size[MAX_FRAGMENT_PER_BLOCK];
                        size_t nfree;           /* Free fragments in a fragmented block.  */
                        size_t first;           /* First free fragment of the block.  */
                        unsigned short frag_size[MAX_FRAGMENT_PER_BLOCK];
+      //void *bt[XBT_BACKTRACE_SIZE][MAX_FRAGMENT_PER_BLOCK]; /* Where it was malloced (or realloced lastly) */
                } busy_frag;
                struct {
                        size_t size; /* Size (in blocks) of a large cluster.  */
                        size_t busy_size; /* Actually used space, in bytes */
                } busy_frag;
                struct {
                        size_t size; /* Size (in blocks) of a large cluster.  */
                        size_t busy_size; /* Actually used space, in bytes */
+                       void *bt[XBT_BACKTRACE_SIZE]; /* Where it was malloced (or realloced lastly) */
+                       int bt_size;
                } busy_block;
                /* Heap information for a free block (that may be the first of a free cluster).  */
                struct {
                } busy_block;
                /* Heap information for a free block (that may be the first of a free cluster).  */
                struct {
@@ -156,6 +156,7 @@ struct mdesc {
 
        /* Semaphore locking the access to the heap */
        sem_t sem;
 
        /* Semaphore locking the access to the heap */
        sem_t sem;
+       char locked;
 
        /* Number of processes that attached the heap */
        unsigned int refcount;
 
        /* Number of processes that attached the heap */
        unsigned int refcount;
@@ -246,10 +247,17 @@ extern void *__mmalloc_remap_core(xbt_mheap_t mdp);
 extern void *mmorecore(struct mdesc *mdp, int size);
 
 /* Thread-safety (if the sem is already created) FIXME: KILLIT*/
 extern void *mmorecore(struct mdesc *mdp, int size);
 
 /* Thread-safety (if the sem is already created) FIXME: KILLIT*/
-#define LOCK(mdp)                                        \
-               sem_wait(&mdp->sem)
-
-#define UNLOCK(mdp)                                        \
-               sem_post(&mdp->sem)
+#define LOCK(mdp)   do {  \
+    if (mdp->locked)      \
+      fprintf(stderr,"panic! I'm not reintrant\n"); \
+               sem_wait(&mdp->sem);  \
+               mdp->locked=1;        \
+  } while(0)
+
+
+#define UNLOCK(mdp)  do {  \
+               sem_post(&mdp->sem);   \
+    mdp->locked=0;         \
+  } while (0)
 
 #endif                          /* __MMPRIVATE_H */
 
 #endif                          /* __MMPRIVATE_H */