Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/profiler/instrument.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
typedef struct {
4
    MVMuint32 items;
5
    MVMuint32 start;
6
    MVMuint32 alloc;
7
8
    MVMProfileCallNode **list;
9
} NodeWorklist;
10
11
0
static void add_node(MVMThreadContext *tc, NodeWorklist *list, MVMProfileCallNode *node) {
12
0
    if (list->start + list->items + 1 < list->alloc) {
13
0
        /* Add at the end */
14
0
        list->items++;
15
0
        list->list[list->start + list->items] = node;
16
0
    } else if (list->start > 0) {
17
0
        /* End reached, add to the start now */
18
0
        list->start--;
19
0
        list->list[list->start] = node;
20
0
    } else {
21
0
        /* Filled up the whole list. Make it bigger */
22
0
        list->alloc *= 2;
23
0
        list->list = MVM_realloc(list->list, list->alloc * sizeof(MVMProfileCallNode *));
24
0
    }
25
0
}
26
27
0
static MVMProfileCallNode *take_node(MVMThreadContext *tc, NodeWorklist *list) {
28
0
    MVMProfileCallNode *result = NULL;
29
0
    if (list->items == 0) {
30
0
        MVM_panic(1, "profiler: tried to take a node from an empty node worklist");
31
0
    }
32
0
    if (list->start > 0) {
33
0
        result = list->list[list->start];
34
0
        list->start++;
35
0
    } else {
36
0
        result = list->list[list->start + list->items];
37
0
        list->items--;
38
0
    }
39
0
    return result;
40
0
}
41
42
/* Adds an instruction to log an allocation. */
43
0
static void add_allocation_logging_at_location(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins, MVMSpeshIns *location) {
44
0
    MVMSpeshIns *alloc_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
45
0
    alloc_ins->info        = MVM_op_get_op(MVM_OP_prof_allocated);
46
0
    alloc_ins->operands    = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand));
47
0
    alloc_ins->operands[0] = ins->operands[0];
48
0
    MVM_spesh_manipulate_insert_ins(tc, bb, location, alloc_ins);
49
0
}
50
51
0
static void add_allocation_logging(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
52
0
    add_allocation_logging_at_location(tc, g, bb, ins, ins);
53
0
}
54
55
0
static void add_nativecall_logging(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
56
0
    MVMSpeshIns *enter_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
57
0
    MVMSpeshIns *exit_ins  = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
58
0
59
0
    enter_ins->info        = MVM_op_get_op(MVM_OP_prof_enternative);
60
0
    enter_ins->operands    = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand));
61
0
    enter_ins->operands[0] = ins->operands[2];
62
0
63
0
    MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, enter_ins);
64
0
65
0
    exit_ins->info         = MVM_op_get_op(MVM_OP_prof_exit);
66
0
67
0
    MVM_spesh_manipulate_insert_ins(tc, bb, ins, exit_ins);
