xemacs-beta / src / free-hook.c

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
/* This file is part of XEmacs.

XEmacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

XEmacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with XEmacs; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

/* Synched up with: Not in FSF. */

/* Debugging hooks for malloc. */

/* These hooks work with gmalloc to catch allocation errors.
   In particular, the following is trapped:

   * Freeing the same pointer twice.
   * Trying to free a pointer not returned by malloc.
   * Trying to realloc a pointer not returned by malloc.

   In addition, every word of every block freed is set to
   0xdeadbeef.  This causes many uses of freed storage to be
   trapped or recognized.

   When you use this, the storage used by the last FREE_QUEUE_LIMIT
   calls to free() is not recycled.  When you call free for the Nth
   time, the (N - FREE_QUEUE_LIMIT)'th block is actually recycled.

   For these last FREE_QUEUE_LIMIT calls to free() a backtrace is
   saved showing where it was called from.  The function
   find_backtrace() is provided here to be called from GDB with a
   pointer (such as would be passed to free()) as argument, e.g.
   (gdb) p/a *find_backtrace (0x234000).  If SAVE_ARGS is defined,
   the first three arguments to each function are saved as well as the
   return addresses.

   If UNMAPPED_FREE is defined, instead of setting every word of freed
   storage to 0xdeadbeef, every call to malloc goes on its own page(s).
   When free() is called, the block is read and write protected.  This
   is very useful when debugging, since it usually generates a bus error
   when the deadbeef hack might only cause some garbage to be printed.
   However, this is too slow for everyday use, since it takes an enormous
   number of pages.


   Some other features that would be useful are:

   * Checking for storage leaks.
     This could be done by a GC-like facility that would scan the data
     segment looking for pointers to allocated storage and tell you
     about those that are no longer referenced.  This could be invoked
     at any time.  Another possibility is to report on what allocated
     storage is still in use when the process is exited.  Typically
     there will be a large amount, so this might not be very useful.
*/

#if defined (EMACS_BTL) && defined (sun4) && !defined (__lucid)
/* currently only works in this configuration */
# define SAVE_STACK
#endif

#ifdef emacs
#ifdef SAVE_STACK
#include "cadillac-btl.h"
#endif
#include <config.h>
#include "lisp.h"
#else
void *malloc (unsigned long);
#endif

#include <stdio.h>

#include "hash.h"

#ifdef UNMAPPED_FREE
#include <sys/mman.h>
#include <sys/param.h>
#define ROUND_UP_TO_PAGE(i) (((i) + PAGEOFFSET) & PAGEMASK)
#endif

#include <sys/types.h>

/* System function prototypes don't belong in C source files */
/* extern void free (void *); */

c_hashtable pointer_table;

extern void (*__free_hook) (void *);
extern void *(*__malloc_hook) (unsigned long);

static void *check_malloc (unsigned long);

typedef void (*fun_ptr) ();

#ifdef SAVE_STACK
#define FREE_QUEUE_LIMIT 1000
#else
/* free_queue is not too useful without backtrace logging */
#define FREE_QUEUE_LIMIT 1
#endif
#define TRACE_LIMIT 20

typedef struct {
  fun_ptr return_pc;
#ifdef SAVE_ARGS
  void *arg[3];
#endif
} fun_entry;

typedef struct {
  void *address;
  unsigned long length;
#ifdef SAVE_STACK
  fun_entry backtrace[TRACE_LIMIT];
#endif
} free_queue_entry;

free_queue_entry free_queue[FREE_QUEUE_LIMIT];

int current_free;

#ifdef SAVE_STACK
static void
init_frame (FRAME *fptr)
{
  FRAME tmp_frame;

#ifdef sparc
  /* Do the system trap ST_FLUSH_WINDOWS */
  asm ("ta 3");
  asm ("st %sp, [%i0+0]");
  asm ("st %fp, [%i0+4]");
#endif

  fptr->pc = (char *) init_frame;
  tmp_frame = *fptr;

  PREVIOUS_FRAME (tmp_frame);

  *fptr = tmp_frame;
  return;
}

#ifdef SAVE_ARGS
static void *
frame_arg (FRAME *fptr, int index)
{
  return ((void *) FRAME_ARG(*fptr, index));
}
#endif

static void
save_backtrace (FRAME *current_frame_ptr, fun_entry *table)
{
  int i = 0;
#ifdef SAVE_ARGS
  int j;
#endif
  FRAME current_frame = *current_frame_ptr;

  /* Get up and out of free() */
  PREVIOUS_FRAME (current_frame);

  /* now do the basic loop adding data until there is no more */
  while (PREVIOUS_FRAME (current_frame) && i < TRACE_LIMIT)
    {
      table[i].return_pc = (void (*)())FRAME_PC (current_frame);
#ifdef SAVE_ARGS
      for (j = 0; j < 3; j++)
	table[i].arg[j] = frame_arg (&current_frame, j);
#endif
      i++;
    }
  memset (&table[i], 0, sizeof (fun_entry) * (TRACE_LIMIT - i));
}

