#include "heap-utils.h"
/* 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
*
*/
/* Controlling variables */
/* if true then prompt for another object/wrapper on each forced GC */
static int prompt_before_checking_references = 0;
/* if true then prompt for another object/wrapper after following a trail */
static int prompt_after_checking_references = 1;
/* set this from the debugger to permit roots to be found at depths
greater than the depth of the first found roots .
*/
static int depth_limit = 0;
/* set this from the debugger to limit the number of trails which can
be reported at any depth depth.
*/
static int trail_limit = 4;
/* set this from the debugger to limit the number of trails which can
be reported when a trail goes cold.
*/
static int cold_trail_limit = 1;
/* set this from the debugger to limit the number of objects which get
reported during processing for each depth.
*/
static int object_report_limit = 4;
/* set this from a debugger if you want to trace to an object */
static void *object_to_follow = 0;
/* set this from a debugger for tracing to the first instance found
with this wrapper.
*/
static void *wrapper_to_follow = 0;
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;
static int hash_val(mps_addr_t obj)
{
return ((int)obj >> TABLE_SHIFT) & TABLE_MASK;
}
static obj_rec_p root_table[TABLE_SIZE] = {NULL}; /* hash table of known roots */
static obj_rec_p processed_table[TABLE_SIZE] = {NULL}; /* hash table of processed objects */
static obj_rec_p current_table[TABLE_SIZE] = {NULL}; /* hash table of current set of objects */
static obj_rec_p child_table[TABLE_SIZE] = {NULL}; /* hash table of child objects */
static obj_rec_p alloc_obj_rec(mps_addr_t obj)
{
obj_rec_p new = alloc_obj(sizeof(obj_rec_st));
new->object = obj;
new->next = NULL;
new->parent = NULL;
return new;
}
static void free_obj_rec(obj_rec_p or)
{
free_obj(or, sizeof(obj_rec_st));
}
static 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;
}
}
static void add_to_table(obj_table table, obj_rec_p objrec)
{
int hash = hash_val(objrec->object);
objrec->next = table[hash];
table[hash] = objrec;
}
static 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 */
static 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;
}
}
}
}
static 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;
}
static 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;
}
static obj_rec_p new_obj_rec(mps_addr_t obj)
{
return add_new_to_table(child_table, obj);
}
static 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;
}
static void clear_object_reference_paths (void)
{
free_table(root_table);
free_table(processed_table);
free_table(current_table);
free_table(child_table);
}
static 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);
}
static 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);
}
}
static 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);
}
static 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);
}
static 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);
}
static 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;
}
}
}
static void display_reference_variables_addresses (void)
{
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);
}
static 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);
}
static 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);
}
static 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);
}
static void add_target_object (mps_addr_t obj)
{
new_obj_rec(obj);
}
static 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);
}
}
extern mps_arena_t arena;
static void add_target_objects_from_wrapper (void *wrapper)
{
mps_arena_formatted_objects_walk(arena, &add_target_object_of_wrapper, wrapper, 0);
}
static obj_rec_p object_in_roots(mps_addr_t object)
{
return find_obj_in_table(root_table, object);
}
static obj_rec_p object_in_processed_set(mps_addr_t object)
{
return find_obj_in_table(processed_table, object);
}
static obj_rec_p object_in_current_set(mps_addr_t object)
{
return find_obj_in_table(current_table, object);
}
static int size_of_current_set()
{
return size_of_table(current_table);
}
static void advance_generation (void)
{
/* 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);
}
static BOOL trace_reference(mps_addr_t object, mps_addr_t parent,
int parent_size, void * env)
{
obj_rec_p refrec = object_in_current_set(object);
if (refrec != NULL) {
/* Have found a ref to the current gen */
if (object_in_processed_set(parent)
|| object_in_current_set(parent)) {
/* 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 FALSE;
} 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(parent);
this_or->parent = refrec;
return FALSE;
}
}
return TRUE;
}
static void look_for_reference(mps_addr_t object, mps_fmt_t format,
mps_pool_t pool,
void *p1, int p2)
{
trace_object(object, trace_reference, NULL);
}
/* traverse the heap looking for any objects which reference this generation */
static void process_generation (void)
{
mps_arena_formatted_objects_walk(arena, &look_for_reference, NULL, 0);
}
static int display_roots_in_current_set (void)
{
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;
}
static 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;
}
}
}
}
static void advance_through_generations (void)
{
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(); /* find children for this generation */
gennum++;
}
}
static 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);
}
}
}
/* Avoid inlining function calls from now on.
* It might confuse the roots walk, by putting extra stuff on the stack.
*/
#pragma inline_depth(0)
/* The following function "cleans" below the stack pointer,
* avoiding any palimpsest problem, whereby spurious "old"
* values turn up
*/
static int recurse_to_clean_the_stack(int count, int dummy1, int dummy2, int dummy3)
{
if (count > 0) {
int res = recurse_to_clean_the_stack(count-1, dummy1, dummy2, dummy3);
return res + dummy1;
} else {
return count;
}
}
static void find_all_roots()
{
recurse_to_clean_the_stack(200, 0, 0, 0);
mps_arena_roots_walk(arena, &record_a_root, 0, 0);
}
static void find_trails()
{
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();
}
static int follow_reference_paths (void)
{
while (object_to_follow || wrapper_to_follow) {
find_all_roots();
find_trails();
object_to_follow = 0;
wrapper_to_follow = 0;
if (prompt_after_checking_references) {
display_reference_variables_addresses();
clear_object_reference_paths();
report_break("Set a new object or wrapper to trace if required\n");
} else {
clear_object_reference_paths();
return 0;
}
}
return 0;
}
void display_reference_trails (void)
{
if (object_to_follow || wrapper_to_follow) {
follow_reference_paths();
} else {
display_reference_variables_addresses();
}
}
syntax highlighted by Code2HTML, v. 0.9.1