This is the mail archive of the guile@sources.redhat.com mailing list for the Guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Patch to allow exhaustive gc checks.


Hello!

Enclosed is a patch to allow for exhaustive reference checking.  This
patch extends the functionality provided when compiling guile with 
SCM_DEBUG_CELL_ACCESSES set to 1.  The scheme level function
set-debug-cell-accesses! allows you to switch the (time consuming) tests
on or off and allows to switch between different levels of checking:

* (set-debug-cell-accesses! #f) switches the checking off.  Please note,
  even if switched off there is a run time penalty just because of the
  fact that the support for debugging was compiled in by setting
  SCM_DEBUG_CELL_ACCESSES to 1 during compilation.

* (set-debug-cell-accesses! #t) switches the checking on.  In this mode,
  with every cell access it is checked whether the cell pointer is
  actually pointing to a cell in guile's heap.  This helps to find
  uninitialized or corrupt SCM objects.  Further, if the cell is actually
  on the heap it is checked whether the cell is a free cell.  Accesses to
  free cells should never happen in a correct program, but may occur with
  uninitialized or corrupt scheme objects and (and this is the
  interesting case) if an object reference became mistakenly invisible for
  the gc, thus causing the object to be collected and marked free although
  some time later the object is used again.

* (set-debug-cell-accesses! <integer greater than 0>)
  In this mode the same checks as before are performed, but there are
  additional gc's performed.  The given integer indicates after how many
  cell accesses a gc will be performed.  The extreme case is, when the
  value 1 is given as parameter:  This means that after every cell access
  a garbage collection is performed.  While this means an extreme
  slowdown, it allows to find out about dropped references and
  uninitialized objects in a very fine grained manner.

This patch is an improved version of a previously sent one.  Using this
patch already indicated the following inconsistencies with object
initialization in guile:

* In struct.c in function scm_make_struct, the following initialization
  order leads temporarily to a partly initialized object on the heap,
  because the object type (word 0) is set before the object is filled with
  data.

  SCM_SET_CELL_WORD_1 (handle, data);
  SCM_SET_CELL_WORD_0 (handle, (scm_bits_t) SCM_STRUCT_DATA (vtable) + scm_tc3_cons_gloc);
  scm_struct_init (handle, tail_elts, init);

  (I already reported this one.)

* Similarly, in vector.c in function scm_make_vector, the vector type and
  length are set before the vector is initialized with the default value.
  Thus, on the heap the object already looks like a valid object while the
  vector contents are not valid SCM objects.

Best regards
Dirk
Warning: Remote host denied authentication agent forwarding.

Index: gc.c
===================================================================
RCS file: /cvs/guile/guile/guile-core/libguile/gc.c,v
retrieving revision 1.146
diff -u -r1.146 gc.c
--- gc.c	2000/07/15 13:44:04	1.146
+++ gc.c	2000/07/18 07:04:57
@@ -92,20 +92,32 @@
 
 #if (SCM_DEBUG_CELL_ACCESSES == 1)
 
+/* Set this to != 0 if every cell that is accessed shall be checked: 
+ */
 unsigned int scm_debug_cell_accesses_p = 0;
 
+/* Set this to 0 if no additional gc's shall be performed, otherwise set it to
+ * the number of cell accesses after which a gc shall be called.
+ */
+static unsigned int debug_cells_gc_interval = 0;
+
 
 /* Assert that the given object is a valid reference to a valid cell.  This
  * test involves to determine whether the object is a cell pointer, whether
  * this pointer actually points into a heap segment and whether the cell
- * pointed to is not a free cell.
+ * pointed to is not a free cell.  Further, additional garbage collections may
+ * get executed after a user defined number of cell accesses.  This helps to
+ * find places in the C code where references are dropped for extremely short
+ * periods.
  */
 void
 scm_assert_cell_valid (SCM cell)
 {
-  if (scm_debug_cell_accesses_p)
+  static unsigned int already_running = 0;
+
+  if (scm_debug_cell_accesses_p && !already_running)
     {
-      scm_debug_cell_accesses_p = 0;  /* disable to avoid recursion */
+      already_running = 1;  /* set to avoid recursion */
 
       if (!scm_cellp (cell))
 	{
@@ -122,14 +134,32 @@
 	     cells during gc.  I don't understand why this happens.  If it is
 	     a bug and gets fixed, the following test should also work while
 	     gc is running.
-	   */
+	  */
 	  if (SCM_FREE_CELL_P (cell))
 	    {
 	      fprintf (stderr, "scm_assert_cell_valid: Accessing free cell: %lx\n", SCM_UNPACK (cell));
 	      abort ();
 	    }
+
+	  /* If desired, perform additional garbage collections after a user
+	   * defined number of cell accesses.
+	   */
+	  if (debug_cells_gc_interval)
+	    {
+	      static unsigned int counter = 0;
+	      
+	      if (counter != 0)
+		{
+		  --counter;
+		}
+	      else
+		{
+		  counter = debug_cells_gc_interval;
+		  scm_igc ("scm_assert_cell_valid");
+		}
+	    }
 	}
-      scm_debug_cell_accesses_p = 1;  /* re-enable */
+      already_running = 0;  /* re-enable */
     }
 }
 
@@ -137,7 +167,11 @@
 SCM_DEFINE (scm_set_debug_cell_accesses_x, "set-debug-cell-accesses!", 1, 0, 0,
 	    (SCM flag),
 	    "If FLAG is #f, cell access checking is disabled.\n"
-	    "If FLAG is #t, cell access checking is enabled.\n"
+	    "If FLAG is #t, cell access checking is enabled, but no\n"
+	    "additional calls to garbage collection are issued.\n"
+	    "If FLAG is a number, cell access checking is enabled,\n"
+	    "with an additional garbage collection after the given\n"
+	    "number of cell accesses.\n"
 	    "This procedure only exists because the compile-time flag\n"
 	    "SCM_DEBUG_CELL_ACCESSES was set to 1.\n")
 #define FUNC_NAME s_scm_set_debug_cell_accesses_x
@@ -145,7 +179,13 @@
   if (SCM_FALSEP (flag)) {
     scm_debug_cell_accesses_p = 0;
   } else if (SCM_EQ_P (flag, SCM_BOOL_T)) {
+    debug_cells_gc_interval = 0;
     scm_debug_cell_accesses_p = 1;
+  } else if (SCM_INUMP (flag)) {
+    long int f = SCM_INUM (flag);
+    if (f <= 0) SCM_OUT_OF_RANGE(1, flag);
+    debug_cells_gc_interval = f;
+    scm_debug_cell_accesses_p = 1;
   } else {
     SCM_WRONG_TYPE_ARG (1, flag);
   }
@@ -2505,6 +2545,45 @@
 static void *
 mark_gc_async (void * hook_data, void *func_data, void *data)
 {
+#if (SCM_DEBUG_CELL_ACCESSES == 1)
+  /* If cell access debugging is enabled, the user may choose to perform
+   * additional garbage collections after an arbitrary number of cell
+   * accesses.  We don't want the scheme level after-gc-hook to be performed
+   * for each of these garbage collections for the following reason:  The
+   * execution of the after-gc-hook causes cell accesses itself.  Thus, if the
+   * after-gc-hook was performed with every gc, and if the gc was performed
+   * after a very small number of cell accesses, then the number of cell
+   * accesses during the execution of the after-gc-hook will suffice to cause
+   * the execution of the next gc.  Then, guile would keep executing the
+   * after-gc-loop forever.
+   *
+   * To overcome this problem, if cell access debugging with additional
+   * garbage collections is enabled, the after-gc-hook is only executed after
+   * a certain number of garbage collections.  This has the effect, that from
+   * the scheme level point of view it seems that garbage collection is
+   * performed with a much lower frequency than it actually is.
+   *
+   * Beware:  In this situation there is no fixed relationship between the
+   * execution counts of the C level garbage collection hooks and the
+   * execution count of the scheme level after-gc-hook.
+   */
+
+  if (scm_debug_cell_accesses_p && debug_cells_gc_interval)
+    {
+      static unsigned int counter = 0;
+      if (counter != 0) 
+	{
+	  --counter;
+	  return NULL;
+	}
+      else 
+	{
+	  /* Dirk:FIXME:: 100000 is an arbitrary value. */
+	  counter = 100000 / debug_cells_gc_interval;
+	}
+    }
+#endif  /* SCM_DEBUG_CELL_ACCESSES == 1 */
+
   scm_system_async_mark (gc_async);
   return NULL;
 }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]