free_queue_entry *
find_backtrace (void *ptr)
{
  int i;

  for (i = 0; i < FREE_QUEUE_LIMIT; i++)
    if (free_queue[i].address == ptr)
      return &free_queue[i];

  return 0;
}
#endif /* SAVE_STACK */

int strict_free_check;

static void
check_free (void *ptr)
{
#ifdef SAVE_STACK
  FRAME start_frame;
  
  init_frame (&start_frame);
#endif

  __free_hook = 0;
  __malloc_hook = 0;
  if (!pointer_table)
    pointer_table = make_hashtable (max (100, FREE_QUEUE_LIMIT * 2));
  if (ptr != 0)
    {
      long size;
#ifdef UNMAPPED_FREE
      unsigned long rounded_up_size;
#endif

      EMACS_INT present = (EMACS_INT) gethash (ptr, pointer_table, (void *) &size);

      if (!present)
	/* This can only happen if you try to free something that didn't
	   come from malloc */
	if (strict_free_check)
	  abort ();
	else
	  {
	    __free_hook = check_free;
	    __malloc_hook = check_malloc;
	    goto end;
	  }

      if (size < 0)
	/* This happens when you free twice */
	if (strict_free_check)
	  abort ();
	else
	  {
	    __free_hook = check_free;
	    __malloc_hook = check_malloc;
	    goto end;
	  }
      puthash (ptr, (void *)-size, pointer_table);
#ifdef UNMAPPED_FREE
      /* Round up size to an even number of pages. */
      rounded_up_size = ROUND_UP_TO_PAGE (size);
      /* Protect the pages freed from all access */
      if (strict_free_check)
	mprotect (ptr, rounded_up_size, PROT_NONE);
#else
      /* Set every word in the block to 0xdeadbeef */
      if (strict_free_check)
	{
	  unsigned long long_length = (size + (sizeof (long) - 1))
	    / sizeof (long);
	  unsigned long i;

	  for (i = 0; i < long_length; i++)
	    ((unsigned long *) ptr)[i] = 0xdeadbeef;
	}
#endif
      free_queue[current_free].address = ptr;
      free_queue[current_free].length = size;
#ifdef SAVE_STACK
      save_backtrace (&start_frame,
		      free_queue[current_free].backtrace);
#endif
      current_free++;
      if (current_free >= FREE_QUEUE_LIMIT)
	current_free = 0;
      /* Really free this if there's something there */
      {
	void *old = free_queue[current_free].address;

	if (old)
	  {
#ifdef UNMAPPED_FREE
	    unsigned long old_len = free_queue[current_free].length;

	    mprotect (old, old_len,  PROT_READ | PROT_WRITE | PROT_EXEC);
#endif
	    free (old);
	    remhash (old, pointer_table);
	  }
      }
    }
  __free_hook = check_free;
  __malloc_hook = check_malloc;

 end:
  return;
}  

static void *
check_malloc (unsigned long size)
{
  unsigned long rounded_up_size;
  void *result;

  __free_hook = 0;
  __malloc_hook = 0;
  if (size == 0)
    {
      result = 0;
      goto end;
    }
#ifdef UNMAPPED_FREE
  /* Round up to an even number of pages. */
  rounded_up_size = ROUND_UP_TO_PAGE (size);
#else
  rounded_up_size = size;
#endif
  result = malloc (rounded_up_size);
  if (!pointer_table)
    pointer_table = make_hashtable (FREE_QUEUE_LIMIT * 2);
  puthash (result, (void *)size, pointer_table);
  __free_hook = check_free;
  __malloc_hook = check_malloc;
 end:
  return result;
}

extern void *(*__realloc_hook) (void *, unsigned long);

#ifdef MIN
#undef MIN
#endif
#define MIN(A, B) ((A) < (B) ? (A) : (B))

/* Don't optimize realloc */

static void *
check_realloc (void * ptr, unsigned long size)
{
  EMACS_INT present;
  unsigned long old_size;
  void *result = malloc (size);
  
  present = (EMACS_INT) gethash (ptr, pointer_table, (void *) &old_size);
  if (!present)
    /* This can only happen by reallocing a pointer that didn't
       come from malloc. */
    abort ();
  if (result == 0)
    goto end;
  memcpy (result, ptr, MIN (size, old_size));
  free (ptr);
 end:
  return result;
}

