
#include "mps.h"        /* MPS Interface */
#include "mpslib.h"     /* plinth interface */
#include <windows.h>


extern mps_arena_t arena;

/* controlling variables */

int check_wrapper_stats = 1;    /* if true, the display a heap breakdown 
				   on each forced GC */



int check_reference_paths = 1;  /* if true, then permit the tracing of
				   object reference trails on on each forced
				   GC, for any rquested object/wrapper */


int prompt_before_checking_references = 0;  /* if true then prompt for another
					      object/wrapper on each forced GC
					      */

int prompt_after_checking_references = 1;  /* if true then prompt for another
					      object/wrapper after following
					      a trail */

int depth_limit = 0;          /* set this from the debugger to permit
				 roots to be found at depths greater than
				 the depth of the first found roots */

int trail_limit = 4;           /* set this from the debugger to limit
                                  the number of trails which can be reported
                                  at any depth depth */

int cold_trail_limit = 1;     /* set this from the debugger to limit
				 the number of trails which can be reported
				 when a trail goes cold */

int object_report_limit = 4;  /* set this from the debugger to limit
				 the number of objects which get reported
				 during processing for each depth */

void *object_to_follow = 0;   /* set this from a debugger if you want 
				 to trace to an object */

void *wrapper_to_follow = 0;  /* set this from a debugger for tracing 
				 to the first instance found with this 
				 wrapper */




/*
extern void *KLpairGYdylanVdylanW;
extern void *KLsimple_object_vectorGYdylanVdylanW;
*/

extern int mps_lib_fputs_(const char *s, int end, mps_lib_FILE *stream);
extern void mps_lib_abort(void);

void report_message (char* message)
{
  mps_lib_FILE *stream = mps_lib_get_stdout();  
  mps_lib_fputs(message, stream);
}

void report_error (char* message)
{
  mps_lib_FILE *stream = mps_lib_get_stderr();  
  mps_lib_fputs("\nError:\n", stream);
  mps_lib_fputs(message, stream);
  mps_lib_fputc('\n', stream);
  mps_lib_abort();
}


void report_break (char* message)
{
  mps_lib_FILE *stream = mps_lib_get_stdout();  
  mps_lib_fputs("Break to debugger:\n    ", stream);
  mps_lib_fputs(message, stream);
  mps_lib_fputc('\n', stream);
  DebugBreak();
}


/* Support for finding the count and size for each class (wrapper) in the heap */



typedef struct wrapper_stats_s *wrapper_stats_t;

typedef struct wrapper_stats_s {
  void *wrapper_address;
  int  usage_count;
  int  usage_size;
} wrapper_stats_s;

#define STAT_SIZE 10000

wrapper_stats_s wrapper_stats[STAT_SIZE];

int wrapper_cursor;
int wrapper_preassignments;


void preassign_wrapper (void* wrapper, int index)
{
  wrapper_stats[wrapper_cursor].wrapper_address = wrapper;
  wrapper_cursor++;
  wrapper_preassignments++;
}

void clear_wrapper_stats ()
{
  int i;
  wrapper_cursor = 0;
  wrapper_preassignments = 0;
  for (i = 0; i < STAT_SIZE; i++) {
    wrapper_stats[i].wrapper_address = (void *)0;
    wrapper_stats[i].usage_count = 0;
    wrapper_stats[i].usage_size = 0;
  }
  /*
  pre_assign_wrapper(&KLpairGYdylanVdylanW);
  pre_assign_wrapper(&KLsimple_object_vectorGYdylanVdylanW);
  */
}

int default_index_for_wrapper (void *wrapper)
{
  int i;
  for (i = wrapper_preassignments; i < wrapper_cursor; i++) {
    if (wrapper_stats[i].wrapper_address == wrapper)
      return(i);
  }
  if (wrapper_cursor < STAT_SIZE) {
    int cursor = wrapper_cursor;
    wrapper_stats[cursor].wrapper_address = wrapper;
    wrapper_cursor++;
    return(cursor);
  }
  report_error("Too many different classes encountered while walking the heap");
  return(STAT_SIZE - 1);
}