68
0
}
69
70
0
static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) {
71
0
    /* Insert entry instruction. */
72
0
    MVMSpeshBB *bb         = g->entry->linear_next;
73
0
    MVMSpeshIns *enter_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
74
0
    enter_ins->info        = MVM_op_get_op(MVM_OP_prof_enter);
75
0
    MVM_spesh_manipulate_insert_ins(tc, bb, NULL, enter_ins);
76
0
77
0
    /* Walk the code and insert profile logging instructions as needed. */
78
0
    while (bb) {
79
0
        MVMSpeshIns *ins = bb->first_ins;
80
0
        while (ins) {
81
0
            switch (ins->info->opcode) {
82
0
            case MVM_OP_return_i:
83
0
            case MVM_OP_return_n:
84
0
            case MVM_OP_return_s:
85
0
            case MVM_OP_return_o:
86
0
            case MVM_OP_return: {
87
0
                /* Log a normal exit prior to returning. */
88
0
                MVMSpeshIns *exit_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
89
0
                exit_ins->info        = MVM_op_get_op(MVM_OP_prof_exit);
90
0
                MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, exit_ins);
91
0
92
0
                /* If the return instruction is a goto target, move to the
93
0
                 * instrumentation instruction. */
94
0
                if (ins->annotations) {
95
0
                    MVMSpeshAnn *ann      = ins->annotations;
96
0
                    MVMSpeshAnn *prev_ann = NULL;
97
0
                    while (ann) {
98
0
                        if (ann->type == MVM_SPESH_ANN_FH_GOTO) {
99
0
                            if (prev_ann)
100
0
                                prev_ann->next = ann->next;
101
0
                            else
102
0
                                ins->annotations = ann->next;
103
0
                            exit_ins->annotations = ann;
104
0
                            ann->next = NULL;
105
0
                            break;
106
0
                        }
107
0
                        prev_ann = ann;
108
0
                        ann = ann->next;
109
0
                    }
110
0
                }
111
0
112
0
                break;
113
0
            }
114
0
            case MVM_OP_invoke_o:
115
0
            case MVM_OP_param_rp_o:
116
0
            case MVM_OP_param_rn_o:
117
0
            case MVM_OP_param_sp:
118
0
            case MVM_OP_param_sn:
119
0
            case MVM_OP_newexception:
120
0
            case MVM_OP_usecapture:
121
0
            case MVM_OP_savecapture:
122
0
            case MVM_OP_takeclosure:
123
0
            case MVM_OP_getattr_o:
124
0
            case MVM_OP_getattrs_o:
125
0
            case MVM_OP_sp_p6ogetvc_o:
126
0
            case MVM_OP_create:
127
0
            case MVM_OP_sp_fastcreate:
128
0
            case MVM_OP_clone:
129
0
            case MVM_OP_box_i:
130
0
            case MVM_OP_box_n:
131
0
            case MVM_OP_box_s:
132
0
            case MVM_OP_iter:
133
0
            case MVM_OP_add_I:
134
0
            case MVM_OP_sub_I:
135
0
            case MVM_OP_mul_I:
136
0
            case MVM_OP_div_I:
137
0
            case MVM_OP_mod_I:
138
0
            case MVM_OP_neg_I:
139
0
            case MVM_OP_abs_I:
140
0
            case MVM_OP_bor_I:
141
0
            case MVM_OP_bxor_I:
142
0
            case MVM_OP_band_I:
143
0
            case MVM_OP_bnot_I:
144
0
            case MVM_OP_blshift_I:
145
0
            case MVM_OP_brshift_I:
146
0
            case MVM_OP_pow_I:
147
0
            case MVM_OP_gcd_I:
148
0
            case MVM_OP_lcm_I:
149
0
            case MVM_OP_expmod_I:
150
0
            case MVM_OP_rand_I:
151
0
            case MVM_OP_coerce_nI:
152
0
            case MVM_OP_coerce_sI:
153
0
            case MVM_OP_radix_I: {
154
0
                add_allocation_logging(tc, g, bb, ins);
155
0
                break;
156
0
            }
157
0
            case MVM_OP_param_op_o:
158
0
            case MVM_OP_param_on_o: {
159
0
                /* These ops jump to a label if they "succeed", so the
160
0
                 * allocation logging goes in another bb. */
161
0
                MVMSpeshBB  *target   = ins->operands[2].ins_bb;
162
0
                MVMSpeshIns *location = target->first_ins;
163
0
                while (location && location->info->opcode == MVM_SSA_PHI) {
164
0
                    location = location->next;
165
0
                }
166
0
                location = location->prev;
167
0
                add_allocation_logging_at_location(tc, g, target, ins, location);
168
0
                break;
169
0
            }
170
0
            case MVM_OP_getlex:
171
0
            case MVM_OP_getlex_no:
172
0
            case MVM_OP_getlexstatic_o:
173
0
            case MVM_OP_getlexperinvtype_o:
174
0
            case MVM_OP_getlexouter:
175
0
            case MVM_OP_getlexrel:
176
0
            case MVM_OP_getlexreldyn:
177
0
            case MVM_OP_getlexrelcaller:
178
0
            case MVM_OP_getlexcaller:
179
0
            {
180
0
                /* We have to check if the target register is actually
181
0
                 * an object register. */
182
0
                if ((g->local_types && g->local_types[ins->operands[0].reg.orig] == MVM_reg_obj)
183
0
                    || (!g->local_types && g->sf->body.local_types[ins->operands[0].reg.orig] == MVM_reg_obj))
184
0
                    add_allocation_logging(tc, g, bb, ins);
185
0
                break;
186
0
            }
187
0
            case MVM_OP_getlexref_i:
188
0
            case MVM_OP_getlexref_n:
189
0
            case MVM_OP_getlexref_s:
190
0
            case MVM_OP_getlexref_ni:
191
0
            case MVM_OP_getlexref_nn:
192
0
            case MVM_OP_getlexref_ns:
193
0
            case MVM_OP_atposref_i:
194
0
            case MVM_OP_atposref_n:
195
0
            case MVM_OP_atposref_s:
196
0
            case MVM_OP_getattrref_i:
197
0
            case MVM_OP_getattrref_n:
198
0
            case MVM_OP_getattrref_s:
199
0
            case MVM_OP_getattrsref_i:
200
0
            case MVM_OP_getattrsref_n:
201
0
            case MVM_OP_getattrsref_s:
202
0
                add_allocation_logging(tc, g, bb, ins);
203
0
                break;
204
0
            case MVM_OP_nativecallinvoke:
205
0
                add_nativecall_logging(tc, g, bb, ins);
206
0
                break;
207
0
            default:
208
0
                /* See if it's an allocating extop. */
209
0
                if (ins->info->opcode == (MVMuint16)-1) {
210
0
                    MVMExtOpRecord *extops     = g->sf->body.cu->body.extops;
211
0
                    MVMuint16       num_extops = g->sf->body.cu->body.num_extops;
212
0
                    MVMuint16       i;
213
0
                    for (i = 0; i < num_extops; i++) {
214
0
                        if (extops[i].info == ins->info) {
215
0
                            if (extops[i].allocating && extops[i].info->num_operands >= 1)
216
0
                                add_allocation_logging(tc, g, bb, ins);
217
0
                            break;
218
0
                        }
219
0
                    }
220
0
                }
221
0
                break;
222
0
            }
223
0
            ins = ins->next;
224
0
        }
225
0
        bb = bb->linear_next;
226
0
    }
