Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/debug/debugserver.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
#include "platform/threads.h"
3
4
0
#define DEBUGSERVER_MAJOR_PROTOCOL_VERSION 1
5
0
#define DEBUGSERVER_MINOR_PROTOCOL_VERSION 1
6
7
#define bool int
8
#define true TRUE
9
#define false FALSE
10
11
#include "cmp.h"
12
13
#ifdef _WIN32
14
    #include <winsock2.h>
15
    #include <ws2tcpip.h>
16
    typedef SOCKET Socket;
17
    #define sa_family_t unsigned int
18
#else
19
    #include "unistd.h"
20
    #include <sys/socket.h>
21
    #include <sys/un.h>
22
23
    typedef int Socket;
24
    #define closesocket close
25
#endif
26
27
typedef enum {
28
    MT_MessageTypeNotUnderstood,
29
    MT_ErrorProcessingMessage,
30
    MT_OperationSuccessful,
31
    MT_IsExecutionSuspendedRequest,
32
    MT_IsExecutionSuspendedResponse,
33
    MT_SuspendAll,
34
    MT_ResumeAll,
35
    MT_SuspendOne,
36
    MT_ResumeOne,
37
    MT_ThreadStarted,
38
    MT_ThreadEnded,
39
    MT_ThreadListRequest,
40
    MT_ThreadListResponse,
41
    MT_ThreadStackTraceRequest,
42
    MT_ThreadStackTraceResponse,
43
    MT_SetBreakpointRequest,
44
    MT_SetBreakpointConfirmation,
45
    MT_BreakpointNotification,
46
    MT_ClearBreakpoint,
47
    MT_ClearAllBreakpoints,
48
    MT_StepInto,
49
    MT_StepOver,
50
    MT_StepOut,
51
    MT_StepCompleted,
52
    MT_ReleaseHandles,
53
    MT_HandleResult,
54
    MT_ContextHandle,
55
    MT_ContextLexicalsRequest,
56
    MT_ContextLexicalsResponse,
57
    MT_OuterContextRequest,
58
    MT_CallerContextRequest,
59
    MT_CodeObjectHandle,
60
    MT_ObjectAttributesRequest,
61
    MT_ObjectAttributesResponse,
62
    MT_DecontainerizeHandle,
63
    MT_FindMethod,
64
    MT_Invoke,
65
    MT_InvokeResult,
66
    MT_UnhandledException,
67
    MT_OperationUnsuccessful,
68
    MT_ObjectMetadataRequest,
69
    MT_ObjectMetadataResponse,
70
    MT_ObjectPositionalsRequest,
71
    MT_ObjectPositionalsResponse,
72
    MT_ObjectAssociativesRequest,
73
    MT_ObjectAssociativesResponse,
74
    MT_HandleEquivalenceRequest,
75
    MT_HandleEquivalenceResponse,
76
} message_type;
77
78
typedef enum {
79
    ArgKind_Handle,
80
    ArgKind_Integer,
81
    ArgKind_Num,
82
    ArgKind_String,
83
} argument_kind;
84
85
typedef struct {
86
    MVMuint8 arg_kind;
87
    union {
88
        MVMint64 i;
89
        MVMnum64 n;
90
        char *s;
91
        MVMint64 o;
92
    } arg_u;
93
} argument_data;
94
95
typedef enum {
96
    FS_type      = 1,
97
    FS_id        = 2,
98
    FS_thread_id = 4,
99
    FS_file      = 8,
100
    FS_line      = 16,
101
    FS_suspend   = 32,
102
    FS_stacktrace = 64,
103
    /* handle_count is just bookkeeping */
104
    FS_handles    = 128,
105
    FS_handle_id  = 256,
106
    FS_frame_number = 512,
107
    FS_arguments    = 1024,
108
} fields_set;
109
110
typedef struct {
111
    MVMuint16 type;
112
    MVMuint64 id;
113
114
    MVMuint32 thread_id;
115
116
    char *file;
117
    MVMuint32 line;
118
119
    MVMuint8  suspend;
120
    MVMuint8  stacktrace;
121
122
    MVMuint16 handle_count;
123
    MVMuint64 *handles;
124
125
    MVMuint64 handle_id;
126
127
    MVMuint32 frame_number;
128
129
    MVMuint32 argument_count;
130
    argument_data *arguments;
131
132
    MVMuint8  parse_fail;
133
    const char *parse_fail_message;
134
135
    fields_set fields_set;
136
} request_data;
137
138
static MVMint32 write_stacktrace_frames(MVMThreadContext *dtc, cmp_ctx_t *ctx, MVMThread *thread);
139
static MVMint32 request_all_threads_suspend(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument);
140
static MVMuint64 allocate_handle(MVMThreadContext *dtc, MVMObject *target);
141
142
/* Breakpoint stuff */
143
0
MVM_PUBLIC void MVM_debugserver_register_line(MVMThreadContext *tc, char *filename, MVMuint32 filename_len, MVMuint32 line_no,  MVMuint32 *file_idx) {
144
0
    MVMDebugServerData *debugserver = tc->instance->debugserver;
145
0
    MVMDebugServerBreakpointTable *table = debugserver->breakpoints;
146
0
    MVMDebugServerBreakpointFileTable *found = NULL;
147
0
    MVMuint32 index = 0;
148
0
149
0
    char *open_paren_pos = (char *)memchr(filename, '(', filename_len);
150
0
151
0
    if (open_paren_pos) {
152
0
        if (open_paren_pos[-1] == ' ') {
153
0
            filename_len = open_paren_pos - filename - 1;
154
0
        }
155
0
    }
156
0
157
0
    uv_mutex_lock(&debugserver->mutex_breakpoints);
158
0
159
0
    if (*file_idx < table->files_used) {
160
0
        MVMDebugServerBreakpointFileTable *file = &table->files[*file_idx];
161
0
        if (file->filename_length == filename_len && memcmp(file->filename, filename, filename_len) == 0)
162
0
            found = file;
163
0
    }
164
0
165
0
    if (!found) {
166
0
        for (index = 0; index < table->files_used; index++) {
167
0
            MVMDebugServerBreakpointFileTable *file = &table->files[index];
168
0
            if (file->filename_length != filename_len)
169
0
                continue;
170
0
            if (memcmp(file->filename, filename, filename_len) != 0)
171
0
                continue;
172
0
            found = file;
173
0
            *file_idx = index;
174
0
            break;
175
0
        }
176
0
    }
177
0
178
0
    if (!found) {
179
0
        if (table->files_used++ >= table->files_alloc) {
180
0
            MVMuint32 old_alloc = table->files_alloc;
181
0
            table->files_alloc *= 2;
182
0
            table->files = MVM_fixed_size_realloc_at_safepoint(tc, tc->instance->fsa, table->files,
183
0
                    old_alloc * sizeof(MVMDebugServerBreakpointFileTable),
184
0
                    table->files_alloc * sizeof(MVMDebugServerBreakpointFileTable));
185
0
            memset((char *)(table->files + old_alloc), 0, (table->files_alloc - old_alloc) * sizeof(MVMDebugServerBreakpointFileTable) - 1);
186
0
            if (tc->instance->debugserver->debugspam_protocol)
187
0
                fprintf(stderr, "table for files increased to %u slots\n", table->files_alloc);
188
0
        }
189
0
190
0
        found = &table->files[table->files_used - 1];
191
0
192
0
        found->filename = MVM_calloc(filename_len + 1, sizeof(char));
193
0
        strncpy(found->filename, filename, filename_len);
194
0
195
0
        if (tc->instance->debugserver->debugspam_protocol)
196
0
            fprintf(stderr, "created new file entry at %u for %s\n", table->files_used - 1, found->filename);
197
0
198
0
        found->filename_length = filename_len;
199
0
200
0
        found->lines_active_alloc = line_no + 32;
201
0
        found->lines_active = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa, found->lines_active_alloc * sizeof(MVMuint8));
202
0
203
0
        *file_idx = table->files_used - 1;
204
0
205
0
        found->breakpoints = NULL;
206
0
        found->breakpoints_alloc = 0;
207
0
        found->breakpoints_used = 0;
208
0
    }
209
0
210
0
    if (found->lines_active_alloc < line_no + 1) {
211
0
        MVMuint32 old_size = found->lines_active_alloc;
212
0
        found->lines_active_alloc *= 2;
213
0
        if (tc->instance->debugserver->debugspam_protocol)
214
0
            fprintf(stderr, "increasing line number table for %s from %u to %u slots\n", found->filename, old_size, found->lines_active_alloc);
215
0
        found->lines_active = MVM_fixed_size_realloc_at_safepoint(tc, tc->instance->fsa,
216
0
                found->lines_active, old_size, found->lines_active_alloc);
217
0
        memset((char *)found->lines_active + old_size, 0, found->lines_active_alloc - old_size - 1);
218
0
    }
219
0
220
0
    uv_mutex_unlock(&debugserver->mutex_breakpoints);
