OSDN Git Service

* config/sparc/sparc.md (movdi_insn_sp64_novis): New pattern.
[pf3gnuchains/gcc-fork.git] / gcc / ggc-page.c
index 879f8fa..a7490f3 100644 (file)
@@ -1,5 +1,5 @@
 /* "Bag-of-pages" garbage collector for the GNU compiler.
-   Copyright (C) 1999 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
 
    This file is part of GNU CC.
 
 #include "system.h"
 #include "tree.h"
 #include "rtl.h"
+#include "tm_p.h"
 #include "varray.h"
 #include "flags.h"
 #include "ggc.h"
 
+#ifdef HAVE_MMAP_ANYWHERE
 #include <sys/mman.h>
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED -1
+#endif
 
+#if !defined (MAP_ANONYMOUS) && defined (MAP_ANON)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
 
 /* Stategy: 
 
    ggc_collect is invoked.  Otherwise, collection is performed only
    when a significant amount of memory has been allocated since the
    last collection.  */
-#undef GGC_ALWAYS_COLLECT.
+#undef GGC_ALWAYS_COLLECT
 
-/* If ENABLE_CHECKING is defined, enable GGC_POISON and
-   GGC_ALWAYS_COLLECT automatically.  */
-#ifdef ENABLE_CHECKING
+#ifdef ENABLE_GC_CHECKING
 #define GGC_POISON
+#endif
+#ifdef ENABLE_GC_ALWAYS_COLLECT
 #define GGC_ALWAYS_COLLECT
 #endif
 
@@ -110,10 +120,10 @@ char *empty_string;
    significant PAGE_L2_BITS and PAGE_L1_BITS are the second and first
    index values in the lookup table, respectively.  
 
-   The topmost leftover bits, if any, are ignored.  For 32-bit
-   architectures and the settings below, there are no leftover bits.
-   For architectures with wider pointers, the lookup tree points to a
-   list of pages, which must be scanned to find the correct one.  */
+   For 32-bit architectures and the settings below, there are no
+   leftover bits.  For architectures with wider pointers, the lookup
+   tree points to a list of pages, which must be scanned to find the
+   correct one.  */
 
 #define PAGE_L1_BITS   (8)
 #define PAGE_L2_BITS   (32 - PAGE_L1_BITS - G.lg_pagesize)
@@ -147,10 +157,7 @@ typedef struct page_entry
   unsigned long *save_in_use_p;
 
   /* Context depth of this page.  */
-  unsigned char context_depth;
-
-  /* The lg of size of objects allocated from this page.  */
-  unsigned char order;
+  unsigned short context_depth;
 
   /* The number of free objects remaining on this page.  */
   unsigned short num_free_objects;
@@ -159,9 +166,8 @@ typedef struct page_entry
      next allocation from this page.  */
   unsigned short next_bit_hint;
 
