comparison src/free-hook.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 859a2309aef8
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 /* This file is part of XEmacs.
2
3 XEmacs is free software; you can redistribute it and/or modify it
4 under the terms of the GNU General Public License as published by the
5 Free Software Foundation; either version 2, or (at your option) any
6 later version.
7
8 XEmacs is distributed in the hope that it will be useful, but WITHOUT
9 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with XEmacs; see the file COPYING. If not, write to
15 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 Boston, MA 02111-1307, USA. */
17
18 /* Synched up with: Not in FSF. */
19
20 /* Debugging hooks for malloc. */
21
22 /* These hooks work with gmalloc to catch allocation errors.
23 In particular, the following is trapped:
24
25 * Freeing the same pointer twice.
26 * Trying to free a pointer not returned by malloc.
27 * Trying to realloc a pointer not returned by malloc.
28
29 In addition, every word of every block freed is set to
30 0xdeadbeef. This causes many uses of freed storage to be
31 trapped or recognized.
32
33 When you use this, the storage used by the last FREE_QUEUE_LIMIT
34 calls to free() is not recycled. When you call free for the Nth
35 time, the (N - FREE_QUEUE_LIMIT)'th block is actually recycled.
36
37 For these last FREE_QUEUE_LIMIT calls to free() a backtrace is
38 saved showing where it was called from. The function
39 find_backtrace() is provided here to be called from GDB with a
40 pointer (such as would be passed to free()) as argument, e.g.
41 (gdb) p/a *find_backtrace (0x234000). If SAVE_ARGS is defined,
42 the first three arguments to each function are saved as well as the
43 return addresses.
44
45 If UNMAPPED_FREE is defined, instead of setting every word of freed
46 storage to 0xdeadbeef, every call to malloc goes on its own page(s).
47 When free() is called, the block is read and write protected. This
48 is very useful when debugging, since it usually generates a bus error
49 when the deadbeef hack might only cause some garbage to be printed.
50 However, this is too slow for everyday use, since it takes an enormous
51 number of pages.
52
53
54 Some other features that would be useful are:
55
56 * Checking for storage leaks.
57 This could be done by a GC-like facility that would scan the data
58 segment looking for pointers to allocated storage and tell you
59 about those that are no longer referenced. This could be invoked
60 at any time. Another possibility is to report on what allocated
61 storage is still in use when the process is exited. Typically
62 there will be a large amount, so this might not be very useful.
63 */
64
65 #if defined (EMACS_BTL) && defined (sun4) && !defined (__lucid)
66 /* currently only works in this configuration */
67 # define SAVE_STACK
68 #endif
69
70 #ifdef emacs
71 #ifdef SAVE_STACK
72 #include "cadillac-btl.h"
73 #endif
74 #include <config.h>
75 #include "lisp.h"
76 #else
77 void *malloc (unsigned long);
78 #endif
79
80 #include <stdio.h>
81
82 #include "hash.h"
83
84 #ifdef UNMAPPED_FREE
85 #include <sys/mman.h>
86 #include <sys/param.h>
87 #define ROUND_UP_TO_PAGE(i) (((i) + PAGEOFFSET) & PAGEMASK)
88 #endif
89
90 #include <sys/types.h>
91
92 /* System function prototypes don't belong in C source files */
93 /* extern void free (void *); */
94
95 c_hashtable pointer_table;
96
97 extern void (*__free_hook) (void *);
98 extern void *(*__malloc_hook) (unsigned long);
99
100 static void *check_malloc (unsigned long);
101
102 typedef void (*fun_ptr) ();
103
104 #ifdef SAVE_STACK
105 #define FREE_QUEUE_LIMIT 1000
106 #else
107 /* free_queue is not too useful without backtrace logging */
108 #define FREE_QUEUE_LIMIT 1
109 #endif
110 #define TRACE_LIMIT 20
111
112 typedef struct {
113 fun_ptr return_pc;
114 #ifdef SAVE_ARGS
115 void *arg[3];
116 #endif
117 } fun_entry;
118
119 typedef struct {
120 void *address;
121 unsigned long length;
122 #ifdef SAVE_STACK
123 fun_entry backtrace[TRACE_LIMIT];
124 #endif
125 } free_queue_entry;
126
127 free_queue_entry free_queue[FREE_QUEUE_LIMIT];
128
129 int current_free;
130
131 #ifdef SAVE_STACK
132 static void
133 init_frame (FRAME *fptr)
134 {
135 FRAME tmp_frame;
136
137 #ifdef sparc
138 /* Do the system trap ST_FLUSH_WINDOWS */
139 asm ("ta 3");
140 asm ("st %sp, [%i0+0]");
141 asm ("st %fp, [%i0+4]");
142 #endif
143
144 fptr->pc = (char *) init_frame;
145 tmp_frame = *fptr;
146
147 PREVIOUS_FRAME (tmp_frame);
148
149 *fptr = tmp_frame;
150 return;
151 }
152
153 #ifdef SAVE_ARGS
154 static void *
155 frame_arg (FRAME *fptr, int index)
156 {
157 return ((void *) FRAME_ARG(*fptr, index));
158 }
159 #endif
160
161 static void
162 save_backtrace (FRAME *current_frame_ptr, fun_entry *table)
163 {
164 int i = 0;
165 #ifdef SAVE_ARGS
166 int j;
167 #endif
168 FRAME current_frame = *current_frame_ptr;
169
170 /* Get up and out of free() */
171 PREVIOUS_FRAME (current_frame);
172
173 /* now do the basic loop adding data until there is no more */
174 while (PREVIOUS_FRAME (current_frame) && i < TRACE_LIMIT)
175 {
176 table[i].return_pc = (void (*)())FRAME_PC (current_frame);
177 #ifdef SAVE_ARGS
178 for (j = 0; j < 3; j++)
179 table[i].arg[j] = frame_arg (&current_frame, j);
180 #endif
181 i++;
182 }
183 memset (&table[i], 0, sizeof (fun_entry) * (TRACE_LIMIT - i));
184 }
185
186 free_queue_entry *
187 find_backtrace (void *ptr)
188 {
189 int i;
190
191 for (i = 0; i < FREE_QUEUE_LIMIT; i++)
192 if (free_queue[i].address == ptr)
193 return &free_queue[i];
194
195 return 0;
196 }
197 #endif /* SAVE_STACK */
198
199 int strict_free_check;
200
201 static void
202 check_free (void *ptr)
203 {
204 #ifdef SAVE_STACK
205 FRAME start_frame;
206
207 init_frame (&start_frame);
208 #endif
209
210 __free_hook = 0;
211 __malloc_hook = 0;
212 if (!pointer_table)
213 pointer_table = make_hashtable (max (100, FREE_QUEUE_LIMIT * 2));
214 if (ptr != 0)
215 {
216 long size;
217 #ifdef UNMAPPED_FREE
218 unsigned long rounded_up_size;
219 #endif
220
221 EMACS_INT present = (EMACS_INT) gethash (ptr, pointer_table, (void *) &size);
222
223 if (!present)
224 /* This can only happen if you try to free something that didn't
225 come from malloc */
226 if (strict_free_check)
227 abort ();
228 else
229 {
230 __free_hook = check_free;
231 __malloc_hook = check_malloc;
232 goto end;
233 }
234
235 if (size < 0)
236 /* This happens when you free twice */
237 if (strict_free_check)
238 abort ();
239 else
240 {
241 __free_hook = check_free;
242 __malloc_hook = check_malloc;
243 goto end;
244 }
245 puthash (ptr, (void *)-size, pointer_table);
246 #ifdef UNMAPPED_FREE
247 /* Round up size to an even number of pages. */
248 rounded_up_size = ROUND_UP_TO_PAGE (size);
249 /* Protect the pages freed from all access */
250 if (strict_free_check)
251 mprotect (ptr, rounded_up_size, PROT_NONE);
252 #else
253 /* Set every word in the block to 0xdeadbeef */
254 if (strict_free_check)
255 {
256 unsigned long long_length = (size + (sizeof (long) - 1))
257 / sizeof (long);
258 unsigned long i;
259
260 for (i = 0; i < long_length; i++)
261 ((unsigned long *) ptr)[i] = 0xdeadbeef;
262 }
263 #endif
264 free_queue[current_free].address = ptr;
265 free_queue[current_free].length = size;
266 #ifdef SAVE_STACK
267 save_backtrace (&start_frame,
268 free_queue[current_free].backtrace);
269 #endif
270 current_free++;
271 if (current_free >= FREE_QUEUE_LIMIT)
272 current_free = 0;
273 /* Really free this if there's something there */
274 {
275 void *old = free_queue[current_free].address;
276
277 if (old)
278 {
279 #ifdef UNMAPPED_FREE
280 unsigned long old_len = free_queue[current_free].length;
281
282 mprotect (old, old_len, PROT_READ | PROT_WRITE | PROT_EXEC);
283 #endif
284 free (old);
285 remhash (old, pointer_table);
286 }
287 }
288 }
289 __free_hook = check_free;
290 __malloc_hook = check_malloc;
291
292 end:
293 return;
294 }
295
296 static void *
297 check_malloc (unsigned long size)
298 {
299 unsigned long rounded_up_size;
300 void *result;
301
302 __free_hook = 0;
303 __malloc_hook = 0;
304 if (size == 0)
305 {
306 result = 0;
307 goto end;
308 }
309 #ifdef UNMAPPED_FREE
310 /* Round up to an even number of pages. */
311 rounded_up_size = ROUND_UP_TO_PAGE (size);
312 #else
313 rounded_up_size = size;
314 #endif
315 result = malloc (rounded_up_size);
316 if (!pointer_table)
317 pointer_table = make_hashtable (FREE_QUEUE_LIMIT * 2);
318 puthash (result, (void *)size, pointer_table);
319 __free_hook = check_free;
320 __malloc_hook = check_malloc;
321 end:
322 return result;
323 }
324
325 extern void *(*__realloc_hook) (void *, unsigned long);
326
327 #ifdef MIN
328 #undef MIN
329 #endif
330 #define MIN(A, B) ((A) < (B) ? (A) : (B))
331
332 /* Don't optimize realloc */
333
334 static void *
335 check_realloc (void * ptr, unsigned long size)
336 {
337 EMACS_INT present;
338 unsigned long old_size;
339 void *result = malloc (size);
340
341 present = (EMACS_INT) gethash (ptr, pointer_table, (void *) &old_size);
342 if (!present)
343 /* This can only happen by reallocing a pointer that didn't
344 come from malloc. */
345 abort ();
346 if (result == 0)
347 goto end;
348 memcpy (result, ptr, MIN (size, old_size));
349 free (ptr);
350 end:
351 return result;
352 }
353
354 void enable_strict_free_check (void);
355 void
356 enable_strict_free_check (void)
357 {
358 strict_free_check = 1;
359 }
360
361 void disable_strict_free_check (void);
362 void
363 disable_strict_free_check (void)
364 {
365 strict_free_check = 0;
366 }
367
368 /* Note: All BLOCK_INPUT stuff removed from this file because it's
369 completely gone in XEmacs */
370
371 static void *
372 block_input_malloc (unsigned long size);
373
374 static void
375 block_input_free (void* ptr)
376 {
377 __free_hook = 0;
378 __malloc_hook = 0;
379 free (ptr);
380 __free_hook = block_input_free;
381 __malloc_hook = block_input_malloc;
382 }
383
384 static void *
385 block_input_malloc (unsigned long size)
386 {
387 void* result;
388 __free_hook = 0;
389 __malloc_hook = 0;
390 result = malloc (size);
391 __free_hook = block_input_free;
392 __malloc_hook = block_input_malloc;
393 return result;
394 }
395
396
397 static void *
398 block_input_realloc (void* ptr, unsigned long size)
399 {
400 void* result;
401 __free_hook = 0;
402 __malloc_hook = 0;
403 __realloc_hook = 0;
404 result = realloc (ptr, size);
405 __free_hook = block_input_free;
406 __malloc_hook = block_input_malloc;
407 __realloc_hook = block_input_realloc;
408 return result;
409 }
410
411 #ifdef emacs
412
413 void disable_free_hook (void);
414 void
415 disable_free_hook (void)
416 {
417 __free_hook = block_input_free;
418 __malloc_hook = block_input_malloc;
419 __realloc_hook = block_input_realloc;
420 }
421
422 void
423 init_free_hook (void)
424 {
425 __free_hook = check_free;
426 __malloc_hook = check_malloc;
427 __realloc_hook = check_realloc;
428 current_free = 0;
429 strict_free_check = 1;
430 }
431
432 void really_free_one_entry (void *, int, int *);
433
434 DEFUN ("really-free", Freally_free, Sreally_free, 0, 1, "P" /*
435 Actually free the storage held by the free() debug hook.
436 A no-op if the free hook is disabled.
437 */ )
438 (arg)
439 Lisp_Object arg;
440 {
441 int count[2];
442 Lisp_Object lisp_count[2];
443
444 if ((__free_hook != 0) && pointer_table)
445 {
446 count[0] = 0;
447 count[1] = 0;
448 __free_hook = 0;
449 maphash ((maphash_function)really_free_one_entry,
450 pointer_table, (void *)&count);
451 memset (free_queue, 0, sizeof (free_queue_entry) * FREE_QUEUE_LIMIT);
452 current_free = 0;
453 __free_hook = check_free;
454 XSETINT (lisp_count[0], count[0]);
455 XSETINT (lisp_count[1], count[1]);
456 return Fcons (lisp_count[0], lisp_count[1]);
457 }
458 else
459 return Fcons (make_int (0), make_int (0));
460 }
461
462 void
463 really_free_one_entry (void *key, int contents, int *countp)
464 {
465 if (contents < 0)
466 {
467 free (key);
468 #ifdef UNMAPPED_FREE
469 mprotect (key, -contents, PROT_READ | PROT_WRITE | PROT_EXEC);
470 #endif
471 remhash (key, pointer_table);
472 countp[0]++;
473 countp[1] += -contents;
474 }
475 }
476
477 void
478 syms_of_free_hook (void)
479 {
480 defsubr (&Sreally_free);
481 }
482
483 #else
484 void (*__free_hook)() = check_free;
485 void *(*__malloc_hook)() = check_malloc;
486 void *(*__realloc_hook)() = check_realloc;
487 #endif
488
489
490 #if defined(DEBUG_INPUT_BLOCKING) || defined (DEBUG_GCPRO)
491
492 /* Note: There is no more input blocking in XEmacs */
493 typedef enum {
494 block_type, unblock_type, totally_type,
495 gcpro1_type, gcpro2_type, gcpro3_type, gcpro4_type, ungcpro_type
496 } blocktype;
497
498 struct block_input_history_struct {
499 char *file;
500 int line;
501 blocktype type;
502 int value;
503 #ifdef SAVE_STACK
504 fun_entry backtrace[TRACE_LIMIT];
505 #endif
506 };
507
508 typedef struct block_input_history_struct block_input_history;
509
510 #endif
511
512 #ifdef DEBUG_INPUT_BLOCKING
513
514 int blhistptr;
515
516 #define BLHISTLIMIT 1000
517
518 block_input_history blhist[BLHISTLIMIT];
519
520 note_block_input (char *file, int line)
521 {
522 note_block (file, line, block_type);
523 if (interrupt_input_blocked > 2) abort();
524 }
525
526 note_unblock_input (char* file, int line)
527 {
528 note_block (file, line, unblock_type);
529 }
530
531 note_totally_unblocked (char* file, int line)
532 {
533 note_block (file, line, totally_type);
534 }
535
536 note_block (char *file, int line, blocktype type)
537 {
538 #ifdef SAVE_STACK
539 FRAME start_frame;
540
541 init_frame (&start_frame);
542 #endif
543
544 blhist[blhistptr].file = file;
545 blhist[blhistptr].line = line;
546 blhist[blhistptr].type = type;
547 blhist[blhistptr].value = interrupt_input_blocked;
548
549 #ifdef SAVE_STACK
550 save_backtrace (&start_frame,
551 blhist[blhistptr].backtrace);
552 #endif
553
554 blhistptr++;
555 if (blhistptr >= BLHISTLIMIT)
556 blhistptr = 0;
557 }
558
559 #endif
560
561
562 #ifdef DEBUG_GCPRO
563
564 int gcprohistptr;
565 #define GCPROHISTLIMIT 1000
566 block_input_history gcprohist[GCPROHISTLIMIT];
567
568 static void
569 log_gcpro (char *file, int line, struct gcpro *value, blocktype type)
570 {
571 FRAME start_frame;
572
573 if (type == ungcpro_type)
574 {
575 if (value == gcprolist) goto OK;
576 if (! gcprolist) abort ();
577 if (value == gcprolist->next) goto OK;
578 if (! gcprolist->next) abort ();
579 if (value == gcprolist->next->next) goto OK;
580 if (! gcprolist->next->next) abort ();
581 if (value == gcprolist->next->next->next) goto OK;
582 abort ();
583 OK:;
584 }
585 #ifdef SAVE_STACK
586 init_frame (&start_frame);
587 #endif
588 gcprohist[gcprohistptr].file = file;
589 gcprohist[gcprohistptr].line = line;
590 gcprohist[gcprohistptr].type = type;
591 gcprohist[gcprohistptr].value = (int) value;
592 #ifdef SAVE_STACK
593 save_backtrace (&start_frame, gcprohist[gcprohistptr].backtrace);
594 #endif
595 gcprohistptr++;
596 if (gcprohistptr >= GCPROHISTLIMIT)
597 gcprohistptr = 0;
598 }
599
600 void
601 debug_gcpro1 (char *file, int line, struct gcpro *gcpro1, Lisp_Object *var)
602 {
603 gcpro1->next = gcprolist; gcpro1->var = var; gcpro1->nvars = 1;
604 gcprolist = gcpro1;
605 log_gcpro (file, line, gcpro1, gcpro1_type);
606 }
607
608 void
609 debug_gcpro2 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
610 Lisp_Object *var1, Lisp_Object *var2)
611 {
612 gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
613 gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
614 gcprolist = gcpro2;
615 log_gcpro (file, line, gcpro2, gcpro2_type);
616 }
617
618 void
619 debug_gcpro3 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
620 struct gcpro *gcpro3, Lisp_Object *var1, Lisp_Object *var2,
621 Lisp_Object *var3)
622 {
623 gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
624 gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
625 gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
626 gcprolist = gcpro3;
627 log_gcpro (file, line, gcpro3, gcpro3_type);
628 }
629
630 void
631 debug_gcpro4 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
632 struct gcpro *gcpro3, struct gcpro *gcpro4, Lisp_Object *var1,
633 Lisp_Object *var2, Lisp_Object *var3, Lisp_Object *var4)
634 {
635 log_gcpro (file, line, gcpro4, gcpro4_type);
636 gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
637 gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
638 gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
639 gcpro4->next = gcpro3; gcpro4->var = var4; gcpro4->nvars = 1;
640 gcprolist = gcpro4;
641 }
642
643 void
644 debug_gcpro5 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
645 struct gcpro *gcpro3, struct gcpro *gcpro4, struct gcpro *gcpro5,
646 Lisp_Object *var1, Lisp_Object *var2, Lisp_Object *var3,
647 Lisp_Object *var4, Lisp_Object *var5)
648 {
649 log_gcpro (file, line, gcpro5, gcpro5_type);
650 gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
651 gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
652 gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
653 gcpro4->next = gcpro3; gcpro4->var = var4; gcpro4->nvars = 1;
654 gcpro5->next = gcpro4; gcpro5->var = var5; gcpro5->nvars = 1;
655 gcprolist = gcpro5;
656 }
657
658 void
659 debug_ungcpro (char *file, int line, struct gcpro *gcpro1)
660 {
661 log_gcpro (file, line, gcpro1, ungcpro_type);
662 gcprolist = gcpro1->next;
663 }
664
665 void
666 show_gcprohist (void)
667 {
668 int i, j;
669 for (i = 0, j = gcprohistptr;
670 i < GCPROHISTLIMIT;
671 i++, j++)
672 {
673 if (j >= GCPROHISTLIMIT)
674 j = 0;
675 printf ("%3d %s %d %s 0x%x\n",
676 j, gcprohist[j].file, gcprohist[j].line,
677 (gcprohist[j].type == gcpro1_type ? "GCPRO1" :
678 gcprohist[j].type == gcpro2_type ? "GCPRO2" :
679 gcprohist[j].type == gcpro3_type ? "GCPRO3" :
680 gcprohist[j].type == gcpro4_type ? "GCPRO4" :
681 gcprohist[j].type == ungcpro_type ? "UNGCPRO" : "???"),
682 gcprohist[j].value);
683 }
684 fflush (stdout);
685 }
686
687 #endif