void enable_strict_free_check (void);  
void
enable_strict_free_check (void)
{
  strict_free_check = 1;
}

void disable_strict_free_check (void);  
void
disable_strict_free_check (void)
{
  strict_free_check = 0;
}

/* Note: All BLOCK_INPUT stuff removed from this file because it's
   completely gone in XEmacs */

static void *
block_input_malloc (unsigned long size);

static void
block_input_free (void* ptr)
{
  __free_hook = 0;
  __malloc_hook = 0;
  free (ptr);
  __free_hook = block_input_free;
  __malloc_hook = block_input_malloc;
}

static void *
block_input_malloc (unsigned long size)
{
  void* result;
  __free_hook = 0;
  __malloc_hook = 0;
  result = malloc (size);
  __free_hook = block_input_free;
  __malloc_hook = block_input_malloc;
  return result;
}


static void *
block_input_realloc (void* ptr, unsigned long size)
{
  void* result;
  __free_hook = 0;
  __malloc_hook = 0;
  __realloc_hook = 0;
  result = realloc (ptr, size);
  __free_hook = block_input_free;
  __malloc_hook = block_input_malloc;
  __realloc_hook = block_input_realloc;
  return result;
}

#ifdef emacs

void disable_free_hook (void);
void
disable_free_hook (void)
{
  __free_hook = block_input_free;
  __malloc_hook = block_input_malloc;
  __realloc_hook = block_input_realloc;
}

void
init_free_hook (void)
{
  __free_hook = check_free;
  __malloc_hook = check_malloc;
  __realloc_hook = check_realloc;
  current_free = 0;
  strict_free_check = 1;
}

void really_free_one_entry (void *, int, int *);

DEFUN ("really-free", Freally_free, 0, 1, "P", /*
Actually free the storage held by the free() debug hook.
A no-op if the free hook is disabled.
*/
       (arg))
{
  int count[2];
  Lisp_Object lisp_count[2];

  if ((__free_hook != 0) && pointer_table)
    {
      count[0] = 0;
      count[1] = 0;
      __free_hook = 0;
      maphash ((maphash_function)really_free_one_entry, 
               pointer_table, (void *)&count);
      memset (free_queue, 0, sizeof (free_queue_entry) * FREE_QUEUE_LIMIT);
      current_free = 0;
      __free_hook = check_free;
      XSETINT (lisp_count[0], count[0]);
      XSETINT (lisp_count[1], count[1]);
      return Fcons (lisp_count[0], lisp_count[1]);
    }
  else
    return Fcons (make_int (0), make_int (0));
}

void
really_free_one_entry (void *key, int contents, int *countp)
{
  if (contents < 0)
    {
      free (key);
#ifdef UNMAPPED_FREE
      mprotect (key, -contents, PROT_READ | PROT_WRITE | PROT_EXEC);
#endif
      remhash (key, pointer_table);
      countp[0]++;
      countp[1] += -contents;
    }
}

void
syms_of_free_hook (void)
{
  DEFSUBR (Freally_free);
}

#else
void (*__free_hook)() = check_free;
void *(*__malloc_hook)() = check_malloc;
void *(*__realloc_hook)() = check_realloc;
#endif


#if defined(DEBUG_INPUT_BLOCKING) || defined (DEBUG_GCPRO)

/* Note: There is no more input blocking in XEmacs */
typedef enum {
  block_type, unblock_type, totally_type,
  gcpro1_type, gcpro2_type, gcpro3_type, gcpro4_type, ungcpro_type
} blocktype;

struct block_input_history_struct {
  char *file;
  int line;
  blocktype type;
  int value;
#ifdef SAVE_STACK
  fun_entry backtrace[TRACE_LIMIT];
#endif
};

typedef struct block_input_history_struct block_input_history;

#endif

#ifdef DEBUG_INPUT_BLOCKING

int blhistptr;

#define BLHISTLIMIT 1000

block_input_history blhist[BLHISTLIMIT];

note_block_input (char *file, int line)
{
  note_block (file, line, block_type);
  if (interrupt_input_blocked > 2) abort();
}

note_unblock_input (char* file, int line)
{
  note_block (file, line, unblock_type);
}

note_totally_unblocked (char* file, int line)
{
  note_block (file, line, totally_type);
}

note_block (char *file, int line, blocktype type)
{
#ifdef SAVE_STACK
  FRAME start_frame;

  init_frame (&start_frame);
#endif
  
  blhist[blhistptr].file = file;
  blhist[blhistptr].line = line;
  blhist[blhistptr].type = type;
  blhist[blhistptr].value = interrupt_input_blocked;

#ifdef SAVE_STACK
  save_backtrace (&start_frame,
		  blhist[blhistptr].backtrace);
#endif

  blhistptr++;
  if (blhistptr >= BLHISTLIMIT)
    blhistptr = 0;
}

