From: Martin Quinson Date: Wed, 8 Feb 2012 10:47:29 +0000 (+0100) Subject: Setup the framework allowing to add backtraces to the malloc meta-data X-Git-Tag: exp_20120216~46 X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/commitdiff_plain/18830ad59a14b63f78de5f8c6864ec894b00f18e Setup the framework allowing to add backtraces to the malloc meta-data - 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. --- diff --git a/include/xbt/ex.h b/include/xbt/ex.h index f23bd37e1d..446dfc97ec 100644 --- a/include/xbt/ex.h +++ b/include/xbt/ex.h @@ -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 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 */ diff --git a/include/xbt/mmalloc.h b/include/xbt/mmalloc.h index a9d33eef1f..e726950b66 100644 --- a/include/xbt/mmalloc.h +++ b/include/xbt/mmalloc.h @@ -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); +void mmalloc_backtrace_display(xbt_mheap_t mdp, void *addr); + #endif /* MMALLOC_H */ diff --git a/src/xbt/backtrace_linux.c b/src/xbt/backtrace_linux.c index 966070ad62..ce4d0d3bea 100644 --- a/src/xbt/backtrace_linux.c +++ b/src/xbt/backtrace_linux.c @@ -25,6 +25,51 @@ void xbt_backtrace_postexit(void) { } +#include +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); @@ -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; @@ -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->bt_strings = NULL; + 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->bt_strings = NULL; /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */ if (!e->used) diff --git a/src/xbt/mmalloc/mm_diff.c b/src/xbt/mmalloc/mm_diff.c index f3b0eed5c3..5a081425dd 100644 --- a/src/xbt/mmalloc/mm_diff.c +++ b/src/xbt/mmalloc/mm_diff.c @@ -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. */ +#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){ diff --git a/src/xbt/mmalloc/mmalloc.c b/src/xbt/mmalloc/mmalloc.c index 816f03465d..1205be3a3a 100644 --- a/src/xbt/mmalloc/mmalloc.c +++ b/src/xbt/mmalloc/mmalloc.c @@ -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.bt_size = 0;// FIXME setup the backtrace 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; - // 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; } @@ -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].busy_block.bt_size = 0; } //printf("(%s) Done mallocing. Result is %p\n",xbt_thread_self_name(),result);fflush(stdout); return (result); diff --git a/src/xbt/mmalloc/mmprivate.h b/src/xbt/mmalloc/mmprivate.h index 24ca7e8449..b2dc18f833 100644 --- a/src/xbt/mmalloc/mmprivate.h +++ b/src/xbt/mmalloc/mmprivate.h @@ -29,7 +29,7 @@ #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 @@ -88,8 +88,6 @@ #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; @@ -98,8 +96,8 @@ struct list { /* 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 @@ -120,7 +118,6 @@ struct list { * 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 { @@ -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]; + //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 */ + 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 { @@ -156,6 +156,7 @@ struct mdesc { /* Semaphore locking the access to the heap */ sem_t sem; + char locked; /* 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*/ -#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 */