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 52struct 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 68struct debug_memory_footer 69{ 70 unsigned magic; 71}; 72 73 74static struct list_head list = { &list, &list }; 75 76pipe_static_mutex(list_mutex); 77 78static unsigned long last_no = 0; 79 80 81static INLINE struct debug_memory_header * 82header_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 90static INLINE void * 91data_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 99static INLINE struct debug_memory_footer * 100footer_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 109void * 110debug_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 145void 146debug_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 181void * 182debug_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 191void * 192debug_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 258unsigned long 259debug_memory_begin(void) 260{ 261 return last_no; 262} 263 264void 265debug_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