#endif


#ifdef DEBUG_GCPRO

int gcprohistptr;
#define GCPROHISTLIMIT 1000
block_input_history gcprohist[GCPROHISTLIMIT];

static void
log_gcpro (char *file, int line, struct gcpro *value, blocktype type)
{
  FRAME start_frame;

  if (type == ungcpro_type)
    {
      if (value == gcprolist) goto OK;
      if (! gcprolist) abort ();
      if (value == gcprolist->next) goto OK;
      if (! gcprolist->next) abort ();
      if (value == gcprolist->next->next) goto OK;
      if (! gcprolist->next->next) abort ();
      if (value == gcprolist->next->next->next) goto OK;
      abort ();
    OK:;
    }
#ifdef SAVE_STACK
  init_frame (&start_frame);
#endif
  gcprohist[gcprohistptr].file = file;
  gcprohist[gcprohistptr].line = line;
  gcprohist[gcprohistptr].type = type;
  gcprohist[gcprohistptr].value = (int) value;
#ifdef SAVE_STACK
  save_backtrace (&start_frame, gcprohist[gcprohistptr].backtrace);
#endif
  gcprohistptr++;
  if (gcprohistptr >= GCPROHISTLIMIT)
    gcprohistptr = 0;
}

void
debug_gcpro1 (char *file, int line, struct gcpro *gcpro1, Lisp_Object *var)
{
  gcpro1->next = gcprolist; gcpro1->var = var; gcpro1->nvars = 1;
  gcprolist = gcpro1;
  log_gcpro (file, line, gcpro1, gcpro1_type);
}

void
debug_gcpro2 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
	      Lisp_Object *var1, Lisp_Object *var2)
{
  gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
  gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
  gcprolist = gcpro2;
  log_gcpro (file, line, gcpro2, gcpro2_type);
}

void
debug_gcpro3 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
	      struct gcpro *gcpro3, Lisp_Object *var1, Lisp_Object *var2,
	      Lisp_Object *var3)
{
  gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
  gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
  gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
  gcprolist = gcpro3;
  log_gcpro (file, line, gcpro3, gcpro3_type);
}

void
debug_gcpro4 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
	      struct gcpro *gcpro3, struct gcpro *gcpro4, Lisp_Object *var1,
	      Lisp_Object *var2, Lisp_Object *var3, Lisp_Object *var4)
{
  log_gcpro (file, line, gcpro4, gcpro4_type);
  gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
  gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
  gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
  gcpro4->next = gcpro3; gcpro4->var = var4; gcpro4->nvars = 1;
  gcprolist = gcpro4;
}

void
debug_gcpro5 (char *file, int line, struct gcpro *gcpro1, struct gcpro *gcpro2,
	      struct gcpro *gcpro3, struct gcpro *gcpro4, struct gcpro *gcpro5,
	      Lisp_Object *var1, Lisp_Object *var2, Lisp_Object *var3,
	      Lisp_Object *var4, Lisp_Object *var5)
{
  log_gcpro (file, line, gcpro5, gcpro5_type);
  gcpro1->next = gcprolist; gcpro1->var = var1; gcpro1->nvars = 1;
  gcpro2->next = gcpro1; gcpro2->var = var2; gcpro2->nvars = 1;
  gcpro3->next = gcpro2; gcpro3->var = var3; gcpro3->nvars = 1;
  gcpro4->next = gcpro3; gcpro4->var = var4; gcpro4->nvars = 1;
  gcpro5->next = gcpro4; gcpro5->var = var5; gcpro5->nvars = 1;
  gcprolist = gcpro5;
}

void
debug_ungcpro (char *file, int line, struct gcpro *gcpro1)
{
  log_gcpro (file, line, gcpro1, ungcpro_type);
  gcprolist = gcpro1->next;
}

void
show_gcprohist (void)
{
  int i, j;
  for (i = 0, j = gcprohistptr;
       i < GCPROHISTLIMIT;
       i++, j++)
    {
      if (j >= GCPROHISTLIMIT)
	j = 0;
      printf ("%3d  %s		%d	%s	0x%x\n",
	      j, gcprohist[j].file, gcprohist[j].line,
	      (gcprohist[j].type == gcpro1_type ? "GCPRO1" :
	       gcprohist[j].type == gcpro2_type ? "GCPRO2" :
	       gcprohist[j].type == gcpro3_type ? "GCPRO3" :
	       gcprohist[j].type == gcpro4_type ? "GCPRO4" :
	       gcprohist[j].type == ungcpro_type ? "UNGCPRO" : "???"),
	      gcprohist[j].value);
    }
  fflush (stdout);
}

#endif
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.