227
0
}
228
229
/* Adds instrumented versions of the unspecialized bytecode. */
230
0
static void add_instrumentation(MVMThreadContext *tc, MVMStaticFrame *sf) {
231
0
    MVMSpeshCode  *sc;
232
0
    MVMStaticFrameInstrumentation *ins;
233
0
    MVMSpeshGraph *sg = MVM_spesh_graph_create(tc, sf, 1, 0);
234
0
    instrument_graph(tc, sg);
235
0
    sc = MVM_spesh_codegen(tc, sg);
236
0
    ins = MVM_calloc(1, sizeof(MVMStaticFrameInstrumentation));
237
0
    ins->instrumented_bytecode        = sc->bytecode;
238
0
    ins->instrumented_handlers        = sc->handlers;
239
0
    ins->instrumented_bytecode_size   = sc->bytecode_size;
240
0
    ins->uninstrumented_bytecode      = sf->body.bytecode;
241
0
    ins->uninstrumented_handlers      = sf->body.handlers;
242
0
    ins->uninstrumented_bytecode_size = sf->body.bytecode_size;
243
0
    sf->body.instrumentation = ins;
244
0
    MVM_spesh_graph_destroy(tc, sg);
245
0
    MVM_free(sc);
246
0
}
247
248
/* Instruments a static frame for profiling, or uses an existing
249
 * instrumentation if it exists. */
