Implement global "gc" function
To dump the current GC state and do a manual collection
This commit is contained in:
parent
7c5465d266
commit
f0811ba8fe
5 changed files with 109 additions and 35 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
11
src/format.c
11
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);
|
||||
}
|
||||
|
|
|
|||
109
src/gc.c
109
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
|
||||
|
|
|
|||
3
src/gc.h
3
src/gc.h
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue