Home | History | Annotate | Download | only in util
      1 /**************************************************************************
      2  *
      3  * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21  * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
     22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 /**
     29  * @file
     30  * Memory debugging.
     31  *
     32  * @author Jos Fonseca <jrfonseca (at) tungstengraphics.com>
     33  */
     34 
     35 #include "pipe/p_config.h"
     36 
     37 #define DEBUG_MEMORY_IMPLEMENTATION
     38 
     39 #include "os/os_memory.h"
     40 #include "os/os_memory_debug.h"
     41 #include "os/os_thread.h"
     42 
     43 #include "util/u_debug.h"
     44 #include "util/u_debug_stack.h"
     45 #include "util/u_double_list.h"
     46 
     47 
     48 #define DEBUG_MEMORY_MAGIC 0x6e34090aU
     49 #define DEBUG_MEMORY_STACK 0 /* XXX: disabled until we have symbol lookup */
     50 
     51 
     52 struct debug_memory_header
     53 {
     54    struct list_head head;
     55 
     56    unsigned long no;
     57    const char *file;
     58    unsigned line;
     59    const char *function;
     60 #if DEBUG_MEMORY_STACK
     61    struct debug_stack_frame backtrace[DEBUG_MEMORY_STACK];
     62 #endif
     63    size_t size;
     64 
     65    unsigned magic;
     66 };
     67 
     68 struct debug_memory_footer
     69 {
     70    unsigned magic;
     71 };
     72 
     73 
     74 static struct list_head list = { &list, &list };
     75 
     76 pipe_static_mutex(list_mutex);
     77 
     78 static unsigned long last_no = 0;
     79 
     80 
     81 static INLINE struct debug_memory_header *
     82 header_from_data(void *data)
     83 {
     84    if(data)
     85       return (struct debug_memory_header *)((char *)data - sizeof(struct debug_memory_header));
     86    else
     87       return NULL;
     88 }
     89 
     90 static INLINE void *
     91 data_from_header(struct debug_memory_header *hdr)
     92 {
     93    if(hdr)
     94       return (void *)((char *)hdr + sizeof(struct debug_memory_header));
     95    else
     96       return NULL;
     97 }
     98 
     99 static INLINE struct debug_memory_footer *
    100 footer_from_header(struct debug_memory_header *hdr)
    101 {
    102    if(hdr)
    103       return (struct debug_memory_footer *)((char *)hdr + sizeof(struct debug_memory_header) + hdr->size);
    104    else
    105       return NULL;
    106 }
    107 
    108 
    109 void *
    110 debug_malloc(const char *file, unsigned line, const char *function,
    111              size_t size)
    112 {
    113    struct debug_memory_header *hdr;
    114    struct debug_memory_footer *ftr;
    115 
    116    hdr = os_malloc(sizeof(*hdr) + size + sizeof(*ftr));
    117    if(!hdr) {
    118       debug_printf("%s:%u:%s: out of memory when trying to allocate %lu bytes\n",
    119                    file, line, function,
    120                    (long unsigned)size);
    121       return NULL;
    122    }
    123 
    124    hdr->no = last_no++;
    125    hdr->file = file;
    126    hdr->line = line;
    127    hdr->function = function;
    128    hdr->size = size;
    129    hdr->magic = DEBUG_MEMORY_MAGIC;
    130 
    131 #if DEBUG_MEMORY_STACK
    132    debug_backtrace_capture(hdr->backtrace, 0, DEBUG_MEMORY_STACK);
    133 #endif
    134 
    135    ftr = footer_from_header(hdr);
    136    ftr->magic = DEBUG_MEMORY_MAGIC;
    137 
    138    pipe_mutex_lock(list_mutex);
    139    LIST_ADDTAIL(&hdr->head, &list);
    140    pipe_mutex_unlock(list_mutex);
    141 
    142    return data_from_header(hdr);
    143 }
    144 
    145 void
    146 debug_free(const char *file, unsigned line, const char *function,
    147            void *ptr)
    148 {
    149    struct debug_memory_header *hdr;
    150    struct debug_memory_footer *ftr;
    151 
    152    if(!ptr)
    153       return;
    154 
    155    hdr = header_from_data(ptr);
    156    if(hdr->magic != DEBUG_MEMORY_MAGIC) {
    157       debug_printf("%s:%u:%s: freeing bad or corrupted memory %p\n",
    158                    file, line, function,
    159                    ptr);
    160       debug_assert(0);
    161       return;
    162    }
    163 
    164    ftr = footer_from_header(hdr);
    165    if(ftr->magic != DEBUG_MEMORY_MAGIC) {
    166       debug_printf("%s:%u:%s: buffer overflow %p\n",
    167                    hdr->file, hdr->line, hdr->function,
    168                    ptr);
    169       debug_assert(0);
    170    }
    171 
    172    pipe_mutex_lock(list_mutex);
    173    LIST_DEL(&hdr->head);
    174    pipe_mutex_unlock(list_mutex);
    175    hdr->magic = 0;
    176    ftr->magic = 0;
    177 
    178    os_free(hdr);
    179 }
    180 
    181 void *
    182 debug_calloc(const char *file, unsigned line, const char *function,
    183              size_t count, size_t size )
    184 {
    185    void *ptr = debug_malloc( file, line, function, count * size );
    186    if( ptr )
    187       memset( ptr, 0, count * size );
    188    return ptr;
    189 }
    190 
    191 void *
    192 debug_realloc(const char *file, unsigned line, const char *function,
    193               void *old_ptr, size_t old_size, size_t new_size )
    194 {
    195    struct debug_memory_header *old_hdr, *new_hdr;
    196    struct debug_memory_footer *old_ftr, *new_ftr;
    197    void *new_ptr;
    198 
    199    if(!old_ptr)
    200       return debug_malloc( file, line, function, new_size );
    201 
    202    if(!new_size) {
    203       debug_free( file, line, function, old_ptr );
    204       return NULL;
    205    }
    206 
    207    old_hdr = header_from_data(old_ptr);
    208    if(old_hdr->magic != DEBUG_MEMORY_MAGIC) {
    209       debug_printf("%s:%u:%s: reallocating bad or corrupted memory %p\n",
    210                    file, line, function,
    211                    old_ptr);
    212       debug_assert(0);
    213       return NULL;
    214    }
    215 
    216    old_ftr = footer_from_header(old_hdr);
    217    if(old_ftr->magic != DEBUG_MEMORY_MAGIC) {
    218       debug_printf("%s:%u:%s: buffer overflow %p\n",
    219                    old_hdr->file, old_hdr->line, old_hdr->function,
    220                    old_ptr);
    221       debug_assert(0);
    222    }
    223 
    224    /* alloc new */
    225    new_hdr = os_malloc(sizeof(*new_hdr) + new_size + sizeof(*new_ftr));
    226    if(!new_hdr) {
    227       debug_printf("%s:%u:%s: out of memory when trying to allocate %lu bytes\n",
    228                    file, line, function,
    229                    (long unsigned)new_size);
    230       return NULL;
    231    }
    232    new_hdr->no = old_hdr->no;
    233    new_hdr->file = old_hdr->file;
    234    new_hdr->line = old_hdr->line;
    235    new_hdr->function = old_hdr->function;
    236    new_hdr->size = new_size;
    237    new_hdr->magic = DEBUG_MEMORY_MAGIC;
    238 
    239    new_ftr = footer_from_header(new_hdr);
    240    new_ftr->magic = DEBUG_MEMORY_MAGIC;
    241 
    242    pipe_mutex_lock(list_mutex);
    243    LIST_REPLACE(&old_hdr->head, &new_hdr->head);
    244    pipe_mutex_unlock(list_mutex);
    245 
    246    /* copy data */
    247    new_ptr = data_from_header(new_hdr);
    248    memcpy( new_ptr, old_ptr, old_size < new_size ? old_size : new_size );
    249 
    250    /* free old */
    251    old_hdr->magic = 0;
    252    old_ftr->magic = 0;
    253    os_free(old_hdr);
    254 
    255    return new_ptr;
    256 }
    257 
    258 unsigned long
    259 debug_memory_begin(void)
    260 {
    261    return last_no;
    262 }
    263 
    264 void
    265 debug_memory_end(unsigned long start_no)
    266 {
    267    size_t total_size = 0;
    268    struct list_head *entry;
    269 
    270    if(start_no == last_no)
    271       return;
    272 
    273    entry = list.prev;
    274    for (; entry != &list; entry = entry->prev) {
    275       struct debug_memory_header *hdr;
    276       void *ptr;
    277       struct debug_memory_footer *ftr;
    278 
    279       hdr = LIST_ENTRY(struct debug_memory_header, entry, head);
    280       ptr = data_from_header(hdr);
    281       ftr = footer_from_header(hdr);
    282 
    283       if(hdr->magic != DEBUG_MEMORY_MAGIC) {
    284          debug_printf("%s:%u:%s: bad or corrupted memory %p\n",
    285                       hdr->file, hdr->line, hdr->function,
    286                       ptr);
    287          debug_assert(0);
    288       }
    289 
    290       if((start_no <= hdr->no && hdr->no < last_no) ||
    291 	 (last_no < start_no && (hdr->no < last_no || start_no <= hdr->no))) {
    292 	 debug_printf("%s:%u:%s: %lu bytes at %p not freed\n",
    293 		      hdr->file, hdr->line, hdr->function,
    294 		      (unsigned long) hdr->size, ptr);
    295 #if DEBUG_MEMORY_STACK
    296 	 debug_backtrace_dump(hdr->backtrace, DEBUG_MEMORY_STACK);
    297 #endif
    298 	 total_size += hdr->size;
    299       }
    300 
    301       if(ftr->magic != DEBUG_MEMORY_MAGIC) {
    302          debug_printf("%s:%u:%s: buffer overflow %p\n",
    303                       hdr->file, hdr->line, hdr->function,
    304                       ptr);
    305          debug_assert(0);
    306       }
    307    }
    308 
    309    if(total_size) {
    310       debug_printf("Total of %lu KB of system memory apparently leaked\n",
    311 		   (unsigned long) (total_size + 1023)/1024);
    312    }
    313    else {
    314       debug_printf("No memory leaks detected.\n");
    315    }
    316 }
    317