int index_for_wrapper (void *wrapper)
{
  /*
  if (wrapper == &KLpairGYdylanVdylanW)
    return(0);
  else if (wrapper == &KLsimple_object_vectorGYdylanVdylanW)
    return(1);
  else
  */
    return default_index_for_wrapper(wrapper);
}

void add_stat_for_object (void *object, void* wrapper, int size)
{
  int index = index_for_wrapper(wrapper);
  wrapper_stats[index].usage_count += 1;
  wrapper_stats[index].usage_size += size;
}

int wrapper_fixed_length_in_words(void* wrapper)
{
  int wf = ((int*)wrapper)[3];
  int fl = wf >> 2;
  return fl + 1; /* 1 for the header itself */
}

int wrapper_vector_scaling_in_bytes(void* wrapper)
{
  int wv = ((int*)wrapper)[4];
  int vf = wv & 7;
  switch (vf) {
  case 7: 
    return(0);
  case 4: 
  case 5: 
    return(1);  /* assume all non-word vectors are byte sized */
  default: 
    return(4);
  }
}

int object_repeated_size (void *object, int fixed_offset)
{
  int tagged_size = ((int*)object)[fixed_offset];
  return tagged_size >> 2;
}

int size_of_object (void *object, void* wrapper)
{
  int fixed = wrapper_fixed_length_in_words(wrapper);
  int vec_scale = wrapper_vector_scaling_in_bytes(wrapper);
  if (vec_scale) {
    return (4 * fixed)
           + 4 /* for the size slot */
           + (vec_scale * object_repeated_size(object, fixed));
  } else {
    return 4 * fixed;
  }
}

int traceable_word_size_of_object (void *object, void* wrapper)
{
  int fixed = wrapper_fixed_length_in_words(wrapper);
  int vec_scale = wrapper_vector_scaling_in_bytes(wrapper);
  if (vec_scale == 4) {
    return fixed
           + 1 /* for the size slot */
           + object_repeated_size(object, fixed);
  } else {
    return fixed;
  }
}

void record_an_object (mps_addr_t object, mps_fmt_t format, mps_pool_t pool, 
		       void *p1, size_t p2)
{
  void *wrapper = *(void **)object;
  if (wrapper && ((int)wrapper & 3) == 0) {
    add_stat_for_object(object, wrapper, size_of_object(object, wrapper));
  }
}

int sort_criterion_for_index (int index)
{
  return wrapper_stats[index].usage_size;
}

int biggest_below_value (int value)
{
  int i;
  int largest_found = -1;
  for (i = 0; i < wrapper_cursor; i++) {
    int count = sort_criterion_for_index(i);
    if ((count < value) && (count > largest_found)) {
      largest_found = count;
    }
  }
  return largest_found;
}


__inline
void *wrapper_class(void *wrapper)
{
  void *iclass = ((void**)wrapper)[1];
  void *class  = ((void**)iclass)[2];

  return class;
}

char* class_name_from_wrapper (void* wrapper)
{
  void *class = wrapper_class(wrapper);
  char *name = (char*)((void**)class)[2];
  return (name + 8);
}

void display_integer (int integer, mps_lib_FILE *stream)
{  /* This is naieve. Assume no more than 7 digits */
  int remainder = integer;
  int leading = 1;
  int power;
  if (integer == 0) {
    /* special case needs the leading zero */
      mps_lib_fputs("       0", stream);
      return;
  }
  for (power = 10000000; power > 0; power = power / 10) {
    int exponent = (int)(log10(power));
    int digit = remainder / power;
    remainder = remainder % power;
    if (digit == 0) {
      mps_lib_fputc(leading ? ' ' : '0', stream);
    } else {
      leading = 0;
      mps_lib_fputc('0' + digit, stream);
    };
    if ((exponent == 6) || (exponent == 3))
      if (digit == 0) {
	mps_lib_fputc(leading ? ' ' : ',', stream);
      } else
	mps_lib_fputc(',', stream);
  }
}