250
0
void MVM_profile_instrument(MVMThreadContext *tc, MVMStaticFrame *sf) {
251
0
    if (!sf->body.instrumentation || sf->body.bytecode != sf->body.instrumentation->instrumented_bytecode) {
252
0
        /* Handle main, non-specialized, bytecode. */
253
0
        if (!sf->body.instrumentation)
254
0
            add_instrumentation(tc, sf);
255
0
        sf->body.bytecode      = sf->body.instrumentation->instrumented_bytecode;
256
0
        sf->body.handlers      = sf->body.instrumentation->instrumented_handlers;
257
0
        sf->body.bytecode_size = sf->body.instrumentation->instrumented_bytecode_size;
258
0
259
0
        /* Throw away any argument guard so we'll never resolve prior
260
0
         * specializations again. */
261
0
        MVM_spesh_arg_guard_discard(tc, sf);
262
0
    }
263
0
}
264
265
/* Ensures we're no longer in instrumented code. */
266
117k
void MVM_profile_ensure_uninstrumented(MVMThreadContext *tc, MVMStaticFrame *sf) {
267
117k
    /* XXX due to multithreading trouble, just turning instrumentation off by
268
117k
     * switching bytecode back does not work. Profiling instrumentation is
269
117k
     * safe to keep around with only a small performance penalty, and CTW
270
117k
     * instrumentation is normally not turned off during run time,
271
117k
     * so for now we'll just do nothing. */
272
117k
}
273
274
/* Starts instrumented profiling. */
275
0
void MVM_profile_instrumented_start(MVMThreadContext *tc, MVMObject *config) {
276
0
    /* Wait for specialization thread to stop working, so it won't trip over
277
0
     * bytecode instrumentation, then enable profiling. */
278
0
    uv_mutex_lock(&(tc->instance->mutex_spesh_sync));
279
0
    while (tc->instance->spesh_working != 0)
280
0
        uv_cond_wait(&(tc->instance->cond_spesh_sync), &(tc->instance->mutex_spesh_sync));
281
0
    tc->instance->profiling = 1;
282
0
    tc->instance->instrumentation_level++;
283
0
    uv_mutex_unlock(&(tc->instance->mutex_spesh_sync));
284
0
}
285
286
/* Simple allocation functions. */
287
0
static MVMObject * new_array(MVMThreadContext *tc) {
288
0
    return MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type);
289
0
}
290
0
static MVMObject * new_hash(MVMThreadContext *tc) {
291
0
    return MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type);
292
0
}
293
0
static MVMObject * box_i(MVMThreadContext *tc, MVMint64 i) {
294
0
    return MVM_repr_box_int(tc, MVM_hll_current(tc)->int_box_type, i);
295
0
}
296
0
static MVMObject * box_s(MVMThreadContext *tc, MVMString *s) {
297
0
    return MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, s);
298
0
}
299
0
static MVMString * str(MVMThreadContext *tc, const char *buf) {
300
0
    return MVM_string_ascii_decode_nt(tc, tc->instance->VMString, buf);
301
0
}
302
303
/* String constants we'll reuse. */
304
typedef struct {
305
    MVMString *total_time;
306
    MVMString *call_graph;
307
    MVMString *name;
308
    MVMString *id;
309
    MVMString *file;
310
    MVMString *line;
311
    MVMString *entries;
312
    MVMString *spesh_entries;
313
    MVMString *jit_entries;
314
    MVMString *inlined_entries;
315
    MVMString *inclusive_time;
316
    MVMString *exclusive_time;
317
    MVMString *callees;
318
    MVMString *allocations;
319
    MVMString *spesh;
320
    MVMString *jit;
321
    MVMString *type;
322
    MVMString *count;
323
    MVMString *gcs;
324
    MVMString *time;
325
    MVMString *full;
326
    MVMString *sequence;
327
    MVMString *responsible;
328
    MVMString *cleared_bytes;
329
    MVMString *retained_bytes;
330
    MVMString *promoted_bytes;
331
    MVMString *gen2_roots;
332
    MVMString *start_time;
333
    MVMString *osr;
334
    MVMString *deopt_one;
335
    MVMString *deopt_all;
336
    MVMString *spesh_time;
337
    MVMString *thread;
338
    MVMString *native_lib;
339
} ProfDumpStrs;
340
341
typedef struct {
342
    MVMThreadContext *tc;
343
    ProfDumpStrs *pds;
344
} ProfTcPdsStruct;
345
346
static MVMObject * dump_call_graph_node(MVMThreadContext *tc, ProfDumpStrs *pds, const MVMProfileCallNode *pcn);
347
0
static MVMObject * dump_call_graph_node_loop(ProfTcPdsStruct *tcpds, const MVMProfileCallNode *pcn) {
348
0
    MVMuint32 i;
349
0
    MVMObject *node_hash;
350
0
351
0
    node_hash = dump_call_graph_node(tcpds->tc, tcpds->pds, pcn);
352
0
353
0
    /* Visit successors in the call graph, dumping them and working out the
354
0
     * exclusive time. */
355
0
    if (pcn->num_succ) {
356
0
        MVMObject *callees        = new_array(tcpds->tc);
357
0
        MVMuint64  exclusive_time = pcn->total_time;
358
0
        for (i = 0; i < pcn->num_succ; i++) {
359
0
            MVM_repr_push_o(tcpds->tc, callees,
360
0
                dump_call_graph_node_loop(tcpds, pcn->succ[i]));
361
0
            exclusive_time -= pcn->succ[i]->total_time;
362
0
        }
363
0
        MVM_repr_bind_key_o(tcpds->tc, node_hash, tcpds->pds->exclusive_time,
364
0
            box_i(tcpds->tc, exclusive_time / 1000));
365
0
        MVM_repr_bind_key_o(tcpds->tc, node_hash, tcpds->pds->callees, callees);
366
0
    }
367
0
    else {
368
0
        MVM_repr_bind_key_o(tcpds->tc, node_hash, tcpds->pds->exclusive_time,
369
0
            box_i(tcpds->tc, pcn->total_time / 1000));
370
0
    }
371
0
372
0
    return node_hash;
373
0
}
374
375
/* Dumps a call graph node. */
376
static MVMObject * dump_call_graph_node(MVMThreadContext *tc, ProfDumpStrs *pds,
377
0
                                        const MVMProfileCallNode *pcn) {
378
0
    MVMObject *node_hash  = new_hash(tc);
379
0
    MVMuint32  i;
380
0
381
0
    /* Let's see if we're dealing with a native call or a regular moar call */
382
0
    if (pcn->sf) {
383
0
        /* Try to resolve the code filename and line number. */
384
0
        MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc,
385
0
            &(pcn->sf->body), 0);
386
0
        MVMint32 fshi = annot ? (MVMint32)annot->filename_string_heap_index : -1;
387
0
388
0
        /* Add name of code object. */
389
0
        MVM_repr_bind_key_o(tc, node_hash, pds->name,
390
0
            box_s(tc, pcn->sf->body.name));
391
0
392
0
        /* Add line number and file name. */
393
0
        if (fshi >= 0 && fshi < pcn->sf->body.cu->body.num_strings)
394
0
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
395
0
                box_s(tc, MVM_cu_string(tc, pcn->sf->body.cu, fshi)));