-  /* Saved number of free objects for pages that aren't in the topmost
-     context during colleciton.  */
-  unsigned short save_num_free_objects;
+  /* The lg of size of objects allocated from this page.  */
+  unsigned char order;
 
   /* A bit vector indicating whether or not objects are in use.  The
      Nth bit is one if the Nth object on this page is allocated.  This
@@ -177,8 +183,8 @@ typedef page_entry **page_table[PAGE_L1_SIZE];
 
 #else
 
-/* On 64-bit hosts, we use two level page tables plus a linked list
-   that disambiguates the top 32-bits.  There will almost always be
+/* On 64-bit hosts, we use the same two level page tables plus a linked
+   list that disambiguates the top 32-bits.  There will almost always be
    exactly one entry in the list.  */
 typedef struct page_table_chain
 {
@@ -216,11 +222,14 @@ static struct globals
   /* Bytes currently allocated at the end of the last collection.  */
   size_t allocated_last_gc;
 
+  /* Total amount of memory mapped.  */
+  size_t bytes_mapped;
+
   /* The current depth in the context stack.  */
-  unsigned char context_depth;
+  unsigned short context_depth;
 
   /* A file descriptor open to /dev/zero for reading.  */
-#ifndef MAP_ANONYMOUS
+#if defined (HAVE_MMAP_ANYWHERE) && !defined(MAP_ANONYMOUS)
   int dev_zero_fd;
 #endif
 
@@ -234,7 +243,7 @@ static struct globals
 
 /* Compute DIVIDEND / DIVISOR, rounded up.  */
 #define DIV_ROUND_UP(Dividend, Divisor) \
-  ((Dividend + Divisor - 1) / Divisor)
+  (((Dividend) + (Divisor) - 1) / (Divisor))
 
 /* The number of objects per allocation page, for objects of size
    2^ORDER.  */
@@ -257,30 +266,61 @@ static struct globals
 #define GGC_MIN_LAST_ALLOCATED (4 * 1024 * 1024)
 
 \f
-static page_entry *lookup_page_table_entry PROTO ((void *));
-static void set_page_table_entry PROTO ((void *, page_entry *));
-static char *alloc_anon PROTO ((char *, size_t));
-static struct page_entry * alloc_page PROTO ((unsigned));
-static void free_page PROTO ((struct page_entry *));
-static void release_pages PROTO ((void));
-static void *alloc_obj PROTO ((size_t, int));
-static int mark_obj PROTO ((void *));
-static void clear_marks PROTO ((void));
-static void sweep_pages PROTO ((void));
+static int ggc_allocated_p PARAMS ((const void *));
+static page_entry *lookup_page_table_entry PARAMS ((const void *));
+static void set_page_table_entry PARAMS ((void *, page_entry *));
+static char *alloc_anon PARAMS ((char *, size_t));
+static struct page_entry * alloc_page PARAMS ((unsigned));
+static void free_page PARAMS ((struct page_entry *));
+static void release_pages PARAMS ((void));
+static void clear_marks PARAMS ((void));
+static void sweep_pages PARAMS ((void));
+static void ggc_recalculate_in_use_p PARAMS ((page_entry *));
 
 #ifdef GGC_POISON
-static void poison PROTO ((void *, size_t));
-static void poison_pages PROTO ((void));
+static void poison_pages PARAMS ((void));
 #endif
 
-void debug_print_page_list PROTO ((int));
+void debug_print_page_list PARAMS ((int));
 \f
+/* Returns non-zero if P was allocated in GC'able memory.  */
+
+static inline int
+ggc_allocated_p (p)
+     const void *p;
+{
+  page_entry ***base;
+  size_t L1, L2;
+
+#if HOST_BITS_PER_PTR <= 32
+  base = &G.lookup[0];
+#else
+  page_table table = G.lookup;
+  size_t high_bits = (size_t) p & ~ (size_t) 0xffffffff;
+  while (1)
+    {
+      if (table == NULL)
+       return 0;
+      if (table->high_bits == high_bits)
+       break;
+      table = table->next;
+    }
+  base = &table->table[0];
+#endif
+
+  /* Extract the level 1 and 2 indicies.  */
+  L1 = LOOKUP_L1 (p);
+  L2 = LOOKUP_L2 (p);
+
+  return base[L1] && base[L1][L2];
+}
+
 /* Traverse the page table and find the entry for a page. 
    Die (probably) if the object wasn't allocated via GC.  */
 
 static inline page_entry *
 lookup_page_table_entry(p)
-     void *p;
+     const void *p;
 {
   page_entry ***base;
   size_t L1, L2;
@@ -302,8 +342,8 @@ lookup_page_table_entry(p)
   return base[L1][L2];
 }
 
-
 /* Set the page table entry for a page.  */
+
 static void
 set_page_table_entry(p, entry)
      void *p;
@@ -340,8 +380,8 @@ found:
   base[L1][L2] = entry;
 }
 
-
 /* Prints the page-entry for object size ORDER, for debugging.  */
+
 void
 debug_print_page_list (order)
      int order;
@@ -358,27 +398,17 @@ debug_print_page_list (order)
   fflush (stdout);
 }
 
-#ifdef GGC_POISON
-/* `Poisons' the region of memory starting at START and extending for
-   LEN bytes.  */
-static inline void
-poison (start, len)
-     void *start;
-     size_t len;
-{
-  memset (start, 0xa5, len);
-}
-#endif
-
 /* Allocate SIZE bytes of anonymous memory, preferably near PREF,
    (if non-null).  */
+
 static inline char *
 alloc_anon (pref, size)
