Implement global "gc" function

To dump the current GC state and do a manual collection
This commit is contained in:
Laria 2022-11-19 22:07:26 +01:00
parent 7c5465d266
commit f0811ba8fe
5 changed files with 109 additions and 35 deletions

View file

@ -142,6 +142,7 @@ bool apfl_format_put_hexbyte(struct apfl_format_writer, unsigned char);
bool apfl_format_put_pos(struct apfl_format_writer, struct apfl_position);
bool apfl_format_put_indent(struct apfl_format_writer, unsigned);
bool apfl_format_put_number(struct apfl_format_writer, apfl_number);
bool apfl_format_put_poiner(struct apfl_format_writer, void *);
// Tokens

View file

@ -114,3 +114,14 @@ apfl_format_put_number(struct apfl_format_writer w, apfl_number number)
}
return true;
}
#define PUT_POINTER_BUFSIZE 34 // Enough for "0x" + 32 hex digits, so enough for a 128-bit pointer.
bool
apfl_format_put_poiner(struct apfl_format_writer w, void *ptr)
{
char buf[PUT_POINTER_BUFSIZE];
size_t len = snprintf(buf, PUT_POINTER_BUFSIZE, "%p", ptr);
assert(len < PUT_POINTER_BUFSIZE);
return write(w, buf, len);
}

109
src/gc.c
View file

@ -434,7 +434,7 @@ sweep(struct gc *gc)
}
#ifdef GC_DEBUG_DUMP_GRAPH_ON_COLLECT
# define DUMP_ON_COLLECT() apfl_gc_debug_dump_graph(gc, stderr)
# define DUMP_ON_COLLECT() apfl_gc_debug_dump_graph(gc, apfl_format_file_writer(stderr))
#else
# define DUMP_ON_COLLECT()
#endif
@ -517,30 +517,50 @@ type_to_string(enum gc_type type)
return "???";
}
struct dump_graph_roots_visitor_data {
struct apfl_format_writer w;
bool success;
};
static void
dump_graph_roots_visitor(void *opaque, struct gc_object *obj)
{
FILE *out = opaque;
fprintf(out, "ROOTS -> obj_%p;\n", (void *)obj);
struct dump_graph_roots_visitor_data *data = opaque;
data->success = data->success
&& apfl_format_put_string(data->w, " ROOTS -> obj_")
&& apfl_format_put_poiner(data->w, (void *)obj)
&& apfl_format_put_string(data->w, "\n");
}
struct dump_graph_visitor_data {
FILE *out;
struct apfl_format_writer w;
struct gc_object *parent;
bool success;
};
static void
dump_graph_visitor(void *opaque, struct gc_object *obj)
{
struct dump_graph_visitor_data *data = opaque;
fprintf(data->out, "obj_%p -> obj_%p\n", (void *)data->parent, (void *)obj);
data->success = data->success
&& apfl_format_put_string(data->w, " obj_")
&& apfl_format_put_poiner(data->w, (void *)data->parent)
&& apfl_format_put_string(data->w, " -> obj_")
&& apfl_format_put_poiner(data->w, (void *)obj)
&& apfl_format_put_string(data->w, "\n");
}
void
apfl_gc_debug_dump_graph(struct gc *gc, FILE *out)
bool
apfl_gc_debug_dump_graph(struct gc *gc, struct apfl_format_writer w)
{
fprintf(out, "digraph G {\n");
visit_roots(gc, dump_graph_roots_visitor, out);
FMT_TRY(apfl_format_put_string(w, "digraph G {\n"));
struct dump_graph_roots_visitor_data roots_visitor_data = {
.w = w,
.success = true,
};
visit_roots(gc, dump_graph_roots_visitor, &roots_visitor_data);
FMT_TRY(roots_visitor_data.success);
for (struct gc_block *block = gc->block; block != NULL; block = block->next) {
int counts[4] = {0, 0, 0, 0};
@ -552,36 +572,57 @@ apfl_gc_debug_dump_graph(struct gc *gc, FILE *out)
continue;
}
fprintf(out, "blk_%p -> obj_%p;\n", (void *)block, (void *)obj);
fprintf(
out,
"obj_%p [style=filled,fillcolor=%s,fontcolor=%s,label=\"Object %p\\ntype: %s\"];\n",
(void *)obj,
dump_graph_bgcolor(obj->status),
dump_graph_fgcolor(obj->status),
(void *)obj,
type_to_string(obj->type)
);
FMT_TRY(apfl_format_put_string(w, " blk_"));
FMT_TRY(apfl_format_put_poiner(w, (void *)block));
FMT_TRY(apfl_format_put_string(w, " -> obj_"));
FMT_TRY(apfl_format_put_poiner(w, (void *)obj));
FMT_TRY(apfl_format_put_string(w, "\n"));
visit_children(obj, dump_graph_visitor, &(struct dump_graph_visitor_data) {
.out = out,
FMT_TRY(apfl_format_put_string(w, " obj_"));
FMT_TRY(apfl_format_put_poiner(w, (void *)obj));
FMT_TRY(apfl_format_put_string(w, "[style=filled,fillcolor="));
FMT_TRY(apfl_format_put_string(w, dump_graph_bgcolor(obj->status)));
FMT_TRY(apfl_format_put_string(w, ",fontcolor="));
FMT_TRY(apfl_format_put_string(w, dump_graph_fgcolor(obj->status)));
FMT_TRY(apfl_format_put_string(w, ",label=\"Object "));
FMT_TRY(apfl_format_put_poiner(w, (void *)obj));
FMT_TRY(apfl_format_put_string(w, "\\ntype: "));
FMT_TRY(apfl_format_put_string(w, type_to_string(obj->type)));
FMT_TRY(apfl_format_put_string(w, "\"];\n"));
struct dump_graph_visitor_data visitor_data = {
.w = w,
.parent = obj,
});
.success = true,
};
visit_children(obj, dump_graph_visitor, &visitor_data);
FMT_TRY(visitor_data.success);
}
fprintf(out, "BLOCKS -> blk_%p;\n", (void *)block);
fprintf(
out,
"blk_%p [label=\"Block %p\\nfree %d, black %d, grey %d, white %d\"];\n",
(void *)block,
(void *)block,
counts[GC_STATUS_FREE],
counts[GC_STATUS_BLACK],
counts[GC_STATUS_GREY],
counts[GC_STATUS_WHITE]
);
FMT_TRY(apfl_format_put_string(w, " BLOCKS -> blk_"));
FMT_TRY(apfl_format_put_poiner(w, (void *)block));
FMT_TRY(apfl_format_put_string(w, ";\n"));
FMT_TRY(apfl_format_put_string(w, " blk_"));
FMT_TRY(apfl_format_put_poiner(w, (void *)block));
FMT_TRY(apfl_format_put_string(w, " [label=\"Block "));
FMT_TRY(apfl_format_put_poiner(w, (void *)block));
FMT_TRY(apfl_format_put_string(w, "\\nfree "));
FMT_TRY(apfl_format_put_int(w, counts[GC_STATUS_FREE]));
FMT_TRY(apfl_format_put_string(w, ", black "));
FMT_TRY(apfl_format_put_int(w, counts[GC_STATUS_BLACK]));
FMT_TRY(apfl_format_put_string(w, ", grey "));
FMT_TRY(apfl_format_put_int(w, counts[GC_STATUS_GREY]));
FMT_TRY(apfl_format_put_string(w, ", white "));
FMT_TRY(apfl_format_put_int(w, counts[GC_STATUS_WHITE]));
FMT_TRY(apfl_format_put_string(w, "\"];\n"));
}
fprintf(out, "}\n");
FMT_TRY(apfl_format_put_string(w, "}\n"));
return true;
}
void

