Mercurial > hg > xemacs-beta
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 (¤t_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 |