0
|
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
|
267
|
80 #if !defined(HAVE_LIBMCHECK)
|
0
|
81 #include <stdio.h>
|
|
82
|
|
83 #include "hash.h"
|
|
84
|
|
85 #ifdef UNMAPPED_FREE
|
|
86 #include <sys/mman.h>
|
|
87 #include <sys/param.h>
|
|
88 #define ROUND_UP_TO_PAGE(i) (((i) + PAGEOFFSET) & PAGEMASK)
|
|
89 #endif
|
|
90
|
|
91 #include <sys/types.h>
|
|
92
|
|
93 /* System function prototypes don't belong in C source files */
|
|
94 /* extern void free (void *); */
|
|
95
|
|
96 c_hashtable pointer_table;
|
|
97
|
|
98 extern void (*__free_hook) (void *);
|
|
99 extern void *(*__malloc_hook) (unsigned long);
|
|
100
|
|
101 static void *check_malloc (unsigned long);
|
|
102
|
|
103 typedef void (*fun_ptr) ();
|
|
104
|
|
105 #ifdef SAVE_STACK
|
|
106 #define FREE_QUEUE_LIMIT 1000
|
|
107 #else
|
|
108 /* free_queue is not too useful without backtrace logging */
|
|
109 #define FREE_QUEUE_LIMIT 1
|
|
110 #endif
|
|
111 #define TRACE_LIMIT 20
|
|
112
|
|
113 typedef struct {
|
|
114 fun_ptr return_pc;
|
|
115 #ifdef SAVE_ARGS
|
|
116 void *arg[3];
|
|
117 #endif
|
|
118 } fun_entry;
|
|
119
|
|
120 typedef struct {
|
|
121 void *address;
|
|
122 unsigned long length;
|
|
123 #ifdef SAVE_STACK
|
|
124 fun_entry backtrace[TRACE_LIMIT];
|
|
125 #endif
|
|
126 } free_queue_entry;
|
|
127
|
|
128 free_queue_entry free_queue[FREE_QUEUE_LIMIT];
|
|
129
|
|
130 int current_free;
|
|
131
|
|
132 #ifdef SAVE_STACK
|
|
133 static void
|
|
134 init_frame (FRAME *fptr)
|
|
135 {
|
|
136 FRAME tmp_frame;
|
|
137
|
|
138 #ifdef sparc
|
|
139 /* Do the system trap ST_FLUSH_WINDOWS */
|
|
140 asm ("ta 3");
|
|
141 asm ("st %sp, [%i0+0]");
|
|
142 asm ("st %fp, [%i0+4]");
|
|
143 #endif
|
|
144
|
|
145 fptr->pc = (char *) init_frame;
|
|
146 tmp_frame = *fptr;
|
|
147
|
|
148 PREVIOUS_FRAME (tmp_frame);
|
|
149
|
|
150 *fptr = tmp_frame;
|
|
151 return;
|
|
152 }
|
|
153
|
|
154 #ifdef SAVE_ARGS
|
|
155 static void *
|
|
156 frame_arg (FRAME *fptr, int index)
|
|
157 {
|
|
158 return ((void *) FRAME_ARG(*fptr, index));
|
|
159 }
|
|
160 #endif
|
|
161
|
|
162 static void
|
|
163 save_backtrace (FRAME *current_frame_ptr, fun_entry *table)
|
|
164 {
|
|
165 int i = 0;
|
|
166 #ifdef SAVE_ARGS
|
|
167 int j;
|
|
168 #endif
|
|
169 FRAME current_frame = *current_frame_ptr;
|
|
170
|
|
171 /* Get up and out of free() */
|
|
172 PREVIOUS_FRAME (current_frame);
|
|
173
|
|
174 /* now do the basic loop adding data until there is no more */
|
|
175 while (PREVIOUS_FRAME (current_frame) && i < TRACE_LIMIT)
|
|
176 {
|
|
177 table[i].return_pc = (void (*)())FRAME_PC (current_frame);
|
|
178 #ifdef SAVE_ARGS
|
|
179 for (j = 0; j < 3; j++)
|
|
180 table[i].arg[j] = frame_arg (¤t_frame, j);
|
|
181 #endif
|
|
182 i++;
|
|
183 }
|
|
184 memset (&table[i], 0, sizeof (fun_entry) * (TRACE_LIMIT - i));
|
|
185 }
|
|
186
|
|
187 free_queue_entry *
|
|
188 find_backtrace (void *ptr)
|
|
189 {
|
|
190 int i;
|
|
191
|
|
192 for (i = 0; i < FREE_QUEUE_LIMIT; i++)
|
|
193 if (free_queue[i].address == ptr)
|
|
194 return &free_queue[i];
|
|
195
|
|
196 return 0;
|
|
197 }
|
|
198 #endif /* SAVE_STACK */
|
|
199
|
|
200 int strict_free_check;
|
|
201
|
|
202 static void
|
|
203 check_free (void *ptr)
|
|
204 {
|
|
205 #ifdef SAVE_STACK
|
|
206 FRAME start_frame;
|
185
|
207
|
0
|
208 init_frame (&start_frame);
|
|
209 #endif
|
|
210
|
|
211 __free_hook = 0;
|
|
212 __malloc_hook = 0;
|
|
213 if (!pointer_table)
|
|
214 pointer_table = make_hashtable (max (100, FREE_QUEUE_LIMIT * 2));
|
|
215 if (ptr != 0)
|
|
216 {
|
|
217 long size;
|
|
218 #ifdef UNMAPPED_FREE
|
|
219 unsigned long rounded_up_size;
|
|
220 #endif
|
|
221
|
185
|
222 EMACS_INT present = (EMACS_INT) gethash (ptr, pointer_table,
|
272
|
223 (CONST void **) &size);
|
0
|
224
|
|
225 if (!present)
|
233
|
226 {
|
0
|
227 /* This can only happen if you try to free something that didn't
|
|
228 come from malloc */
|
269
|
229 #if !defined(__linux__)
|
267
|
230 /* I originally wrote: "There's really no need to drop core."
|
|
231 I have seen the error of my ways. -slb */
|
|
232 if (strict_free_check)
|
272
|
233 abort ();
|
267
|
234 #endif
|
|
235 printf("Freeing unmalloc'ed memory at %p\n", ptr);
|
|
236 __free_hook = check_free;
|
|
237 __malloc_hook = check_malloc;
|
|
238 goto end;
|
233
|
239 }
|
0
|
240
|
|
241 if (size < 0)
|
233
|
242 {
|
267
|
243 /* This happens when you free twice */
|
269
|
244 #if !defined(__linux__)
|
267
|
245 /* See above comment. */
|
|
246 if (strict_free_check)
|
272
|
247 abort ();
|
267
|
248 #endif
|
|
249 printf("Freeing %p twice\n", ptr);
|
|
250 __free_hook = check_free;
|
|
251 __malloc_hook = check_malloc;
|
|
252 goto end;
|
233
|
253 }
|
267
|
254
|
0
|
255 puthash (ptr, (void *)-size, pointer_table);
|
|
256 #ifdef UNMAPPED_FREE
|
|
257 /* Round up size to an even number of pages. */
|
|
258 rounded_up_size = ROUND_UP_TO_PAGE (size);
|
|
259 /* Protect the pages freed from all access */
|
|
260 if (strict_free_check)
|
|
261 mprotect (ptr, rounded_up_size, PROT_NONE);
|
|
262 #else
|
|
263 /* Set every word in the block to 0xdeadbeef */
|
|
264 if (strict_free_check)
|
|
265 {
|
|
266 unsigned long long_length = (size + (sizeof (long) - 1))
|
|
267 / sizeof (long);
|
|
268 unsigned long i;
|
|
269
|
|
270 for (i = 0; i < long_length; i++)
|
|
271 ((unsigned long *) ptr)[i] = 0xdeadbeef;
|
|
272 }
|
|
273 #endif
|
|
274 free_queue[current_free].address = ptr;
|
|
275 free_queue[current_free].length = size;
|
|
276 #ifdef SAVE_STACK
|
|
277 save_backtrace (&start_frame,
|
|
278 free_queue[current_free].backtrace);
|
|
279 #endif
|
|
280 current_free++;
|
|
281 if (current_free >= FREE_QUEUE_LIMIT)
|
|
282 current_free = 0;
|
|
283 /* Really free this if there's something there */
|
|
284 {
|
|
285 void *old = free_queue[current_free].address;
|
|
286
|
|
287 if (old)
|
|
288 {
|
|
289 #ifdef UNMAPPED_FREE
|
|
290 unsigned long old_len = free_queue[current_free].length;
|
|
291
|
|
292 mprotect (old, old_len, PROT_READ | PROT_WRITE | PROT_EXEC);
|
|
293 #endif
|
|
294 free (old);
|
|
295 remhash (old, pointer_table);
|
|
296 }
|
|
297 }
|
|
298 }
|
|
299 __free_hook = check_free;
|
|
300 __malloc_hook = check_malloc;
|
|
301
|
|
302 end:
|
|
303 return;
|
185
|
304 }
|
0
|
305
|
|
306 static void *
|
|
307 check_malloc (unsigned long size)
|
|
308 {
|
|
309 unsigned long rounded_up_size;
|
|
310 void *result;
|
|
311
|
|
312 __free_hook = 0;
|
|
313 __malloc_hook = 0;
|
|
314 if (size == 0)
|
|
315 {
|
|
316 result = 0;
|
|
317 goto end;
|
|
318 }
|
|
319 #ifdef UNMAPPED_FREE
|
|
320 /* Round up to an even number of pages. */
|
|
321 rounded_up_size = ROUND_UP_TO_PAGE (size);
|
|
322 #else
|
|
323 rounded_up_size = size;
|
|
324 #endif
|
|
325 result = malloc (rounded_up_size);
|
|
326 if (!pointer_table)
|
|
327 pointer_table = make_hashtable (FREE_QUEUE_LIMIT * 2);
|
|
328 puthash (result, (void *)size, pointer_table);
|
|
329 __free_hook = check_free;
|
|
330 __malloc_hook = check_malloc;
|
|
331 end:
|
|
332 return result;
|
|
333 }
|
|
334
|
|
335 extern void *(*__realloc_hook) (void *, unsigned long);
|
|
336
|
|
337 #ifdef MIN
|
|
338 #undef MIN
|
|
339 #endif
|
|
340 #define MIN(A, B) ((A) < (B) ? (A) : (B))
|
|
341
|
|
342 /* Don't optimize realloc */
|
|
343
|
|
344 static void *
|
|
345 check_realloc (void * ptr, unsigned long size)
|
|
346 {
|
|
347 EMACS_INT present;
|
|
348 unsigned long old_size;
|
|
349 void *result = malloc (size);
|
185
|
350
|
269
|
351 if (!ptr) return result;
|
272
|
352 present = (EMACS_INT) gethash (ptr, pointer_table, (CONST void **) &old_size);
|
0
|
353 if (!present)
|
267
|
354 {
|
0
|
355 /* This can only happen by reallocing a pointer that didn't
|
|
356 come from malloc. */
|
269
|
357 #if !defined(__linux__)
|
267
|
358 /* see comment in check_free(). */
|
|
359 abort ();
|
|
360 #endif
|
|
361 printf("Realloc'ing unmalloc'ed pointer at %p\n", ptr);
|
|
362 }
|
|
363
|
0
|
364 if (result == 0)
|
|
365 goto end;
|
|
366 memcpy (result, ptr, MIN (size, old_size));
|
|
367 free (ptr);
|
|
368 end:
|
|
369 return result;
|
|
370 }
|
|
371
|
185
|
372 void enable_strict_free_check (void);
|
0
|
373 void
|
|
374 enable_strict_free_check (void)
|
|
375 {
|
|
376 strict_free_check = 1;
|
|
377 }
|
|
378
|
185
|
379 void disable_strict_free_check (void);
|
0
|
380 void
|
|
381 disable_strict_free_check (void)
|
|
382 {
|
|
383 strict_free_check = 0;
|
|
384 }
|
|
385
|
|
386 /* Note: All BLOCK_INPUT stuff removed from this file because it's
|
|
387 completely gone in XEmacs */
|
|
388
|
|
389 static void *
|
|
390 block_input_malloc (unsigned long size);
|
|
391
|
|
392 static void
|
|
393 block_input_free (void* ptr)
|
|
394 {
|
|
395 __free_hook = 0;
|
|
396 __malloc_hook = 0;
|
|
397 free (ptr);
|
|
398 __free_hook = block_input_free;
|
|
399 __malloc_hook = block_input_malloc;
|
|
400 }
|
|
401
|
|
402 static void *
|
|
403 block_input_malloc (unsigned long size)
|
|
404 {
|
|
405 void* result;
|
|
406 __free_hook = 0;
|
|
407 __malloc_hook = 0;
|
|
408 result = malloc (size);
|
|
409 __free_hook = block_input_free;
|
|
410 __malloc_hook = block_input_malloc;
|
|
411 return result;
|
|
412 }
|
|
413
|
|
414
|
|
415 static void *
|
|
416 block_input_realloc (void* ptr, unsigned long size)
|
|
417 {
|
|
418 void* result;
|
|
419 __free_hook = 0;
|
|
420 __malloc_hook = 0;
|
|
421 __realloc_hook = 0;
|
|
422 result = realloc (ptr, size);
|
|
423 __free_hook = block_input_free;
|
|
424 __malloc_hook = block_input_malloc;
|
|
425 __realloc_hook = block_input_realloc;
|
|
426 return result;
|
|
427 }
|
|
428
|
|
429 #ifdef emacs
|
|
430
|
|
431 void disable_free_hook (void);
|
|
432 void
|
|
433 disable_free_hook (void)
|
|
434 {
|
|
435 __free_hook = block_input_free;
|
|
436 __malloc_hook = block_input_malloc;
|
|
437 __realloc_hook = block_input_realloc;
|
|
438 }
|
|
439
|
|
440 void
|
|
441 init_free_hook (void)
|
|
442 {
|
|
443 __free_hook = check_free;
|
|
444 __malloc_hook = check_malloc;
|
|
445 __realloc_hook = check_realloc;
|
|
446 current_free = 0;
|
|
447 strict_free_check = 1;
|
|
448 }
|
|
449
|
|
450 void really_free_one_entry (void *, int, int *);
|
|
451
|
20
|
452 DEFUN ("really-free", Freally_free, 0, 1, "P", /*
|
0
|
453 Actually free the storage held by the free() debug hook.
|
|
454 A no-op if the free hook is disabled.
|
20
|
455 */
|
|
456 (arg))
|
0
|
457 {
|
|
458 int count[2];
|
|
459 Lisp_Object lisp_count[2];
|
|
460
|
|
461 if ((__free_hook != 0) && pointer_table)
|
|
462 {
|
|
463 count[0] = 0;
|
|
464 count[1] = 0;
|
|
465 __free_hook = 0;
|
185
|
466 maphash ((maphash_function)really_free_one_entry,
|
0
|
467 pointer_table, (void *)&count);
|
|
468 memset (free_queue, 0, sizeof (free_queue_entry) * FREE_QUEUE_LIMIT);
|
|
469 current_free = 0;
|
|
470 __free_hook = check_free;
|
|
471 XSETINT (lisp_count[0], count[0]);
|
|
472 XSETINT (lisp_count[1], count[1]);
|
|
473 return Fcons (lisp_count[0], lisp_count[1]);
|
|
474 }
|
|
475 else
|
|
476 return Fcons (make_int (0), make_int (0));
|
|
477 }
|
|
478
|
|
479 void
|
|
480 really_free_one_entry (void *key, int contents, int *countp)
|
|
481 {
|
|
482 if (contents < 0)
|
|
483 {
|
|
484 free (key);
|
|
485 #ifdef UNMAPPED_FREE
|
|
486 mprotect (key, -contents, PROT_READ | PROT_WRITE | PROT_EXEC);
|
|
487 #endif
|
|
488 remhash (key, pointer_table);
|
|
489 countp[0]++;
|
|
490 countp[1] += -contents;
|
|
491 }
|
|
492 }
|
|
493
|
|
494 void
|
|
495 syms_of_free_hook (void)
|
|
496 {
|
20
|
497 DEFSUBR (Freally_free);
|
0
|
498 }
|
|
499
|
|
500 #else
|
|
501 void (*__free_hook)() = check_free;
|
|
502 void *(*__malloc_hook)() = check_malloc;
|
|
503 void *(*__realloc_hook)() = check_realloc;
|
|
504 #endif
|
|
505
|
267
|
506 #endif /* !defined(HAVE_LIBMCHECK) */
|
0
|
507
|
|
508 #if defined(DEBUG_INPUT_BLOCKING) || defined (DEBUG_GCPRO)
|
|
509
|
|
510 /* Note: There is no more input blocking in XEmacs */
|
|
511 typedef enum {
|
|
512 block_type, unblock_type, totally_type,
|
|
513 gcpro1_type, gcpro2_type, gcpro3_type, gcpro4_type, ungcpro_type
|
|
514 } blocktype;
|
|
515
|
185
|
516 struct block_input_history_struct
|
|
517 {
|
0
|
518 char *file;
|
|
519 int line;
|
|
520 blocktype type;
|
|
521 int value;
|
|
522 #ifdef SAVE_STACK
|
|
523 fun_entry backtrace[TRACE_LIMIT];
|
|
524 #endif
|
|
525 };
|
|
526
|
|
527 typedef struct block_input_history_struct block_input_history;
|
|
528
|
|
529 #endif
|
|
530
|
|
531 #ifdef DEBUG_INPUT_BLOCKING
|
|
532
|
|
533 int blhistptr;
|
|
534
|
|
535 #define BLHISTLIMIT 1000
|
|
536
|
|
537 block_input_history blhist[BLHISTLIMIT];
|
|
538
|
|
539 note_block_input (char *file, int line)
|
|
540 {
|
|
541 note_block (file, line, block_type);
|
|
542 if (interrupt_input_blocked > 2) abort();
|
|
543 }
|
|
544
|
|
545 note_unblock_input (char* file, int line)
|
|
546 {
|
|
547 note_block (file, line, unblock_type);
|
|
548 }
|
|
549
|
|
550 note_totally_unblocked (char* file, int line)
|
|
551 {
|
|
552 note_block (file, line, totally_type);
|
|
553 }
|
|
554
|
|
555 note_block (char *file, int line, blocktype type)
|
|
556 {
|
|
557 #ifdef SAVE_STACK
|
|
558 FRAME start_frame;
|
|
559
|
|
560 init_frame (&start_frame);
|
|
561 #endif
|
185
|
562
|
0
|
563 blhist[blhistptr].file = file;
|
|
564 blhist[blhistptr].line = line;
|
|
565 blhist[blhistptr].type = type;
|
|
566 blhist[blhistptr].value = interrupt_input_blocked;
|
|
567
|
|
568 #ifdef SAVE_STACK
|
|
569 save_backtrace (&start_frame,
|
|
570 blhist[blhistptr].backtrace);
|
|
571 #endif
|
|
572
|
|
573 blhistptr++;
|
|
574 if (blhistptr >= BLHISTLIMIT)
|
|
575 blhistptr = 0;
|
|
576 }
|
|
577
|
|
578 #endif
|
|
579
|
|
580
|
|
581 #ifdef DEBUG_GCPRO
|
|
582
|
|
583 int gcprohistptr;
|
|
584 #define GCPROHISTLIMIT 1000
|
|
585 block_input_history gcprohist[GCPROHISTLIMIT];
|
|
586
|
|
587 static void
|
|
588 log_gcpro (char *file, int line, struct gcpro *value, blocktype type)
|
|
589 {
|
|
590 FRAME start_frame;
|
|
591
|
|
592 if (type == ungcpro_type)
|
|
593 {
|
|
594 if (value == gcprolist) goto OK;
|
|
595 if (! gcprolist) abort ();
|
|
596 if (value == gcprolist->next) goto OK;
|
|
597 if (! gcprolist->next) abort ();
|
|
598 if (value == gcprolist->next->next) goto OK;
|
|
599 if (! gcprolist->next->next) abort ();
|
|
600 if (value == gcprolist->next->next->next) goto OK;
|
|
601 abort ();
|
|
602 OK:;
|
|
603 }
|
|
604 #ifdef SAVE_STACK
|
|
605 init_frame (&start_frame);
|
|
606 #endif
|
|
607 gcprohist[gcprohistptr].file = file;
|
|
608 gcprohist[gcprohistptr].line = line;
|
|
609 gcprohist[gcprohistptr].type = type;
|
|
610 gcprohist[gcprohistptr].value = (int) value;
|
|
611 #ifdef SAVE_STACK
|
|
612 save_backtrace (&start_frame, gcprohist[gcprohistptr].backtrace);
|
|
613 #endif
|
|
614 gcprohistptr++;
|
|
615 if (gcprohistptr >= GCPROHISTLIMIT)
|
|
616 gcprohistptr = 0;
|
|
617 }
|
|
618
|
|
619 void
|
|
620 debug_gcpro1 (char *file, int line, struct gcpro *gcpro1, Lisp_Object *var)
|
|
621 {
|
|
622 gcpro1->next = gcprolist; gcpro1->var = var; gcpro1->nvars = 1;
|
|
623 gcprolist = gcpro1;
|
|
624 log_gcpro (file, line, gcpro1, gcpro1_type);
|
|
625 }
|
|
626
|
|
627 void
|
|
628 debug_gcpro2 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
|
|
629 Lisp_Object *var1, Lisp_Object *var2)
|
|
630 {
|
|
631 gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
|
|
632 gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
|
|
633 gcprolist = gcpro2;
|
|
634 log_gcpro (file, line, gcpro2, gcpro2_type);
|
|
635 }
|
|
636
|
|
637 void
|
|
638 debug_gcpro3 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
|
|
639 struct gcpro *gcpro3, Lisp_Object *var1, Lisp_Object *var2,
|
|
640 Lisp_Object *var3)
|
|
641 {
|
|
642 gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
|
|
643 gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
|
|
644 gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
|
|
645 gcprolist = gcpro3;
|
|
646 log_gcpro (file, line, gcpro3, gcpro3_type);
|
|
647 }
|
|
648
|
|
649 void
|
|
650 debug_gcpro4 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
|
|
651 struct gcpro *gcpro3, struct gcpro *gcpro4, Lisp_Object *var1,
|
|
652 Lisp_Object *var2, Lisp_Object *var3, Lisp_Object *var4)
|
|
653 {
|
|
654 log_gcpro (file, line, gcpro4, gcpro4_type);
|
|
655 gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
|
|
656 gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
|
|
657 gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
|
|
658 gcpro4->next = gcpro3; gcpro4->var = var4; gcpro4->nvars = 1;
|
|
659 gcprolist = gcpro4;
|
|
660 }
|
|
661
|
|
662 void
|
|
663 debug_gcpro5 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
|
|
664 struct gcpro *gcpro3, struct gcpro *gcpro4, struct gcpro *gcpro5,
|
|
665 Lisp_Object *var1, Lisp_Object *var2, Lisp_Object *var3,
|
|
666 Lisp_Object *var4, Lisp_Object *var5)
|
|
667 {
|
|
668 log_gcpro (file, line, gcpro5, gcpro5_type);
|
|
669 gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
|
|
670 gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
|
|
671 gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
|
|
672 gcpro4->next = gcpro3; gcpro4->var = var4; gcpro4->nvars = 1;
|
|
673 gcpro5->next = gcpro4; gcpro5->var = var5; gcpro5->nvars = 1;
|
|
674 gcprolist = gcpro5;
|
|
675 }
|
|
676
|
|
677 void
|
|
678 debug_ungcpro (char *file, int line, struct gcpro *gcpro1)
|
|
679 {
|
|
680 log_gcpro (file, line, gcpro1, ungcpro_type);
|
|
681 gcprolist = gcpro1->next;
|
|
682 }
|
|
683
|
|
684 void
|
|
685 show_gcprohist (void)
|
|
686 {
|
|
687 int i, j;
|
|
688 for (i = 0, j = gcprohistptr;
|
|
689 i < GCPROHISTLIMIT;
|
|
690 i++, j++)
|
|
691 {
|
|
692 if (j >= GCPROHISTLIMIT)
|
|
693 j = 0;
|
|
694 printf ("%3d %s %d %s 0x%x\n",
|
|
695 j, gcprohist[j].file, gcprohist[j].line,
|
|
696 (gcprohist[j].type == gcpro1_type ? "GCPRO1" :
|
|
697 gcprohist[j].type == gcpro2_type ? "GCPRO2" :
|
|
698 gcprohist[j].type == gcpro3_type ? "GCPRO3" :
|
|
699 gcprohist[j].type == gcpro4_type ? "GCPRO4" :
|
|
700 gcprohist[j].type == ungcpro_type ? "UNGCPRO" : "???"),
|
|
701 gcprohist[j].value);
|
|
702 }
|
|
703 fflush (stdout);
|
|
704 }
|
|
705
|
|
706 #endif
|