-     char *pref;
+     char *pref ATTRIBUTE_UNUSED;
      size_t size;
 {
   char *page;
 
+#ifdef HAVE_MMAP_ANYWHERE
 #ifdef MAP_ANONYMOUS
   page = (char *) mmap (pref, size, PROT_READ | PROT_WRITE,
                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
@@ -391,6 +421,19 @@ alloc_anon (pref, size)
       fputs ("Virtual memory exhausted!\n", stderr);
       exit(1);
     }
+#else
+#ifdef HAVE_VALLOC
+  page = (char *) valloc (size);
+  if (!page)
+    {
+      fputs ("Virtual memory exhausted!\n", stderr);
+      exit(1);
+    }
+#endif /* HAVE_VALLOC */
+#endif /* HAVE_MMAP_ANYWHERE */
+
+  /* Remember that we allocated this memory.  */
+  G.bytes_mapped += size;
 
   return page;
 }
@@ -398,6 +441,7 @@ alloc_anon (pref, size)
 /* Allocate a new page for allocating objects of size 2^ORDER,
    and return an entry for it.  The entry is not added to the
    appropriate page_table list.  */
+
 static inline struct page_entry *
 alloc_page (order)
      unsigned order;
@@ -438,7 +482,7 @@ alloc_page (order)
     }
   else
     {
-      /* Actually allocate the memory, using mmap.  */
+      /* Actually allocate the memory.  */
       page = alloc_anon (NULL, entry_size);
     }
 
@@ -467,8 +511,8 @@ alloc_page (order)
   return entry;
 }
 
+/* For a page that is no longer needed, put it on the free page list.  */
 
-/* Free a page when it's no longer needed.  */
 static inline void
 free_page (entry)
      page_entry *entry;
@@ -484,11 +528,12 @@ free_page (entry)
   G.free_pages = entry;
 }
 
+/* Release the free page cache to the system.  */
 
-/* Release the page cache to the system.  */
-static inline void
+static void
 release_pages ()
 {
+#ifdef HAVE_MMAP_ANYWHERE
   page_entry *p, *next;
   char *start;
   size_t len;
@@ -512,6 +557,7 @@ release_pages ()
       else
        {
          munmap (start, len);
+         G.bytes_mapped -= len;
          start = p->page;
          len = p->bytes;
        }
@@ -520,12 +566,27 @@ release_pages ()
     }
 
   munmap (start, len);
+  G.bytes_mapped -= len;
+#else
+#ifdef HAVE_VALLOC
+  page_entry *p, *next;
+
+  for (p = G.free_pages; p ; p = next)
+    {
+      next = p->next;
+      free (p->page);
+      G.bytes_mapped -= p->bytes;
+      free (p);
+    }
+#endif /* HAVE_VALLOC */
+#endif /* HAVE_MMAP_ANYWHERE */
+
   G.free_pages = NULL;
 }
 
-
 /* This table provides a fast way to determine ceil(log_2(size)) for
    allocation requests.  The minimum allocation size is four bytes.  */
