From 641c7a1d13c08eb175f1cd1eaa3fb149df838d2f Mon Sep 17 00:00:00 2001 From: Timo Paulssen Date: Wed, 25 Apr 2018 17:37:10 +0200 Subject: [PATCH] debugserver: add a "handle equivalence class" command lets you find out if handles actually refer to the same object. --- docs/debug-server-protocol.md | 27 ++++++++++ src/debug/debugserver.c | 93 +++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/docs/debug-server-protocol.md b/docs/debug-server-protocol.md index 51b9700f75..2f1fe282e4 100644 --- a/docs/debug-server-protocol.md +++ b/docs/debug-server-protocol.md @@ -922,3 +922,30 @@ associative features, like a hash. } } } + +### Handle Equivalence Request (46) + +Ask the debugserver to check if handles refer to the same object. + + { + "type": 46, + "id": $id, + "handles": [ + 1, 2, 3, 4, 5, 6, 7 + ] + } + +### Handle equivalence Response (47) + +For any object that is referred to by multiple handles from +the request, return a list of all the handles that belong to +the given object. + + { + "type": 47, + "id": $id, + "classes": [ + [1, 3], + [2, 5, 7] + ] + } diff --git a/src/debug/debugserver.c b/src/debug/debugserver.c index 810c30de22..973af80abd 100644 --- a/src/debug/debugserver.c +++ b/src/debug/debugserver.c @@ -68,6 +68,8 @@ typedef enum { MT_ObjectPositionalsResponse, MT_ObjectAssociativesRequest, MT_ObjectAssociativesResponse, + MT_HandleEquivalenceRequest, + MT_HandleEquivalenceResponse, } message_type; typedef enum { @@ -381,6 +383,7 @@ MVMuint8 check_requirements(request_data *data) { REQUIRE(FS_line, "A line field is required"); break; + case MT_HandleEquivalenceRequest: case MT_ReleaseHandles: REQUIRE(FS_handles, "A handles field is required"); break; @@ -1173,6 +1176,93 @@ static MVMObject *find_handle_target(MVMThreadContext *dtc, MVMuint64 id) { return NULL; } +static MVMuint64 find_representant(MVMint16 *representant, MVMuint64 index) { + MVMuint64 last = index; + + while (representant[last] != last) { + last = representant[last]; + } + + return last; +} + +static void send_handle_equivalence_classes(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) { + MVMuint16 *representant = MVM_calloc(argument->handle_count, sizeof(MVMuint64)); + MVMObject **objects = MVM_calloc(argument->handle_count, sizeof(MVMObject *)); + MVMuint16 *counts = MVM_calloc(argument->handle_count, sizeof(MVMuint16)); + MVMuint16 idx; + MVMuint16 classes_count = 0; + + /* Set up our sentinel values. Any object that represents itself + * is the end of a chain. At the beginning, everything represents + * itself. + * Critically, this allows us to use 0 as a valid representant. */ + for (idx = 0; idx < argument->handle_count; idx++) { + representant[idx] = idx; + } + + for (idx = 0; idx < argument->handle_count; idx++) { + MVMuint16 other_idx; + objects[idx] = find_handle_target(dtc, argument->handles[idx]); + + for (other_idx = 0; other_idx < idx; other_idx++) { + if (representant[other_idx] != other_idx) + continue; + if (objects[idx] == objects[other_idx]) { + representant[other_idx] = idx; + fprintf(stderr, "representant for %3d is now %3d\n", other_idx, idx); + } + } + } + + /* First, we have to count how many distinct classes there are. + * Whenever we hit 2, we know we've found a class. */ + for (idx = 0; idx < argument->handle_count; idx++) { + MVMuint16 the_repr = find_representant(representant, idx); + counts[the_repr]++; + fprintf(stderr, "%3d increased %3d count to %d\n", idx, the_repr, counts[the_repr]); + if (counts[the_repr] == 2) + classes_count++; + } + + /* Send the header of the message */ + cmp_write_map(ctx, 3); + cmp_write_str(ctx, "id", 2); + cmp_write_integer(ctx, argument->id); + cmp_write_str(ctx, "type", 4); + cmp_write_integer(ctx, MT_HandleEquivalenceResponse); + cmp_write_str(ctx, "classes", 7); + + fprintf(stderr, "number of classes: %d\n", classes_count); + + /* Now we can write out the classes by following the representant + * chain until we find one whose representant is itself. */ + cmp_write_array(ctx, classes_count); + for (idx = 0; idx < argument->handle_count; idx++) { + if (representant[idx] != idx) { + MVMuint16 count = counts[find_representant(representant, idx)]; + MVMuint16 pointer = idx; + cmp_write_array(ctx, count); + fprintf(stderr, "writing class of %3d starting at %3d: ", count, idx); + do { + MVMuint16 current_representant = representant[pointer]; + representant[pointer] = pointer; + cmp_write_integer(ctx, argument->handles[pointer]); + pointer = current_representant; + fprintf(stderr, "%3d ", pointer); + } while (representant[pointer] != pointer); + + cmp_write_integer(ctx, argument->handles[pointer]); + fprintf(stderr, "\n"); + } + } + + fprintf(stderr, "done!\n"); + + MVM_free(representant); + MVM_free(objects); +} + static MVMint32 create_context_or_code_obj_debug_handle(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument, MVMThread *thread) { MVMInstance *vm = dtc->instance; MVMThread *to_do = thread ? thread : find_thread_by_id(vm, argument->thread_id); @@ -2498,6 +2588,9 @@ static void debugserver_worker(MVMThreadContext *tc, MVMCallsite *callsite, MVMR communicate_error(tc, &ctx, &argument); } break; + case MT_HandleEquivalenceRequest: + send_handle_equivalence_classes(tc, &ctx, &argument); + break; default: /* Unknown command or NYI */ if (tc->instance->debugserver->debugspam_protocol) fprintf(stderr, "unknown command type (or NYI)\n");