void display_hex_address (void *address, mps_lib_FILE *stream)
{  
  unsigned int integer = (unsigned int)address;
  unsigned int remainder = integer;
  int leading = 1;
  unsigned int power;
  for (power = 0x10000000; power > 0; power = power / 0x10) {
    unsigned int digit = remainder / power;
    remainder = remainder % power;
    if (digit == 0) {
      mps_lib_fputc(leading ? ' ' : '0', stream);
    } else if (digit > 9) {
      leading = 0;
      mps_lib_fputc('A' + digit - 10, stream);
    } else {
      leading = 0;
      mps_lib_fputc('0' + digit, stream);
    }
  }
}



int padding_for_string(char *string, int length)
{
  int padding = length;
  char *remaining = string;
  while (*remaining) {
    remaining++;
    padding--;
  }
  return padding;
}

void display_padding_for_string(char *string, char pad, int field, mps_lib_FILE *stream)
{
  int i;
  int padding = padding_for_string(string, field);
  for (i = 0; i < padding; i++)
    mps_lib_fputc(' ', stream);
}

#define class_name_size 45


void display_stat_line (char *message, int count, int size, mps_lib_FILE *stream)
{
  mps_lib_fputs_(message, class_name_size, stream);
  display_padding_for_string(message, ' ', class_name_size, stream);
  display_integer(count, stream);
  mps_lib_fputc(' ', stream);
  display_integer(size, stream);
  mps_lib_fputc('\n', stream);
}

void display_one_wrapper(int index, mps_lib_FILE *stream)
{
  wrapper_stats_t stat = wrapper_stats + index;
  char *class_name = class_name_from_wrapper(stat->wrapper_address);
  display_stat_line(class_name, stat->usage_count, stat->usage_size, stream);
}


void display_wrappers_of_size(int size, mps_lib_FILE *stream)
{
  int i;
  for (i = 0; i < wrapper_cursor; i++) {
    int count = sort_criterion_for_index(i);
    if (count == size) {
      display_one_wrapper(i, stream);
    }
  }
}

void display_totals(mps_lib_FILE *stream)
{
  int i;
  int tot_size = 0;
  int tot_count = 0;
  for (i = 0; i < wrapper_cursor; i++) {
    tot_size += wrapper_stats[i].usage_size;
    tot_count += wrapper_stats[i].usage_count;
  }
  display_stat_line("TOTAL:", tot_count, tot_size, stream);
}


#define very_big 0x7fffffff

void display_wrapper_stats ()
{
  int largest;
  mps_lib_FILE *stream = mps_lib_get_stdout();
  char *message = "Start of heap statistics";
  mps_lib_fputc('\n', stream);
  mps_lib_fputs(message, stream);
  display_padding_for_string(message, ' ', class_name_size, stream);
  mps_lib_fputs("   (count)     (size)", stream);
  mps_lib_fputs("\n\n", stream);
  display_totals(stream);
  mps_lib_fputc('\n', stream);
  for (largest = biggest_below_value(very_big); 
       largest >= 0; 
       largest = biggest_below_value(largest)) {
    display_wrappers_of_size(largest, stream);
  }
  mps_lib_fputs("End of heap statistics\n\n", stream);
}



/* Following reference paths via a reverse trace */

/* Strategy:
 * 1. Find all objects directly referenced from the roots of the GC
 * 2. Start with a set of "parent" objects we want to track.
 * 3. Look to see if any object in the set is directly referenced from a root
 * 4. Walk the heap looking for all objects which reference objects in the set.
 * 5. Record those objects as "children" of the parents - but only if they're new.
 * 6. Consider next generation, by treating the children as next set
 * 7. Repeat from step 3
 *
 */

typedef struct obj_rec_st *obj_rec_p;

typedef struct obj_rec_st {
  obj_rec_p next;     /* Next recorded object with this hash value */
  obj_rec_p parent;   /* parent object that this object references  */
  mps_addr_t object;  /* The Dylan object this records */
} obj_rec_st;



#define TABLE_SIZE 0x1000
#define TABLE_MASK (TABLE_SIZE - 1)
#define TABLE_SHIFT 3

typedef obj_rec_p *obj_table;


int hash_val(mps_addr_t obj)
{
  return ((int)obj >> TABLE_SHIFT) & TABLE_MASK;
}


obj_rec_p root_table[TABLE_SIZE] = {NULL};      /* hash table of known roots */
obj_rec_p processed_table[TABLE_SIZE] = {NULL}; /* hash table of processed objects */
obj_rec_p current_table[TABLE_SIZE] = {NULL};   /* hash table of current set of objects */
obj_rec_p child_table[TABLE_SIZE] = {NULL};     /* hash table of child objects */


HANDLE process_heap = 0;

obj_rec_p alloc_obj_rec(mps_addr_t obj)
{
  obj_rec_p new;
  if (process_heap == 0) {
    process_heap = GetProcessHeap();
  }
  new = HeapAlloc(process_heap, 0, sizeof(obj_rec_st));
  new->object = obj;
  new->next = NULL;
  new->parent = NULL;
  return new;
}

void free_obj_rec(obj_rec_p or)
{
  HeapFree(process_heap, 0, or);
}

void free_table(obj_table table)
{
  int i;
  for (i = 0; i < TABLE_SIZE; i++) {
    obj_rec_p next = table[i];
    while (next != NULL) {
      obj_rec_p this = next;
      next = next->next;
      free_obj_rec(this);
    }
    table[i] = NULL;
  }
}


void add_to_table(obj_table table, obj_rec_p objrec)
{
  int hash = hash_val(objrec->object);
  objrec->next = table[hash];
  table[hash] = objrec;
}


obj_rec_p find_obj_in_table(obj_table table, mps_addr_t obj)
{
  int hash = hash_val(obj);
  obj_rec_p current = table[hash];
  while (current != NULL) {
    if (current->object == obj) {
      return current;
    }
    current = current->next;
  }
  return NULL;
}

/* fold the src table into the dest - clearing src in the process */
void merge_tables(obj_table dest, obj_table src)
{
  int i;
  for (i = 0; i < TABLE_SIZE;  i++) {
    obj_rec_p dchain = dest[i];
    obj_rec_p schain = src[i];
    if (schain != NULL) {
      dest[i] = schain;
      src[i] = NULL;
      if (dchain != NULL) {
        while (schain->next != NULL) {
          schain = schain->next;
        }
        schain->next = dchain;
      }
    } 
  }
}

int size_of_table(obj_table table)
{
  int i;
  int size = 0;
  for (i = 0; i < TABLE_SIZE; i++) {
    obj_rec_p current = table[i];
    while (current != NULL) {
      size++;
      current = current->next;
    }
  }
  return size;
}


obj_rec_p add_new_to_table(obj_table table, mps_addr_t obj)
{
  obj_rec_p new = alloc_obj_rec(obj);
  add_to_table(table, new);
  return new;
}

obj_rec_p new_obj_rec(mps_addr_t obj)
{
  return add_new_to_table(child_table, obj);
}

obj_rec_p new_root(mps_addr_t obj, mps_addr_t root)
{
  obj_rec_p new = add_new_to_table(root_table, obj);
  obj_rec_p parent = alloc_obj_rec(root);
  new->parent = parent;
  return new;
}


void clear_object_reference_paths ()
{
  free_table(root_table);
  free_table(processed_table);
  free_table(current_table);
  free_table(child_table);
}



void display_object_reference(void* object, mps_lib_FILE *stream)
{
  void *wrapper = ((void**)object)[0];
  char *class_name = class_name_from_wrapper(wrapper);
  mps_lib_fputs("    ", stream);
  mps_lib_fputs(class_name, stream);
  display_padding_for_string(class_name, ' ', class_name_size, stream);
  display_hex_address(object, stream);
  mps_lib_fputc('\n', stream);
}

void display_reference_paths (obj_rec_p or, mps_lib_FILE *stream)
{
  if (or != 0) {
    display_reference_paths(or->parent, stream);
    display_object_reference(or->object, stream);
  }
}