+
 static unsigned char const size_lookup[257] = 
 { 
   2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 
@@ -549,8 +610,9 @@ static unsigned char const size_lookup[257] =
 
 /* Allocate a chunk of memory of SIZE bytes.  If ZERO is non-zero, the
    memory is zeroed; otherwise, its contents are undefined.  */
-static void *
-alloc_obj (size, zero)
+
+void *
+ggc_alloc_obj (size, zero)
      size_t size;
      int zero;
 {
@@ -573,9 +635,7 @@ alloc_obj (size, zero)
 
   /* If there is no page for this object size, or all pages in this
      context are full, allocate a new page.  */
-  if (entry == NULL 
-      || entry->num_free_objects == 0 
-      || entry->context_depth != G.context_depth)
+  if (entry == NULL || entry->num_free_objects == 0)
     {
       struct page_entry *new_entry;
       new_entry = alloc_page (order);
@@ -646,8 +706,9 @@ alloc_obj (size, zero)
 #ifdef GGC_POISON
   /* `Poison' the entire allocated object before zeroing the requested area,
      so that bytes beyond the end, if any, will not necessarily be zero.  */
-  poison (result, 1 << order);
+  memset (result, 0xaf, 1 << order);
 #endif
+
   if (zero)
     memset (result, 0, size);
 
@@ -663,13 +724,13 @@ alloc_obj (size, zero)
   return result;
 }
 
-
-/* If P is not marked, marks it and returns 0.  Otherwise returns 1.
+/* If P is not marked, marks it and return false.  Otherwise return true.
    P must have been allocated by the GC allocator; it mustn't point to
    static objects, stack variables, or memory allocated with malloc.  */
-static int
-mark_obj (p)
-     void *p;
+
+int
+ggc_set_mark (p)
+     const void *p;
 {
   page_entry *entry;
   unsigned bit, word;
@@ -677,7 +738,7 @@ mark_obj (p)
 
   /* Look up the page on which the object is alloced.  If the object
      wasn't allocated by the collector, we'll probably die.  */
-  entry = lookup_page_table_entry(p);
+  entry = lookup_page_table_entry (p);
 #ifdef ENABLE_CHECKING
   if (entry == NULL)
     abort ();
@@ -685,7 +746,7 @@ mark_obj (p)
 
   /* Calculate the index of the object on the page; this is its bit
      position in the in_use_p bitmap.  */
-  bit = (((char *) p) - entry->page) >> entry->order;
+  bit = (((const char *) p) - entry->page) >> entry->order;
   word = bit / HOST_BITS_PER_LONG;
   mask = (unsigned long) 1 << (bit % HOST_BITS_PER_LONG);
   
@@ -705,15 +766,35 @@ mark_obj (p)
   return 0;
 }
 
+/* Mark P, but check first that it was allocated by the collector.  */
+
+void
+ggc_mark_if_gcable (p)
+     const void *p;
+{
+  if (p && ggc_allocated_p (p))
+    ggc_set_mark (p);
+}
+
+/* Return the size of the gc-able object P.  */
+
+size_t
+ggc_get_size (p)
+     const void *p;
+{
+  page_entry *pe = lookup_page_table_entry (p);
+  return 1 << pe->order;
+}
 \f
 /* Initialize the ggc-mmap allocator.  */
+
 void
 init_ggc ()
 {
   G.pagesize = getpagesize();
   G.lg_pagesize = exact_log2 (G.pagesize);
 
-#ifndef MAP_ANONYMOUS
+#if defined (HAVE_MMAP_ANYWHERE) && !defined(MAP_ANONYMOUS)
   G.dev_zero_fd = open ("/dev/zero", O_RDONLY);
   if (G.dev_zero_fd == -1)
     abort ();
@@ -727,10 +808,32 @@ init_ggc ()
 
   G.allocated_last_gc = GGC_MIN_LAST_ALLOCATED;
 
+#ifdef HAVE_MMAP_ANYWHERE
+  /* StunOS has an amazing off-by-one error for the first mmap allocation
+     after fiddling with RLIMIT_STACK.  The result, as hard as it is to
+     believe, is an unaligned page allocation, which would cause us to
+     hork badly if we tried to use it.  */
+  {
+    char *p = alloc_anon (NULL, G.pagesize);
+    if ((size_t)p & (G.pagesize - 1))
+      {
+       /* How losing.  Discard this one and try another.  If we still
+          can't get something useful, give up.  */
+
+       p = alloc_anon (NULL, G.pagesize);
+       if ((size_t)p & (G.pagesize - 1))
+         abort ();
+      }
+    munmap (p, G.pagesize);
+  }
+#endif
+
   empty_string = ggc_alloc_string ("", 0);
   ggc_add_string_root (&empty_string, 1);
 }
 
+/* Increment the `GC context'.  Objects allocated in an outer context
+   are never freed, eliminating the need to register their roots.  */
 
 void
 ggc_push_context ()
@@ -742,6 +845,46 @@ ggc_push_context ()
     abort ();
 }
 
+/* Merge the SAVE_IN_USE_P and IN_USE_P arrays in P so that IN_USE_P
+   reflects reality.  Recalculate NUM_FREE_OBJECTS as well.  */
+
+static void
+ggc_recalculate_in_use_p (p)
+     page_entry *p;
+{
+  unsigned int i;
+  size_t num_objects;
+
+  /* Because the past-the-end bit in in_use_p is always set, we 
+     pretend there is one additional object.  */
+  num_objects = OBJECTS_PER_PAGE (p->order) + 1;
+
+  /* Reset the free object count.  */
+  p->num_free_objects = num_objects;
+
+  /* Combine the IN_USE_P and SAVE_IN_USE_P arrays.  */
+  for (i = 0; 
+       i < DIV_ROUND_UP (BITMAP_SIZE (num_objects),
+                        sizeof (*p->in_use_p));
+       ++i)
+    {
+      unsigned long j;
+
+      /* Something is in use if it is marked, or if it was in use in a
+        context further down the context stack.  */
+      p->in_use_p[i] |= p->save_in_use_p[i];
+
+      /* Decrement the free object count for every object allocated.  */
+      for (j = p->in_use_p[i]; j; j >>= 1)
+       p->num_free_objects -= (j & 1);
+    }
+
+  if (p->num_free_objects >= num_objects)
+    abort ();
+}
+
+/* Decrement the `GC context'.  All objects allocated since the 
+   previous ggc_push_context are migrated to the outer context.  */
 
 void
 ggc_pop_context ()
@@ -755,89 +898,27 @@ ggc_pop_context ()
      left over are imported into the previous context.  */
   for (order = 2; order < HOST_BITS_PER_PTR; order++)
     {
-      size_t num_objects = OBJECTS_PER_PAGE (order);
-      size_t bitmap_size = BITMAP_SIZE (num_objects);
-
       page_entry *p;
 
       for (p = G.pages[order]; p != NULL; p = p->next)
        {
          if (p->context_depth > depth)
-           {
-             p->context_depth = depth;
-           }
+           p->context_depth = depth;
 
          /* If this page is now in the topmost context, and we'd
             saved its allocation state, restore it.  */
          else if (p->context_depth == depth && p->save_in_use_p)
            {
-             memcpy (p->in_use_p, p->save_in_use_p, bitmap_size);
+             ggc_recalculate_in_use_p (p);
              free (p->save_in_use_p);
              p->save_in_use_p = 0;
-             p->num_free_objects = p->save_num_free_objects;
            }
        }
     }
 }