396
0
        else if (pcn->sf->body.cu->body.filename)
397
0
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
398
0
                box_s(tc, pcn->sf->body.cu->body.filename));
399
0
        else
400
0
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
401
0
                box_s(tc, tc->instance->str_consts.empty));
402
0
        MVM_repr_bind_key_o(tc, node_hash, pds->line,
403
0
            box_i(tc, annot ? (MVMint32)annot->line_number : -1));
404
0
        MVM_free(annot);
405
0
406
0
        /* Use static frame memory address to get a unique ID. */
407
0
        MVM_repr_bind_key_o(tc, node_hash, pds->id,
408
0
            box_i(tc, (MVMint64)pcn->sf));
409
0
    } else {
410
0
        MVMString *function_name_string =
411
0
            MVM_string_utf8_c8_decode(tc, tc->instance->VMString,
412
0
                                      pcn->native_target_name, strlen(pcn->native_target_name));
413
0
414
0
        MVM_repr_bind_key_o(tc, node_hash, pds->name,
415
0
            box_s(tc, function_name_string));
416
0
        MVM_repr_bind_key_o(tc, node_hash, pds->file,
417
0
            box_s(tc, pds->native_lib));
418
0
419
0
        MVM_repr_bind_key_o(tc, node_hash, pds->line,
420
0
            box_i(tc, -2));
421
0
422
0
        /* Use the address of the name string as unique ID. a hack, but oh well. */
423
0
        MVM_repr_bind_key_o(tc, node_hash, pds->id,
424
0
            box_i(tc, (MVMint64)pcn->native_target_name));
425
0
    }
426
0
427
0
    /* Entry counts. */
428
0
    if (pcn->total_entries)
429
0
        MVM_repr_bind_key_o(tc, node_hash, pds->entries,
430
0
            box_i(tc, pcn->total_entries));
431
0
    if (pcn->specialized_entries)
432
0
        MVM_repr_bind_key_o(tc, node_hash, pds->spesh_entries,
433
0
            box_i(tc, pcn->specialized_entries));
434
0
    if (pcn->jit_entries)
435
0
        MVM_repr_bind_key_o(tc, node_hash, pds->jit_entries,
436
0
            box_i(tc, pcn->jit_entries));
437
0
    if (pcn->inlined_entries)
438
0
        MVM_repr_bind_key_o(tc, node_hash, pds->inlined_entries,
439
0
            box_i(tc, pcn->inlined_entries));
440
0
441
0
    /* Total (inclusive) time. */
