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