-
-
-struct rtx_def *
-ggc_alloc_rtx (nslots)
-     int nslots;
-{
-  return (struct rtx_def *) 
-    alloc_obj (sizeof (struct rtx_def) + (nslots - 1) * sizeof (rtunion), 1);
-}
-
-
-struct rtvec_def *
-ggc_alloc_rtvec (nelt)
-     int nelt;
-{
-  return (struct rtvec_def *)
-    alloc_obj (sizeof (struct rtvec_def) + (nelt - 1) * sizeof (rtx), 1);
-}
-
-
-union tree_node *
-ggc_alloc_tree (length)
-     int length;
-{
-  return (union tree_node *) alloc_obj (length, 1);
-}
-
-
-char *
-ggc_alloc_string (contents, length)
-     const char *contents;
-     int length;
-{
-  char *string;
-
-  if (length < 0)
-    {
-      if (contents == NULL)
-       return NULL;
-      length = strlen (contents);
-    }
-
-  string = (char *) alloc_obj (length + 1, 0);
-  if (contents != NULL)
-    memcpy (string, contents, length);
-  string[length] = 0;
-
-  return string;
-}
-
-
-void *
-ggc_alloc (size)
-     size_t size;
-{
-  return alloc_obj (size, 0);
-}
-
 \f
+/* Unmark all objects.  */
+
 static inline void
 clear_marks ()
 {
@@ -846,7 +927,7 @@ clear_marks ()
   for (order = 2; order < HOST_BITS_PER_PTR; order++)
     {
       size_t num_objects = OBJECTS_PER_PAGE (order);
-      size_t bitmap_size = BITMAP_SIZE (num_objects);
+      size_t bitmap_size = BITMAP_SIZE (num_objects + 1);
       page_entry *p;
 
       for (p = G.pages[order]; p != NULL; p = p->next)
@@ -860,12 +941,11 @@ clear_marks ()
          /* Pages that aren't in the topmost context are not collected;
             nevertheless, we need their in-use bit vectors to store GC
             marks.  So, back them up first.  */
-         if (p->context_depth < G.context_depth
-             && ! p->save_in_use_p)
+         if (p->context_depth < G.context_depth)
            {
-             p->save_in_use_p = xmalloc (bitmap_size);
+             if (! p->save_in_use_p)
+               p->save_in_use_p = xmalloc (bitmap_size);
              memcpy (p->save_in_use_p, p->in_use_p, bitmap_size);
-             p->save_num_free_objects = p->num_free_objects;
            }
 
          /* Reset reset the number of free objects and clear the
@@ -880,6 +960,9 @@ clear_marks ()
     }
 }
 
+/* Free all empty pages.  Partially empty pages need no attention
+   because the `mark' bit doubles as an `unused' bit.  */
+
 static inline void
 sweep_pages ()
 {
@@ -968,10 +1051,18 @@ sweep_pages ()
          p = next;
        } 
       while (! done);
+
+      /* Now, restore the in_use_p vectors for any pages from contexts
+         other than the current one.  */
+      for (p = G.pages[order]; p; p = p->next)
+       if (p->context_depth != G.context_depth)
+         ggc_recalculate_in_use_p (p);
     }
 }
 
 #ifdef GGC_POISON
+/* Clobber all free objects.  */
+
 static inline void
 poison_pages ()
 {
@@ -986,19 +1077,29 @@ poison_pages ()
       for (p = G.pages[order]; p != NULL; p = p->next)
        {
          size_t i;
+
+         if (p->context_depth != G.context_depth)
+           /* Since we don't do any collection for pages in pushed
+              contexts, there's no need to do any poisoning.  And
+              besides, the IN_USE_P array isn't valid until we pop
+              contexts.  */
+           continue;
+
          for (i = 0; i < num_objects; i++)
            {
              size_t word, bit;
              word = i / HOST_BITS_PER_LONG;
              bit = i % HOST_BITS_PER_LONG;
              if (((p->in_use_p[word] >> bit) & 1) == 0)
-               poison (p->page + i * size, size);
+               memset (p->page + i * size, 0xa5, size);
            }
        }
     }
 }
 #endif
 
+/* Top level mark-and-sweep routine.  */
+
 void
 ggc_collect ()
 {
@@ -1026,12 +1127,13 @@ ggc_collect ()
 
   clear_marks ();
   ggc_mark_roots ();
-  sweep_pages ();
   
 #ifdef GGC_POISON
   poison_pages ();
 #endif
 
+  sweep_pages ();
+
   G.allocated_last_gc = G.allocated;
   if (G.allocated_last_gc < GGC_MIN_LAST_ALLOCATED)
     G.allocated_last_gc = GGC_MIN_LAST_ALLOCATED;
@@ -1039,46 +1141,64 @@ ggc_collect ()
   time = get_run_time () - time;
   gc_time += time;
 
-  time = (time + 500) / 1000;
   if (!quiet_flag)
-    fprintf (stderr, "%luk in %d.%03d}", 
-            (unsigned long) G.allocated / 1024, time / 1000, time % 1000);
+    {
+      fprintf (stderr, "%luk in %.3f}", 
+              (unsigned long) G.allocated / 1024, time * 1e-6);
+    }
 }
 
+/* Print allocation statistics.  */
 
-int
-ggc_set_mark_rtx (r)
-     rtx r;
+void
+ggc_page_print_statistics ()
 {
-  return mark_obj (r);
-}
+  struct ggc_statistics stats;
+  unsigned int i;
 
-int
-ggc_set_mark_rtvec (v)
-     rtvec v;
-{
-  return mark_obj (v);
-}
+  /* Clear the statistics.  */
+  memset (&stats, 0, sizeof (stats));
+  
+  /* Make sure collection will really occur.  */
+  G.allocated_last_gc = 0;
 
-int
-ggc_set_mark_tree (t)
-     tree t;
-{
-  return mark_obj (t);
-}
+  /* Collect and print the statistics common across collectors.  */
+  ggc_print_statistics (stderr, &stats);
 
-void
-ggc_mark_string (s)
-     char *s;
-{
-  if (s)
-    mark_obj (s);
-}
+  /* Release free pages so that we will not count the bytes allocated
+     there as part of the total allocated memory.  */
+  release_pages ();
 
-void 
-ggc_mark (p)
-     void *p;
-{
-  if (p)
-    mark_obj (p);
+  /* Collect some information about the various sizes of 
+     allocation.  */
+  fprintf (stderr, "\n%-4s%-16s%-16s\n", "Log", "Allocated", "Used");
+  for (i = 0; i < HOST_BITS_PER_PTR; ++i)
+    {
+      page_entry *p;
+      size_t allocated;
+      size_t in_use;
+
+      /* Skip empty entries.  */
+      if (!G.pages[i])
+       continue;
+
+      allocated = in_use = 0;
+
+      /* Figure out the total number of bytes allocated for objects of
+        this size, and how many of them are actually in use.  */
+      for (p = G.pages[i]; p; p = p->next)
+       {
+         allocated += p->bytes;
+         in_use += 
+           (OBJECTS_PER_PAGE (i) - p->num_free_objects) * (1 << i);
+       }
+      fprintf (stderr, "%-3d %-15lu %-15lu\n", i, 
+              (unsigned long) allocated, (unsigned long) in_use);
+    }
+
+  /* Print out some global information.  */
+  fprintf (stderr, "\nTotal bytes marked: %lu\n", 
+          (unsigned long) G.allocated);
+  fprintf (stderr, "Total bytes mapped: %lu\n", 
+          (unsigned long) G.bytes_mapped);
 }