221
0
}
222
223
0
static void stop_point_hit(MVMThreadContext *tc) {
224
0
    while (1) {
225
0
        /* We're in total regular boring execution. Set ourselves to
226
0
         * interrupted for suspend reasons */
227
0
        if (MVM_cas(&tc->gc_status, MVMGCStatus_NONE, MVMGCStatus_INTERRUPT | MVMSuspendState_SUSPEND_REQUEST)
228
0
                == MVMGCStatus_NONE) {
229
0
            break;
230
0
        }
231
0
        /* Looks like another thread just interrupted us; join in on GC and
232
0
         * then this loop will store the suspend request flag when we're back
233
0
         * to MVMGCStatus_NONE. */
234
0
        if (MVM_load(&tc->gc_status) == MVMGCStatus_INTERRUPT) {
235
0
            MVM_gc_enter_from_interrupt(tc);
236
0
        }
237
0
        /* Perhaps the debugserver just asked us to suspend, too. It's not
238
0
         * important for our suspend request flag to survive or something. */
239
0
        if (MVM_load(&tc->gc_status) == (MVMGCStatus_INTERRUPT | MVMSuspendState_SUSPEND_REQUEST)) {
240
0
            break;
241
0
        }
242
0
    }
243
0
    MVM_gc_enter_from_interrupt(tc);
244
0
}
245
246
0
static MVMuint8 breakpoint_hit(MVMThreadContext *tc, MVMDebugServerBreakpointFileTable *file, MVMuint32 line_no) {
247
0
    cmp_ctx_t *ctx = NULL;
248
0
    MVMDebugServerBreakpointInfo *info;
249
0
    MVMuint32 index;
250
0
    MVMuint8 must_suspend = 0;
251
0
252
0
    if (tc->instance->debugserver && tc->instance->debugserver->messagepack_data) {
253
0
        ctx = (cmp_ctx_t*)tc->instance->debugserver->messagepack_data;
254
0
    }
255
0
256
0
    for (index = 0; index < file->breakpoints_used; index++) {
257
0
        info = &file->breakpoints[index];
258
0
259
0
        if (info->line_no == line_no) {
260
0
            if (tc->instance->debugserver->debugspam_protocol)
261
0
                fprintf(stderr, "hit a breakpoint\n");
262
0
            if (ctx) {
263
0
                uv_mutex_lock(&tc->instance->debugserver->mutex_network_send);
264
0
                cmp_write_map(ctx, 4);
265
0
                cmp_write_str(ctx, "id", 2);
266
0
                cmp_write_integer(ctx, info->breakpoint_id);
267
0
                cmp_write_str(ctx, "type", 4);
268
0
                cmp_write_integer(ctx, MT_BreakpointNotification);
269
0
                cmp_write_str(ctx, "thread", 6);
270
0
                cmp_write_integer(ctx, tc->thread_id);
271
0
                cmp_write_str(ctx, "frames", 6);
272
0
                if (info->send_backtrace) {
273
0
                    write_stacktrace_frames(tc, ctx, tc->thread_obj);
274
0
                } else {
275
0
                    cmp_write_nil(ctx);
276
0
                }
277
0
                uv_mutex_unlock(&tc->instance->debugserver->mutex_network_send);
278
0
            }
279
0
            if (info->shall_suspend) {
280
0
                must_suspend = 1;
281
0
            }
282
0
        }
283
0
    }
284
0
285
0
    return must_suspend;
286
0
}
287
0
static void step_point_hit(MVMThreadContext *tc) {
288
0
    cmp_ctx_t *ctx = (cmp_ctx_t*)tc->instance->debugserver->messagepack_data;
289
0
290
0
    uv_mutex_lock(&tc->instance->debugserver->mutex_network_send);
291
0
    cmp_write_map(ctx, 4);
292
0
    cmp_write_str(ctx, "id", 2);
293
0
    cmp_write_integer(ctx, tc->step_message_id);
294
0
    cmp_write_str(ctx, "type", 4);
295
0
    cmp_write_integer(ctx, MT_StepCompleted);
296
0
    cmp_write_str(ctx, "thread", 6);
297
0
    cmp_write_integer(ctx, tc->thread_id);
298
0
    cmp_write_str(ctx, "frames", 6);
299
0
    write_stacktrace_frames(tc, ctx, tc->thread_obj);
300
0
    uv_mutex_unlock(&tc->instance->debugserver->mutex_network_send);
301
0
302
0
    tc->step_mode = MVMDebugSteppingMode_NONE;
303
0
    tc->step_mode_frame = NULL;
304
0
}
305
306
0
MVM_PUBLIC void MVM_debugserver_breakpoint_check(MVMThreadContext *tc, MVMuint32 file_idx, MVMuint32 line_no) {
307
0
    MVMDebugServerData *debugserver = tc->instance->debugserver;
308
0
    MVMuint8 shall_suspend = 0;
309
0
310
0
    if (debugserver->any_breakpoints_at_all && (file_idx != tc->cur_file_idx || line_no != tc->cur_line_no)) {
311
0
        MVMDebugServerBreakpointTable *table = debugserver->breakpoints;
312
0
        MVMDebugServerBreakpointFileTable *found = &table->files[file_idx];
313
0
314
0
        if (debugserver->any_breakpoints_at_all && found->breakpoints_used && found->lines_active[line_no]) {
315
0
            shall_suspend |= breakpoint_hit(tc, found, line_no);
316
0
        }
317
0
    }
318
0
319
0
    tc->cur_line_no = line_no;
320
0
    tc->cur_file_idx = file_idx;
321
0
322
0
    if (tc->step_mode) {
323
0
        if (tc->step_mode == MVMDebugSteppingMode_STEP_OVER) {
324
0
            if (line_no != tc->step_mode_line_no && tc->step_mode_frame == tc->cur_frame) {
325
0
326
0
                if (tc->instance->debugserver->debugspam_protocol)
327
0
                    fprintf(stderr, "hit a stepping point: step over; %u != %u, %p == %p\n", line_no, tc->step_mode_line_no, tc->step_mode_frame, tc->cur_frame);
328
0
                step_point_hit(tc);
329
0
                shall_suspend = 1;
330
0
            }
331
0
        }
332
0
        else if (tc->step_mode == MVMDebugSteppingMode_STEP_INTO) {
333
0
            if (line_no != tc->step_mode_line_no && tc->step_mode_frame == tc->cur_frame
334
0
                    || tc->step_mode_frame != tc->cur_frame) {
335
0
                if (tc->instance->debugserver->debugspam_protocol) {
336
0
                    if (line_no != tc->step_mode_line_no && tc->step_mode_frame == tc->cur_frame)
337
0
                        fprintf(stderr, "hit a stepping point: step into; %u != %u, %p == %p\n",
338
0
                                line_no, tc->step_mode_line_no, tc->step_mode_frame, tc->cur_frame);
339
0
                    else
340
0
                        fprintf(stderr, "hit a stepping point: step into; %u,   %u, %p != %p\n",
341
0
                                line_no, tc->step_mode_line_no, tc->step_mode_frame, tc->cur_frame);
342
0
                }
343
0
                step_point_hit(tc);
344
0
                shall_suspend = 1;
345
0
            }
346
0
        }
347
0
        /* Nothing to do for STEP_OUT. */
348
0
        /* else if (tc->step_mode == MVMDebugSteppingMode_STEP_OUT) { } */
349
0
    }
350
0
351
0
    if (shall_suspend)
352
0
        stop_point_hit(tc);
353
0
}
354
355
356
0
#define REQUIRE(field, message) do { if(!(data->fields_set & (field))) { data->parse_fail = 1; data->parse_fail_message = (message); return 0; }; accepted = accepted | (field); } while (0)
357
358
0
MVMuint8 check_requirements(MVMThreadContext *tc, request_data *data) {
359
0
    fields_set accepted = FS_id | FS_type;
360
0
361
0
    REQUIRE(FS_id, "An id field is required");
362
0
    REQUIRE(FS_type, "A type field is required");
363
0
    switch (data->type) {
364
0
        case MT_IsExecutionSuspendedRequest:
365
0
        case MT_SuspendAll:
366
0
        case MT_ResumeAll:
367
0
        case MT_ThreadListRequest:
368
0
        case MT_ClearAllBreakpoints:
369
0
            /* All of these messages only take id and type */
370
0
            break;
371
0
372
0
        case MT_SuspendOne:
373
0
        case MT_ResumeOne:
374
0
        case MT_ThreadStackTraceRequest:
375
0
        case MT_StepInto:
376
0
        case MT_StepOver:
377
0
        case MT_StepOut:
378
0
            REQUIRE(FS_thread_id, "A thread field is required");
379
0
            break;
380
0
381
0
        case MT_SetBreakpointRequest:
382
0
            REQUIRE(FS_suspend, "A suspend field is required");
383
0
            REQUIRE(FS_stacktrace, "A stacktrace field is required");
384
0
            /* Fall-Through */
385
0
        case MT_ClearBreakpoint:
386
0
            REQUIRE(FS_file, "A file field is required");
387
0
            REQUIRE(FS_line, "A line field is required");
388
0
            break;
389
0
390
0
        case MT_HandleEquivalenceRequest:
391
0
        case MT_ReleaseHandles:
392
0
            REQUIRE(FS_handles, "A handles field is required");
393
0
            break;
394
0
395
0
        case MT_FindMethod:
396
0
            /* TODO we've got to have some name field or something */
397
0
            /* Fall-Through */
398
0
        case MT_DecontainerizeHandle:
399
0
            REQUIRE(FS_thread_id, "A thread field is required");
400
0
            /* Fall-Through */
401
0
        case MT_ContextLexicalsRequest:
402
0
        case MT_OuterContextRequest:
403
0
        case MT_CallerContextRequest:
404
0
        case MT_ObjectAttributesRequest:
405
0
        case MT_ObjectMetadataRequest:
406
0
        case MT_ObjectPositionalsRequest:
407
0
        case MT_ObjectAssociativesRequest:
408
0
            REQUIRE(FS_handle_id, "A handle field is required");
409
0
            break;
410
0
411
0
        case MT_ContextHandle:
412
0
        case MT_CodeObjectHandle:
413
0
            REQUIRE(FS_thread_id, "A thread field is required");
414
0
            REQUIRE(FS_frame_number, "A frame field is required");
415
0
            break;
416
0
417
0
        case MT_Invoke:
418
0
            REQUIRE(FS_handle_id, "A handle field is required");
419
0
            REQUIRE(FS_thread_id, "A thread field is required");
420
0
            REQUIRE(FS_arguments, "An arguments field is required");
421
0
            break;
422
0
423
0
        default:
424
0
            break;
425
0
    }
426
0
427
0
    if (data->fields_set != accepted) {
428
0
        if (tc->instance->debugserver->debugspam_protocol)
429
0
            fprintf(stderr, "debugserver: too many fields in message of type %d: accepted 0x%x, got 0x%x\n", data->type, accepted, data->fields_set);
430
0
    }
431
0
}
432
433
0
static MVMuint16 big_endian_16(MVMuint16 number) {
434
0
#ifdef MVM_BIGENDIAN
435
    return number;
436
#else
437
0
    char *bytes = (char *)&number;
438
0
    char tmp;
439
0
    tmp = bytes[1];
440
0
    bytes[1] = bytes[0];
441
0
    bytes[0] = tmp;
442
0
    return *((MVMuint16 *)bytes);
443
0
#endif
444
0
}
445
446
0
static void send_greeting(Socket *sock) {
447
0
    char buffer[24] = "MOARVM-REMOTE-DEBUG\0";
448
0
    MVMuint16 version = big_endian_16(DEBUGSERVER_MAJOR_PROTOCOL_VERSION);
449
0
    MVMuint16 *verptr = (MVMuint16 *)(&buffer[strlen("MOARVM-REMOTE-DEBUG") + 1]);
450
0
451
0
    *verptr = version;
452
0
    verptr++;
453
0
454
0
    version = big_endian_16(DEBUGSERVER_MINOR_PROTOCOL_VERSION);
455
0
456
0
    *verptr = version;
457
0
    send(*sock, buffer, 24, 0);
458
0
}
459
460
0
static int receive_greeting(Socket *sock) {
461
0
    const char *expected = "MOARVM-REMOTE-CLIENT-OK";
462
0
    char buffer[24];
463
0
    int received = 0;
464
0
465
0
    memset(buffer, 0, sizeof(buffer));
466
0
467
0
    received = recv(*sock, buffer, sizeof(buffer), 0);
468
0
    if (received != sizeof(buffer)) {
469
0
        return 0;
470
0
    }
471
0
    if (memcmp(buffer, expected, sizeof(buffer)) == 0) {
472
0
        return 1;
473
0
    }
474
0
    return 0;
475
0
}
476
477
0
static void communicate_error(MVMThreadContext *tc, cmp_ctx_t *ctx, request_data *argument) {
478
0
    if (argument) {
479
0
        if (tc->instance->debugserver->debugspam_protocol)
480
0
            fprintf(stderr, "communicating an error\n");
481
0
        cmp_write_map(ctx, 2);
482
0
        cmp_write_str(ctx, "id", 2);
483
0
        cmp_write_integer(ctx, argument->id);
484
0
        cmp_write_str(ctx, "type", 4);
485
0
        cmp_write_integer(ctx, MT_ErrorProcessingMessage);
486
0
    }
487
0
}
488
489
0
static void communicate_success(MVMThreadContext *tc, cmp_ctx_t *ctx, request_data *argument) {
490
0
    if (argument) {
491
0
        if (tc->instance->debugserver->debugspam_protocol)
492
0
            fprintf(stderr, "communicating success\n");
493
0
        cmp_write_map(ctx, 2);
494
0
        cmp_write_str(ctx, "id", 2);
495
0
        cmp_write_integer(ctx, argument->id);
496
0
        cmp_write_str(ctx, "type", 4);
497
0
        cmp_write_integer(ctx, MT_OperationSuccessful);
498
0
    }
499
0
}
500
501
/* Send spontaneous events */
502
173
MVM_PUBLIC void MVM_debugserver_notify_thread_creation(MVMThreadContext *tc) {
503
173
    if (tc->instance->debugserver && tc->instance->debugserver->messagepack_data) {
504
0
        cmp_ctx_t *ctx = (cmp_ctx_t*)tc->instance->debugserver->messagepack_data;
505
0
        MVMuint64 event_id;
506
0
507
0
        uv_mutex_lock(&tc->instance->debugserver->mutex_network_send);
508
0
509
0
        event_id = tc->instance->debugserver->event_id;
510
0
        tc->instance->debugserver->event_id += 2;
511
0
512
0
        cmp_write_map(ctx, 5);
513
0
        cmp_write_str(ctx, "id", 2);
514
0
        cmp_write_integer(ctx, event_id);
515
0
        cmp_write_str(ctx, "type", 4);
516
0
        cmp_write_integer(ctx, MT_ThreadStarted);
517
0
518
0
        cmp_write_str(ctx, "thread", 6);
519
0
        cmp_write_integer(ctx, tc->thread_obj->body.thread_id);
520
0
521
0
        cmp_write_str(ctx, "native_id", 9);
522
0
        cmp_write_integer(ctx, tc->thread_obj->body.native_thread_id);
523
0
524
0
        cmp_write_str(ctx, "app_lifetime", 12);
525
0
        cmp_write_integer(ctx, tc->thread_obj->body.app_lifetime);
526
0
527
0
        uv_mutex_unlock(&tc->instance->debugserver->mutex_network_send);
528
0
    }
529
173
}
530
531
25
MVM_PUBLIC void MVM_debugserver_notify_thread_destruction(MVMThreadContext *tc) {
532
25
    if (tc->instance->debugserver && tc->instance->debugserver->messagepack_data) {
533
0
        cmp_ctx_t *ctx = (cmp_ctx_t*)tc->instance->debugserver->messagepack_data;
534
0
        MVMuint64 event_id;
535
0
536
0
        uv_mutex_lock(&tc->instance->debugserver->mutex_network_send);
537
0
538
0
        event_id = tc->instance->debugserver->event_id;
539
0
        tc->instance->debugserver->event_id += 2;
540
0
541
0
        cmp_write_map(ctx, 3);
542
0
        cmp_write_str(ctx, "id", 2);
543
0
        cmp_write_integer(ctx, event_id);
544
0
        cmp_write_str(ctx, "type", 4);
545
0
        cmp_write_integer(ctx, MT_ThreadEnded);
546
0
547
0
        cmp_write_str(ctx, "thread", 6);
548
0
        cmp_write_integer(ctx, tc->thread_obj->body.thread_id);
549
0
550
0
        uv_mutex_unlock(&tc->instance->debugserver->mutex_network_send);
551
0
    }
552
25
}
553
554
0
MVM_PUBLIC void MVM_debugserver_notify_unhandled_exception(MVMThreadContext *tc, MVMException *ex) {
555
0
    if (tc->instance->debugserver && tc->instance->debugserver->messagepack_data) {
556
0
        cmp_ctx_t *ctx = (cmp_ctx_t*)tc->instance->debugserver->messagepack_data;
557
0
        MVMuint64 event_id;
558
0
559
0
        uv_mutex_lock(&tc->instance->debugserver->mutex_network_send);
560
0
561
0
        request_all_threads_suspend(tc, ctx, NULL);
562
0
563
0
        event_id = tc->instance->debugserver->event_id;
564
0
        tc->instance->debugserver->event_id += 2;
565
0
566
0
        cmp_write_map(ctx, 5);
567
0
        cmp_write_str(ctx, "id", 2);
568
0
        cmp_write_integer(ctx, event_id);
569
0
        cmp_write_str(ctx, "type", 4);
570
0
        cmp_write_integer(ctx, MT_UnhandledException);
571
0
572
0
        cmp_write_str(ctx, "handle", 6);
573
0
        cmp_write_integer(ctx, allocate_handle(tc, (MVMObject *)ex));
574
0
575
0
        cmp_write_str(ctx, "thread", 6);
576
0
        cmp_write_integer(ctx, tc->thread_obj->body.thread_id);
577
0
578
0
        cmp_write_str(ctx, "frames", 6);
579
0
        write_stacktrace_frames(tc, ctx, tc->thread_obj);
580
0
581
0
        uv_mutex_unlock(&tc->instance->debugserver->mutex_network_send);
582
0
583
0
        MVM_gc_enter_from_interrupt(tc);
584
0
    }
585
0
}
586
587
0
static MVMuint8 is_thread_id_eligible(MVMInstance *vm, MVMuint32 id) {
588
0
    if (id == vm->debugserver->thread_id || id == vm->speshworker_thread_id) {
589
0
        return 0;
590
0
    }
591
0
    return 1;
592
0
}
593
594
/* Send replies to requests send by the client */
595
596
0
static MVMThread *find_thread_by_id(MVMInstance *vm, MVMint32 id) {
597
0
    MVMThread *cur_thread = 0;
598
0
599
0
    if (!is_thread_id_eligible(vm, id)) {
600
0
        return NULL;
601
0
    }
602
0
603
0
    uv_mutex_lock(&vm->mutex_threads);
604
0
    cur_thread = vm->threads;
605
0
    while (cur_thread) {
606
0
        if (cur_thread->body.thread_id == id) {
607
0
            break;
608
0
        }
609
0
        cur_thread = cur_thread->body.next;
610
0
    }
611
0
    uv_mutex_unlock(&vm->mutex_threads);
612
0
    return cur_thread;
613
0
}
614
615
0
static MVMint32 request_thread_suspends(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument, MVMThread *thread) {
616
0
    MVMThread *to_do = thread ? thread : find_thread_by_id(dtc->instance, argument->thread_id);
617
0
    MVMThreadContext *tc = to_do ? to_do->body.tc : NULL;
618
0
619
0
    if (!tc)
620
0
        return 1;
621
0
622
0
    MVM_gc_mark_thread_blocked(dtc);
623
0
624
0
    while (1) {
625
0
        /* Is the thread currently doing completely ordinary code execution? */
626
0
        if (MVM_cas(&tc->gc_status, MVMGCStatus_NONE, MVMGCStatus_INTERRUPT | MVMSuspendState_SUSPEND_REQUEST)
627
0
                == MVMGCStatus_NONE) {
628
0
            break;
629
0
        }
630
0
        /* Is the thread in question currently blocked, i.e. spending time in
631
0
         * some long-running piece of C code, waiting for I/O, etc.?
632
0
         * If so, just store the suspend request bit so when it unblocks itself
633
0
         * it'll suspend execution. */
634
0
        if (MVM_cas(&tc->gc_status, MVMGCStatus_UNABLE, MVMGCStatus_UNABLE | MVMSuspendState_SUSPEND_REQUEST)
635
0
                == MVMGCStatus_UNABLE) {
636
0
            break;
637
0
        }
638
0
        /* Was the thread faster than us? For example by running into
639
0
         * a breakpoint, completing a step, or encountering an
640
0
         * unhandled exception? If so, we're done here. */
641
0
        if ((MVM_load(&tc->gc_status) & MVMSUSPENDSTATUS_MASK) == MVMSuspendState_SUSPEND_REQUEST) {
642
0
            break;
643
0
        }
644
0
        MVM_platform_thread_yield();
645
0
    }
646
0
647
0
    if (argument && argument->type == MT_SuspendOne)
648
0
        communicate_success(tc, ctx,  argument);
649
0
650
0
    MVM_gc_mark_thread_unblocked(dtc);
651
0
    if (tc->instance->debugserver->debugspam_protocol)
652
0
        fprintf(stderr, "thread %u successfully suspended\n", tc->thread_id);
653
0
654
0
    return 0;
655
0
}
656
657
0
static MVMint32 request_all_threads_suspend(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
658
0
    MVMInstance *vm = dtc->instance;
659
0
    MVMThread *cur_thread = 0;
660
0
    MVMuint32 success = 1;
661
0
662
0
    uv_mutex_lock(&vm->mutex_threads);
663
0
664
0
    /* TODO track which threads we successfully suspended so we can wake them
665
0
     * up again if an error occured */
666
0
667
0
    cur_thread = vm->threads;
668
0
    while (cur_thread) {
669
0
        if (is_thread_id_eligible(vm, cur_thread->body.thread_id)) {
670
0
            AO_t current = MVM_load(&cur_thread->body.tc->gc_status);
671
0
            if (current == MVMGCStatus_NONE || current == MVMGCStatus_UNABLE) {
672
0
                MVMint32 result = request_thread_suspends(dtc, ctx, argument, cur_thread);
673
0
                if (result == 1) {
674
0
                    success = 0;
675
0
                    break;
676
0
                }
677
0
            }
678
0
        }
679
0
        cur_thread = cur_thread->body.next;
680
0
    }
681
0
682
0
    if (success)
683
0
        communicate_success(dtc, ctx, argument);
684
0
    else
685
0
        communicate_error(dtc, ctx, argument);
686
0
687
0
    uv_mutex_unlock(&vm->mutex_threads);
688
0
}
689
690
0
static MVMint32 request_thread_resumes(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument, MVMThread *thread) {
691
0
    MVMInstance *vm = dtc->instance;
692
0
    MVMThread *to_do = thread ? thread : find_thread_by_id(vm, argument->thread_id);
693
0
    MVMThreadContext *tc = to_do ? to_do->body.tc : NULL;
694
0
    AO_t current;
695
0
696
0
    if (!tc) {
697
0
        return 1;
698
0
    }
699
0
700
0
    current = MVM_load(&tc->gc_status);
701
0
702
0
    if (current != (MVMGCStatus_UNABLE | MVMSuspendState_SUSPENDED)
703
0
            && (current & MVMSUSPENDSTATUS_MASK) != MVMSuspendState_SUSPEND_REQUEST) {
704
0
        fprintf(stderr, "wrong state to resume from: %lu\n", MVM_load(&tc->gc_status));
705
0
        return 1;
706
0
    }
707
0
708
0
    MVM_gc_mark_thread_blocked(dtc);
709
0
710
0
    while(1) {
711
0
        current = MVM_cas(&tc->gc_status, MVMGCStatus_UNABLE | MVMSuspendState_SUSPENDED, MVMGCStatus_UNABLE);
712
0
        if (current == (MVMGCStatus_UNABLE | MVMSuspendState_SUSPENDED)) {
713
0
            /* Success! We signalled the thread and can now tell it to
714
0
             * mark itself unblocked, which takes care of any looming GC
715
0
             * and related business. */
716
0
            uv_cond_broadcast(&vm->debugserver->tell_threads);
717
0
            break;
718
0
        } else if ((current & MVMGCSTATUS_MASK) == MVMGCStatus_STOLEN) {
719
0
            uv_mutex_lock(&tc->instance->mutex_gc_orchestrate);
720
0
            if (tc->instance->in_gc) {
721
0
                uv_cond_wait(&tc->instance->cond_blocked_can_continue,
722
0
                    &tc->instance->mutex_gc_orchestrate);
723
0
            }
724
0
            uv_mutex_unlock(&tc->instance->mutex_gc_orchestrate);
725
0
        } else {
726
0
            if (current == (MVMGCStatus_UNABLE | MVMSuspendState_SUSPEND_REQUEST)) {
727
0
                if (MVM_cas(&tc->gc_status, current, MVMGCStatus_UNABLE) == current) {
728
0
                    break;
729
0
                }
730
0
            }
731
0
        }
732
0
    }
733
0
734
0
    MVM_gc_mark_thread_unblocked(dtc);
735
0
736
0
    if (argument && argument->type == MT_ResumeOne)
737
0
        communicate_success(tc, ctx, argument);
738
0
739
0
    if (tc->instance->debugserver->debugspam_protocol)
740
0
        fprintf(stderr, "success resuming thread; its status is now %lu\n", MVM_load(&tc->gc_status));
741
0
742
0
    return 0;
743
0
}
744
745
0
static MVMint32 request_all_threads_resume(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
746
0
    MVMInstance *vm = dtc->instance;
747
0
    MVMThread *cur_thread = 0;
748
0
    MVMuint8 success = 1;
749
0
750
0
    uv_mutex_lock(&vm->mutex_threads);
751
0
    cur_thread = vm->threads;
752
0
    while (cur_thread) {
753
0
        if (cur_thread != dtc->thread_obj) {
754
0
            AO_t current = MVM_load(&cur_thread->body.tc->gc_status);
755
0
            if (current == (MVMGCStatus_UNABLE | MVMSuspendState_SUSPENDED) ||
756
0
                    current == (MVMGCStatus_INTERRUPT | MVMSuspendState_SUSPEND_REQUEST) ||
757
0
                    current == (MVMGCStatus_STOLEN | MVMSuspendState_SUSPEND_REQUEST)) {
758
0
                if (request_thread_resumes(dtc, ctx, argument, cur_thread)) {
759
0
                    if (vm->debugserver->debugspam_protocol)
760
0
                        fprintf(stderr, "failure to resume thread %u\n", cur_thread->body.thread_id);
761
0
                    success = 0;
762
0
                    break;
763
0
                }
764
0
            }
765
0
        }
766
0
        cur_thread = cur_thread->body.next;
767
0
    }
768
0
769
0
    if (success)
770
0
        communicate_success(dtc, ctx, argument);
771
0
    else
772
0
        communicate_error(dtc, ctx, argument);
773
0
774
0
    uv_mutex_unlock(&vm->mutex_threads);
775
0
776
0
    return !success;
777
0
}
778
779
0
static MVMint32 write_stacktrace_frames(MVMThreadContext *dtc, cmp_ctx_t *ctx, MVMThread *thread) {
780
0
    MVMThreadContext *tc = thread->body.tc;
781
0
    MVMuint64 stack_size = 0;
782
0
783
0
    MVMFrame *cur_frame = tc->cur_frame;
784
0
785
0
    while (cur_frame != NULL) {
786
0
        stack_size++;
787
0
        cur_frame = cur_frame->caller;
788
0
    }
789
0
790
0
    if (tc->instance->debugserver->debugspam_protocol)
791
0
        fprintf(stderr, "dumping a stack trace of %lu frames\n", stack_size);
792
0
793
0
    cmp_write_array(ctx, stack_size);
794
0
795
0
    cur_frame = tc->cur_frame;
796
0
    stack_size = 0; /* To check if we've got the topmost frame or not */
797
0
798
0
    while (cur_frame != NULL) {
799
0
        MVMString *bc_filename = cur_frame->static_info->body.cu->body.filename;
800
0
        MVMString *name     = cur_frame->static_info->body.name;
801
0
802
0
        MVMuint8 *cur_op = stack_size != 0 ? cur_frame->return_address : *(tc->interp_cur_op);
803
0
        MVMuint32 offset = cur_op - MVM_frame_effective_bytecode(cur_frame);
804
0
        MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &cur_frame->static_info->body,
805
0
                                          offset > 0 ? offset - 1 : 0);
806
0
807
0
        MVMint32 line_number = annot ? annot->line_number : 1;
808
0
        MVMint16 string_heap_index = annot ? annot->filename_string_heap_index : 1;
809
0
810
0
        char *tmp1 = annot && string_heap_index < cur_frame->static_info->body.cu->body.num_strings
811
0
            ? MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc,
812
0
                    cur_frame->static_info->body.cu, string_heap_index))
