#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);
}
syntax highlighted by Code2HTML, v. 0.9.1