amduat-pel: add edges dump command

This commit is contained in:
Carl Niklas Rydberg 2026-01-23 20:58:09 +01:00
parent d9122b53bb
commit 337466b073

View file

@ -53,6 +53,17 @@ enum {
};
static const char *const AMDUAT_PEL_CLI_DEFAULT_ROOT = ".amduat-asl";
static const char *const AMDUAT_PEL_CLI_EDGE_SCHEMA = "tgk/edge";
enum {
AMDUAT_PEL_CLI_EDGE_MAGIC_LEN = 8,
AMDUAT_PEL_CLI_EDGE_VERSION = 1
};
static const uint8_t k_amduat_pel_cli_edge_magic[
AMDUAT_PEL_CLI_EDGE_MAGIC_LEN] = {
'A', 'S', 'L', 'E', 'D', 'G', '1', '\0'
};
typedef struct {
const char *root;
@ -81,6 +92,7 @@ static void amduat_pel_cli_print_usage(FILE *stream) {
" matcache Materialization cache tools (get, sid).\n"
" ptr Named pointer tools (get, cas).\n"
" scheme Scheme refs, type tags, profile IDs.\n"
" edges Edge record tools (dump).\n"
" help Show help for a command.\n"
"\n"
"global options:\n"
@ -157,6 +169,9 @@ static void amduat_pel_cli_print_usage(FILE *stream) {
" amduat-pel col view --name NAME --from OFFSET --limit N\n"
" [--dump-program-ref]\n"
"\n"
"edges:\n"
" amduat-pel edges dump [--limit N]\n"
"\n"
"scheme:\n"
" amduat-pel scheme show [--format text|json] [--ref-format hex|bytes]\n"
" amduat-pel scheme dag-ref [--format hex|bytes]\n"
@ -3378,6 +3393,123 @@ static void amduat_pel_cli_program_free(amduat_pel_program_t *program) {
program->roots_len = 0u;
}
static bool amduat_pel_cli_read_u32_le(const uint8_t *data,
size_t len,
size_t *offset,
uint32_t *out) {
if (len - *offset < 4u) {
return false;
}
*out = (uint32_t)data[*offset] |
((uint32_t)data[*offset + 1u] << 8) |
((uint32_t)data[*offset + 2u] << 16) |
((uint32_t)data[*offset + 3u] << 24);
*offset += 4u;
return true;
}
static bool amduat_pel_cli_is_ascii(amduat_octets_t bytes) {
if (bytes.len == 0u || bytes.data == NULL) {
return false;
}
for (size_t i = 0u; i < bytes.len; ++i) {
uint8_t c = bytes.data[i];
if (c < 0x20u || c > 0x7eu) {
return false;
}
}
return true;
}
static bool amduat_pel_cli_edge_payload_decode(amduat_octets_t payload,
amduat_reference_t *out_src,
amduat_reference_t *out_dst,
char **out_rel) {
size_t offset = 0u;
uint32_t version = 0u;
uint32_t src_len = 0u;
uint32_t dst_len = 0u;
uint32_t rel_len = 0u;
uint8_t flags = 0u;
amduat_octets_t src_bytes;
amduat_octets_t dst_bytes;
char *rel = NULL;
if (out_src == NULL || out_dst == NULL || out_rel == NULL) {
return false;
}
*out_src = amduat_reference(0u, amduat_octets(NULL, 0u));
*out_dst = amduat_reference(0u, amduat_octets(NULL, 0u));
*out_rel = NULL;
if (payload.len < AMDUAT_PEL_CLI_EDGE_MAGIC_LEN + 4u + 1u ||
payload.data == NULL) {
return false;
}
if (memcmp(payload.data, k_amduat_pel_cli_edge_magic,
AMDUAT_PEL_CLI_EDGE_MAGIC_LEN) != 0) {
return false;
}
offset += AMDUAT_PEL_CLI_EDGE_MAGIC_LEN;
if (!amduat_pel_cli_read_u32_le(payload.data, payload.len, &offset,
&version) ||
version != AMDUAT_PEL_CLI_EDGE_VERSION) {
return false;
}
if (!amduat_pel_cli_read_u32_le(payload.data, payload.len, &offset,
&src_len)) {
return false;
}
if (payload.len - offset < src_len) {
return false;
}
src_bytes = amduat_octets(payload.data + offset, src_len);
offset += src_len;
if (!amduat_pel_cli_read_u32_le(payload.data, payload.len, &offset,
&dst_len)) {
return false;
}
if (payload.len - offset < dst_len) {
return false;
}
dst_bytes = amduat_octets(payload.data + offset, dst_len);
offset += dst_len;
if (!amduat_pel_cli_read_u32_le(payload.data, payload.len, &offset,
&rel_len)) {
return false;
}
if (payload.len - offset < rel_len + 1u) {
return false;
}
if (!amduat_pel_cli_is_ascii(
amduat_octets(payload.data + offset, rel_len))) {
return false;
}
rel = (char *)malloc(rel_len + 1u);
if (rel == NULL) {
return false;
}
memcpy(rel, payload.data + offset, rel_len);
rel[rel_len] = '\0';
offset += rel_len;
flags = payload.data[offset++];
(void)flags;
if (offset != payload.len) {
free(rel);
return false;
}
if (!amduat_enc_asl1_core_decode_reference_v1(src_bytes, out_src) ||
!amduat_enc_asl1_core_decode_reference_v1(dst_bytes, out_dst)) {
amduat_pel_cli_free_reference(out_src);
amduat_pel_cli_free_reference(out_dst);
free(rel);
return false;
}
*out_rel = rel;
return true;
}
static bool amduat_pel_cli_build_collection_view_program(
amduat_pel_program_t *out_program) {
static const char k_op_snapshot[] = "collection.snapshot_decode_v1";
@ -3469,6 +3601,159 @@ static bool amduat_pel_cli_build_collection_view_program(
return true;
}
static bool amduat_pel_cli_collection_view(
amduat_asl_store_t *store,
amduat_asl_pointer_store_t *pointer_store,
const char *name,
uint64_t from,
uint32_t limit,
amduat_asl_collection_view_t *out_view) {
amduat_reference_t snapshot_ref;
amduat_reference_t log_head_ref;
amduat_reference_t none_ref;
amduat_reference_t params_ref;
amduat_reference_t program_ref;
amduat_reference_t input_refs[4];
amduat_pel_program_t program;
amduat_octets_t program_bytes = amduat_octets(NULL, 0u);
amduat_pel_run_result_t run_result;
amduat_artifact_t view_artifact;
amduat_asl_collection_view_t view;
amduat_type_tag_t expected_type_tag;
amduat_asl_encoding_profile_id_t expected_profile;
char *snapshot_name = NULL;
char *log_head_name = NULL;
bool snapshot_exists = false;
bool log_head_exists = false;
bool ok = false;
if (out_view == NULL) {
return false;
}
memset(out_view, 0, sizeof(*out_view));
memset(&snapshot_ref, 0, sizeof(snapshot_ref));
memset(&log_head_ref, 0, sizeof(log_head_ref));
memset(&none_ref, 0, sizeof(none_ref));
memset(&params_ref, 0, sizeof(params_ref));
memset(&program_ref, 0, sizeof(program_ref));
memset(&run_result, 0, sizeof(run_result));
memset(&view, 0, sizeof(view));
if (store == NULL || pointer_store == NULL || name == NULL) {
return false;
}
if (!amduat_pel_cli_get_none_ref(store, &none_ref)) {
return false;
}
if (!amduat_pel_cli_build_collection_head_name(name, &snapshot_name) ||
!amduat_pel_cli_build_collection_log_head_name(name, &log_head_name)) {
goto view_cleanup;
}
if (amduat_asl_pointer_get(pointer_store, snapshot_name,
&snapshot_exists, &snapshot_ref) !=
AMDUAT_ASL_POINTER_OK) {
goto view_cleanup;
}
if (!snapshot_exists &&
!amduat_reference_clone(none_ref, &snapshot_ref)) {
goto view_cleanup;
}
if (amduat_asl_pointer_get(pointer_store, log_head_name,
&log_head_exists, &log_head_ref) !=
AMDUAT_ASL_POINTER_OK) {
goto view_cleanup;
}
if (!log_head_exists &&
!amduat_reference_clone(none_ref, &log_head_ref)) {
goto view_cleanup;
}
if (!amduat_pel_cli_put_view_params(store, from, limit, &params_ref)) {
goto view_cleanup;
}
if (!amduat_pel_cli_build_collection_view_program(&program)) {
goto view_cleanup;
}
if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) {
amduat_pel_cli_program_free(&program);
goto view_cleanup;
}
amduat_pel_cli_program_free(&program);
if (!amduat_pel_program_dag_desc_get_program_binding(&expected_type_tag,
&expected_profile)) {
goto view_cleanup;
}
if (expected_profile != AMDUAT_PEL_ENC_PROGRAM_DAG_V1) {
goto view_cleanup;
}
{
amduat_artifact_t program_artifact =
amduat_artifact_with_type(program_bytes, expected_type_tag);
if (amduat_asl_store_put(store, program_artifact, &program_ref) !=
AMDUAT_ASL_STORE_OK) {
goto view_cleanup;
}
}
input_refs[0] = snapshot_ref;
input_refs[1] = log_head_ref;
input_refs[2] = params_ref;
input_refs[3] = params_ref;
if (!amduat_pel_surf_run_with_result(store,
amduat_pel_program_dag_scheme_ref(),
program_ref,
input_refs,
4u,
true,
params_ref,
&run_result)) {
goto view_cleanup;
}
if (!run_result.has_result_value ||
run_result.result_value.core_result.status != AMDUAT_PEL_EXEC_STATUS_OK ||
run_result.output_refs_len != 1u) {
goto view_cleanup;
}
memset(&view_artifact, 0, sizeof(view_artifact));
if (amduat_asl_store_get(store, run_result.output_refs[0],
&view_artifact) != AMDUAT_ASL_STORE_OK) {
goto view_cleanup;
}
if (!view_artifact.has_type_tag ||
view_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_COLLECTION_VIEW_1 ||
!amduat_asl_collection_view_decode_v1(view_artifact.bytes, &view)) {
amduat_artifact_free(&view_artifact);
goto view_cleanup;
}
amduat_artifact_free(&view_artifact);
*out_view = view;
memset(&view, 0, sizeof(view));
ok = true;
view_cleanup:
amduat_asl_collection_view_free(&view);
if (run_result.has_result_value) {
amduat_enc_pel1_result_free(&run_result.result_value);
}
if (run_result.output_refs != NULL) {
amduat_pel_surf_free_refs(run_result.output_refs,
run_result.output_refs_len);
}
amduat_pel_surf_free_ref(&run_result.result_ref);
amduat_pel_cli_free_reference(&snapshot_ref);
amduat_pel_cli_free_reference(&log_head_ref);
amduat_pel_cli_free_reference(&none_ref);
amduat_pel_cli_free_reference(&params_ref);
amduat_pel_cli_free_reference(&program_ref);
free((void *)program_bytes.data);
free(snapshot_name);
free(log_head_name);
return ok;
}
static int amduat_pel_cli_cmd_log_append(
int argc,
char **argv,
@ -4435,6 +4720,140 @@ static int amduat_pel_cli_cmd_col(
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
static int amduat_pel_cli_cmd_edges_dump(
int argc,
char **argv,
const amduat_pel_cli_global_opts_t *global) {
uint32_t limit = UINT32_MAX;
amduat_asl_store_t store;
amduat_asl_store_fs_t fs;
amduat_asl_pointer_store_t pointer_store;
amduat_asl_collection_view_t view;
int i;
if (global == NULL) {
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
for (i = 0; i < argc; ++i) {
if (strcmp(argv[i], "--limit") == 0) {
unsigned long long tmp;
if (i + 1 >= argc) {
fprintf(stderr, "error: --limit requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
tmp = strtoull(argv[++i], NULL, 10);
if (tmp > UINT32_MAX) {
fprintf(stderr, "error: --limit out of range\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
limit = (uint32_t)tmp;
} else if (strcmp(argv[i], "--help") == 0 ||
strcmp(argv[i], "-h") == 0) {
amduat_pel_cli_print_usage(stdout);
return AMDUAT_PEL_CLI_EXIT_OK;
} else {
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
}
if (!amduat_pel_cli_init_store(global->root, &store, &fs)) {
fprintf(stderr, "error: failed to initialize store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
if (!amduat_asl_pointer_store_init(&pointer_store, global->root)) {
fprintf(stderr, "error: failed to init pointer store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
memset(&view, 0, sizeof(view));
if (!amduat_pel_cli_collection_view(&store,
&pointer_store,
"daemon/edges",
0u,
limit,
&view)) {
fprintf(stderr, "error: failed to compute collection view\n");
return AMDUAT_PEL_CLI_EXIT_STORE;
}
for (size_t idx = 0u; idx < view.refs_len; ++idx) {
amduat_reference_t record_ref = view.refs[idx];
amduat_asl_record_t record;
amduat_reference_t src_ref;
amduat_reference_t dst_ref;
char *rel = NULL;
amduat_asl_store_error_t err;
bool schema_ok = false;
memset(&record, 0, sizeof(record));
err = amduat_asl_record_store_get(&store, record_ref, &record);
if (err != AMDUAT_ASL_STORE_OK) {
continue;
}
if (record.schema.len == strlen(AMDUAT_PEL_CLI_EDGE_SCHEMA) &&
memcmp(record.schema.data,
AMDUAT_PEL_CLI_EDGE_SCHEMA,
record.schema.len) == 0) {
schema_ok = true;
}
if (!schema_ok ||
!amduat_pel_cli_edge_payload_decode(record.payload,
&src_ref,
&dst_ref,
&rel)) {
amduat_asl_record_free(&record);
continue;
}
amduat_asl_record_free(&record);
fputs(rel, stdout);
fputc(' ', stdout);
if (!amduat_format_ref_write_text(stdout, src_ref,
global->ref_format)) {
fprintf(stderr, "error: failed to format ref\n");
free(rel);
amduat_pel_cli_free_reference(&src_ref);
amduat_pel_cli_free_reference(&dst_ref);
amduat_asl_collection_view_free(&view);
return AMDUAT_PEL_CLI_EXIT_CODEC;
}
fputs(" -> ", stdout);
if (!amduat_format_ref_write_text(stdout, dst_ref,
global->ref_format)) {
fprintf(stderr, "error: failed to format ref\n");
free(rel);
amduat_pel_cli_free_reference(&src_ref);
amduat_pel_cli_free_reference(&dst_ref);
amduat_asl_collection_view_free(&view);
return AMDUAT_PEL_CLI_EXIT_CODEC;
}
fputc('\n', stdout);
free(rel);
amduat_pel_cli_free_reference(&src_ref);
amduat_pel_cli_free_reference(&dst_ref);
}
amduat_asl_collection_view_free(&view);
return AMDUAT_PEL_CLI_EXIT_OK;
}
static int amduat_pel_cli_cmd_edges(
int argc,
char **argv,
const amduat_pel_cli_global_opts_t *global) {
if (argc < 1) {
fprintf(stderr, "error: edges requires a subcommand\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (strcmp(argv[0], "dump") == 0) {
return amduat_pel_cli_cmd_edges_dump(argc - 1, argv + 1, global);
}
fprintf(stderr, "error: unknown edges subcommand: %s\n", argv[0]);
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
static int amduat_pel_cli_cmd_help(int argc, char **argv) {
(void)argc;
(void)argv;
@ -4531,6 +4950,9 @@ int main(int argc, char **argv) {
if (strcmp(command, "col") == 0) {
return amduat_pel_cli_cmd_col(argc - i, argv + i, &global);
}
if (strcmp(command, "edges") == 0) {
return amduat_pel_cli_cmd_edges(argc - i, argv + i, &global);
}
if (strcmp(command, "matcache") == 0) {
return amduat_pel_cli_cmd_matcache(argc - i, argv + i, &global);
}