void display_cold_trail_intro (int gensize)
{
  mps_lib_FILE *stream = mps_lib_get_stdout();
  mps_lib_fputs("\nFailed to find any objects referenced from a root.\n", stream);  
  mps_lib_fputs("Hence all detected objects are 'floating garbage'.\n", stream);  
  mps_lib_fputs("There were ", stream);  
  display_integer(gensize, stream);
  mps_lib_fputs(" objects encountered while processing the last depth.\n", stream);  
  mps_lib_fputs("Here are some cold trails:\n", stream);  
}

void display_cold_trail_details (obj_rec_p obj)
{
  mps_lib_FILE *stream = mps_lib_get_stdout();
  mps_lib_fputs("\nStart of a cold trail of object references\n", stream);  
  display_reference_paths(obj, stream);
  mps_lib_fputs("End of cold trail\n", stream);
}

void display_trail_details (obj_rec_p obj, obj_rec_p root)
{
  mps_lib_FILE *stream = mps_lib_get_stdout();
  mps_lib_fputs("\nStart of a trail of object references\n", stream);  
  display_reference_paths(obj, stream);
  mps_lib_fputs("Referenced from a root at address                     ", stream);  
  display_hex_address(root->parent->object, stream);
  mps_lib_fputc('\n', stream);

  mps_lib_fputs("End of trail\n", stream);
}

void display_generation_details (int gennum, int gensize, obj_table table)
{
  int i;
  int count = 0;
  mps_lib_FILE *stream = mps_lib_get_stdout();
  mps_lib_fputs("Processing depth ", stream);
  display_integer(gennum, stream);  
  mps_lib_fputs(" containing ", stream);
  display_integer(gensize, stream);  
  mps_lib_fputs(" objects:\n", stream);
  for (i = 0; i < TABLE_SIZE; i++) {
    obj_rec_p current = table[i];
    while (current != NULL) {
      count++;
      if (count > object_report_limit) {
        mps_lib_fputs("    To display more objects, ", stream);
        mps_lib_fputs("use the debugger to set the variable at address: ", stream);  
        display_hex_address(&object_report_limit, stream);
        mps_lib_fputc('\n', stream);
        return;
      } else {
        display_object_reference(current->object, stream);
      }
      current = current->next;
    }
  }
}

void display_reference_variables_addresses ()
{
  mps_lib_FILE *stream = mps_lib_get_stdout();
  mps_lib_fputs("\nTo find a trail of references to an object, \n", stream);
  mps_lib_fputs("use the debugger to set one of the following variables:\n", stream);  
  mps_lib_fputs("      object_to_follow  - set address ", stream);
  display_hex_address(&object_to_follow, stream);
  mps_lib_fputc('\n', stream);
  mps_lib_fputs("      wrapper_to_follow - set address ", stream);  
  display_hex_address(&wrapper_to_follow, stream);
  mps_lib_fputc('\n', stream);
}

void display_gen_limit_addresses (int gennum)
{
  mps_lib_FILE *stream = mps_lib_get_stdout();
  mps_lib_fputs("\nTo find deeper trails of references to an object, \n", stream);
  mps_lib_fputs("use the debugger to set the following variable\n", stream);  
  mps_lib_fputs("to a depth limit greater than ", stream);  
  display_integer(gennum, stream);  
  mps_lib_fputs(":\n", stream);
  mps_lib_fputs("      depth_limit  - set address ", stream);
  display_hex_address(&depth_limit, stream);
  mps_lib_fputc('\n', stream);
}

void display_trail_limit_addresses (int rootnum)
{
  mps_lib_FILE *stream = mps_lib_get_stdout();
  mps_lib_fputs("\nTo display more trails of references at this depth, \n", stream);
  mps_lib_fputs("use the debugger to set the following variable\n", stream);  
  mps_lib_fputs("to a trail limit greater than ", stream);  
  display_integer(rootnum, stream);  
  mps_lib_fputs(":\n", stream);
  mps_lib_fputs("      trail_limit  - set address ", stream);
  display_hex_address(&trail_limit, stream);
  mps_lib_fputc('\n', stream);
}

