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

#include "heap-utils.h"

#define unused(param)   ((void)param)

wrapper_stats_s wrapper_stats[STAT_SIZE];

static int wrapper_cursor;
static int wrapper_preassignments;


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

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

void clear_wrapper_stats (void)
{
  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);
  */
}

static 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);
}


static 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);
  unused(object);
  wrapper_stats[index].usage_count += 1;
  wrapper_stats[index].usage_size += size;
}

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

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

static 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;
}


static 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);
}

static 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);
}


static 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);
    }
  }
}

static 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 (void)
{
  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);
}


void display_heap_stats_order_1 (void)
{
  clear_wrapper_stats();
  mps_arena_formatted_objects_walk(arena, &record_order_1_object, 0, 0);
  display_wrapper_stats();
}
