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 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 *
header_from_data(void * data)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 *
data_from_header(struct debug_memory_header * hdr)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 *
footer_from_header(struct debug_memory_header * hdr)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 *
debug_malloc(const char * file,unsigned line,const char * function,size_t size)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
debug_free(const char * file,unsigned line,const char * function,void * ptr)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 *
debug_calloc(const char * file,unsigned line,const char * function,size_t count,size_t size)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 *
debug_realloc(const char * file,unsigned line,const char * function,void * old_ptr,size_t old_size,size_t new_size)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
debug_memory_begin(void)259 debug_memory_begin(void)
260 {
261    return last_no;
262 }
263 
264 void
debug_memory_end(unsigned long start_no)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