813
0
            : NULL;
814
0
        char *filename_c = bc_filename
815
0
            ? MVM_string_utf8_encode_C_string(tc, bc_filename)
816
0
            : NULL;
817
0
        char *name_c = name
818
0
            ? MVM_string_utf8_encode_C_string(tc, name)
819
0
            : NULL;
820
0
821
0
        MVMObject *code_ref = cur_frame->code_ref;
822
0
        MVMCode *code_obj = code_ref && REPR(code_ref)->ID == MVM_REPR_ID_MVMCode ? (MVMCode*)code_ref : NULL;
823
0
        char *debugname = code_obj && code_obj->body.code_object ? MVM_6model_get_debug_name(tc, code_obj->body.code_object) : "";
824
0
825
0
        MVM_free(annot);
826
0
827
0
        cmp_write_map(ctx, 5);
828
0
        cmp_write_str(ctx, "file", 4);
829
0
        cmp_write_str(ctx, tmp1, tmp1 ? strlen(tmp1) : 0);
830
0
        cmp_write_str(ctx, "line", 4);
831
0
        cmp_write_integer(ctx, line_number);
832
0
        cmp_write_str(ctx, "bytecode_file", 13);
833
0
        if (bc_filename)
834
0
            cmp_write_str(ctx, filename_c, strlen(filename_c));
835
0
        else
836
0
            cmp_write_nil(ctx);
837
0
        cmp_write_str(ctx, "name", 4);
838
0
        cmp_write_str(ctx, name_c, name_c ? strlen(name_c) : 0);
839
0
        cmp_write_str(ctx, "type", 4);
840
0
        cmp_write_str(ctx, debugname, strlen(debugname));
841
0
842
0
        if (bc_filename)
843
0
            MVM_free(filename_c);
844
0
        if (name)
845
0
            MVM_free(name_c);
846
0
        if (tmp1)
847
0
            MVM_free(tmp1);
848
0
849
0
        cur_frame = cur_frame->caller;
850
0
        stack_size++;
851
0
    }