View file

@ -8,6 +8,7 @@ extern "C" {
#include <stdint.h>
#include "apfl.h"
#include "format.h"
#include "hashmap.h"
@ -68,7 +69,7 @@ struct gc_object *apfl_gc_object_from_ptr(void *, enum gc_type);
void apfl_gc_init(struct gc *, struct apfl_allocator, gc_roots_getter, void *roots_getter_opaque);
void apfl_gc_deinit(struct gc *);
void apfl_gc_debug_dump_graph(struct gc *, FILE *);
bool apfl_gc_debug_dump_graph(struct gc *, struct apfl_format_writer);
size_t apfl_gc_tmproots_begin(struct gc *gc);
void apfl_gc_tmproots_restore(struct gc *gc, size_t);

View file

@ -310,6 +310,25 @@ impl_while(apfl_ctx ctx)
}
}
static void
impl_gc(apfl_ctx ctx)
{
ONE_ARG(ctx, "gc");
apfl_tostring(ctx, -1);
struct apfl_string_view sv = apfl_get_string(ctx, -1);
if (apfl_string_eq(sv, "dump")) {
struct apfl_format_writer w = apfl_get_output_writer(ctx);
TRY_FORMAT(ctx, apfl_gc_debug_dump_graph(&ctx->gc, w));
} else if (apfl_string_eq(sv, "collect")) {
apfl_gc_full(&ctx->gc);
} else {
apfl_raise_const_error(ctx, APFL_RESULT_ERR, "Unknown gc command");
}
apfl_drop(ctx, -1);
}
static const struct global_def globals[] = {
{"if", impl_if},
{"==", impl_eq},
@ -325,6 +344,7 @@ static const struct global_def globals[] = {
{"len", len},
{"type", type},
{"while", impl_while},
{"gc", impl_gc},
{NULL, NULL},
};