442
0
    MVM_repr_bind_key_o(tc, node_hash, pds->inclusive_time,
443
0
        box_i(tc, pcn->total_time / 1000));
444
0
445
0
    /* OSR and deopt counts. */
446
0
    if (pcn->osr_count)
447
0
        MVM_repr_bind_key_o(tc, node_hash, pds->osr,
448
0
            box_i(tc, pcn->osr_count));
449
0
    if (pcn->deopt_one_count)
450
0
        MVM_repr_bind_key_o(tc, node_hash, pds->deopt_one,
451
0
            box_i(tc, pcn->deopt_one_count));
452
0
    if (pcn->deopt_all_count)
453
0
        MVM_repr_bind_key_o(tc, node_hash, pds->deopt_all,
454
0
            box_i(tc, pcn->deopt_all_count));
455
0
456
0
    if (pcn->num_alloc) {
457
0
        /* Emit allocations. */
458
0
        MVMObject *alloc_list = new_array(tc);
459
0
        MVM_repr_bind_key_o(tc, node_hash, pds->allocations, alloc_list);
460
0
        for (i = 0; i < pcn->num_alloc; i++) {
461
0
            MVMObject *alloc_info = new_hash(tc);
462
0
            MVMProfileAllocationCount *alloc = &pcn->alloc[i];
463
0
464
0
            MVMObject *type       = pcn->alloc[i].type;
465
0
466
0
            MVM_repr_bind_key_o(tc, alloc_info, pds->id, box_i(tc, (MVMint64)type));
467
0
            MVM_repr_bind_key_o(tc, alloc_info, pds->type, type);
468
0
            if (alloc->allocations_spesh)
469
0
                MVM_repr_bind_key_o(tc, alloc_info, pds->spesh,
470
0
                    box_i(tc, alloc->allocations_spesh));
471
0
            if (alloc->allocations_jit)
472
0
                MVM_repr_bind_key_o(tc, alloc_info, pds->jit,
473
0
                    box_i(tc, alloc->allocations_jit));
474
0
            MVM_repr_bind_key_o(tc, alloc_info, pds->count,
475
0
                box_i(tc, alloc->allocations_interp
476
0
                          + alloc->allocations_spesh
477
0
                          + alloc->allocations_jit));
478
0
            MVM_repr_push_o(tc, alloc_list, alloc_info);
479
0
        }
480
0
    }
481
0
482
0
    return node_hash;
483
0
}
484
485
/* Dumps data from a single thread. */
486
static MVMObject * dump_thread_data(MVMThreadContext *tc, ProfDumpStrs *pds,
487
                                    MVMThreadContext *othertc,
488
0
                                    const MVMProfileThreadData *ptd) {
489
0
    MVMObject *thread_hash = new_hash(tc);
490
0
    MVMObject *thread_gcs  = new_array(tc);
491
0
    MVMuint64 absolute_start_time;
492
0
    MVMuint32  i;
493
0
494
0
    ProfTcPdsStruct tcpds;
495
0
496
0
    tcpds.tc = tc;
497
0
    tcpds.pds = pds;
498
0
499
0
    /* Use the main thread's start time for absolute timings */
500
0
    absolute_start_time = tc->instance->main_thread->prof_data->start_time;
501
0
502
0
    /* Add time. */
503
0
    MVM_repr_bind_key_o(tc, thread_hash, pds->total_time,
504
0
        box_i(tc, (ptd->end_time - ptd->start_time) / 1000));
505
0
506
0
    /* Add call graph. */
507
0
    if (ptd->call_graph)
508
0
        MVM_repr_bind_key_o(tc, thread_hash, pds->call_graph,
509
0
            dump_call_graph_node_loop(&tcpds, ptd->call_graph));
510
0
511
0
    /* Add GCs. */
512
0
    for (i = 0; i < ptd->num_gcs; i++) {
513
0
        MVMObject *gc_hash = new_hash(tc);
514
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->time,
515
0
            box_i(tc, ptd->gcs[i].time / 1000));
516
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->full,
517
0
            box_i(tc, ptd->gcs[i].full));
518
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->sequence,
519
0
            box_i(tc, ptd->gcs[i].gc_seq_num - 1));
520
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->responsible,
521
0
            box_i(tc, ptd->gcs[i].responsible));