852
0
}
853
854
0
static MVMint32 request_thread_stacktrace(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument, MVMThread *thread) {
855
0
    MVMThread *to_do = thread ? thread : find_thread_by_id(dtc->instance, argument->thread_id);
856
0
857
0
    if (!to_do)
858
0
        return 1;
859
0
860
0
    if ((to_do->body.tc->gc_status & MVMGCSTATUS_MASK) != MVMGCStatus_UNABLE) {
861
0
        return 1;
862
0
    }
863
0
864
0
    cmp_write_map(ctx, 3);
865
0
    cmp_write_str(ctx, "id", 2);
866
0
    cmp_write_integer(ctx, argument->id);
867
0
    cmp_write_str(ctx, "type", 4);
868
0
    cmp_write_integer(ctx, MT_ThreadStackTraceResponse);
869
0
    cmp_write_str(ctx, "frames", 6);
870
0
871
0
    write_stacktrace_frames(dtc, ctx, to_do);
872
0
873
0
    return 0;
874
0
}
875
876
0
static void send_thread_info(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
877
0
    MVMInstance *vm = dtc->instance;
878
0
    MVMint32 threadcount = 0;
879
0
    MVMThread *cur_thread;
880
0
    char infobuf[32] = "THL";
881
0
882
0
    uv_mutex_lock(&vm->mutex_threads);
883
0
    cur_thread = vm->threads;
884
0
    while (cur_thread) {
885
0
        threadcount++;
886
0
        cur_thread = cur_thread->body.next;
887
0
    }
888
0
889
0
    cmp_write_map(ctx, 3);
890
0
    cmp_write_str(ctx, "id", 2);
891
0
    cmp_write_integer(ctx, argument->id);
892
0
    cmp_write_str(ctx, "type", 4);
893
0
    cmp_write_integer(ctx, MT_ThreadListResponse);
894
0
    cmp_write_str(ctx, "threads", 7);
895
0
896
0
    cmp_write_array(ctx, threadcount);
897
0
898
0
    cur_thread = vm->threads;
899
0
    while (cur_thread) {
900
0
        cmp_write_map(ctx, 5);
901
0
902
0
        cmp_write_str(ctx, "thread", 6);
903
0
        cmp_write_integer(ctx, cur_thread->body.thread_id);
904
0
905
0
        cmp_write_str(ctx, "native_id", 9);
906
0
        cmp_write_integer(ctx, cur_thread->body.native_thread_id);
907
0
908
0
        cmp_write_str(ctx, "app_lifetime", 12);
909
0
        cmp_write_bool(ctx, cur_thread->body.app_lifetime);
910
0
911
0
        cmp_write_str(ctx, "suspended", 9);
912
0
        cmp_write_bool(ctx, (MVM_load(&cur_thread->body.tc->gc_status) & MVMSUSPENDSTATUS_MASK) != MVMSuspendState_NONE);
913
0
914
0
        cmp_write_str(ctx, "num_locks", 9);
915
0
        cmp_write_integer(ctx, MVM_thread_lock_count(dtc, (MVMObject *)cur_thread));
916
0
917
0
        cur_thread = cur_thread->body.next;
918
0
    }
919
0
    uv_mutex_unlock(&vm->mutex_threads);
920
0
}
921
922
0
static MVMuint64 send_is_execution_suspended_info(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
923
0
    MVMInstance *vm = dtc->instance;
924
0
    MVMuint8 result = 1;
925
0
    MVMThread *cur_thread;
926
0
927
0
    uv_mutex_lock(&vm->mutex_threads);
928
0
    cur_thread = vm->threads;
929
0
    while (cur_thread) {
930
0
        if ((MVM_load(&cur_thread->body.tc->gc_status) & MVMSUSPENDSTATUS_MASK) != MVMSuspendState_SUSPENDED
931
0
                && cur_thread->body.thread_id != vm->debugserver->thread_id
932
0
                && cur_thread->body.thread_id != vm->speshworker_thread_id) {
933
0
            result = 0;
934
0
            break;
935
0
        }
936
0
        cur_thread = cur_thread->body.next;
937
0
    }
938
0
939
0
    uv_mutex_unlock(&vm->mutex_threads);
940
0
941
0
    cmp_write_map(ctx, 3);
942
0
    cmp_write_str(ctx, "id", 2);
943
0
    cmp_write_integer(ctx, argument->id);
944
0
    cmp_write_str(ctx, "type", 4);
945
0
    cmp_write_integer(ctx, MT_IsExecutionSuspendedResponse);
946
0
    cmp_write_str(ctx, "suspended", 9);
947
0
    cmp_write_bool(ctx, result);
948
0
}
949
950
0
MVMuint8 setup_step(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument, MVMDebugSteppingMode mode, MVMThread *thread) {
951
0
    MVMThread *to_do = thread ? thread : find_thread_by_id(dtc->instance, argument->thread_id);
952
0
    MVMThreadContext *tc;
953
0
954
0
    if (!to_do)
955
0
        return 1;
956
0
957
0
    if ((to_do->body.tc->gc_status & MVMGCSTATUS_MASK) != MVMGCStatus_UNABLE) {
958
0
        return 1;
959
0
    }
960
0
961
0
    tc = to_do->body.tc;
962
0
    tc->step_mode_frame = tc->cur_frame;
963
0
    tc->step_message_id = argument->id;
964
0
    tc->step_mode_line_no = tc->cur_line_no;
965
0
    tc->step_mode_file_idx = tc->cur_file_idx;
966
0
967
0
    tc->step_mode = mode;
968
0
969
0
    request_thread_resumes(dtc, ctx, NULL, to_do);
970
0
971
0
    return 0;
972
0
}
973
974
0
void MVM_debugserver_add_breakpoint(MVMThreadContext *tc, cmp_ctx_t *ctx, request_data *argument) {
975
0
    MVMDebugServerData *debugserver = tc->instance->debugserver;
976
0
    MVMDebugServerBreakpointTable *table = debugserver->breakpoints;
977
0
    MVMDebugServerBreakpointFileTable *found = NULL;
978
0
    MVMDebugServerBreakpointInfo *bp_info = NULL;
979
0
    MVMuint32 index = 0;
980
0
981
0
    if (tc->instance->debugserver->debugspam_protocol)
982
0
        fprintf(stderr, "asked to set a breakpoint for file %s line %u to send id %lu\n", argument->file, argument->line, argument->id);
983
0
984
0
    MVM_debugserver_register_line(tc, argument->file, strlen(argument->file), argument->line, &index);
985
0
986
0
    uv_mutex_lock(&debugserver->mutex_breakpoints);
987
0
988
0
    found = &table->files[index];
989
0
990
0
    /* Create breakpoint first so that if a thread breaks on the activated line
991
0
     * the breakpoint information already exists */
992
0
    if (found->breakpoints_alloc == 0) {
993
0
        found->breakpoints_alloc = 4;
994
0
        found->breakpoints = MVM_fixed_size_alloc_zeroed(tc, tc->instance->fsa,
995
0
                found->breakpoints_alloc * sizeof(MVMDebugServerBreakpointInfo));
996
0
    }
997
0
    if (found->breakpoints_used++ >= found->breakpoints_alloc) {
998
0
        MVMuint32 old_alloc = found->breakpoints_alloc;
999
0
        found->breakpoints_alloc *= 2;
1000
0
        found->breakpoints = MVM_fixed_size_realloc_at_safepoint(tc, tc->instance->fsa, found->breakpoints,
1001
0
                old_alloc * sizeof(MVMDebugServerBreakpointInfo),
1002
0
                found->breakpoints_alloc * sizeof(MVMDebugServerBreakpointInfo));
1003
0
        if (tc->instance->debugserver->debugspam_protocol)
1004
0
            fprintf(stderr, "table for breakpoints increased to %u slots\n", found->breakpoints_alloc);
1005
0
    }
1006
0
1007
0
    bp_info = &found->breakpoints[found->breakpoints_used - 1];
1008
0
1009
0
    bp_info->breakpoint_id = argument->id;
1010
0
    bp_info->line_no = argument->line;
1011
0
    bp_info->shall_suspend = argument->suspend;
1012
0
    bp_info->send_backtrace = argument->stacktrace;
1013
0
1014
0
    debugserver->any_breakpoints_at_all++;
1015
0
1016
0
    if (tc->instance->debugserver->debugspam_protocol)
1017
0
        fprintf(stderr, "breakpoint settings: index %u bpid %lu lineno %u suspend %u backtrace %u\n", found->breakpoints_used - 1, argument->id, argument->line, argument->suspend, argument->stacktrace);
1018
0
1019
0
    found->lines_active[argument->line] = 1;
1020
0
1021
0
    cmp_write_map(ctx, 3);
1022
0
    cmp_write_str(ctx, "id", 2);
1023
0
    cmp_write_integer(ctx, argument->id);
1024
0
    cmp_write_str(ctx, "type", 4);
1025
0
    cmp_write_integer(ctx, MT_SetBreakpointConfirmation);
1026
0
    cmp_write_str(ctx, "line", 4);
1027
0
    cmp_write_integer(ctx, argument->line);
1028
0
1029
0
    uv_mutex_unlock(&debugserver->mutex_breakpoints);
1030
0
}
1031
1032
0
void MVM_debugserver_clear_all_breakpoints(MVMThreadContext *tc, cmp_ctx_t *ctx, request_data *argument) {
1033
0
    MVMDebugServerData *debugserver = tc->instance->debugserver;
1034
0
    MVMDebugServerBreakpointTable *table = debugserver->breakpoints;
1035
0
    MVMDebugServerBreakpointFileTable *found = NULL;
1036
0
    MVMuint32 index;
1037
0
1038
0
    uv_mutex_lock(&debugserver->mutex_breakpoints);
1039
0
1040
0
    for (index = 0; index < table->files_used; index++) {
1041
0
        found = &table->files[index];
1042
0
        memset(found->lines_active, 0, found->lines_active_alloc * sizeof(MVMuint8));
1043
0
        found->breakpoints_used = 0;
1044
0
    }
1045
0
1046
0
    debugserver->any_breakpoints_at_all = 0;
1047
0
1048
0
    uv_mutex_unlock(&debugserver->mutex_breakpoints);
1049
0
1050
0
    /* When a client disconnects, we clear all breakpoints but don't
1051
0
     * send a confirmation. In this case ctx and argument will be NULL */
1052
0
    if (ctx && argument)
1053
0
        communicate_success(tc, ctx, argument);
1054
0
}
1055
1056
0
void MVM_debugserver_clear_breakpoint(MVMThreadContext *tc, cmp_ctx_t *ctx, request_data *argument) {
1057
0
    MVMDebugServerData *debugserver = tc->instance->debugserver;
1058
0
    MVMDebugServerBreakpointTable *table = debugserver->breakpoints;
1059
0
    MVMDebugServerBreakpointFileTable *found = NULL;
1060
0
    MVMuint32 index = 0;
1061
0
    MVMuint32 bpidx = 0;
1062
0
    MVMuint32 num_cleared = 0;
1063
0
1064
0
    MVM_debugserver_register_line(tc, argument->file, strlen(argument->file), argument->line, &index);
1065
0
1066
0
    if (tc->instance->debugserver->debugspam_protocol)
1067
0
        fprintf(stderr, "asked to clear breakpoints for file %s line %u\n", argument->file, argument->line);
1068
0
1069
0
    uv_mutex_lock(&debugserver->mutex_breakpoints);
1070
0
1071
0
    found = &table->files[index];
1072
0
1073
0
    if (tc->instance->debugserver->debugspam_protocol) {
1074
0
        fprintf(stderr, "dumping all breakpoints\n");
1075
0
        for (bpidx = 0; bpidx < found->breakpoints_used; bpidx++) {
1076
0
            MVMDebugServerBreakpointInfo *bp_info = &found->breakpoints[bpidx];
1077
0
            fprintf(stderr, "breakpoint index %u has id %lu, is at line %u\n", bpidx, bp_info->breakpoint_id, bp_info->line_no);
1078
0
        }
1079
0
    }
1080
0
1081
0
    if (tc->instance->debugserver->debugspam_protocol)
1082
0
        fprintf(stderr, "trying to clear breakpoints\n\n");
1083
0
    for (bpidx = 0; bpidx < found->breakpoints_used; bpidx++) {
1084
0
        MVMDebugServerBreakpointInfo *bp_info = &found->breakpoints[bpidx];
1085
0
        if (tc->instance->debugserver->debugspam_protocol)
1086
0
            fprintf(stderr, "breakpoint index %u has id %lu, is at line %u\n", bpidx, bp_info->breakpoint_id, bp_info->line_no);
1087
0
1088
0
        if (bp_info->line_no == argument->line) {
1089
0
            if (tc->instance->debugserver->debugspam_protocol)
1090
0
                fprintf(stderr, "breakpoint with id %ld cleared\n", bp_info->breakpoint_id);
1091
0
            found->breakpoints[bpidx] = found->breakpoints[--found->breakpoints_used];
1092
0
            num_cleared++;
1093
0
            bpidx--;
1094
0
            debugserver->any_breakpoints_at_all--;
1095
0
        }
1096
0
    }
1097
0
1098
0
    uv_mutex_unlock(&debugserver->mutex_breakpoints);
1099
0
1100
0
    if (num_cleared > 0)
1101
0
        communicate_success(tc, ctx, argument);
1102
0
    else
1103
0
        communicate_error(tc, ctx, argument);
1104
0
}
1105
1106
0
static void release_all_handles(MVMThreadContext *dtc) {
1107
0
    MVMDebugServerHandleTable *dht = dtc->instance->debugserver->handle_table;
1108
0
    dht->used = 0;
1109
0
    dht->next_id = 1;
1110
0
}
1111
1112
0
static MVMuint64 release_handles(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
1113
0
    MVMDebugServerHandleTable *dht = dtc->instance->debugserver->handle_table;
1114
0
1115
0
    MVMuint16 handle_index = 0;
1116
0
    MVMuint16 id_index = 0;
1117
0
    MVMuint16 handles_cleared = 0;
1118
0
    for (handle_index = 0; handle_index < dht->used; handle_index++) {
1119
0
        for (id_index = 0; id_index < argument->handle_count; id_index++) {
1120
0
            if (argument->handles[id_index] == dht->entries[handle_index].id) {
1121
0
                dht->used--;
1122
0
                dht->entries[handle_index].id = dht->entries[dht->used].id;
1123
0
                dht->entries[handle_index].target = dht->entries[dht->used].target;
1124
0
                handle_index--;
1125
0
                handles_cleared++;
1126
0
                break;
1127
0
            }
1128
0
        }
1129
0
    }
1130
0
    if (handles_cleared != argument->handle_count) {
1131
0
        return 1;
1132
0
    } else {
1133
0
        return 0;
1134
0
    }
1135
0
}
1136
1137
0
static MVMuint64 allocate_handle(MVMThreadContext *dtc, MVMObject *target) {
1138
0
    if (!target || MVM_is_null(dtc, target)) {
1139
0
        return 0;
1140
0
    } else {
1141
0
        MVMDebugServerHandleTable *dht = dtc->instance->debugserver->handle_table;
1142
0
1143
0
        MVMuint64 id = dht->next_id++;
1144
0
1145
0
        if (dht->used + 1 > dht->allocated) {
1146
0
            if (dht->allocated < 8192)
1147
0
                dht->allocated *= 2;
1148
0
            else
1149
0
                dht->allocated += 1024;
1150
0
            dht->entries = MVM_realloc(dht->entries, sizeof(MVMDebugServerHandleTableEntry) * dht->allocated);
1151
0
        }
1152
0
1153
0
        dht->entries[dht->used].id = id;
1154
0
        dht->entries[dht->used].target = target;
1155
0
        dht->used++;
1156
0
1157
0
        return id;
1158
0
    }
1159
0
}
1160
1161
0
static MVMuint64 allocate_and_send_handle(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument, MVMObject *target) {
1162
0
    MVMuint64 id = allocate_handle(dtc, target);
1163
0
    cmp_write_map(ctx, 3);
1164
0
    cmp_write_str(ctx, "id", 2);
1165
0
    cmp_write_integer(ctx, argument->id);
1166
0
    cmp_write_str(ctx, "type", 4);
1167
0
    cmp_write_integer(ctx, MT_HandleResult);
1168
0
    cmp_write_str(ctx, "handle", 6);
1169
0
    cmp_write_integer(ctx, id);
1170
0
1171
0
    return id;
1172
0
}
1173
1174
0
static MVMObject *find_handle_target(MVMThreadContext *dtc, MVMuint64 id) {
1175
0
    MVMDebugServerHandleTable *dht = dtc->instance->debugserver->handle_table;
1176
0
    MVMuint32 index;
1177
0
1178
0
    for (index = 0; index < dht->used; index++) {
1179
0
        if (dht->entries[index].id == id)
1180
0
            return dht->entries[index].target;
1181
0
    }
1182
0
    return NULL;
1183
0
}
1184
1185
0
static MVMuint64 find_representant(MVMuint16 *representant, MVMuint64 index) {
1186
0
    MVMuint64 last = index;
1187
0
1188
0
    while (representant[last] != last) {
1189
0
        last = representant[last];
1190
0
    }
1191
0
1192
0
    return last;
1193
0
}
1194
1195
0
static void send_handle_equivalence_classes(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
1196
0
    MVMuint16  *representant = MVM_calloc(argument->handle_count, sizeof(MVMuint64));
1197
0
    MVMObject **objects      = MVM_calloc(argument->handle_count, sizeof(MVMObject *));
1198
0
    MVMuint16  *counts       = MVM_calloc(argument->handle_count, sizeof(MVMuint16));
1199
0
    MVMuint16   idx;
1200
0
    MVMuint16   classes_count = 0;
1201
0
1202
0
    /* Set up our sentinel values. Any object that represents itself
1203
0
     * is the end of a chain. At the beginning, everything represents
1204
0
     * itself.
1205
0
     * Critically, this allows us to use 0 as a valid representant. */
1206
0
    for (idx = 0; idx < argument->handle_count; idx++) {
1207
0
        representant[idx] = idx;
1208
0
    }
1209
0
1210
0
    for (idx = 0; idx < argument->handle_count; idx++) {
1211
0
        MVMuint16 other_idx;
1212
0
        objects[idx] = find_handle_target(dtc, argument->handles[idx]);
1213
0
1214
0
        for (other_idx = 0; other_idx < idx; other_idx++) {
1215
0
            if (representant[other_idx] != other_idx)
1216
0
                continue;
1217
0
            if (objects[idx] == objects[other_idx]) {
1218
0
                representant[other_idx] = idx;
1219
0
            }
1220
0
        }
1221
0
    }
1222
0
1223
0
    /* First, we have to count how many distinct classes there are.
1224
0
     * Whenever we hit 2, we know we've found a class. */
1225
0
    for (idx = 0; idx < argument->handle_count; idx++) {
1226
0
        MVMuint16 the_repr = find_representant(representant, idx);
1227
0
        counts[the_repr]++;
1228
0
        if (counts[the_repr] == 2)
1229
0
            classes_count++;
1230
0
    }
1231
0
1232
0
    /* Send the header of the message */
1233
0
    cmp_write_map(ctx, 3);
1234
0
    cmp_write_str(ctx, "id", 2);
1235
0
    cmp_write_integer(ctx, argument->id);
1236
0
    cmp_write_str(ctx, "type", 4);
1237
0
    cmp_write_integer(ctx, MT_HandleEquivalenceResponse);
1238
0
    cmp_write_str(ctx, "classes", 7);
1239
0
1240
0
    /* Now we can write out the classes by following the representant
1241
0
     * chain until we find one whose representant is itself. */
1242
0
    cmp_write_array(ctx, classes_count);
1243
0
    for (idx = 0; idx < argument->handle_count; idx++) {
1244
0
        if (representant[idx] != idx) {
1245
0
            MVMuint16 count = counts[find_representant(representant, idx)];
1246
0
            MVMuint16 pointer = idx;
1247
0
            cmp_write_array(ctx, count);
1248
0
            do {
1249
0
                MVMuint16 current_representant = representant[pointer];
1250
0
                representant[pointer] = pointer;
1251
0
                cmp_write_integer(ctx, argument->handles[pointer]);
1252
0
                pointer = current_representant;
1253
0
            } while (representant[pointer] != pointer);
1254
0
1255
0
            cmp_write_integer(ctx, argument->handles[pointer]);
1256
0
        }
1257
0
    }
1258
0
1259
0
    MVM_free(representant);
1260
0
    MVM_free(objects);
1261
0
    MVM_free(counts);
1262
0
}
1263
1264
0
static MVMint32 create_context_or_code_obj_debug_handle(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument, MVMThread *thread) {
1265
0
    MVMInstance *vm = dtc->instance;
1266
0
    MVMThread *to_do = thread ? thread : find_thread_by_id(vm, argument->thread_id);
1267
0
1268
0
    if (!to_do) {
1269
0
        if (dtc->instance->debugserver->debugspam_protocol)
1270
0
            fprintf(stderr, "no thread found for context/code obj handle (or thread not eligible)\n");
1271
0
        return 1;
1272
0
    }
1273
0
1274
0
    if ((to_do->body.tc->gc_status & MVMGCSTATUS_MASK) != MVMGCStatus_UNABLE) {
1275
0
        if (dtc->instance->debugserver->debugspam_protocol)
1276
0
            fprintf(stderr, "can only retrieve a context or code obj handle if thread is 'UNABLE' (is %lu)\n", to_do->body.tc->gc_status);
1277
0
        return 1;
1278
0
    }
1279
0
1280
0
    {
1281
0
1282
0
    MVMFrame *cur_frame = to_do->body.tc->cur_frame;
1283
0
    MVMuint32 frame_idx;
1284
0
1285
0
    for (frame_idx = 0;
1286
0
            cur_frame && frame_idx < argument->frame_number;
1287
0
            frame_idx++, cur_frame = cur_frame->caller) { }
1288
0
1289
0
    if (!cur_frame) {
1290
0
        if (dtc->instance->debugserver->debugspam_protocol)
1291
0
            fprintf(stderr, "couldn't create context/coderef handle: no such frame %d\n", argument->frame_number);
1292
0
        return 1;
1293
0
    }
1294
0
1295
0
    if (argument->type == MT_ContextHandle) {
1296
0
        MVMROOT(dtc, cur_frame, {
1297
0
            if (MVM_FRAME_IS_ON_CALLSTACK(dtc, cur_frame)) {
1298
0
                cur_frame = MVM_frame_debugserver_move_to_heap(dtc, to_do->body.tc, cur_frame);
1299
0
            }
1300
0
            allocate_and_send_handle(dtc, ctx, argument, MVM_frame_context_wrapper(dtc, cur_frame));
1301
0
        });
1302
0
    } else if (argument->type == MT_CodeObjectHandle) {
1303
0
        allocate_and_send_handle(dtc, ctx, argument, cur_frame->code_ref);
1304
0
    } else {
1305
0
        if (dtc->instance->debugserver->debugspam_protocol)
1306
0
            fprintf(stderr, "Did not expect to see create_context_or_code_obj_debug_handle called with a %d type\n", argument->type);
1307
0
        return 1;
1308
0
    }
1309
0
1310
0
    }
1311
0
1312
0
    return 0;
1313
0
}
1314
1315
0
static MVMint32 create_caller_or_outer_context_debug_handle(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument, MVMThread *thread) {
1316
0
    MVMObject *this_ctx = argument->handle_id
1317
0
        ? find_handle_target(dtc, argument->handle_id)
1318
0
        : dtc->instance->VMNull;
1319
0
1320
0
    MVMFrame *frame;
1321
0
    if (!IS_CONCRETE(this_ctx) || REPR(this_ctx)->ID != MVM_REPR_ID_MVMContext) {
1322
0
        if (dtc->instance->debugserver->debugspam_protocol)
1323
0
            fprintf(stderr, "outer/caller context handle must refer to a definite MVMContext object\n");
1324
0
        return 1;
1325
0
    }
1326
0
    if (argument->type == MT_OuterContextRequest) {
1327
0
        if ((frame = ((MVMContext *)this_ctx)->body.context->outer))
1328
0
            this_ctx = MVM_frame_context_wrapper(dtc, frame);
1329
0
    } else if (argument->type == MT_CallerContextRequest) {
1330
0
        if ((frame = ((MVMContext *)this_ctx)->body.context->caller))
1331
0
            this_ctx = MVM_frame_context_wrapper(dtc, frame);
1332
0
    }
1333
0
1334
0
    allocate_and_send_handle(dtc, ctx, argument, this_ctx);
1335
0
    return 0;
1336
0
}
1337
1338
0
static MVMint32 request_context_lexicals(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
1339
0
    MVMObject *this_ctx = argument->handle_id
1340
0
        ? find_handle_target(dtc, argument->handle_id)
1341
0
        : dtc->instance->VMNull;
1342
0
    MVMStaticFrame *static_info;
1343
0
    MVMLexicalRegistry *lexical_names;
1344
0
1345
0
    MVMFrame *frame;
1346
0
    if (MVM_is_null(dtc, this_ctx) || !IS_CONCRETE(this_ctx) || REPR(this_ctx)->ID != MVM_REPR_ID_MVMContext) {
1347
0
        if (dtc->instance->debugserver->debugspam_protocol)
1348
0
            fprintf(stderr, "getting lexicals: context handle must refer to a definite MVMContext object\n");
1349
0
        return 1;
1350
0
    }
1351
0
    if (!(frame = ((MVMContext *)this_ctx)->body.context)) {
1352
0
        if (dtc->instance->debugserver->debugspam_protocol)
1353
0
            fprintf(stderr, "context doesn't have a frame?!\n");
1354
0
        return 1;
1355
0
    }
1356
0
1357
0
    static_info = frame->static_info;
1358
0
    lexical_names = static_info->body.lexical_names;
1359
0
    if (lexical_names) {
1360
0
        MVMLexicalRegistry *entry, *tmp;
1361
0
        unsigned bucket_tmp;
1362
0
        MVMuint64 lexcount = HASH_CNT(hash_handle, lexical_names);
1363
0
        MVMuint64 lexical_index = 0;
1364
0
1365
0
        cmp_write_map(ctx, 3);
1366
0
        cmp_write_str(ctx, "id", 2);
1367
0
        cmp_write_integer(ctx, argument->id);
1368
0
        cmp_write_str(ctx, "type", 4);
1369
0
        cmp_write_integer(ctx, MT_ContextLexicalsResponse);
1370
0
1371
0
        cmp_write_str(ctx, "lexicals", 8);
1372
0
        cmp_write_map(ctx, lexcount);
1373
0
1374
0
        if (dtc->instance->debugserver->debugspam_protocol)
1375
0
            fprintf(stderr, "will write %lu lexicals\n", lexcount);
1376
0
1377
0
        HASH_ITER(hash_handle, lexical_names, entry, tmp, bucket_tmp) {
1378
0
            MVMuint16 lextype = static_info->body.lexical_types[entry->value];
1379
0
            MVMRegister *result = &frame->env[entry->value];
1380
0
            char *c_key_name;
1381
0
1382
0
            if (entry->key && IS_CONCRETE(entry->key))
1383
0
                c_key_name = MVM_string_utf8_encode_C_string(dtc, entry->key);
1384
0
            else {
1385
0
                c_key_name = MVM_malloc(12 + 16);
1386
0
                sprintf(c_key_name, "<lexical %lu>", lexical_index);
1387
0
            }
1388
0
1389
0
            cmp_write_str(ctx, c_key_name, strlen(c_key_name));
1390
0
1391
0
            MVM_free(c_key_name);
1392
0
1393
0
            if (lextype == MVM_reg_obj) { /* Object */
1394
0
                char *debugname;
1395
0
1396
0
                if (!result->o) {
1397
0
                    /* XXX this can't allocate? */
1398
0
                    MVM_frame_vivify_lexical(dtc, frame, entry->value);
1399
0
                }
1400
0
1401
0
                cmp_write_map(ctx, 5);
1402
0
1403
0
                cmp_write_str(ctx, "kind", 4);
1404
0
                cmp_write_str(ctx, "obj", 3);
1405
0
1406
0
                cmp_write_str(ctx, "handle", 6);
1407
0
                cmp_write_integer(ctx, allocate_handle(dtc, result->o));
1408
0
1409
0
                debugname = MVM_6model_get_debug_name(dtc, result->o);
1410
0
1411
0
                cmp_write_str(ctx, "type", 4);
1412
0
                cmp_write_str(ctx, debugname, strlen(debugname));
1413
0
1414
0
                cmp_write_str(ctx, "concrete", 8);
1415
0
                cmp_write_bool(ctx, IS_CONCRETE(result->o));
1416
0
1417
0
                cmp_write_str(ctx, "container", 9);
1418
0
                cmp_write_bool(ctx, STABLE(result->o)->container_spec == NULL ? 0 : 1);
1419
0
            } else {
1420
0
                cmp_write_map(ctx, 2);
1421
0
1422
0
                cmp_write_str(ctx, "kind", 4);
1423
0
                cmp_write_str(ctx,
1424
0
                        lextype == MVM_reg_int64 ? "int" :
1425
0
                        lextype == MVM_reg_num32 ? "num" :
1426
0
                        lextype == MVM_reg_str   ? "str" :
1427
0
                        "???", 3);
1428
0
1429
0
                cmp_write_str(ctx, "value", 5);
1430
0
                if (lextype == MVM_reg_int64) {
1431
0
                    cmp_write_integer(ctx, result->i64);
1432
0
                } else if (lextype == MVM_reg_num64) {
1433
0
                    cmp_write_double(ctx, result->n64);
1434
0
                } else if (lextype == MVM_reg_str) {
1435
0
                    if (result->s && IS_CONCRETE(result->s)) {
1436
0
                        char *c_value = MVM_string_utf8_encode_C_string(dtc, result->s);
1437
0
                        cmp_write_str(ctx, c_value, strlen(c_value));
1438
0
                        MVM_free(c_value);
1439
0
                    } else {
1440
0
                        cmp_write_nil(ctx);
1441
0
                    }
1442
0
                } else {
1443
0
                    if (dtc->instance->debugserver->debugspam_protocol)
1444
0
                        fprintf(stderr, "what lexical type is %d supposed to be?\n", lextype);
1445
0
                    cmp_write_nil(ctx);
1446
0
                }
1447
0
            }
1448
0
            if (dtc->instance->debugserver->debugspam_protocol)
1449
0
                fprintf(stderr, "wrote a lexical\n");
1450
0
            lexical_index++;
1451
0
        }
1452
0
    } else {
1453
0
        cmp_write_map(ctx, 3);
1454
0
        cmp_write_str(ctx, "id", 2);
1455
0
        cmp_write_integer(ctx, argument->id);
1456
0
        cmp_write_str(ctx, "type", 4);
1457
0
        cmp_write_integer(ctx, MT_ContextLexicalsResponse);
1458
0
1459
0
        cmp_write_str(ctx, "lexicals", 8);
1460
0
        cmp_write_map(ctx, 0);
1461
0
    }
1462
0
    if (dtc->instance->debugserver->debugspam_protocol)
1463
0
        fprintf(stderr, "done writing lexicals\n");
1464
0
    return 0;
1465
0
}
1466
1467
0
MVM_STATIC_INLINE MVMObject * get_obj_at_offset(void *data, MVMint64 offset) {
1468
0
    void *location = (char *)data + offset;
1469
0
    return *((MVMObject **)location);
1470
0
}
1471
0
static MVMint32 request_object_attributes(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
1472
0
    MVMInstance *vm = dtc->instance;
1473
0
    MVMObject *target = argument->handle_id
1474
0
        ? find_handle_target(dtc, argument->handle_id)
1475
0
        : dtc->instance->VMNull;
1476
0
1477
0
    if (MVM_is_null(dtc, target)) {
1478
0
        if (vm->debugserver->debugspam_protocol)
1479
0
            fprintf(stderr, "target of attributes request is null\n");
1480
0
        return 1;
1481
0
    }
1482
0
1483
0
    if (!IS_CONCRETE(target)) {
1484
0
        if (vm->debugserver->debugspam_protocol)
1485
0
            fprintf(stderr, "target of attributes request is not concrete\n");
1486
0
        return 1;
1487
0
    }
1488
0
1489
0
    if (dtc->instance->debugserver->debugspam_protocol)
1490
0
        fprintf(stderr, "writing attributes of a %s\n", MVM_6model_get_debug_name(dtc, target));
1491
0
1492
0
    cmp_write_map(ctx, 3);
1493
0
    cmp_write_str(ctx, "id", 2);
1494
0
    cmp_write_integer(ctx, argument->id);
1495
0
    cmp_write_str(ctx, "type", 4);
1496
0
    cmp_write_integer(ctx, MT_ObjectAttributesResponse);
1497
0
1498
0
    cmp_write_str(ctx, "attributes", 10);
1499
0
1500
0
    if (REPR(target)->ID == MVM_REPR_ID_P6opaque) {
1501
0
        MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData *)STABLE(target)->REPR_data;
1502
0
        MVMP6opaqueBody *data = MVM_p6opaque_real_data(dtc, OBJECT_BODY(target));
1503
0
        if (repr_data) {
1504
0
            MVMint16 num_attributes = 0;
1505
0
            MVMP6opaqueNameMap * name_to_index_mapping = repr_data->name_to_index_mapping;
1506
0
1507
0
            while (name_to_index_mapping != NULL && name_to_index_mapping->class_key != NULL) {
1508
0
                num_attributes += name_to_index_mapping->num_attrs;
1509
0
                name_to_index_mapping++;
1510
0
            }
1511
0
1512
0
            name_to_index_mapping = repr_data->name_to_index_mapping;
1513
0
1514
0
            cmp_write_array(ctx, num_attributes);
1515
0
1516
0
            if (vm->debugserver->debugspam_protocol)
1517
0
                fprintf(stderr, "going to write out %d attributes\n", num_attributes);
1518
0
1519
0
            if (name_to_index_mapping != NULL) {
1520
0
                MVMint16 i;
1521
0
                MVMP6opaqueNameMap *cur_map_entry = name_to_index_mapping;
1522
0
1523
0
                while (cur_map_entry->class_key != NULL) {
1524
0
                    MVMint16 i;
1525
0
                    MVMint64 slot;
1526
0
                    char *class_name = MVM_6model_get_stable_debug_name(dtc, cur_map_entry->class_key->st);
1527
0
1528
0
                    if (vm->debugserver->debugspam_protocol)
1529
0
                        fprintf(stderr, "class %s has %d attributes\n", class_name, cur_map_entry->num_attrs);
1530
0
1531
0
                    for (i = 0; i < cur_map_entry->num_attrs; i++) {
1532
0
                        char * name = MVM_string_utf8_encode_C_string(dtc, cur_map_entry->names[i]);
1533
0
1534
0
                        slot = cur_map_entry->slots[i];
1535
0
                        if (slot >= 0) {
1536
0
                            MVMuint16 const offset = repr_data->attribute_offsets[slot];
1537
0
                            MVMSTable * const attr_st = repr_data->flattened_stables[slot];
1538
0
                            if (attr_st == NULL) {
1539
0
                                MVMObject *value = get_obj_at_offset(data, offset);
1540
0
                                char *value_debug_name = value ? MVM_6model_get_debug_name(dtc, value) : "VMNull";
1541
0
1542
0
                                if (vm->debugserver->debugspam_protocol)
1543
0
                                    fprintf(stderr, "Writing an object attribute\n");
1544
0
1545
0
                                cmp_write_map(ctx, 7);
1546
0
1547
0
                                cmp_write_str(ctx, "name", 4);
1548
0
                                cmp_write_str(ctx, name, strlen(name));
1549
0
1550
0
                                cmp_write_str(ctx, "class", 5);
1551
0
                                cmp_write_str(ctx, class_name, strlen(class_name));
1552
0
1553
0
                                cmp_write_str(ctx, "kind", 4);
1554
0
                                cmp_write_str(ctx, "obj", 3);
1555
0
1556
0
                                cmp_write_str(ctx, "handle", 6);
1557
0
                                cmp_write_integer(ctx, allocate_handle(dtc, value));
1558
0
1559
0
                                cmp_write_str(ctx, "type", 4);
1560
0
                                cmp_write_str(ctx, value_debug_name, strlen(value_debug_name));
1561
0
1562
0
                                cmp_write_str(ctx, "concrete", 8);
1563
0
                                cmp_write_bool(ctx, !MVM_is_null(dtc, value) && IS_CONCRETE(value));
1564
0
1565
0
                                cmp_write_str(ctx, "container", 9);
1566
0
                                if (MVM_is_null(dtc, value))
1567
0
                                    cmp_write_bool(ctx, 0);
1568
0
                                else
1569
0
                                    cmp_write_bool(ctx, STABLE(value)->container_spec == NULL ? 0 : 1);
1570
0
                            }
1571
0
                            else {
1572
0
                                const MVMStorageSpec *attr_storage_spec = attr_st->REPR->get_storage_spec(dtc, attr_st);
1573
0
1574
0
                                if (vm->debugserver->debugspam_protocol)
1575
0
                                    fprintf(stderr, "Writing a native attribute\n");
1576
0
1577
0
                                cmp_write_map(ctx, 4);
1578
0
1579
0
                                cmp_write_str(ctx, "name", 4);
1580
0
                                cmp_write_str(ctx, name, strlen(name));
1581
0
1582
0
                                cmp_write_str(ctx, "class", 5);
1583
0
                                cmp_write_str(ctx, class_name, strlen(class_name));
1584
0
1585
0
                                cmp_write_str(ctx, "kind", 4);
1586
0
1587
0
                                switch (attr_storage_spec->boxed_primitive) {
1588
0
                                    case MVM_STORAGE_SPEC_BP_INT:
1589
0
                                        cmp_write_str(ctx, "int", 3);
1590
0
                                        cmp_write_str(ctx, "value", 5);
1591
0
                                        cmp_write_integer(ctx, attr_st->REPR->box_funcs.get_int(dtc, attr_st, target, (char *)data + offset));
1592
0
                                        break;
1593
0
                                    case MVM_STORAGE_SPEC_BP_NUM:
1594
0
                                        cmp_write_str(ctx, "num", 3);
1595
0
                                        cmp_write_str(ctx, "value", 5);
1596
0
                                        cmp_write_double(ctx, attr_st->REPR->box_funcs.get_num(dtc, attr_st, target, (char *)data + offset));
1597
0
                                        break;
1598
0
                                    case MVM_STORAGE_SPEC_BP_STR: {
1599
0
                                        MVMString * const s = attr_st->REPR->box_funcs.get_str(dtc, attr_st, target, (char *)data + offset);
1600
0
                                        char * str;
1601
0
                                        if (s)
1602
0
                                            str = MVM_string_utf8_encode_C_string(dtc, s);
1603
0
                                        cmp_write_str(ctx, "str", 3);
1604
0
                                        cmp_write_str(ctx, "value", 5);
1605
0
                                        if (s) {
1606
0
                                            cmp_write_str(ctx, str, strlen(str));
1607
0
                                            MVM_free(str);
1608
0
                                        }
1609
0
                                        else {
1610
0
                                            cmp_write_nil(ctx);
1611
0
                                        }
1612
0
                                        break;
1613
0
                                    }
1614
0
                                    default:
1615
0
                                        cmp_write_str(ctx, "error", 5);
1616
0
                                        cmp_write_str(ctx, "value", 5);
1617
0
                                        cmp_write_str(ctx, "error", 5);
1618
0
                                        break;
1619
0
                                }
1620
0
                            }
1621
0
                        }
1622
0
1623
0
                        MVM_free(name);
1624
0
                    }
1625
0
                    cur_map_entry++;
1626
0
                }
1627
0
            }
1628
0
            return 0;
1629
0
        }
1630
0
        else {
1631
0
            if (vm->debugserver->debugspam_protocol)
1632
0
                fprintf(stderr, "This class isn't composed yet!\n");
1633
0
            cmp_write_str(ctx, "error: not composed yet", 22);
1634
0
            return 0;
1635
0
        }
1636
0
    } else {
1637
0
        cmp_write_array(ctx, 0);
1638
0
        return 0;
1639
0
    }
