home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / lucid / lemacs-19.6 / src / free-hook.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-10  |  13.7 KB  |  609 lines

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