522
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->cleared_bytes,
523
0
            box_i(tc, ptd->gcs[i].cleared_bytes));
524
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->retained_bytes,
525
0
            box_i(tc, ptd->gcs[i].retained_bytes));
526
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->promoted_bytes,
527
0
            box_i(tc, ptd->gcs[i].promoted_bytes));
528
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->gen2_roots,
529
0
            box_i(tc, ptd->gcs[i].num_gen2roots));
530
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->start_time,
531
0
            box_i(tc, (ptd->gcs[i].abstime - absolute_start_time) / 1000));
532
0
        MVM_repr_push_o(tc, thread_gcs, gc_hash);
533
0
    }
534
0
    MVM_repr_bind_key_o(tc, thread_hash, pds->gcs, thread_gcs);
535
0
536
0
    /* Add spesh time. */
537
0
    MVM_repr_bind_key_o(tc, thread_hash, pds->spesh_time,
538
0
        box_i(tc, ptd->spesh_time / 1000));
539
0
540
0
    /* Add thread id. */
541
0
    MVM_repr_bind_key_o(tc, thread_hash, pds->thread,
542
0
        box_i(tc, othertc->thread_id));
543
0
544
0
    return thread_hash;
545
0
}
546
547
338
void MVM_profile_dump_instrumented_data(MVMThreadContext *tc) {
548
338
    if (tc->prof_data && tc->prof_data->collected_data) {
549
0
        ProfDumpStrs pds;
550
0
        MVMThread *thread;
551
0
552
0
        /* We'll allocate the data in gen2, but as we want to keep it, but to be
553
0
         * sure we don't trigger a GC run. */
554
0
        MVM_gc_allocate_gen2_default_set(tc);
555
0
556
0
        /* Some string constants to re-use. */
557
0
        pds.total_time      = str(tc, "total_time");
558
0
        pds.call_graph      = str(tc, "call_graph");
559
0
        pds.name            = str(tc, "name");
560
0
        pds.id              = str(tc, "id");
561
0
        pds.file            = str(tc, "file");
562
0
        pds.line            = str(tc, "line");
563
0
        pds.entries         = str(tc, "entries");
564
0
        pds.spesh_entries   = str(tc, "spesh_entries");
565
0
        pds.jit_entries     = str(tc, "jit_entries");
566
0
        pds.inlined_entries = str(tc, "inlined_entries");
567
0
        pds.inclusive_time  = str(tc, "inclusive_time");
568
0
        pds.exclusive_time  = str(tc, "exclusive_time");
569
0
        pds.callees         = str(tc, "callees");
570
0
        pds.allocations     = str(tc, "allocations");
571
0
        pds.type            = str(tc, "type");
572
0
        pds.count           = str(tc, "count");
573
0
        pds.spesh           = str(tc, "spesh");
574
0
        pds.jit             = str(tc, "jit");
575
0
        pds.gcs             = str(tc, "gcs");
576
0
        pds.time            = str(tc, "time");
577
0
        pds.full            = str(tc, "full");
578
0
        pds.sequence        = str(tc, "sequence");
579
0
        pds.responsible     = str(tc, "responsible");
580
0
        pds.cleared_bytes   = str(tc, "cleared_bytes");
581
0
        pds.retained_bytes  = str(tc, "retained_bytes");
582
0
        pds.promoted_bytes  = str(tc, "promoted_bytes");
583
0
        pds.gen2_roots      = str(tc, "gen2_roots");
584
0
        pds.start_time      = str(tc, "start_time");
585
0
        pds.osr             = str(tc, "osr");
586
0
        pds.deopt_one       = str(tc, "deopt_one");
587
0
        pds.deopt_all       = str(tc, "deopt_all");
588
0
        pds.spesh_time      = str(tc, "spesh_time");
589
0
        pds.thread          = str(tc, "thread");
590
0
        pds.native_lib      = str(tc, "native library");
591
0
592
0
        /* Record end time. */
593
0
        tc->prof_data->end_time = uv_hrtime();
594
0
595
0
        MVM_repr_push_o(tc, tc->prof_data->collected_data, dump_thread_data(tc, &pds, tc, tc->prof_data));
596
0
        while (tc->prof_data->current_call)
597
0
            MVM_profile_log_exit(tc);
598
0
599
0
        /* Get all thread's data */
600
0
        thread = tc->instance->threads;
601
0
602
0
        while (thread) {
603
0
            MVMThreadContext *othertc = thread->body.tc;
604
0
            /* Check for othertc to exist because joining threads nulls out
605
0
             * the tc entry in the thread object. */
606
0
            if (othertc && othertc->prof_data && othertc != tc) {
607
0
                /* If we have any call frames still on the profile stack, exit them. */
608
0
                while (othertc->prof_data->current_call)
609
0
                    MVM_profile_log_exit(othertc);
610
0
611
0
                /* Record end time. */
612
0
                othertc->prof_data->end_time = uv_hrtime();
613
0
614
0
                MVM_gc_allocate_gen2_default_set(othertc);
615
0
                MVM_repr_push_o(tc, tc->prof_data->collected_data, dump_thread_data(tc, &pds, othertc, othertc->prof_data));
616
0
                MVM_gc_allocate_gen2_default_clear(othertc);
617
0
            }
618
0
            thread = thread->body.next;
619
0
        }
620
0
        MVM_gc_allocate_gen2_default_clear(tc);
621
0
    }
622
338
}
623
624
/* Dumps data from all threads into an array of per-thread data. */
625
0
static MVMObject * dump_data(MVMThreadContext *tc) {
626
0
    MVMObject *collected_data;
627
0
628
0
    /* Build up threads array. */
629
0
    /* XXX Only main thread for now. */
630
0
631
0
    tc->prof_data->collected_data = new_array(tc);
632
0
633
0
    /* We rely on the GC orchestration to stop all threads and the
634
0
     * "main" gc thread to dump all thread data for us */
635
0
    MVM_gc_enter_from_allocator(tc);
636
0
637
0
    collected_data = tc->prof_data->collected_data;
638
0
    tc->prof_data->collected_data = NULL;
639
0
640
0
    return collected_data;
641
0
}
642
643
/* Ends profiling, builds the result data structure, and returns it. */
644
0
MVMObject * MVM_profile_instrumented_end(MVMThreadContext *tc) {
645
0
646
0
    /* Disable profiling. */
647
0
    uv_mutex_lock(&(tc->instance->mutex_spesh_sync));
648
0
    while (tc->instance->spesh_working != 0)
649
0
        uv_cond_wait(&(tc->instance->cond_spesh_sync), &(tc->instance->mutex_spesh_sync));
650
0
    tc->instance->profiling = 0;
651
0
    tc->instance->instrumentation_level++;
652
0
    uv_mutex_unlock(&(tc->instance->mutex_spesh_sync));
653
0
654
0
    /* Build and return result data structure. */
655
0
    return dump_data(tc);
656
0
}
657
658
659
/* Marks objects held in the profiling graph. */
660
0
static void mark_call_graph_node(MVMThreadContext *tc, MVMProfileCallNode *node, NodeWorklist *nodelist, MVMGCWorklist *worklist) {
661
0
    MVMuint32 i;
662
0
    MVM_gc_worklist_add(tc, worklist, &(node->sf));
663
0
    for (i = 0; i < node->num_alloc; i++)
664
0
        MVM_gc_worklist_add(tc, worklist, &(node->alloc[i].type));
665
0
    for (i = 0; i < node->num_succ; i++)
666
0
        add_node(tc, nodelist, node->succ[i]);
667
0
}
668
754
void MVM_profile_instrumented_mark_data(MVMThreadContext *tc, MVMGCWorklist *worklist) {
669
754
    if (tc->prof_data) {
670
0
        /* Allocate our worklist on the stack. */
671
0
        NodeWorklist nodelist;
672
0
        nodelist.items = 0;
673
0
        nodelist.start = 0;
674
0
        nodelist.alloc = 256;
675
0
        nodelist.list = MVM_malloc(nodelist.alloc * sizeof(MVMProfileCallNode *));
676
0
677
0
        add_node(tc, &nodelist, tc->prof_data->call_graph);
678
0
679
0
        while (nodelist.items) {
680
0
            MVMProfileCallNode *node = take_node(tc, &nodelist);
681
0
            if (node)
682
0
                mark_call_graph_node(tc, node, &nodelist, worklist);
683
0
        }
684
0
685
0
        MVM_gc_worklist_add(tc, worklist, &(tc->prof_data->collected_data));
686
0
687
0
        MVM_free(nodelist.list);
688
0
    }
689
754
}