1640
0
1641
0
    return 1;
1642
0
}
1643
0
static void write_object_features(MVMThreadContext *tc, cmp_ctx_t *ctx, MVMuint8 attributes, MVMuint8 positional, MVMuint8 associative) {
1644
0
    cmp_write_str(ctx, "attr_features", 13);
1645
0
    cmp_write_bool(ctx, attributes);
1646
0
    cmp_write_str(ctx, "pos_features", 12);
1647
0
    cmp_write_bool(ctx, positional);
1648
0
    cmp_write_str(ctx, "ass_features", 12);
1649
0
    cmp_write_bool(ctx, associative);
1650
0
}
1651
0
static void write_vmarray_slot_type(MVMThreadContext *tc, cmp_ctx_t *ctx, MVMuint8 slot_type) {
1652
0
    char *text = "unknown";
1653
0
    switch (slot_type) {
1654
0
        case MVM_ARRAY_OBJ: text = "obj"; break;
1655
0
        case MVM_ARRAY_STR: text = "str"; break;
1656
0
        case MVM_ARRAY_I64: text = "i64"; break;
1657
0
        case MVM_ARRAY_I32: text = "i32"; break;
1658
0
        case MVM_ARRAY_I16: text = "i16"; break;
1659
0
        case MVM_ARRAY_I8:  text = "i8"; break;
1660
0
        case MVM_ARRAY_N64: text = "n64"; break;
1661
0
        case MVM_ARRAY_N32: text = "n32"; break;
1662
0
        case MVM_ARRAY_U64: text = "u64"; break;
1663
0
        case MVM_ARRAY_U32: text = "u32"; break;
1664
0
        case MVM_ARRAY_U16: text = "u16"; break;
1665
0
        case MVM_ARRAY_U8:  text = "u8";  break;
1666
0
        case MVM_ARRAY_U4:  text = "u4"; break;
1667
0
        case MVM_ARRAY_U2:  text = "u2"; break;
1668
0
        case MVM_ARRAY_U1:  text = "u1"; break;
1669
0
        case MVM_ARRAY_I4:  text = "i4"; break;
1670
0
        case MVM_ARRAY_I2:  text = "i2"; break;
1671
0
        case MVM_ARRAY_I1:  text = "i1"; break;
1672
0
    }
1673
0
    cmp_write_str(ctx, text, strlen(text));
1674
0
}
1675
0
static MVMuint16 write_vmarray_slot_kind(MVMThreadContext *tc, cmp_ctx_t *ctx, MVMuint8 slot_type) {
1676
0
    char *text = "unknown";
1677
0
    MVMuint16 kind = 0;
1678
0
    switch (slot_type) {
1679
0
        case MVM_ARRAY_OBJ: text = "obj"; kind = MVM_reg_obj; break;
1680
0
        case MVM_ARRAY_STR: text = "str"; kind = MVM_reg_str; break;
1681
0
        case MVM_ARRAY_I64:
1682
0
        case MVM_ARRAY_I32:
1683
0
        case MVM_ARRAY_I16:
1684
0
        case MVM_ARRAY_I8:  text = "int"; kind = MVM_reg_int64; break;
1685
0
        case MVM_ARRAY_N64:
1686
0
        case MVM_ARRAY_N32: text = "num"; kind = MVM_reg_num64; break;
1687
0
        case MVM_ARRAY_U64:
1688
0
        case MVM_ARRAY_U32:
1689
0
        case MVM_ARRAY_U16:
1690
0
        case MVM_ARRAY_U8:
1691
0
        case MVM_ARRAY_U4:
1692
0
        case MVM_ARRAY_U2:
1693
0
        case MVM_ARRAY_U1:
1694
0
        case MVM_ARRAY_I4:
1695
0
        case MVM_ARRAY_I2:
1696
0
        case MVM_ARRAY_I1:  text = "int"; kind = MVM_reg_int64; break;
1697
0
    }
1698
0
    cmp_write_str(ctx, text, strlen(text));
1699
0
    return kind;
1700
0
}
1701
0
static MVMint32 request_object_metadata(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
1702
0
    MVMInstance *vm = dtc->instance;
1703
0
    MVMObject *target = argument->handle_id
1704
0
        ? find_handle_target(dtc, argument->handle_id)
1705
0
        : dtc->instance->VMNull;
1706
0
1707
0
    MVMint64 slots = 2; /* Always have the repr name and debug name */
1708
0
    MVMuint32 repr_id;
1709
0
1710
0
    if (MVM_is_null(dtc, target)) {
1711
0
        return 1;
1712
0
    }
1713
0
1714
0
    cmp_write_map(ctx, 3);
1715
0
    cmp_write_str(ctx, "id", 2);
1716
0
    cmp_write_integer(ctx, argument->id);
1717
0
    cmp_write_str(ctx, "type", 4);
1718
0
    cmp_write_integer(ctx, MT_ObjectMetadataResponse);
1719
0
1720
0
    cmp_write_str(ctx, "metadata", 8);
1721
0
1722
0
    if (IS_CONCRETE(target)) {
1723
0
        slots++;
1724
0
        if (REPR(target)->unmanaged_size)
1725
0
            slots++;
1726
0
    }
1727
0
1728
0
    repr_id = REPR(target)->ID;
1729
0
1730
0
    if (repr_id == MVM_REPR_ID_P6opaque) {
1731
0
        MVMP6opaqueREPRData *repr_data = (MVMP6opaqueREPRData*)(STABLE(target)->REPR_data);
1732
0
        MVMP6opaqueBody *body = &((MVMP6opaque *)target)->body;
1733
0
        if (IS_CONCRETE(target)) {
1734
0
            slots += 1; /* Replaced? */
1735
0
        }
1736
0
1737
0
        slots += 5; /* pos/ass del slots, int/num/str unbox slots */
1738
0
        /*slots++;    [> storage spec <]*/
1739
0
        slots += 3; /* features */
1740
0
        cmp_write_map(ctx, slots);
1741
0
1742
0
        cmp_write_str(ctx, "p6opaque_pos_delegate_slot", 21);
1743
0
        cmp_write_int(ctx, repr_data->pos_del_slot);
1744
0
        cmp_write_str(ctx, "p6opaque_ass_delegate_slot", 21);
1745
0
        cmp_write_int(ctx, repr_data->ass_del_slot);
1746
0
1747
0
        cmp_write_str(ctx, "p6opaque_unbox_int_slot", 23);
1748
0
        cmp_write_int(ctx, repr_data->unbox_int_slot);
1749
0
        cmp_write_str(ctx, "p6opaque_unbox_num_slot", 23);
1750
0
        cmp_write_int(ctx, repr_data->unbox_num_slot);
1751
0
        cmp_write_str(ctx, "p6opaque_unbox_str_slot", 23);
1752
0
        cmp_write_int(ctx, repr_data->unbox_str_slot);
1753
0
1754
0
        if (IS_CONCRETE(target)) {
1755
0
            cmp_write_str(ctx, "p6opaque_body_replaced", 22);
1756
0
            cmp_write_bool(ctx, !!body->replaced);
1757
0
        }
1758
0
1759
0
        write_object_features(dtc, ctx, 1, 0, 0);
1760
0
1761
0
        /* TODO maybe output additional unbox slots, too? */
1762
0
1763
0
        /*cmp_write_str(ctx, "storage_spec", 12);*/
1764
0
        /*write_storage_spec(dtc, ctx, repr_data->storage_spec);*/
1765
0
    }
1766
0
    else if (repr_id == MVM_REPR_ID_VMArray) {
1767
0
        MVMArrayREPRData *repr_data = (MVMArrayREPRData *)(STABLE(target)->REPR_data);
1768
0
        char *debugname = repr_data->elem_type ? MVM_6model_get_debug_name(dtc, repr_data->elem_type) : NULL;
1769
0
        if (IS_CONCRETE(target)) {
1770
0
            slots += 3; /* slots allocated / used, storage size */
1771
0
        }
1772
0
        slots += 3;
1773
0
        slots += 3; /* features */
1774
0
        cmp_write_map(ctx, slots);
1775
0
1776
0
        cmp_write_str(ctx, "vmarray_elem_size", 17);
1777
0
        cmp_write_int(ctx, repr_data->elem_size);
1778
0
1779
0
        cmp_write_str(ctx, "vmarray_slot_type", 17);
1780
0
        write_vmarray_slot_type(dtc, ctx, repr_data->slot_type);
1781
0
1782
0
        cmp_write_str(ctx, "vmarray_elem_type", 17);
1783
0
        if (debugname)
1784
0
            cmp_write_str(ctx, debugname, strlen(debugname));
1785
0
        else
1786
0
            cmp_write_nil(ctx);
1787
0
1788
0
        if (IS_CONCRETE(target)) {
1789
0
            MVMArrayBody *body = (MVMArrayBody *)OBJECT_BODY(target);
1790
0
1791
0
            cmp_write_str(ctx, "positional_elems", 16);
1792
0
            cmp_write_int(ctx, body->elems);
1793
0
            cmp_write_str(ctx, "vmarray_start", 13);
1794
0
            cmp_write_int(ctx, body->start);
1795
0
            cmp_write_str(ctx, "vmarray_ssize", 13);
1796
0
            cmp_write_int(ctx, body->ssize);
1797
0
        }
1798
0
1799
0
        write_object_features(dtc, ctx, 0, 1, 0);
1800
0
    }
1801
0
    else if (repr_id == MVM_REPR_ID_MVMHash) {
1802
0
        if (IS_CONCRETE(target)) {
1803
0
            slots += 4; /* num_buckets, num_items, nonideal_items, ineff_expands */
1804
0
        }
1805
0
        slots += 3; /* features */
1806
0
        cmp_write_map(ctx, slots);
1807
0
1808
0
        if (IS_CONCRETE(target)) {
1809
0
            MVMHashBody *body = (MVMHashBody *)OBJECT_BODY(target);
1810
0
            MVMHashEntry *handle = body->hash_head;
1811
0
            UT_hash_table *tbl = handle ? body->hash_head->hash_handle.tbl : 0;
1812
0
1813
0
            if (tbl) {
1814
0
                cmp_write_str(ctx, "mvmhash_num_buckets", 19);
1815
0
                cmp_write_int(ctx, tbl->num_buckets);
1816
0
                cmp_write_str(ctx, "mvmhash_num_items", 17);
1817
0
                cmp_write_int(ctx, tbl->num_items);
1818
0
                cmp_write_str(ctx, "mvmhash_nonideal_items", 22);
1819
0
                cmp_write_int(ctx, tbl->nonideal_items);
1820
0
                cmp_write_str(ctx, "mvmhash_ineff_expands", 21);
1821
0
                cmp_write_int(ctx, tbl->ineff_expands);
1822
0
            }
1823
0
            else {
1824
0
                cmp_write_str(ctx, "mvmhash_num_buckets", 19);
1825
0
                cmp_write_int(ctx, 0);
1826
0
                cmp_write_str(ctx, "mvmhash_num_items", 17);
1827
0
                cmp_write_int(ctx, 0);
1828
0
                cmp_write_str(ctx, "mvmhash_nonideal_items", 22);
1829
0
                cmp_write_int(ctx, 0);
1830
0
                cmp_write_str(ctx, "mvmhash_ineff_expands", 21);
1831
0
                cmp_write_int(ctx, 0);
1832
0
            }
1833
0
        }
1834
0
1835
0
        write_object_features(dtc, ctx, 0, 0, 1);
1836
0
    }
1837
0
    else if (repr_id == MVM_REPR_ID_MVMContext) {
1838
0
        MVMFrame *frame = ((MVMContext *)target)->body.context;
1839
0
        MVMArgProcContext *argctx = &frame->params;
1840
0
        MVMCallsite *cs = argctx->callsite;
1841
0
        MVMCallsiteEntry *cse = argctx->arg_flags ? argctx->arg_flags : cs->arg_flags;
1842
0
        MVMuint16 flag_idx;
1843
0
        MVMuint16 flag_count = argctx->arg_flags ? argctx->flag_count : cs->flag_count;
1844
0
        char *name = MVM_string_utf8_encode_C_string(dtc, frame->static_info->body.name);
1845
0
        char *cuuid = MVM_string_utf8_encode_C_string(dtc, frame->static_info->body.cuuid);
1846
0
1847
0
        slots += 8;
1848
0
        slots += 3; /* features */
1849
0
1850
0
        cmp_write_map(ctx, slots);
1851
0
1852
0
        cmp_write_str(ctx, "frame_on_heap", 13);
1853
0
        cmp_write_bool(ctx, !MVM_FRAME_IS_ON_CALLSTACK(dtc, frame));
1854
0
1855
0
        cmp_write_str(ctx, "frame_work_size", 15);
1856
0
        cmp_write_int(ctx, frame->allocd_work);
1857
0
        cmp_write_str(ctx, "frame_env_size", 15);
1858
0
        cmp_write_int(ctx, frame->allocd_env);
1859
0
1860
0
        cmp_write_str(ctx, "frame_name", 10);
1861
0
        cmp_write_str(ctx, name, strlen(name));
1862
0
        cmp_write_str(ctx, "frame_cuuid", 11);
1863
0
        cmp_write_str(ctx, cuuid, strlen(cuuid));
1864
0
1865
0
        cmp_write_str(ctx, "frame_num_locals", 16);
1866
0
        cmp_write_int(ctx, frame->static_info->body.num_locals);
1867
0
1868
0
        cmp_write_str(ctx, "frame_num_lexicals", 18);
1869
0
        cmp_write_int(ctx, frame->static_info->body.num_lexicals);
1870
0
1871
0
        cmp_write_str(ctx, "callsite_flags", 14);
1872
0
        cmp_write_array(ctx, flag_count);
1873
0
1874
0
        for (flag_idx = 0; flag_idx < flag_count; flag_idx++) {
1875
0
            MVMCallsiteEntry entry = cse[flag_idx];
1876
0
            MVMuint8 entry_count =
1877
0
                !!(entry & MVM_CALLSITE_ARG_OBJ)
1878
0
                + !!(entry & MVM_CALLSITE_ARG_INT)
1879
0
                + !!(entry & MVM_CALLSITE_ARG_NUM)
1880
0
                + !!(entry & MVM_CALLSITE_ARG_STR)
1881
0
                + !!(entry & MVM_CALLSITE_ARG_NAMED)
1882
0
                + !!(entry & MVM_CALLSITE_ARG_FLAT)
1883
0
                + !!(entry & MVM_CALLSITE_ARG_FLAT_NAMED);
1884
0
            cmp_write_array(ctx, entry_count ? entry_count : 0);
1885
0
            if (entry & MVM_CALLSITE_ARG_OBJ)
1886
0
                cmp_write_str(ctx, "obj", 3);
1887
0
            if (entry & MVM_CALLSITE_ARG_INT)
1888
0
                cmp_write_str(ctx, "int", 3);
1889
0
            if (entry & MVM_CALLSITE_ARG_NUM)
1890
0
                cmp_write_str(ctx, "num", 3);
1891
0
            if (entry & MVM_CALLSITE_ARG_STR)
1892
0
                cmp_write_str(ctx, "str", 3);
1893
0
            if (entry & MVM_CALLSITE_ARG_NAMED)
1894
0
                cmp_write_str(ctx, "named", 5);
1895
0
            if (entry & MVM_CALLSITE_ARG_FLAT)
1896
0
                cmp_write_str(ctx, "flat", 4);
1897
0
            if (entry & MVM_CALLSITE_ARG_FLAT_NAMED)
1898
0
                cmp_write_str(ctx, "flat&named", 10);
1899
0
            if (!entry_count)
1900
0
                cmp_write_str(ctx, "nothing", 7);
1901
0
        }
1902
0
1903
0
        
1904
0
1905
0
        MVM_free(name);
1906
0
        MVM_free(cuuid);
1907
0
1908
0
        write_object_features(dtc, ctx, 0, 0, 0);
1909
0
    }
1910
0
    else if (repr_id == MVM_REPR_ID_P6str && IS_CONCRETE(target)) {
1911
0
        MVMP6strBody *body = (MVMP6strBody *)OBJECT_BODY(target);
1912
0
        MVMString *string = body->value;
1913
0
1914
0
        char *value = MVM_string_utf8_encode_C_string(dtc, string);
1915
0
        slots += 2;
1916
0
        slots += 3; /* features */
1917
0
1918
0
        if (string->body.storage_type == MVM_STRING_STRAND)
1919
0
            slots += 5;
1920
0
1921
0
        cmp_write_map(ctx, slots);
1922
0
1923
0
        cmp_write_str(ctx, "string_value", 12);
1924
0
        cmp_write_str(ctx, value, strlen(value));
1925
0
1926
0
        cmp_write_str(ctx, "string_storage_kind", 19);
1927
0
        switch (string->body.storage_type) {
1928
0
            case MVM_STRING_GRAPHEME_32:    cmp_write_str(ctx, "grapheme32", 10); break;
1929
0
            case MVM_STRING_GRAPHEME_ASCII: cmp_write_str(ctx, "graphemeASCII", 13); break;
1930
0
            case MVM_STRING_GRAPHEME_8:     cmp_write_str(ctx, "grapheme8", 9); break;
1931
0
            case MVM_STRING_STRAND:         cmp_write_str(ctx, "strands", 7); break;
1932
0
            default: cmp_write_str(ctx, "???", 3);
1933
0
        }
1934
0
1935
0
        if (string->body.storage_type == MVM_STRING_STRAND) {
1936
0
            MVMuint16 num_strands = string->body.num_strands;
1937
0
            MVMuint16 strand_idx;
1938
0
            cmp_write_str(ctx, "string_strand_count", 19);
1939
0
            cmp_write_int(ctx, num_strands);
1940
0
1941
0
            cmp_write_str(ctx, "string_strand_starts", 20);
1942
0
            cmp_write_array(ctx, num_strands);
1943
0
            for (strand_idx = 0; strand_idx < num_strands; strand_idx++) {
1944
0
                cmp_write_int(ctx, string->body.storage.strands[strand_idx].start);
1945
0
            }
1946
0
            cmp_write_str(ctx, "string_strand_ends", 18);
1947
0
            cmp_write_array(ctx, num_strands);
1948
0
            for (strand_idx = 0; strand_idx < num_strands; strand_idx++) {
1949
0
                cmp_write_int(ctx, string->body.storage.strands[strand_idx].end);
1950
0
            }
1951
0
            cmp_write_str(ctx, "string_strand_repetitions", 25);
1952
0
            cmp_write_array(ctx, num_strands);
1953
0
            for (strand_idx = 0; strand_idx < num_strands; strand_idx++) {
1954
0
                cmp_write_int(ctx, string->body.storage.strands[strand_idx].repetitions);
1955
0
            }
1956
0
            cmp_write_str(ctx, "string_strand_target_length", 27);
1957
0
            cmp_write_array(ctx, num_strands);
1958
0
            for (strand_idx = 0; strand_idx < num_strands; strand_idx++) {
1959
0
                cmp_write_int(ctx, string->body.storage.strands[strand_idx].blob_string->body.num_graphs);
1960
0
            }
1961
0
        }
1962
0
1963
0
        write_object_features(dtc, ctx, 0, 0, 0);
1964
0
1965
0
        MVM_free(value);
1966
0
    }
1967
0
    else if (repr_id == MVM_REPR_ID_ReentrantMutex && IS_CONCRETE(target)) {
1968
0
        MVMReentrantMutexBody *body = (MVMReentrantMutexBody *)OBJECT_BODY(target);
1969
0
1970
0
        slots += 3;
1971
0
        slots += 3; /* features */
1972
0
1973
0
        cmp_write_map(ctx, slots);
1974
0
1975
0
        cmp_write_str(ctx, "mutex_identity", 14);
1976
0
        cmp_write_int(ctx, (uintptr_t)body->mutex);
1977
0
        cmp_write_str(ctx, "mutex_holder", 12);
1978
0
        cmp_write_int(ctx, MVM_load(&body->holder_id));
1979
0
        cmp_write_str(ctx, "mutex_lock_count", 16);
1980
0
        cmp_write_int(ctx, MVM_load(&body->lock_count));
1981
0
1982
0
        write_object_features(dtc, ctx, 0, 0, 0);
1983
0
    }
1984
0
    else if (repr_id == MVM_REPR_ID_Semaphore && IS_CONCRETE(target)) {
1985
0
        MVMSemaphoreBody *body = (MVMSemaphoreBody *)OBJECT_BODY(target);
1986
0
1987
0
        slots += 1;
1988
0
        slots += 3; /* features */
1989
0
1990
0
        cmp_write_map(ctx, slots);
1991
0
1992
0
        cmp_write_str(ctx, "semaphore_identity", 14);
1993
0
        cmp_write_int(ctx, (uintptr_t)body->sem);
1994
0
1995
0
        write_object_features(dtc, ctx, 0, 0, 0);
1996
0
    }
1997
0
    else {
1998
0
        cmp_write_map(ctx, slots);
1999
0
    }
2000
0
2001
0
    if (REPR(target)->unmanaged_size && IS_CONCRETE(target)) {
2002
0
        cmp_write_str(ctx, "unmanaged_size", 14);
2003
0
        cmp_write_int(ctx, REPR(target)->unmanaged_size(dtc, STABLE(target), OBJECT_BODY(target)));
2004
0
    }
2005
0
2006
0
    if (IS_CONCRETE(target)) {
2007
0
        cmp_write_str(ctx, "size", 4);
2008
0
        cmp_write_int(ctx, target->header.size);
2009
0
    }
2010
0
2011
0
    cmp_write_str(ctx, "repr_name", 9);
2012
0
    cmp_write_str(ctx, REPR(target)->name, strlen(REPR(target)->name));
2013
0
2014
0
    {
2015
0
        char *debug_name = MVM_6model_get_debug_name(dtc, target);
2016
0
        cmp_write_str(ctx, "debug_name", 10);
2017
0
        cmp_write_str(ctx, debug_name, strlen(debug_name));
2018
0
    }
2019
0
2020
0
    return 0;
2021
0
}
2022
2023
0
static MVMint32 request_object_positionals(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
2024
0
    MVMObject *target = argument->handle_id
2025
0
        ? find_handle_target(dtc, argument->handle_id)
2026
0
        : dtc->instance->VMNull;
2027
0
2028
0
    MVMint64 slots;
2029
0
2030
0
    if (MVM_is_null(dtc, target)) {
2031
0
        return 1;
2032
0
    }
2033
0
2034
0
    if (REPR(target)->ID == MVM_REPR_ID_VMArray) {
2035
0
        MVMArrayBody *body = (MVMArrayBody *)OBJECT_BODY(target);
2036
0
        MVMArrayREPRData *repr_data = (MVMArrayREPRData *)STABLE(target)->REPR_data;
2037
0
        MVMuint16 kind;
2038
0
        MVMint64 index;
2039
0
2040
0
        cmp_write_map(ctx, 5);
2041
0
        cmp_write_str(ctx, "id", 2);
2042
0
        cmp_write_integer(ctx, argument->id);
2043
0
        cmp_write_str(ctx, "type", 4);
2044
0
        cmp_write_integer(ctx, MT_ObjectPositionalsResponse);
2045
0
2046
0
        cmp_write_str(ctx, "kind", 4);
2047
0
        kind = write_vmarray_slot_kind(dtc, ctx, repr_data->slot_type);
2048
0
2049
0
        cmp_write_str(ctx, "start", 5);
2050
0
        cmp_write_int(ctx, 0);
2051
0
2052
0
        cmp_write_str(ctx, "contents", 8);
2053
0
        cmp_write_array(ctx, body->elems);
2054
0
2055
0
        for (index = 0; index < body->elems; index++) {
2056
0
            MVMRegister target_reg;
2057
0
            REPR(target)->pos_funcs.at_pos(dtc, STABLE(target), target, body, index, &target_reg, kind);
2058
0
2059
0
            switch (kind) {
2060
0
                case MVM_reg_obj: {
2061
0
                    MVMObject *value = target_reg.o;
2062
0
                    char *value_debug_name = value ? MVM_6model_get_debug_name(dtc, value) : "VMNull";
2063
0
                    cmp_write_map(ctx, 4);
2064
0
2065
0
                    cmp_write_str(ctx, "handle", 6);
2066
0
                    cmp_write_integer(ctx, allocate_handle(dtc, value));
2067
0
2068
0
                    cmp_write_str(ctx, "type", 4);
2069
0
                    cmp_write_str(ctx, value_debug_name, strlen(value_debug_name));
2070
0
2071
0
                    cmp_write_str(ctx, "concrete", 8);
2072
0
                    cmp_write_bool(ctx, !MVM_is_null(dtc, value) && IS_CONCRETE(value));
2073
0
2074
0
                    cmp_write_str(ctx, "container", 9);
2075
0
                    if (MVM_is_null(dtc, value))
2076
0
                        cmp_write_bool(ctx, 0);
2077
0
                    else
2078
0
                        cmp_write_bool(ctx, STABLE(value)->container_spec == NULL ? 0 : 1);
2079
0
                    break;
2080
0
                }
2081
0
                case MVM_reg_int64: {
2082
0
                    cmp_write_int(ctx, target_reg.i64);
2083
0
                    break;
2084
0
                }
2085
0
                case MVM_reg_num64:
2086
0
                    cmp_write_double(ctx, target_reg.n64);
2087
0
                    break;
2088
0
                case MVM_reg_str: {
2089
0
                    char *c_value = MVM_string_utf8_encode_C_string(dtc, target_reg.s);
2090
0
                    cmp_write_str(ctx, c_value, strlen(c_value));
2091
0
                    MVM_free(c_value);
2092
0
                    break;
2093
0
                }
2094
0
                default:
2095
0
                    cmp_write_nil(ctx);
2096
0
            }
2097
0
        }
2098
0
2099
0
        return 0;
2100
0
    }
2101
0
2102
0
    return 1;
2103
0
}
2104
2105
0
static MVMint32 request_object_associatives(MVMThreadContext *dtc, cmp_ctx_t *ctx, request_data *argument) {
2106
0
    MVMObject *target = argument->handle_id
2107
0
        ? find_handle_target(dtc, argument->handle_id)
2108
0
        : dtc->instance->VMNull;
2109
0
2110
0
    if (MVM_is_null(dtc, target)) {
2111
0
        return 1;
2112
0
    }
2113
0
    if (!IS_CONCRETE(target)) {
2114
0
        return 1;
2115
0
    }
2116
0
2117
0
    if (REPR(target)->ID == MVM_REPR_ID_MVMHash) {
2118
0
        MVMHashBody *body = (MVMHashBody *)OBJECT_BODY(target);
2119
0
        MVMuint64 count = HASH_CNT(hash_handle, body->hash_head);
2120
0
2121
0
        MVMHashEntry *entry = NULL;
2122
0
        MVMHashEntry *tmp = NULL;
2123
0
        unsigned bucket_tmp;
2124
0
2125
0
        cmp_write_map(ctx, 4);
2126
0
        cmp_write_str(ctx, "id", 2);
2127
0
        cmp_write_integer(ctx, argument->id);
2128
0
        cmp_write_str(ctx, "type", 4);
2129
0
        cmp_write_integer(ctx, MT_ObjectAssociativesResponse);
2130
0
2131
0
        cmp_write_str(ctx, "kind", 4);
2132
0
        cmp_write_str(ctx, "obj", 3);
2133
0
2134
0
        cmp_write_str(ctx, "contents", 8);
2135
0
        cmp_write_map(ctx, count);
2136
0
2137
0
        HASH_ITER(hash_handle, body->hash_head, entry, tmp, bucket_tmp) {
2138
0
            char *key = MVM_string_utf8_encode_C_string(dtc, entry->hash_handle.key);
2139
0
            MVMObject *value = entry->value;
2140
0
            char *value_debug_name = value ? MVM_6model_get_debug_name(dtc, value) : "VMNull";
2141
0
2142
0
            cmp_write_str(ctx, key, strlen(key));
2143
0
2144
0
            cmp_write_map(ctx, 4);
2145
0
2146
0
            cmp_write_str(ctx, "handle", 6);
2147
0
            cmp_write_integer(ctx, allocate_handle(dtc, value));
2148
0
2149
0
            cmp_write_str(ctx, "type", 4);
2150
0
            cmp_write_str(ctx, value_debug_name, strlen(value_debug_name));
2151
0
2152
0
            cmp_write_str(ctx, "concrete", 8);
2153
0
            cmp_write_bool(ctx, !MVM_is_null(dtc, value) && IS_CONCRETE(value));
2154
0
2155
0
            cmp_write_str(ctx, "container", 9);
2156
0
            if (MVM_is_null(dtc, value))
2157
0
                cmp_write_bool(ctx, 0);
2158
0
            else
2159
0
                cmp_write_bool(ctx, STABLE(value)->container_spec == NULL ? 0 : 1);
2160
0
2161
0
            MVM_free(key);
2162
0
        }
2163
0
    }
2164
0
}
2165
2166
MVMuint8 debugspam_network;
2167
2168
0
static bool socket_reader(cmp_ctx_t *ctx, void *data, size_t limit) {
2169
0
    size_t idx;
2170
0
    size_t total_read = 0;
2171
0
    size_t read;
2172
0
    MVMuint8 *orig_data = (MVMuint8 *)data;
2173
0
    if (debugspam_network)
2174
0
        fprintf(stderr, "asked to read %lu bytes\n", limit);
2175
0
    while (total_read < limit) {
2176
0
        if ((read = recv(*((Socket*)ctx->buf), data, limit, 0)) == -1) {
2177
0
            if (debugspam_network)
2178
0
                fprintf(stderr, "minus one\n");
2179
0
            return 0;
2180
0
        } else if (read == 0) {
2181
0
            if (debugspam_network)
2182
0
                fprintf(stderr, "end of file - socket probably closed\nignore warnings about parse errors\n");
2183
0
            return 0;
2184
0
        }
2185
0
        if (debugspam_network)
2186
0
            fprintf(stderr, "%lu ", read);
2187
0
        data = (void *)(((MVMuint8*)data) + read);
2188
0
        total_read += read;
2189
0
    }
2190
0
2191
0
    if (debugspam_network) {
2192
0
        fprintf(stderr, "... recv received %lu bytes\n", total_read);
2193
0
        fprintf(stderr, "cmp read: ");
2194
0
        for (idx = 0; idx < limit; idx++) {
2195
0
            fprintf(stderr, "%x ", orig_data[idx]);
2196
0
        }
2197
0
        fprintf(stderr, "\n");
2198
0
    }
2199
0
    return 1;
2200
0
}
2201
2202
0
static size_t socket_writer(cmp_ctx_t *ctx, const void *data, size_t limit) {
2203
0
    size_t idx;
2204
0
    size_t total_sent = 0;
2205
0
    size_t sent;
2206
0
    MVMuint8 *orig_data = (MVMuint8 *)data;
2207
0
    if (debugspam_network)
2208
0
        fprintf(stderr, "asked to send %3lu bytes: ", limit);
2209
0
    while (total_sent < limit) {
2210
0
        if ((sent = send(*(Socket*)ctx->buf, data, limit, 0)) == -1) {
2211
0
            if (debugspam_network)
2212
0
                fprintf(stderr, "but couldn't (socket disconnected?)\n");
2213
0
            return 0;
2214
0
        } else if (sent == 0) {
2215
0
            if (debugspam_network)
2216
0
                fprintf(stderr, "send encountered end of file\n");
2217
0
            return 0;
2218
0
        }
2219
0
        if (debugspam_network)
2220
0
            fprintf(stderr, "%2lu ", sent);
2221
0
        data = (void *)(((MVMuint8*)data) + sent);
2222
0
        total_sent += sent;
2223
0
    }
2224
0
    if (debugspam_network)
2225
0
        fprintf(stderr, "... send sent %3lu bytes\n", total_sent);
2226
0
    return 1;
2227
0
}
2228
2229
0
static bool is_valid_int(cmp_object_t *obj, MVMuint64 *result) {
2230
0
    switch (obj->type) {
2231
0
        case CMP_TYPE_POSITIVE_FIXNUM:
2232
0
        case CMP_TYPE_UINT8:
2233
0
            *result = obj->as.u8;
2234
0
            break;
2235
0
        case CMP_TYPE_UINT16:
2236
0
            *result = obj->as.u16;
2237
0
            break;
2238
0
        case CMP_TYPE_UINT32:
2239
0
            *result = obj->as.u32;
2240
0
            break;
2241
0
        case CMP_TYPE_UINT64:
2242
0
            *result = obj->as.u64;
2243
0
            break;
2244
0
        case CMP_TYPE_NEGATIVE_FIXNUM:
2245
0
        case CMP_TYPE_SINT8:
2246
0
            *result = obj->as.s8;
2247
0
            break;
2248
0
        case CMP_TYPE_SINT16:
2249
0
            *result = obj->as.s16;
2250
0
            break;
2251
0
        case CMP_TYPE_SINT32:
2252
0
            *result = obj->as.s32;
2253
0
            break;
2254
0
        case CMP_TYPE_SINT64:
2255
0
            *result = obj->as.s64;
2256
0
            break;
2257
0
        case CMP_TYPE_BOOLEAN:
2258
0
            *result = obj->as.boolean;
2259
0
            break;
2260
0
        default:
2261
0
            return 0;
2262
0
    }
2263
0
    return 1;
2264
0
}
2265
2266
0
#define CHECK(operation, message) do { if(!(operation)) { data->parse_fail = 1; data->parse_fail_message = (message); if (tc->instance->debugserver->debugspam_protocol) fprintf(stderr, "CMP error: %s; %s\n", cmp_strerror(ctx), message); return 0; } } while(0)
2267
0
#define FIELD_FOUND(field, duplicated_message) do { if(data->fields_set & (field)) { data->parse_fail = 1; data->parse_fail_message = duplicated_message;  return 0; }; field_to_set = (field); } while (0)
2268
2269
0
MVMint8 skip_all_read_data(cmp_ctx_t *ctx, MVMuint32 size) {
2270
0
    char dump[1024];
2271
0
2272
0
    while (size > 1024) {
2273
0
        if (!socket_reader(ctx, dump, 1024)) {
2274
0
            return 0;
2275
0
        }
2276
0
        size -= 1024;
2277
0
    }
2278
0
    if (!socket_reader(ctx, dump, size)) {
2279
0
        return 0;
2280
0
    }
2281
0
    return 1;
2282
0
}
2283
2284
0
MVMint8 skip_whole_object(MVMThreadContext *tc, cmp_ctx_t *ctx, request_data *data) {
2285
0
    cmp_object_t obj;
2286
0
    MVMuint32 obj_size = 0;
2287
0
    MVMuint32 index;
2288
0
2289
0
    CHECK(cmp_read_object(ctx, &obj), "couldn't skip object from unknown key");
2290
0
2291
0
    switch (obj.type) {
2292
0
        case CMP_TYPE_FIXMAP:
2293
0
        case CMP_TYPE_MAP16:
2294
0
        case CMP_TYPE_MAP32:
2295
0
            obj_size = obj.as.map_size * 2;
2296
0
2297
0
            for (index = 0; index < obj_size; index++) {
2298
0
                if (!skip_whole_object(tc, ctx, data)) {
2299
0
                    return 0;
2300
0
                }
2301
0
            }
2302
0
            break;
2303
0
        case CMP_TYPE_FIXARRAY:
2304
0
        case CMP_TYPE_ARRAY16:
2305
0
        case CMP_TYPE_ARRAY32:
2306
0
            obj_size = obj.as.array_size;
2307
0
2308
0
            for (index = 0; index < obj_size; index++) {
2309
0
                if (!skip_whole_object(tc, ctx, data)) {
2310
0
                    return 0;
2311
0
                }
2312
0
            }
2313
0
            break;
2314
0
        case CMP_TYPE_FIXSTR:
2315
0
        case CMP_TYPE_STR8:
2316
0
        case CMP_TYPE_STR16:
2317
0
        case CMP_TYPE_STR32:
2318
0
            obj_size = obj.as.str_size;
2319
0
            CHECK(skip_all_read_data(ctx, obj_size), "could not skip string data");
2320
0
            break;
2321
0
        case CMP_TYPE_BIN8:
2322
0
        case CMP_TYPE_BIN16:
2323
0
        case CMP_TYPE_BIN32:
2324
0
            obj_size = obj.as.bin_size;
2325
0
            CHECK(skip_all_read_data(ctx, obj_size), "could not skip string data");
2326
0
            break;
2327
0
        case CMP_TYPE_EXT8:
2328
0
        case CMP_TYPE_EXT16:
2329
0
        case CMP_TYPE_EXT32:
2330
0
        case CMP_TYPE_FIXEXT1:
2331
0
        case CMP_TYPE_FIXEXT2:
2332
0
        case CMP_TYPE_FIXEXT4:
2333
0
        case CMP_TYPE_FIXEXT8:
2334
0
        case CMP_TYPE_FIXEXT16:
2335
0
            obj_size = obj.as.ext.size;
2336
0
            CHECK(skip_all_read_data(ctx, obj_size), "could not skip string data");
2337
0
            break;
2338
0
        case CMP_TYPE_POSITIVE_FIXNUM:
2339
0
        case CMP_TYPE_NIL:
2340
0
        case CMP_TYPE_BOOLEAN:
2341
0
        case CMP_TYPE_FLOAT:
2342
0
        case CMP_TYPE_DOUBLE:
2343
0
        case CMP_TYPE_NEGATIVE_FIXNUM:
2344
0
        case CMP_TYPE_UINT8:
2345
0
        case CMP_TYPE_UINT16:
2346
0
        case CMP_TYPE_UINT32:
2347
0
        case CMP_TYPE_UINT64:
2348
0
        case CMP_TYPE_SINT8:
2349
0
        case CMP_TYPE_SINT16:
2350
0
        case CMP_TYPE_SINT32:
2351
0
        case CMP_TYPE_SINT64:
2352
0
            break;
2353
0
        default:
2354
0
            CHECK(0, "could not skip object: unhandled type");
2355
0
    }
2356
0
    return 1;
2357
0
}
2358
2359
0
MVMint32 parse_message_map(MVMThreadContext *tc, cmp_ctx_t *ctx, request_data *data) {
2360
0
    MVMuint32 map_size = 0;
2361
0
    MVMuint32 i;
2362
0
    cmp_object_t obj;
2363
0
2364
0
    memset(data, 0, sizeof(request_data));
2365
0
2366
0
    CHECK(cmp_read_object(ctx, &obj), "couldn't read envelope object!");
2367
0
2368
0
    switch (obj.type) {
2369
0
        case CMP_TYPE_FIXMAP:
2370
0
        case CMP_TYPE_MAP16:
2371
0
        case CMP_TYPE_MAP32:
2372
0
            map_size = obj.as.map_size;
2373
0
            break;
2374
0
        default:
2375
0
            if (tc->instance->debugserver->debugspam_protocol)
2376
0
                fprintf(stderr, "expected a map, but got %d\n", obj.type);
2377
0
            data->parse_fail = 1;
2378
0
            data->parse_fail_message = "expected a map as the envelope";
2379
0
            return 0;
2380
0
    }
2381
0
2382
0
    for (i = 0; i < map_size; i++) {
2383
0
        char key_str[16];
2384
0
        MVMuint32 str_size = 16;
2385
0
2386
0
        fields_set field_to_set = 0;
2387
0
        MVMint32   type_to_parse = 0;
2388
0
2389
0
        CHECK(cmp_read_str(ctx, key_str, &str_size), "Couldn't read string key");
2390
0
2391
0
        if (strncmp(key_str, "type", 15) == 0) {
2392
0
            FIELD_FOUND(FS_type, "type field duplicated");
2393
0
            type_to_parse = 1;
2394
0
        }
2395
0
        else if (strncmp(key_str, "id", 15) == 0) {
2396
0
            FIELD_FOUND(FS_id, "id field duplicated");
2397
0
            type_to_parse = 1;
2398
0
        }
2399
0
        else if (strncmp(key_str, "thread", 15) == 0) {
2400
0
            FIELD_FOUND(FS_thread_id, "thread field duplicated");
2401
0
            type_to_parse = 1;
2402
0
        }
2403
0
        else if (strncmp(key_str, "frame", 15) == 0) {
2404
0
            FIELD_FOUND(FS_frame_number, "frame number field duplicated");
2405
0
            type_to_parse = 1;
2406
0
        }
2407
0
        else if (strncmp(key_str, "handle", 15) == 0) {
2408
0
            FIELD_FOUND(FS_handle_id, "handle field duplicated");
2409
0
            type_to_parse = 1;
2410
0
        }
2411
0
        else if (strncmp(key_str, "line", 15) == 0) {
2412
0
            FIELD_FOUND(FS_line, "line field duplicated");
2413
0
            type_to_parse = 1;
2414
0
        }
2415
0
        else if (strncmp(key_str, "suspend", 15) == 0) {
2416
0
            FIELD_FOUND(FS_suspend, "suspend field duplicated");
2417
0
            type_to_parse = 1;
2418
0
        }
2419
0
        else if (strncmp(key_str, "stacktrace", 15) == 0) {
2420
0
            FIELD_FOUND(FS_stacktrace, "stacktrace field duplicated");
2421
0
            type_to_parse = 1;
2422
0
        }
2423
0
        else if (strncmp(key_str, "file", 15) == 0) {
2424
0
            FIELD_FOUND(FS_file, "file field duplicated");
2425
0
            type_to_parse = 2;
2426
0
        }
2427
0
        else if (strncmp(key_str, "handles", 15) == 0) {
2428
0
            FIELD_FOUND(FS_handles, "handles field duplicated");
2429
0
            type_to_parse = 3;
2430
0
        }
2431
0
        else {
2432
0
            if (tc->instance->debugserver->debugspam_protocol)
2433
0
                fprintf(stderr, "the hell is a %s?\n", key_str);
2434
0
            type_to_parse = -1;
2435
0
        }
2436
0
2437
0
        if (type_to_parse == 1) {
2438
0
            cmp_object_t object;
2439
0
            MVMuint64 result;
2440
0
            CHECK(cmp_read_object(ctx, &object), "Couldn't read value for a key");
2441
0
            CHECK(is_valid_int(&object, &result), "Couldn't read integer value for a key");
2442
0
            switch (field_to_set) {
2443
0
                case FS_type:
2444
0
                    data->type = result;
2445
0
                    break;
2446
0
                case FS_id:
2447
0
                    data->id = result;
2448
0
                    break;
2449
0
                case FS_thread_id:
2450
0
                    data->thread_id = result;
2451
0
                    break;
2452
0
                case FS_frame_number:
2453
0
                    data->frame_number = result;
2454
0
                    break;
2455
0
                case FS_handle_id:
2456
0
                    data->handle_id = result;
2457
0
                    break;
2458
0
                case FS_line:
2459
0
                    data->line = result;
2460
0
                    break;
2461
0
                case FS_suspend:
2462
0
                    data->suspend = result;
2463
0
                    break;
2464
0
                case FS_stacktrace:
2465
0
                    data->stacktrace = result;
2466
0
                    break;
2467
0
                default:
2468
0
                    data->parse_fail = 1;
2469
0
                    data->parse_fail_message = "Int field to set NYI";
2470
0
                    return 0;
2471
0
            }
2472
0
            data->fields_set = data->fields_set | field_to_set;
2473
0
        }
2474
0
        else if (type_to_parse == 2) {
2475
0
            uint32_t strsize = 1024;
2476
0
            char *string = MVM_calloc(strsize, sizeof(char));
2477
0
            if (tc->instance->debugserver->debugspam_protocol)
2478
0
                fprintf(stderr, "reading a string for %s\n", key_str);
2479
0
            CHECK(cmp_read_str(ctx, string, &strsize), "Couldn't read string for a key");
2480
0
2481
0
            switch (field_to_set) {
2482
0
                case FS_file:
2483
0
                    data->file = string;
2484
0
                    break;
2485
0
                default:
2486
0
                    data->parse_fail = 1;
2487
0
                    data->parse_fail_message = "Str field to set NYI";
2488
0
                    return 0;
2489
0
            }
2490
0
            data->fields_set = data->fields_set | field_to_set;
2491
0
        }
2492
0
        else if (type_to_parse == 3) {
2493
0
            uint32_t arraysize = 0;
2494
0
            uint32_t index;
2495
0
            CHECK(cmp_read_array(ctx, &arraysize), "Couldn't read array for a key");
2496
0
            data->handle_count = arraysize;
2497
0
            data->handles = MVM_malloc(arraysize * sizeof(MVMuint64));
2498
0
            for (index = 0; index < arraysize; index++) {
2499
0
                cmp_object_t object;
2500
0
                MVMuint64 result;
2501
0
                CHECK(cmp_read_object(ctx, &object), "Couldn't read value for a key");
2502
0
                CHECK(is_valid_int(&object, &result), "Couldn't read integer value for a key");
2503
0
                data->handles[index] = result;
2504
0
            }
2505
0
            data->fields_set = data->fields_set | field_to_set;
2506
0
        }
2507
0
        else if (type_to_parse == -1) {
2508
0
            skip_whole_object(tc, ctx, data);
2509
0
        }
2510
0
    }
2511
0
2512
0
    return check_requirements(tc, data);
2513
0
}
2514
2515
0
#define COMMUNICATE_RESULT(operation) do { if((operation)) { communicate_error(tc, &ctx, &argument); } else { communicate_success(tc, &ctx, &argument); } } while (0)
2516
0
#define COMMUNICATE_ERROR(operation) do { if((operation)) { communicate_error(tc, &ctx, &argument); } } while (0)
2517
2518
0
static void debugserver_worker(MVMThreadContext *tc, MVMCallsite *callsite, MVMRegister *args) {
2519
0
    int continue_running = 1;
2520
0
    MVMint32 command_serial;
2521
0
    Socket listensocket;
2522
0
    MVMInstance *vm = tc->instance;
2523
0
    MVMuint64 port = vm->debugserver->port;
2524
0
2525
0
    vm->debugserver->thread_id = tc->thread_obj->body.thread_id;
2526
0
2527
0
    {
2528
0
        char portstr[16];
2529
0
        struct addrinfo *res;
2530
0
        int error;
2531
0
2532
0
        snprintf(portstr, 16, "%lu", port);
2533
0
2534
0
        getaddrinfo("localhost", portstr, NULL, &res);
2535
0
2536
0
        listensocket = socket(res->ai_family, SOCK_STREAM, 0);
2537
0
        if (listensocket == -1)
2538
0
            MVM_panic(1, "Could not create file descriptor for socket: %s", strerror(errno));
2539
0
2540
0
#ifndef _WIN32
2541
0
        {
2542
0
            int one = 1;
2543
0
            setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
2544
0
        }
2545
0
#endif
2546
0
2547
0
        if (bind(listensocket, res->ai_addr, res->ai_addrlen) == -1) {
2548
0
            MVM_panic(1, "Could not bind to socket: %s", strerror(errno));
2549
0
        }
2550
0
2551
0
        freeaddrinfo(res);
2552
0
2553
0
        if (listen(listensocket, 1) == -1) {
2554
0
            MVM_panic(1, "Could not listen on socket: %s", strerror(errno));
2555
0
        }
2556
0
    }
2557
0
2558
0
    while(continue_running) {
2559
0
        Socket clientsocket;
2560
0
        int len;
2561
0
        char *buffer[32];
2562
0
        cmp_ctx_t ctx;
2563
0
2564
0
        MVM_gc_mark_thread_blocked(tc);
2565
0
        clientsocket = accept(listensocket, NULL, NULL);
2566
0
        MVM_gc_mark_thread_unblocked(tc);
2567
0
2568
0
        send_greeting(&clientsocket);
2569
0
2570
0
        if (!receive_greeting(&clientsocket)) {
2571
0
            if (tc->instance->debugserver->debugspam_protocol)
2572
0
                fprintf(stderr, "did not receive greeting properly\n");
2573
0
            close(clientsocket);
2574
0
            continue;
2575
0
        }
2576
0
2577
0
        cmp_init(&ctx, &clientsocket, socket_reader, NULL, socket_writer);
2578
0
2579
0
        vm->debugserver->messagepack_data = (void*)&ctx;
2580
0
2581
0
        while (clientsocket) {
2582
0
            request_data argument;
2583
0
2584
0
            MVM_gc_mark_thread_blocked(tc);
2585
0
            parse_message_map(tc, &ctx, &argument);
2586
0
            MVM_gc_mark_thread_unblocked(tc);
2587
0
2588
0
            uv_mutex_lock(&vm->debugserver->mutex_network_send);
2589
0
2590
0
            if (argument.parse_fail) {
2591
0
                if (tc->instance->debugserver->debugspam_protocol)
2592
0
                    fprintf(stderr, "failed to parse this message: %s\n", argument.parse_fail_message);
2593
0
                cmp_write_map(&ctx, 3);
2594
0
2595
0
                cmp_write_str(&ctx, "id", 2);
2596
0
                cmp_write_integer(&ctx, argument.id);
2597
0
2598
0
                cmp_write_str(&ctx, "type", 4);
2599
0
                cmp_write_integer(&ctx, 1);
2600
0
2601
0
                cmp_write_str(&ctx, "reason", 6);
2602
0
                cmp_write_str(&ctx, argument.parse_fail_message, strlen(argument.parse_fail_message));
2603
0
                close(clientsocket);
2604
0
                uv_mutex_unlock(&vm->debugserver->mutex_network_send);
2605
0
                break;
2606
0
            }
2607
0
2608
0
            if (vm->debugserver->debugspam_protocol)
2609
0
                fprintf(stderr, "debugserver received packet %lu, command %u\n", argument.id, argument.type);
2610
0
2611
0
            switch (argument.type) {
2612
0
                case MT_IsExecutionSuspendedRequest:
2613
0
                    send_is_execution_suspended_info(tc, &ctx, &argument);
2614
0
                    break;
2615
0
                case MT_SuspendAll:
2616
0
                    COMMUNICATE_ERROR(request_all_threads_suspend(tc, &ctx, &argument));
2617
0
                    break;
2618
0
                case MT_ResumeAll:
2619
0
                    COMMUNICATE_ERROR(request_all_threads_resume(tc, &ctx, &argument));
2620
0
                    break;
2621
0
                case MT_SuspendOne:
2622
0
                    COMMUNICATE_ERROR(request_thread_suspends(tc, &ctx, &argument, NULL));
2623
0
                    break;
2624
0
                case MT_ResumeOne:
2625
0
                    COMMUNICATE_ERROR(request_thread_resumes(tc, &ctx, &argument, NULL));
2626
0
                    break;
2627
0
                case MT_ThreadListRequest:
2628
0
                    send_thread_info(tc, &ctx, &argument);
2629
0
                    break;
2630
0
                case MT_ThreadStackTraceRequest:
2631
0
                    if (request_thread_stacktrace(tc, &ctx, &argument, NULL)) {
2632
0
                        communicate_error(tc, &ctx, &argument);
2633
0
                    }
2634
0
                    break;
2635
0
                case MT_SetBreakpointRequest:
2636
0
                    MVM_debugserver_add_breakpoint(tc, &ctx, &argument);
2637
0
                    break;
2638
0
                case MT_ClearBreakpoint:
2639
0
                    MVM_debugserver_clear_breakpoint(tc, &ctx, &argument);
2640
0
                    break;
2641
0
                case MT_ClearAllBreakpoints:
2642
0
                    MVM_debugserver_clear_all_breakpoints(tc, &ctx, &argument);
2643
0
                    break;
2644
0
                case MT_StepInto:
2645
0
                    COMMUNICATE_RESULT(setup_step(tc, &ctx, &argument, MVMDebugSteppingMode_STEP_INTO, NULL));
2646
0
                    break;
2647
0
                case MT_StepOver:
2648
0
                    COMMUNICATE_RESULT(setup_step(tc, &ctx, &argument, MVMDebugSteppingMode_STEP_OVER, NULL));
2649
0
                    break;
2650
0
                case MT_StepOut:
2651
0
                    COMMUNICATE_RESULT(setup_step(tc, &ctx, &argument, MVMDebugSteppingMode_STEP_OUT, NULL));
2652
0
                    break;
2653
0
                case MT_ObjectAttributesRequest:
2654
0
                    if (request_object_attributes(tc, &ctx, &argument)) {
2655
0
                        communicate_error(tc, &ctx, &argument);
2656
0
                    }
2657
0
                    break;
2658
0
                case MT_ReleaseHandles:
2659
0
                    COMMUNICATE_RESULT(release_handles(tc, &ctx, &argument));
2660
0
                    MVM_free(argument.handles);
2661
0
                    break;
2662
0
                case MT_ContextHandle:
2663
0
                case MT_CodeObjectHandle:
2664
0
                    if (create_context_or_code_obj_debug_handle(tc, &ctx, &argument, NULL)) {
2665
0
                        communicate_error(tc, &ctx, &argument);
2666
0
                    }
2667
0
                    break;
2668
0
                case MT_CallerContextRequest:
2669
0
                case MT_OuterContextRequest:
2670
0
                    if (create_caller_or_outer_context_debug_handle(tc, &ctx, &argument, NULL)) {
2671
0
                        communicate_error(tc, &ctx, &argument);
2672
0
                    }
2673
0
                    break;
2674
0
                case MT_ContextLexicalsRequest:
2675
0
                    if (request_context_lexicals(tc, &ctx, &argument)) {
2676
0
                        communicate_error(tc, &ctx, &argument);
2677
0
                    }
2678
0
                    break;
2679
0
                case MT_ObjectMetadataRequest:
2680
0
                    if (request_object_metadata(tc, &ctx, &argument)) {
2681
0
                        communicate_error(tc, &ctx, &argument);
2682
0
                    }
2683
0
                    break;
2684
0
                case MT_ObjectPositionalsRequest:
2685
0
                    if (request_object_positionals(tc, &ctx, &argument)) {
2686
0
                        communicate_error(tc, &ctx, &argument);
2687
0
                    }
2688
0
                    break;
2689
0
                case MT_ObjectAssociativesRequest:
2690
0
                    if (request_object_associatives(tc, &ctx, &argument)) {
2691
0
                        communicate_error(tc, &ctx, &argument);
2692
0
                    }
2693
0
                    break;
2694
0
                case MT_HandleEquivalenceRequest:
2695
0
                    send_handle_equivalence_classes(tc, &ctx, &argument);
2696
0
                    break;
2697
0
                default: /* Unknown command or NYI */
2698
0
                    if (tc->instance->debugserver->debugspam_protocol)
2699
0
                        fprintf(stderr, "unknown command type (or NYI)\n");
2700
0
                    cmp_write_map(&ctx, 2);
2701
0
                    cmp_write_str(&ctx, "id", 2);
2702
0
                    cmp_write_integer(&ctx, argument.id);
2703
0
                    cmp_write_str(&ctx, "type", 4);
2704
0
                    cmp_write_integer(&ctx, 0);
2705
0
                    break;
2706
0
            }
2707
0
2708
0
            uv_mutex_unlock(&vm->debugserver->mutex_network_send);
2709
0
        }
2710
0
        MVM_debugserver_clear_all_breakpoints(tc, NULL, NULL);
2711
0
        release_all_handles(tc);
2712
0
        vm->debugserver->messagepack_data = NULL;
2713
0
    }
2714
0
}
2715
2716
/* XXX stolen verbatim from src/moar.c; maybe put into a header somewhere */
2717
0
#define init_mutex(loc, name) do { \
2718
0
    if ((init_stat = uv_mutex_init(&loc)) < 0) { \
2719
0
        fprintf(stderr, "MoarVM: Initialization of " name " mutex failed\n    %s\n", \
2720
0
            uv_strerror(init_stat)); \
2721
0
        exit(1); \
2722
0
    } \
