353 lines
9.6 KiB
C
353 lines
9.6 KiB
C
|
|
|
|
#ifdef DOTOUT_BUILD
|
|
#include <debug.h>
|
|
|
|
#include <stringtree/new.h>
|
|
#include <stringtree/append.h>
|
|
#include <stringtree/println.h>
|
|
#include <stringtree/free.h>
|
|
|
|
#include <value/struct.h>
|
|
#include <value/prettyprint.h>
|
|
#include <value/foreach_accessible_subvalue.h>
|
|
|
|
#include "struct.h"
|
|
#include "dotout.h"
|
|
|
|
static size_t counter = 0;
|
|
|
|
static const char* value_kind_to_string[] = {
|
|
[vk_uninitalized] = "uninitalized",
|
|
[vk_null] = "null",
|
|
[vk_builtin_lambda] = "builtin-lambda",
|
|
[vk_envrionment] = "environment",
|
|
[vk_identifier] = "identifier",
|
|
[vk_boolean] = "boolean",
|
|
[vk_integer] = "integer",
|
|
[vk_list] = "list",
|
|
[vk_string] = "string",
|
|
[vk_quote] = "quote",
|
|
};
|
|
|
|
void gc_dotout(
|
|
const struct gc* this,
|
|
const char* label_fmt,
|
|
...)
|
|
{
|
|
ENTER;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
sprintf(path, "/tmp/12-%08lu-gc.dot", counter++);
|
|
|
|
dpvs(path);
|
|
|
|
FILE* stream = fopen(path, "w");
|
|
|
|
fprintf(stream, ""
|
|
"digraph {" "\n"
|
|
"node [" "\n"
|
|
"shape = box" "\n"
|
|
"fontcolor = black" "\n"
|
|
"color = black" "\n"
|
|
"fillcolor = white" "\n"
|
|
"style = filled" "\n"
|
|
"]" "\n"
|
|
|
|
"edge [" "\n"
|
|
"color = white" "\n"
|
|
"]" "\n"
|
|
|
|
"fontcolor = white" "\n"
|
|
|
|
"bgcolor = \"#331122\"" "\n"
|
|
|
|
"overlap = false" "\n"
|
|
|
|
"rankdir = LR" "\n"
|
|
"");
|
|
|
|
// print label:
|
|
{
|
|
fprintf(stream, "label = \"");
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, label_fmt);
|
|
|
|
vfprintf(stream, label_fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fprintf(stream, "\"" "\n");
|
|
}
|
|
|
|
#if 0
|
|
fprintf(stream, ""
|
|
"a [" "\n"
|
|
"label = \"total_bytes_currently_allocated = %lu\"" "\n"
|
|
"]" "\n"
|
|
"", this->bytes_currently_allocated);
|
|
|
|
fprintf(stream, ""
|
|
"b [" "\n"
|
|
"label = \"last_garbage_collection = %lu\"" "\n"
|
|
"]" "\n"
|
|
"", this->bytes_of_last_garbage_collection);
|
|
|
|
fprintf(stream, ""
|
|
"internally_referenced_head [ label = \"internally_referenced head\" ]" "\n"
|
|
"internally_referenced_tail [ label = \"internally_referenced tail\" ]" "\n"
|
|
"");
|
|
|
|
fprintf(stream, ""
|
|
"reaped_head [ label = \"reaped head\" ]" "\n"
|
|
"reaped_tail [ label = \"reaped tail\" ]" "\n"
|
|
"");
|
|
|
|
fprintf(stream, ""
|
|
"grey_head [ label = \"grey head\" ]" "\n"
|
|
"grey_tail [ label = \"grey tail\" ]" "\n"
|
|
"");
|
|
|
|
fprintf(stream, ""
|
|
"white_head [ label = \"white head\" ]" "\n"
|
|
"white_tail [ label = \"white tail\" ]" "\n"
|
|
"");
|
|
|
|
fprintf(stream, ""
|
|
"externally_referenced_head [ label = \"externally_referenced head\" ]" "\n"
|
|
"externally_referenced_tail [ label = \"externally_referenced tail\" ]" "\n"
|
|
"");
|
|
#endif
|
|
|
|
#if 1
|
|
if (this->internally_referenced.head)
|
|
{
|
|
fprintf(stream, ""
|
|
"internally_referenced_head:e -> \"%p\":w [ style = dashed ]" "\n"
|
|
|
|
"\"%p\":e -> internally_referenced_tail:w [ style = dashed ]" "\n"
|
|
"", this->internally_referenced.head, this->internally_referenced.tail);
|
|
}
|
|
|
|
if (this->reaped.head)
|
|
{
|
|
fprintf(stream, ""
|
|
"reaped_head:e -> \"%p\":w [ style = dashed ]" "\n"
|
|
|
|
"\"%p\":e -> reaped_tail:w [ style = dashed ]" "\n"
|
|
"", this->reaped.head, this->reaped.tail);
|
|
}
|
|
|
|
if (this->grey.head)
|
|
{
|
|
fprintf(stream, ""
|
|
"grey_head:e -> \"%p\":w [ style = dashed ]" "\n"
|
|
|
|
"\"%p\":e -> grey_tail:w [ style = dashed ]" "\n"
|
|
"", this->grey.head, this->grey.tail);
|
|
}
|
|
|
|
if (this->white.head)
|
|
{
|
|
fprintf(stream, ""
|
|
"white_head:e -> \"%p\":w [ style = dashed ]" "\n"
|
|
|
|
"\"%p\":e -> white_tail:w [ style = dashed ]" "\n"
|
|
"", this->white.head, this->white.tail);
|
|
}
|
|
|
|
if (this->externally_referenced.head)
|
|
{
|
|
fprintf(stream, ""
|
|
"externally_referenced_head:e -> \"%p\":w [ style = dashed ]" "\n"
|
|
|
|
"\"%p\":e -> externally_referenced_tail:w [ style = dashed ]" "\n"
|
|
"", this->externally_referenced.head, this->externally_referenced.tail);
|
|
}
|
|
#endif
|
|
|
|
for (size_t i = 0; i < this->blocks.n; i++)
|
|
{
|
|
struct block_info info = this->blocks.data[i];
|
|
|
|
struct value* start = info.start;
|
|
|
|
for (size_t j = 0, m = info.cap; j < m; j++)
|
|
{
|
|
struct value* value = &start[j];
|
|
|
|
const char* kindstring = value_kind_to_string[value->kind];
|
|
|
|
if (!kindstring)
|
|
{
|
|
dpvu(value->kind);
|
|
|
|
TODO;
|
|
}
|
|
|
|
struct stringtree* tree = new_stringtree();
|
|
|
|
stringtree_append_formatstr(tree, "\"%p\" [", value);
|
|
|
|
stringtree_append_formatstr(tree, "shape = record" "\n");
|
|
|
|
if (value->grey.in_set)
|
|
{
|
|
stringtree_append_formatstr(tree, "fillcolor = grey" "\n");
|
|
}
|
|
else if (value->white.in_set)
|
|
{
|
|
stringtree_append_formatstr(tree, "fillcolor = white" "\n");
|
|
}
|
|
else if (value->reaped.in_set)
|
|
{
|
|
stringtree_append_formatstr(tree, "fillcolor = purple" "\n");
|
|
}
|
|
else if (value->externally_referenced.in_set)
|
|
{
|
|
stringtree_append_formatstr(tree, "fillcolor = green" "\n");
|
|
}
|
|
else
|
|
{
|
|
stringtree_append_formatstr(tree, "fillcolor = darkgreen" "\n");
|
|
}
|
|
|
|
stringtree_append_formatstr(tree, "label = \"");
|
|
|
|
stringtree_append_formatstr(tree, "(%lu, %lu)", i, j);
|
|
|
|
#if 0
|
|
if (value->kind != vk_uninitalized)
|
|
{
|
|
struct stringtree* pprint = value_prettyprint(value);
|
|
|
|
stringtree_append_formatstr(tree, "| ");
|
|
|
|
stringtree_append_stringtree(tree, pprint);
|
|
|
|
free_stringtree(pprint);
|
|
}
|
|
#endif
|
|
|
|
#if 1
|
|
stringtree_append_formatstr(tree, "| kind = %s", kindstring);
|
|
#endif
|
|
|
|
if (value->externally_referenced.in_set)
|
|
{
|
|
stringtree_append_formatstr(tree, "| refcount = %lu",
|
|
value->external_refcount);
|
|
}
|
|
else
|
|
{
|
|
assert(value->external_refcount == 0);
|
|
}
|
|
|
|
if (value->internally_referenced.in_set)
|
|
{
|
|
stringtree_append_string_const(tree, "| internally_referenced");
|
|
}
|
|
|
|
if (value->reaped.in_set)
|
|
{
|
|
stringtree_append_string_const(tree, "| reaped");
|
|
}
|
|
|
|
if (value->white.in_set)
|
|
{
|
|
stringtree_append_string_const(tree, "| white");
|
|
}
|
|
|
|
if (value->grey.in_set)
|
|
{
|
|
stringtree_append_string_const(tree, "| grey");
|
|
}
|
|
|
|
if (value->externally_referenced.in_set)
|
|
{
|
|
stringtree_append_string_const(tree, "| externally_referenced");
|
|
}
|
|
|
|
stringtree_append_formatstr(tree, "\"" "\n");
|
|
|
|
stringtree_append_formatstr(tree, "]");
|
|
|
|
stringtree_println(tree, stream);
|
|
|
|
if (value->kind != vk_uninitalized)
|
|
{
|
|
value_foreach_accessible_subvalue(value, ({
|
|
void callback(struct value* subvalue)
|
|
{
|
|
fprintf(stream, ""
|
|
"\"%p\":e -> \"%p\":w [ style = solid ]" "\n"
|
|
"", value, subvalue);
|
|
}
|
|
callback;
|
|
}));
|
|
}
|
|
|
|
#if 1
|
|
if (value->internally_referenced.next)
|
|
{
|
|
fprintf(stream, ""
|
|
"\"%p\":e -> \"%p\":w [ style = dashed]" "\n"
|
|
"", value, value->internally_referenced.next);
|
|
}
|
|
|
|
if (value->reaped.next)
|
|
{
|
|
fprintf(stream, ""
|
|
"\"%p\":e -> \"%p\":w [ style = dashed ]" "\n"
|
|
"", value, value->reaped.next);
|
|
}
|
|
|
|
if (value->grey.next)
|
|
{
|
|
fprintf(stream, ""
|
|
"\"%p\":e -> \"%p\":w [ style = dashed ]" "\n"
|
|
"", value, value->grey.next);
|
|
}
|
|
|
|
if (value->white.next)
|
|
{
|
|
fprintf(stream, ""
|
|
"\"%p\":e -> \"%p\":w [ style = dashed ]" "\n"
|
|
"", value, value->white.next);
|
|
}
|
|
|
|
if (value->externally_referenced.next)
|
|
{
|
|
fprintf(stream, ""
|
|
"\"%p\":e -> \"%p\":w [ style = dashed ]" "\n"
|
|
"", value, value->externally_referenced.next);
|
|
}
|
|
#endif
|
|
|
|
free_stringtree(tree);
|
|
}
|
|
}
|
|
|
|
fprintf(stream, ""
|
|
"}" "\n"
|
|
"");
|
|
|
|
fclose(stream);
|
|
|
|
EXIT;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|