diff --git a/src/apfl.h b/src/apfl.h index f2edde6..d6f4251 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -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 diff --git a/src/format.c b/src/format.c index 6b52860..ff5c7e3 100644 --- a/src/format.c +++ b/src/format.c @@ -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); +} diff --git a/src/gc.c b/src/gc.c index 67be04f..b1dd3f7 100644 --- a/src/gc.c +++ b/src/gc.c @@ -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 diff --git a/src/gc.h b/src/gc.h index 168b868..8573280 100644 --- a/src/gc.h +++ b/src/gc.h @@ -8,6 +8,7 @@ extern "C" { #include #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); diff --git a/src/globals.c b/src/globals.c index 2c501f4..d234588 100644 --- a/src/globals.c +++ b/src/globals.c @@ -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}, };