Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  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@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.  * Set to 1 to enable checking of freed blocks of memory.
  53.  * Basically, don't really deallocate freed memory; keep it in the list
  54.  * but mark it as freed and do extra checking in debug_memory_check().
  55.  * This can detect some cases of use-after-free.  But note that since we
  56.  * never really free anything this will use a lot of memory.
  57.  */
  58. #define DEBUG_FREED_MEMORY 0
  59. #define DEBUG_FREED_BYTE 0x33
  60.  
  61.  
  62. struct debug_memory_header
  63. {
  64.    struct list_head head;
  65.    
  66.    unsigned long no;
  67.    const char *file;
  68.    unsigned line;
  69.    const char *function;
  70. #if DEBUG_MEMORY_STACK
  71.    struct debug_stack_frame backtrace[DEBUG_MEMORY_STACK];
  72. #endif
  73.    size_t size;
  74. #if DEBUG_FREED_MEMORY
  75.    boolean freed;  /**< Is this a freed block? */
  76. #endif
  77.  
  78.    unsigned magic;
  79.    unsigned tag;
  80. };
  81.  
  82. struct debug_memory_footer
  83. {
  84.    unsigned magic;
  85. };
  86.  
  87.  
  88. static struct list_head list = { &list, &list };
  89.  
  90. pipe_static_mutex(list_mutex);
  91.  
  92. static unsigned long last_no = 0;
  93.  
  94.  
  95. static INLINE struct debug_memory_header *
  96. header_from_data(void *data)
  97. {
  98.    if(data)
  99.       return (struct debug_memory_header *)((char *)data - sizeof(struct debug_memory_header));
  100.    else
  101.       return NULL;
  102. }
  103.  
  104. static INLINE void *
  105. data_from_header(struct debug_memory_header *hdr)
  106. {
  107.    if(hdr)
  108.       return (void *)((char *)hdr + sizeof(struct debug_memory_header));
  109.    else
  110.       return NULL;
  111. }
  112.  
  113. static INLINE struct debug_memory_footer *
  114. footer_from_header(struct debug_memory_header *hdr)
  115. {
  116.    if(hdr)
  117.       return (struct debug_memory_footer *)((char *)hdr + sizeof(struct debug_memory_header) + hdr->size);
  118.    else
  119.       return NULL;
  120. }
  121.  
  122.  
  123. void *
  124. debug_malloc(const char *file, unsigned line, const char *function,
  125.              size_t size)
  126. {
  127.    struct debug_memory_header *hdr;
  128.    struct debug_memory_footer *ftr;
  129.    
  130.    hdr = os_malloc(sizeof(*hdr) + size + sizeof(*ftr));
  131.    if(!hdr) {
  132.       debug_printf("%s:%u:%s: out of memory when trying to allocate %lu bytes\n",
  133.                    file, line, function,
  134.                    (long unsigned)size);
  135.       return NULL;
  136.    }
  137.  
  138.    hdr->no = last_no++;
  139.    hdr->file = file;
  140.    hdr->line = line;
  141.    hdr->function = function;
  142.    hdr->size = size;
  143.    hdr->magic = DEBUG_MEMORY_MAGIC;
  144.    hdr->tag = 0;
  145. #if DEBUG_FREED_MEMORY
  146.    hdr->freed = FALSE;
  147. #endif
  148.  
  149. #if DEBUG_MEMORY_STACK
  150.    debug_backtrace_capture(hdr->backtrace, 0, DEBUG_MEMORY_STACK);
  151. #endif
  152.  
  153.    ftr = footer_from_header(hdr);
  154.    ftr->magic = DEBUG_MEMORY_MAGIC;
  155.    
  156.    pipe_mutex_lock(list_mutex);
  157.    LIST_ADDTAIL(&hdr->head, &list);
  158.    pipe_mutex_unlock(list_mutex);
  159.    
  160.    return data_from_header(hdr);
  161. }
  162.  
  163. void
  164. debug_free(const char *file, unsigned line, const char *function,
  165.            void *ptr)
  166. {
  167.    struct debug_memory_header *hdr;
  168.    struct debug_memory_footer *ftr;
  169.    
  170.    if(!ptr)
  171.       return;
  172.    
  173.    hdr = header_from_data(ptr);
  174.    if(hdr->magic != DEBUG_MEMORY_MAGIC) {
  175.       debug_printf("%s:%u:%s: freeing bad or corrupted memory %p\n",
  176.                    file, line, function,
  177.                    ptr);
  178.       debug_assert(0);
  179.       return;
  180.    }
  181.  
  182.    ftr = footer_from_header(hdr);
  183.    if(ftr->magic != DEBUG_MEMORY_MAGIC) {
  184.       debug_printf("%s:%u:%s: buffer overflow %p\n",
  185.                    hdr->file, hdr->line, hdr->function,
  186.                    ptr);
  187.       debug_assert(0);
  188.    }
  189.  
  190. #if DEBUG_FREED_MEMORY
  191.    /* Check for double-free */
  192.    assert(!hdr->freed);
  193.    /* Mark the block as freed but don't really free it */
  194.    hdr->freed = TRUE;
  195.    /* Save file/line where freed */
  196.    hdr->file = file;
  197.    hdr->line = line;
  198.    /* set freed memory to special value */
  199.    memset(ptr, DEBUG_FREED_BYTE, hdr->size);
  200. #else
  201.    pipe_mutex_lock(list_mutex);
  202.    LIST_DEL(&hdr->head);
  203.    pipe_mutex_unlock(list_mutex);
  204.    hdr->magic = 0;
  205.    ftr->magic = 0;
  206.    
  207.    os_free(hdr);
  208. #endif
  209. }
  210.  
  211. void *
  212. debug_calloc(const char *file, unsigned line, const char *function,
  213.              size_t count, size_t size )
  214. {
  215.    void *ptr = debug_malloc( file, line, function, count * size );
  216.    if( ptr )
  217.       memset( ptr, 0, count * size );
  218.    return ptr;
  219. }
  220.  
  221. void *
  222. debug_realloc(const char *file, unsigned line, const char *function,
  223.               void *old_ptr, size_t old_size, size_t new_size )
  224. {
  225.    struct debug_memory_header *old_hdr, *new_hdr;
  226.    struct debug_memory_footer *old_ftr, *new_ftr;
  227.    void *new_ptr;
  228.    
  229.    if(!old_ptr)
  230.       return debug_malloc( file, line, function, new_size );
  231.    
  232.    if(!new_size) {
  233.       debug_free( file, line, function, old_ptr );
  234.       return NULL;
  235.    }
  236.    
  237.    old_hdr = header_from_data(old_ptr);
  238.    if(old_hdr->magic != DEBUG_MEMORY_MAGIC) {
  239.       debug_printf("%s:%u:%s: reallocating bad or corrupted memory %p\n",
  240.                    file, line, function,
  241.                    old_ptr);
  242.       debug_assert(0);
  243.       return NULL;
  244.    }
  245.    
  246.    old_ftr = footer_from_header(old_hdr);
  247.    if(old_ftr->magic != DEBUG_MEMORY_MAGIC) {
  248.       debug_printf("%s:%u:%s: buffer overflow %p\n",
  249.                    old_hdr->file, old_hdr->line, old_hdr->function,
  250.                    old_ptr);
  251.       debug_assert(0);
  252.    }
  253.  
  254.    /* alloc new */
  255.    new_hdr = os_malloc(sizeof(*new_hdr) + new_size + sizeof(*new_ftr));
  256.    if(!new_hdr) {
  257.       debug_printf("%s:%u:%s: out of memory when trying to allocate %lu bytes\n",
  258.                    file, line, function,
  259.                    (long unsigned)new_size);
  260.       return NULL;
  261.    }
  262.    new_hdr->no = old_hdr->no;
  263.    new_hdr->file = old_hdr->file;
  264.    new_hdr->line = old_hdr->line;
  265.    new_hdr->function = old_hdr->function;
  266.    new_hdr->size = new_size;
  267.    new_hdr->magic = DEBUG_MEMORY_MAGIC;
  268.    new_hdr->tag = 0;
  269. #if DEBUG_FREED_MEMORY
  270.    new_hdr->freed = FALSE;
  271. #endif
  272.    
  273.    new_ftr = footer_from_header(new_hdr);
  274.    new_ftr->magic = DEBUG_MEMORY_MAGIC;
  275.    
  276.    pipe_mutex_lock(list_mutex);
  277.    LIST_REPLACE(&old_hdr->head, &new_hdr->head);
  278.    pipe_mutex_unlock(list_mutex);
  279.  
  280.    /* copy data */
  281.    new_ptr = data_from_header(new_hdr);
  282.    memcpy( new_ptr, old_ptr, old_size < new_size ? old_size : new_size );
  283.  
  284.    /* free old */
  285.    old_hdr->magic = 0;
  286.    old_ftr->magic = 0;
  287.    os_free(old_hdr);
  288.  
  289.    return new_ptr;
  290. }
  291.  
  292. unsigned long
  293. debug_memory_begin(void)
  294. {
  295.    return last_no;
  296. }
  297.  
  298. void
  299. debug_memory_end(unsigned long start_no)
  300. {
  301.    size_t total_size = 0;
  302.    struct list_head *entry;
  303.  
  304.    if(start_no == last_no)
  305.       return;
  306.  
  307.    entry = list.prev;
  308.    for (; entry != &list; entry = entry->prev) {
  309.       struct debug_memory_header *hdr;
  310.       void *ptr;
  311.       struct debug_memory_footer *ftr;
  312.  
  313.       hdr = LIST_ENTRY(struct debug_memory_header, entry, head);
  314.       ptr = data_from_header(hdr);
  315.       ftr = footer_from_header(hdr);
  316.  
  317.       if(hdr->magic != DEBUG_MEMORY_MAGIC) {
  318.          debug_printf("%s:%u:%s: bad or corrupted memory %p\n",
  319.                       hdr->file, hdr->line, hdr->function,
  320.                       ptr);
  321.          debug_assert(0);
  322.       }
  323.  
  324.       if((start_no <= hdr->no && hdr->no < last_no) ||
  325.          (last_no < start_no && (hdr->no < last_no || start_no <= hdr->no))) {
  326.          debug_printf("%s:%u:%s: %lu bytes at %p not freed\n",
  327.                       hdr->file, hdr->line, hdr->function,
  328.                       (unsigned long) hdr->size, ptr);
  329. #if DEBUG_MEMORY_STACK
  330.          debug_backtrace_dump(hdr->backtrace, DEBUG_MEMORY_STACK);
  331. #endif
  332.          total_size += hdr->size;
  333.       }
  334.  
  335.       if(ftr->magic != DEBUG_MEMORY_MAGIC) {
  336.          debug_printf("%s:%u:%s: buffer overflow %p\n",
  337.                       hdr->file, hdr->line, hdr->function,
  338.                       ptr);
  339.          debug_assert(0);
  340.       }
  341.    }
  342.  
  343.    if(total_size) {
  344.       debug_printf("Total of %lu KB of system memory apparently leaked\n",
  345.                    (unsigned long) (total_size + 1023)/1024);
  346.    }
  347.    else {
  348.       debug_printf("No memory leaks detected.\n");
  349.    }
  350. }
  351.  
  352.  
  353. /**
  354.  * Put a tag (arbitrary integer) on a memory block.
  355.  * Can be useful for debugging.
  356.  */
  357. void
  358. debug_memory_tag(void *ptr, unsigned tag)
  359. {
  360.    struct debug_memory_header *hdr;
  361.    
  362.    if (!ptr)
  363.       return;
  364.    
  365.    hdr = header_from_data(ptr);
  366.    if (hdr->magic != DEBUG_MEMORY_MAGIC) {
  367.       debug_printf("%s corrupted memory at %p\n", __FUNCTION__, ptr);
  368.       debug_assert(0);
  369.    }
  370.  
  371.    hdr->tag = tag;
  372. }
  373.  
  374.  
  375. /**
  376.  * Check the given block of memory for validity/corruption.
  377.  */
  378. void
  379. debug_memory_check_block(void *ptr)
  380. {
  381.    struct debug_memory_header *hdr;
  382.    struct debug_memory_footer *ftr;
  383.    
  384.    if (!ptr)
  385.       return;
  386.    
  387.    hdr = header_from_data(ptr);
  388.    ftr = footer_from_header(hdr);
  389.  
  390.    if (hdr->magic != DEBUG_MEMORY_MAGIC) {
  391.       debug_printf("%s:%u:%s: bad or corrupted memory %p\n",
  392.                    hdr->file, hdr->line, hdr->function, ptr);
  393.       debug_assert(0);
  394.    }
  395.  
  396.    if (ftr->magic != DEBUG_MEMORY_MAGIC) {
  397.       debug_printf("%s:%u:%s: buffer overflow %p\n",
  398.                    hdr->file, hdr->line, hdr->function, ptr);
  399.       debug_assert(0);
  400.    }
  401. }
  402.  
  403.  
  404.  
  405. /**
  406.  * We can periodically call this from elsewhere to do a basic sanity
  407.  * check of the heap memory we've allocated.
  408.  */
  409. void
  410. debug_memory_check(void)
  411. {
  412.    struct list_head *entry;
  413.  
  414.    entry = list.prev;
  415.    for (; entry != &list; entry = entry->prev) {
  416.       struct debug_memory_header *hdr;
  417.       struct debug_memory_footer *ftr;
  418.       const char *ptr;
  419.  
  420.       hdr = LIST_ENTRY(struct debug_memory_header, entry, head);
  421.       ftr = footer_from_header(hdr);
  422.       ptr = (const char *) data_from_header(hdr);
  423.  
  424.       if (hdr->magic != DEBUG_MEMORY_MAGIC) {
  425.          debug_printf("%s:%u:%s: bad or corrupted memory %p\n",
  426.                       hdr->file, hdr->line, hdr->function, ptr);
  427.          debug_assert(0);
  428.       }
  429.  
  430.       if (ftr->magic != DEBUG_MEMORY_MAGIC) {
  431.          debug_printf("%s:%u:%s: buffer overflow %p\n",
  432.                       hdr->file, hdr->line, hdr->function, ptr);
  433.          debug_assert(0);
  434.       }
  435.  
  436. #if DEBUG_FREED_MEMORY
  437.       /* If this block is marked as freed, check that it hasn't been touched */
  438.       if (hdr->freed) {
  439.          int i;
  440.          for (i = 0; i < hdr->size; i++) {
  441.             if (ptr[i] != DEBUG_FREED_BYTE) {
  442.                debug_printf("Memory error: byte %d of block at %p of size %d is 0x%x\n",
  443.                             i, ptr, hdr->size, ptr[i]);
  444.                debug_printf("Block was freed at %s:%d\n", hdr->file, hdr->line);
  445.             }
  446.             assert(ptr[i] == DEBUG_FREED_BYTE);
  447.          }
  448.       }
  449. #endif
  450.    }
  451. }
  452.