Skip to content

Commit

Permalink
debugserver: add a "handle equivalence class" command
Browse files Browse the repository at this point in the history
lets you find out if handles actually refer to the
same object.
  • Loading branch information
timo committed Apr 25, 2018
1 parent 6f40cbd commit 641c7a1
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
27 changes: 27 additions & 0 deletions docs/debug-server-protocol.md
Expand Up @@ -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]
]
}
93 changes: 93 additions & 0 deletions src/debug/debugserver.c
Expand Up @@ -68,6 +68,8 @@ typedef enum {
MT_ObjectPositionalsResponse,
MT_ObjectAssociativesRequest,
MT_ObjectAssociativesResponse,
MT_HandleEquivalenceRequest,
MT_HandleEquivalenceResponse,
} message_type;

typedef enum {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down

0 comments on commit 641c7a1

Please sign in to comment.