void display_cold_trail_limit_addresses (int reported)
{
  mps_lib_FILE *stream = mps_lib_get_stdout();
  mps_lib_fputs("\nTo display more cold trails, \n", stream);
  mps_lib_fputs("use the debugger to set the following variable\n", stream);  
  mps_lib_fputs("to a trail limit greater than ", stream);  
  display_integer(reported, stream);  
  mps_lib_fputs(":\n", stream);
  mps_lib_fputs("      cold_trail_limit  - set address ", stream);
  display_hex_address(&cold_trail_limit, stream);
  mps_lib_fputc('\n', stream);
}

void add_target_object (mps_addr_t obj)
{
  new_obj_rec(obj);
}

void add_target_object_of_wrapper (mps_addr_t object, 
				   mps_fmt_t format, mps_pool_t pool, 
				   void *wrapper, int p2)
{
  void **found = (void**)object;
  void *found_wrapper = *found;
  if (found_wrapper == wrapper) {
    add_target_object(object);
  }
}


void add_target_objects_from_wrapper (void *wrapper)
{
  mps_arena_formatted_objects_walk(arena, &add_target_object_of_wrapper, wrapper, 0);
}



obj_rec_p object_in_roots(mps_addr_t object)
{
  return find_obj_in_table(root_table, object);
}

obj_rec_p object_in_processed_set(mps_addr_t object)
{
  return find_obj_in_table(processed_table, object);
}

obj_rec_p object_in_current_set(mps_addr_t object)
{
  return find_obj_in_table(current_table, object);
}


int size_of_current_set()
{
  return size_of_table(current_table);
}

void advance_generation ()
{
  /* merge the current into the processed set */
  /* then merge the children to make them thecurrent set */
  merge_tables(processed_table, current_table);
  merge_tables(current_table, child_table);
}



void look_for_reference (mps_addr_t object, mps_fmt_t format, mps_pool_t pool, 
			 void *p1, int p2)
{
  mps_addr_t *refs = (mps_addr_t*)object;
  mps_addr_t wrapper = *refs;
  int size;
  int i;
  if ((wrapper == NULL) || ((int)wrapper & 3) != 0) {
    return; /* not a proper object */
  }
  size = traceable_word_size_of_object(object, wrapper);
  for (i = 1; i < size; i++) {
    obj_rec_p refrec = object_in_current_set(refs[i]);
    if (refrec != NULL) {
      /* Have found a ref to the current gen */
      if (object_in_processed_set(object)
          || object_in_current_set(object)) {
	/* Found a reference from a known parent or sibling. */
	/* Since the trail to the parent has already been followed, */
	/* we can be sure that this cannot have any effect */
	/* on whether a child object is a root */
	return;
      } else {
	/* Found an "interesting" reference, so find or make an */
	/* obj_rec for the reference source, and record the references */
        obj_rec_p this_or = new_obj_rec(object);
        this_or->parent = refrec;
        return;
      }
    }
  }
}


/* traverse the heap looking for any objects which reference this generation */
void process_generation ()
{
  mps_arena_formatted_objects_walk(arena, &look_for_reference, NULL, 0);
}



int display_roots_in_current_set ()
{
  int rootnum = 0;
  int i;
  for (i = 0; i < TABLE_SIZE; i++) {
    obj_rec_p current = current_table[i];
    while (current != NULL) {
      obj_rec_p refrec = object_in_roots(current->object);
      if (refrec != NULL) {
        rootnum++;
        if (trail_limit == rootnum) {
          display_trail_limit_addresses(rootnum); /* prompt to increase limit */
          report_break("Set the trail limit to show more if required\n");
        }
        if (trail_limit > rootnum) {
          display_trail_details(current, refrec);
        }
      }
      current = current->next;
    }
  }
  return rootnum;
}

void display_cold_trail()
{
  int size = size_of_current_set();
  int reported = 0;
  int i;
  if (size > 0) {
    /* don't display for null entry sets */
    display_cold_trail_intro(size);
    for (i = 0; i < TABLE_SIZE; i++) {
      obj_rec_p current = current_table[i];
      while (current != NULL) {
        if (cold_trail_limit == reported) {
          /* prompt to increase limit */
          display_cold_trail_limit_addresses(reported); 
          report_break("Set the cold trail limit to show more if required\n");
        }
        if (cold_trail_limit > reported) {
          display_cold_trail_details(current);
        }
        reported++;
        current = current->next;
      }
    }
  }
}

void advance_through_generations ()
{
  int gennum = 0;
  while (TRUE) {
    int roots;
    int gensize;
    gensize = size_of_table(child_table);
    if (gensize == 0) {
      /* failed to find a trail from a root */
      /* We must have etected a clique kept alive by GC conservatism */
      display_cold_trail();
      return;
    }
    display_generation_details(gennum, gensize, child_table);
    advance_generation();
    roots = display_roots_in_current_set();
    if (roots > 0) {
      /* check to see if we really want to continue */
      if (depth_limit <= gennum) {
	display_gen_limit_addresses(gennum); /* prompt to increase limit */
	report_break("Set the depth limit to trace further if required\n");
	if (depth_limit <= gennum) {
	  return;
	}
      }
    }
    process_generation(gennum); /* find children for this generation */
    gennum++;
  }
}
    
void record_a_root (mps_addr_t *objectref, mps_root_t root,
                    void *p1, size_t p2)
{
  void **object = (void **)(*objectref);
  void *wrapper = *object;
  if (wrapper && ((int)wrapper & 3) == 0) {
    obj_rec_p rootrec = object_in_roots(object);
    if (rootrec == NULL) {
      /* Have found a new root - so add to the set */
      new_root(object, objectref);
    }
  }
}


void find_all_roots()
{
  mps_arena_roots_walk(arena, &record_a_root, 0, 0);
}

int follow_reference_paths ()
{
  while (object_to_follow || wrapper_to_follow) {
    clear_object_reference_paths();
    find_all_roots();
    if (wrapper_to_follow) {
      report_message("\nTracing paths to all objects with selected wrapper ...\n");
      add_target_objects_from_wrapper(wrapper_to_follow);
    } else if (object_to_follow) {
      report_message("\nTracing paths to selected object ...\n");
      add_target_object(object_to_follow);
    }
    advance_through_generations();

    object_to_follow = 0;
    wrapper_to_follow = 0;
    if (prompt_after_checking_references) {  
      display_reference_variables_addresses();
      report_break("Set a new object or wrapper to trace if required\n");
    } else {
      return 0;
    }
  }
  return 0;
}


void display_stats_for_memory_usage ()
{
  if (check_wrapper_stats) {
    clear_wrapper_stats();
    mps_arena_formatted_objects_walk(arena, &record_an_object, 0, 0);
    display_wrapper_stats();
  }
  if (check_reference_paths) {
    if (object_to_follow || wrapper_to_follow) {
      follow_reference_paths();
    } else {
      display_reference_variables_addresses();
    }
  }
}



#include "break.c"


void display_wrapper_breakpoints()
{
  mps_lib_FILE *stream = mps_lib_get_stdout();
  if (wrapper_breaks_cursor >= 0)
    {
      int i;
      mps_lib_fputs("Object allocation breakpoints\n\n", stream);
      mps_lib_fputs("   (class-name)                                    (count)\n\n", stream);
      for (i = 0; i < wrapper_breaks_cursor + 1; i++) {
	wrapper_stats_t wrapper_record = wrapper_breaks + i;
	char *class_name = class_name_from_wrapper(wrapper_record->wrapper_address);

	mps_lib_fputs_(class_name, class_name_size, stream);
	display_padding_for_string(class_name, ' ', class_name_size, stream);
	display_integer(wrapper_record->usage_size, stream);
	mps_lib_fputc('\n', stream);
      }
    }
  else
    mps_lib_fputs("No active object allocation breakpoints\n\n", stream);
}