2723
0
} while (0)
2724
0
#define init_cond(loc, name) do { \
2725
0
    if ((init_stat = uv_cond_init(&loc)) < 0) { \
2726
0
        fprintf(stderr, "MoarVM: Initialization of " name " condition variable failed\n    %s\n", \
2727
0
            uv_strerror(init_stat)); \
2728
0
        exit(1); \
2729
0
    } \
2730
0
} while (0)
2731
0
MVM_PUBLIC void MVM_debugserver_init(MVMThreadContext *tc, MVMuint32 port) {
2732
0
    MVMInstance *vm = tc->instance;
2733
0
    MVMDebugServerData *debugserver = MVM_calloc(1, sizeof(MVMDebugServerData));
2734
0
    MVMObject *worker_entry_point;
2735
0
    int threadCreateError;
2736
0
    int init_stat;
2737
0
2738
0
    init_mutex(debugserver->mutex_cond, "debug server orchestration");
2739
0
    init_mutex(debugserver->mutex_network_send, "debug server network socket lock");
2740
0
    init_mutex(debugserver->mutex_request_list, "debug server request list lock");
2741
0
    init_mutex(debugserver->mutex_breakpoints, "debug server breakpoint management lock");
2742
0
    init_cond(debugserver->tell_threads, "debugserver signals threads");
2743
0
    init_cond(debugserver->tell_worker, "threads signal debugserver");
2744
0
2745
0
    debugserver->handle_table = MVM_malloc(sizeof(MVMDebugServerHandleTable));
2746
0
2747
0
    debugserver->handle_table->allocated = 32;
2748
0
    debugserver->handle_table->used      = 0;
2749
0
    debugserver->handle_table->next_id   = 1;
2750
0
    debugserver->handle_table->entries   = MVM_calloc(debugserver->handle_table->allocated, sizeof(MVMDebugServerHandleTableEntry));
2751
0
2752
0
    debugserver->breakpoints = MVM_malloc(sizeof(MVMDebugServerBreakpointTable));
2753
0
2754
0
    debugserver->breakpoints->files_alloc = 32;
2755
0
    debugserver->breakpoints->files_used  = 0;
2756
0
    debugserver->breakpoints->files       =
2757
0
        MVM_fixed_size_alloc_zeroed(tc, vm->fsa, debugserver->breakpoints->files_alloc * sizeof(MVMDebugServerBreakpointFileTable));
2758
0
2759
0
    debugserver->event_id = 2;
2760
0
    debugserver->port = port;
2761
0
2762
0
    if (getenv("MDS_NETWORK")) {
2763
0
        debugspam_network = 1;
2764
0
        debugserver->debugspam_network = 1;
2765
0
    } else {
2766
0
        debugspam_network = 0;
2767
0
    }
2768
0
    if (getenv("MDS_PROTOCOL")) {
2769
0
        debugserver->debugspam_protocol = 1;
2770
0
    }
2771
0
2772
0
    vm->debugserver = debugserver;
2773
0
2774
0
    worker_entry_point = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTCCode);
2775
0
    ((MVMCFunction *)worker_entry_point)->body.func = debugserver_worker;
2776
0
    MVM_thread_run(tc, MVM_thread_new(tc, worker_entry_point, 1));
2777
0
}
2778
2779
338
MVM_PUBLIC void MVM_debugserver_mark_handles(MVMThreadContext *tc, MVMGCWorklist *worklist, MVMHeapSnapshotState *snapshot) {
2780
338
    MVMInstance *vm = tc->instance;
2781
338
    if (vm->debugserver) {
2782
0
        MVMDebugServerHandleTable *table = vm->debugserver->handle_table;
2783
0
        MVMuint32 idx;
2784
0
2785
0
        if (table == NULL)
2786
0
            return;
2787
0
2788
0
        for (idx = 0; idx < table->used; idx++) {
2789
0
            if (worklist)
2790
0
                MVM_gc_worklist_add(tc, worklist, &(table->entries[idx].target));
2791
0
            else
2792
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, snapshot,
2793
0
                    (MVMCollectable *)table->entries[idx].target, "Debug Handle");
2794
0
        }
2795
0
    }
2796
338
}