Merge remote branch 'origin/master' into pipe-video
[mesa.git] / src / gallium / auxiliary / util / u_debug_memory.c
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
42 #include "util/u_debug.h"
43 #include "util/u_debug_stack.h"
44 #include "util/u_double_list.h"
45
46
47 #define DEBUG_MEMORY_MAGIC 0x6e34090aU
48 #define DEBUG_MEMORY_STACK 0 /* XXX: disabled until we have symbol lookup */
49
50
51 struct debug_memory_header
52 {
53 struct list_head head;
54
55 unsigned long no;
56 const char *file;
57 unsigned line;
58 const char *function;
59 #if DEBUG_MEMORY_STACK
60 struct debug_stack_frame backtrace[DEBUG_MEMORY_STACK];
61 #endif
62 size_t size;
63
64 unsigned magic;
65 };
66
67 struct debug_memory_footer
68 {
69 unsigned magic;
70 };
71
72
73 static struct list_head list = { &list, &list };
74
75 static unsigned long last_no = 0;
76
77
78 static INLINE struct debug_memory_header *
79 header_from_data(void *data)
80 {
81 if(data)
82 return (struct debug_memory_header *)((char *)data - sizeof(struct debug_memory_header));
83 else
84 return NULL;
85 }
86
87 static INLINE void *
88 data_from_header(struct debug_memory_header *hdr)
89 {
90 if(hdr)
91 return (void *)((char *)hdr + sizeof(struct debug_memory_header));
92 else
93 return NULL;
94 }
95
96 static INLINE struct debug_memory_footer *
97 footer_from_header(struct debug_memory_header *hdr)
98 {
99 if(hdr)
100 return (struct debug_memory_footer *)((char *)hdr + sizeof(struct debug_memory_header) + hdr->size);
101 else
102 return NULL;
103 }
104
105
106 void *
107 debug_malloc(const char *file, unsigned line, const char *function,
108 size_t size)
109 {
110 struct debug_memory_header *hdr;
111 struct debug_memory_footer *ftr;
112
113 hdr = os_malloc(sizeof(*hdr) + size + sizeof(*ftr));
114 if(!hdr) {
115 debug_printf("%s:%u:%s: out of memory when trying to allocate %lu bytes\n",
116 file, line, function,
117 (long unsigned)size);
118 return NULL;
119 }
120
121 hdr->no = last_no++;
122 hdr->file = file;
123 hdr->line = line;
124 hdr->function = function;
125 hdr->size = size;
126 hdr->magic = DEBUG_MEMORY_MAGIC;
127
128 #if DEBUG_MEMORY_STACK
129 debug_backtrace_capture(hdr->backtrace, 0, DEBUG_MEMORY_STACK);
130 #endif
131
132 ftr = footer_from_header(hdr);
133 ftr->magic = DEBUG_MEMORY_MAGIC;
134
135 LIST_ADDTAIL(&hdr->head, &list);
136
137 return data_from_header(hdr);
138 }
139
140 void
141 debug_free(const char *file, unsigned line, const char *function,
142 void *ptr)
143 {
144 struct debug_memory_header *hdr;
145 struct debug_memory_footer *ftr;
146
147 if(!ptr)
148 return;
149
150 hdr = header_from_data(ptr);
151 if(hdr->magic != DEBUG_MEMORY_MAGIC) {
152 debug_printf("%s:%u:%s: freeing bad or corrupted memory %p\n",
153 file, line, function,
154 ptr);
155 debug_assert(0);
156 return;
157 }
158
159 ftr = footer_from_header(hdr);
160 if(ftr->magic != DEBUG_MEMORY_MAGIC) {
161 debug_printf("%s:%u:%s: buffer overflow %p\n",
162 hdr->file, hdr->line, hdr->function,
163 ptr);
164 debug_assert(0);
165 }
166
167 LIST_DEL(&hdr->head);
168 hdr->magic = 0;
169 ftr->magic = 0;
170
171 os_free(hdr);
172 }
173
174 void *
175 debug_calloc(const char *file, unsigned line, const char *function,
176 size_t count, size_t size )
177 {
178 void *ptr = debug_malloc( file, line, function, count * size );
179 if( ptr )
180 memset( ptr, 0, count * size );
181 return ptr;
182 }
183
184 void *
185 debug_realloc(const char *file, unsigned line, const char *function,
186 void *old_ptr, size_t old_size, size_t new_size )
187 {
188 struct debug_memory_header *old_hdr, *new_hdr;
189 struct debug_memory_footer *old_ftr, *new_ftr;
190 void *new_ptr;
191
192 if(!old_ptr)
193 return debug_malloc( file, line, function, new_size );
194
195 if(!new_size) {
196 debug_free( file, line, function, old_ptr );
197 return NULL;
198 }
199
200 old_hdr = header_from_data(old_ptr);
201 if(old_hdr->magic != DEBUG_MEMORY_MAGIC) {
202 debug_printf("%s:%u:%s: reallocating bad or corrupted memory %p\n",
203 file, line, function,
204 old_ptr);
205 debug_assert(0);
206 return NULL;
207 }
208
209 old_ftr = footer_from_header(old_hdr);
210 if(old_ftr->magic != DEBUG_MEMORY_MAGIC) {
211 debug_printf("%s:%u:%s: buffer overflow %p\n",
212 old_hdr->file, old_hdr->line, old_hdr->function,
213 old_ptr);
214 debug_assert(0);
215 }
216
217 /* alloc new */
218 new_hdr = os_malloc(sizeof(*new_hdr) + new_size + sizeof(*new_ftr));
219 if(!new_hdr) {
220 debug_printf("%s:%u:%s: out of memory when trying to allocate %lu bytes\n",
221 file, line, function,
222 (long unsigned)new_size);
223 return NULL;
224 }
225 new_hdr->no = old_hdr->no;
226 new_hdr->file = old_hdr->file;
227 new_hdr->line = old_hdr->line;
228 new_hdr->function = old_hdr->function;
229 new_hdr->size = new_size;
230 new_hdr->magic = DEBUG_MEMORY_MAGIC;
231
232 new_ftr = footer_from_header(new_hdr);
233 new_ftr->magic = DEBUG_MEMORY_MAGIC;
234
235 LIST_REPLACE(&old_hdr->head, &new_hdr->head);
236
237 /* copy data */
238 new_ptr = data_from_header(new_hdr);
239 memcpy( new_ptr, old_ptr, old_size < new_size ? old_size : new_size );
240
241 /* free old */
242 old_hdr->magic = 0;
243 old_ftr->magic = 0;
244 os_free(old_hdr);
245
246 return new_ptr;
247 }
248
249 unsigned long
250 debug_memory_begin(void)
251 {
252 return last_no;
253 }
254
255 void
256 debug_memory_end(unsigned long start_no)
257 {
258 size_t total_size = 0;
259 struct list_head *entry;
260
261 if(start_no == last_no)
262 return;
263
264 entry = list.prev;
265 for (; entry != &list; entry = entry->prev) {
266 struct debug_memory_header *hdr;
267 void *ptr;
268 struct debug_memory_footer *ftr;
269
270 hdr = LIST_ENTRY(struct debug_memory_header, entry, head);
271 ptr = data_from_header(hdr);
272 ftr = footer_from_header(hdr);
273
274 if(hdr->magic != DEBUG_MEMORY_MAGIC) {
275 debug_printf("%s:%u:%s: bad or corrupted memory %p\n",
276 hdr->file, hdr->line, hdr->function,
277 ptr);
278 debug_assert(0);
279 }
280
281 if((start_no <= hdr->no && hdr->no < last_no) ||
282 (last_no < start_no && (hdr->no < last_no || start_no <= hdr->no))) {
283 debug_printf("%s:%u:%s: %lu bytes at %p not freed\n",
284 hdr->file, hdr->line, hdr->function,
285 (unsigned long) hdr->size, ptr);
286 #if DEBUG_MEMORY_STACK
287 debug_backtrace_dump(hdr->backtrace, DEBUG_MEMORY_STACK);
288 #endif
289 total_size += hdr->size;
290 }
291
292 if(ftr->magic != DEBUG_MEMORY_MAGIC) {
293 debug_printf("%s:%u:%s: buffer overflow %p\n",
294 hdr->file, hdr->line, hdr->function,
295 ptr);
296 debug_assert(0);
297 }
298 }
299
300 if(total_size) {
301 debug_printf("Total of %lu KB of system memory apparently leaked\n",
302 (unsigned long) (total_size + 1023)/1024);
303 }
304 else {
305 debug_printf("No memory leaks detected.\n");
306 }
307 }