Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/profiler/instrument.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Adds an instruction to log an allocation. */
4
0
static void add_allocation_logging(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
5
0
    MVMSpeshIns *alloc_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
6
0
    alloc_ins->info        = MVM_op_get_op(MVM_OP_prof_allocated);
7
0
    alloc_ins->operands    = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand));
8
0
    alloc_ins->operands[0] = ins->operands[0];
9
0
    MVM_spesh_manipulate_insert_ins(tc, bb, ins, alloc_ins);
10
0
}
11
12
0
static void add_nativecall_logging(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, MVMSpeshIns *ins) {
13
0
    MVMSpeshIns *enter_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
14
0
    MVMSpeshIns *exit_ins  = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
15
0
16
0
    enter_ins->info        = MVM_op_get_op(MVM_OP_prof_enternative);
17
0
    enter_ins->operands    = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshOperand));
18
0
    enter_ins->operands[0] = ins->operands[2];
19
0
20
0
    MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, enter_ins);
21
0
22
0
    exit_ins->info         = MVM_op_get_op(MVM_OP_prof_exit);
23
0
24
0
    MVM_spesh_manipulate_insert_ins(tc, bb, ins, exit_ins);
25
0
}
26
27
0
static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) {
28
0
    /* Insert entry instruction. */
29
0
    MVMSpeshBB *bb         = g->entry->linear_next;
30
0
    MVMSpeshIns *enter_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
31
0
    enter_ins->info        = MVM_op_get_op(MVM_OP_prof_enter);
32
0
    MVM_spesh_manipulate_insert_ins(tc, bb, NULL, enter_ins);
33
0
34
0
    /* Walk the code and insert profile logging instructions as needed. */
35
0
    while (bb) {
36
0
        MVMSpeshIns *ins = bb->first_ins;
37
0
        while (ins) {
38
0
            switch (ins->info->opcode) {
39
0
            case MVM_OP_return_i:
40
0
            case MVM_OP_return_n:
41
0
            case MVM_OP_return_s:
42
0
            case MVM_OP_return_o:
43
0
            case MVM_OP_return: {
44
0
                /* Log a normal exit prior to returning. */
45
0
                MVMSpeshIns *exit_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
46
0
                exit_ins->info        = MVM_op_get_op(MVM_OP_prof_exit);
47
0
                MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, exit_ins);
48
0
49
0
                /* If the return instruction is a goto target, move to the
50
0
                 * instrumentation instruction. */
51
0
                if (ins->annotations) {
52
0
                    MVMSpeshAnn *ann      = ins->annotations;
53
0
                    MVMSpeshAnn *prev_ann = NULL;
54
0
                    while (ann) {
55
0
                        if (ann->type == MVM_SPESH_ANN_FH_GOTO) {
56
0
                            if (prev_ann)
57
0
                                prev_ann->next = ann->next;
58
0
                            else
59
0
                                ins->annotations = ann->next;
60
0
                            exit_ins->annotations = ann;
61
0
                            ann->next = NULL;
62
0
                            break;
63
0
                        }
64
0
                        prev_ann = ann;
65
0
                        ann = ann->next;
66
0
                    }
67
0
                }
68
0
69
0
                break;
70
0
            }
71
0
            case MVM_OP_invoke_o:
72
0
            case MVM_OP_param_rp_o:
73
0
            case MVM_OP_param_op_o:
74
0
            case MVM_OP_param_rn_o:
75
0
            case MVM_OP_param_on_o:
76
0
            case MVM_OP_param_sp:
77
0
            case MVM_OP_param_sn:
78
0
            case MVM_OP_newexception:
79
0
            case MVM_OP_usecapture:
80
0
            case MVM_OP_savecapture:
81
0
            case MVM_OP_takeclosure:
82
0
            case MVM_OP_getattr_o:
83
0
            case MVM_OP_getattrs_o:
84
0
            case MVM_OP_sp_p6ogetvc_o:
85
0
            case MVM_OP_create:
86
0
            case MVM_OP_sp_fastcreate:
87
0
            case MVM_OP_clone:
88
0
            case MVM_OP_box_i:
89
0
            case MVM_OP_box_n:
90
0
            case MVM_OP_box_s:
91
0
            case MVM_OP_iter:
92
0
            case MVM_OP_add_I:
93
0
            case MVM_OP_sub_I:
94
0
            case MVM_OP_mul_I:
95
0
            case MVM_OP_div_I:
96
0
            case MVM_OP_mod_I:
97
0
            case MVM_OP_neg_I:
98
0
            case MVM_OP_abs_I:
99
0
            case MVM_OP_bor_I:
100
0
            case MVM_OP_bxor_I:
101
0
            case MVM_OP_band_I:
102
0
            case MVM_OP_bnot_I:
103
0
            case MVM_OP_blshift_I:
104
0
            case MVM_OP_brshift_I:
105
0
            case MVM_OP_pow_I:
106
0
            case MVM_OP_gcd_I:
107
0
            case MVM_OP_lcm_I:
108
0
            case MVM_OP_expmod_I:
109
0
            case MVM_OP_rand_I:
110
0
            case MVM_OP_coerce_nI:
111
0
            case MVM_OP_coerce_sI:
112
0
            case MVM_OP_radix_I: {
113
0
                add_allocation_logging(tc, g, bb, ins);
114
0
                break;
115
0
            }
116
0
            case MVM_OP_getlex:
117
0
            case MVM_OP_getlex_no:
118
0
            case MVM_OP_getlexstatic_o:
119
0
            case MVM_OP_getlexperinvtype_o:
120
0
            case MVM_OP_getlexouter:
121
0
            case MVM_OP_getlexrel:
122
0
            case MVM_OP_getlexreldyn:
123
0
            case MVM_OP_getlexrelcaller:
124
0
            case MVM_OP_getlexcaller:
125
0
            {
126
0
                /* We have to check if the target register is actually
127
0
                 * an object register. */
128
0
                if ((g->local_types && g->local_types[ins->operands[0].reg.orig] == MVM_reg_obj)
129
0
                    || (!g->local_types && g->sf->body.local_types[ins->operands[0].reg.orig] == MVM_reg_obj))
130
0
                    add_allocation_logging(tc, g, bb, ins);
131
0
                break;
132
0
            }
133
0
            case MVM_OP_getlexref_i:
134
0
            case MVM_OP_getlexref_n:
135
0
            case MVM_OP_getlexref_s:
136
0
            case MVM_OP_getlexref_ni:
137
0
            case MVM_OP_getlexref_nn:
138
0
            case MVM_OP_getlexref_ns:
139
0
            case MVM_OP_atposref_i:
140
0
            case MVM_OP_atposref_n:
141
0
            case MVM_OP_atposref_s:
142
0
            case MVM_OP_getattrref_i:
143
0
            case MVM_OP_getattrref_n:
144
0
            case MVM_OP_getattrref_s:
145
0
            case MVM_OP_getattrsref_i:
146
0
            case MVM_OP_getattrsref_n:
147
0
            case MVM_OP_getattrsref_s:
148
0
                add_allocation_logging(tc, g, bb, ins);
149
0
                break;
150
0
            case MVM_OP_nativecallinvoke:
151
0
                add_nativecall_logging(tc, g, bb, ins);
152
0
                break;
153
0
            default:
154
0
                /* See if it's an allocating extop. */
155
0
                if (ins->info->opcode == (MVMuint16)-1) {
156
0
                    MVMExtOpRecord *extops     = g->sf->body.cu->body.extops;
157
0
                    MVMuint16       num_extops = g->sf->body.cu->body.num_extops;
158
0
                    MVMuint16       i;
159
0
                    for (i = 0; i < num_extops; i++) {
160
0
                        if (extops[i].info == ins->info) {
161
0
                            if (extops[i].allocating && extops[i].info->num_operands >= 1)
162
0
                                add_allocation_logging(tc, g, bb, ins);
163
0
                            break;
164
0
                        }
165
0
                    }
166
0
                }
167
0
                break;
168
0
            }
169
0
            ins = ins->next;
170
0
        }
171
0
        bb = bb->linear_next;
172
0
    }
173
0
}
174
175
/* Adds instrumented versions of the unspecialized bytecode. */
176
0
static void add_instrumentation(MVMThreadContext *tc, MVMStaticFrame *sf) {
177
0
    MVMSpeshCode  *sc;
178
0
    MVMStaticFrameInstrumentation *ins;
179
0
    MVMSpeshGraph *sg = MVM_spesh_graph_create(tc, sf, 1, 0);
180
0
    instrument_graph(tc, sg);
181
0
    sc = MVM_spesh_codegen(tc, sg);
182
0
    ins = MVM_calloc(1, sizeof(MVMStaticFrameInstrumentation));
183
0
    ins->instrumented_bytecode        = sc->bytecode;
184
0
    ins->instrumented_handlers        = sc->handlers;
185
0
    ins->instrumented_bytecode_size   = sc->bytecode_size;
186
0
    ins->uninstrumented_bytecode      = sf->body.bytecode;
187
0
    ins->uninstrumented_handlers      = sf->body.handlers;
188
0
    ins->uninstrumented_bytecode_size = sf->body.bytecode_size;
189
0
    sf->body.instrumentation = ins;
190
0
    MVM_spesh_graph_destroy(tc, sg);
191
0
    MVM_free(sc);
192
0
}
193
194
/* Instruments a static frame for profiling, or uses an existing
195
 * instrumentation if it exists. */
196
0
void MVM_profile_instrument(MVMThreadContext *tc, MVMStaticFrame *sf) {
197
0
    if (!sf->body.instrumentation || sf->body.bytecode != sf->body.instrumentation->instrumented_bytecode) {
198
0
        /* Handle main, non-specialized, bytecode. */
199
0
        if (!sf->body.instrumentation)
200
0
            add_instrumentation(tc, sf);
201
0
        sf->body.bytecode      = sf->body.instrumentation->instrumented_bytecode;
202
0
        sf->body.handlers      = sf->body.instrumentation->instrumented_handlers;
203
0
        sf->body.bytecode_size = sf->body.instrumentation->instrumented_bytecode_size;
204
0
205
0
        /* Throw away any specializations; we'll need to reproduce them as
206
0
         * instrumented versions. */
207
0
        sf->body.num_spesh_candidates = 0;
208
0
        sf->body.spesh_candidates     = NULL;
209
0
    }
210
0
}
211
212
/* Ensures we're no longer in instrumented code. */
213
102k
void MVM_profile_ensure_uninstrumented(MVMThreadContext *tc, MVMStaticFrame *sf) {
214
102k
    if (sf->body.instrumentation && sf->body.bytecode == sf->body.instrumentation->instrumented_bytecode) {
215
0
        /* Switch to uninstrumented code. */
216
0
        sf->body.bytecode      = sf->body.instrumentation->uninstrumented_bytecode;
217
0
        sf->body.handlers      = sf->body.instrumentation->uninstrumented_handlers;
218
0
        sf->body.bytecode_size = sf->body.instrumentation->uninstrumented_bytecode_size;
219
0
220
0
        /* Throw away specializations, which may also be instrumented. */
221
0
        sf->body.num_spesh_candidates = 0;
222
0
        sf->body.spesh_candidates     = NULL;
223
0
224
0
        /* XXX For now, due to bugs, disable spesh here. */
225
0
        tc->instance->spesh_enabled = 0;
226
0
    }
227
102k
}
228
229
/* Starts instrumted profiling. */
230
0
void MVM_profile_instrumented_start(MVMThreadContext *tc, MVMObject *config) {
231
0
    /* Enable profiling. */
232
0
    tc->instance->profiling = 1;
233
0
    tc->instance->instrumentation_level++;
234
0
}
235
236
/* Simple allocation functions. */
237
0
static MVMObject * new_array(MVMThreadContext *tc) {
238
0
    return MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type);
239
0
}
240
0
static MVMObject * new_hash(MVMThreadContext *tc) {
241
0
    return MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type);
242
0
}
243
0
static MVMObject * box_i(MVMThreadContext *tc, MVMint64 i) {
244
0
    return MVM_repr_box_int(tc, MVM_hll_current(tc)->int_box_type, i);
245
0
}
246
0
static MVMObject * box_s(MVMThreadContext *tc, MVMString *s) {
247
0
    return MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, s);
248
0
}
249
0
static MVMString * str(MVMThreadContext *tc, const char *buf) {
250
0
    return MVM_string_ascii_decode_nt(tc, tc->instance->VMString, buf);
251
0
}
252
253
/* String constants we'll reuse. */
254
typedef struct {
255
    MVMString *total_time;
256
    MVMString *call_graph;
257
    MVMString *name;
258
    MVMString *id;
259
    MVMString *file;
260
    MVMString *line;
261
    MVMString *entries;
262
    MVMString *spesh_entries;
263
    MVMString *jit_entries;
264
    MVMString *inlined_entries;
265
    MVMString *inclusive_time;
266
    MVMString *exclusive_time;
267
    MVMString *callees;
268
    MVMString *allocations;
269
    MVMString *spesh;
270
    MVMString *jit;
271
    MVMString *type;
272
    MVMString *count;
273
    MVMString *gcs;
274
    MVMString *time;
275
    MVMString *full;
276
    MVMString *cleared_bytes;
277
    MVMString *retained_bytes;
278
    MVMString *promoted_bytes;
279
    MVMString *gen2_roots;
280
    MVMString *osr;
281
    MVMString *deopt_one;
282
    MVMString *deopt_all;
283
    MVMString *spesh_time;
284
    MVMString *native_lib;
285
} ProfDumpStrs;
286
287
/* Dumps a call graph node. */
288
static MVMObject * dump_call_graph_node(MVMThreadContext *tc, ProfDumpStrs *pds,
289
0
                                        const MVMProfileCallNode *pcn) {
290
0
    MVMObject *node_hash  = new_hash(tc);
291
0
    MVMuint32  i;
292
0
293
0
    /* Let's see if we're dealing with a native call or a regular moar call */
294
0
    if (pcn->sf) {
295
0
        /* Try to resolve the code filename and line number. */
296
0
        MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc,
297
0
            &(pcn->sf->body), 0);
298
0
        MVMint32 fshi = annot ? (MVMint32)annot->filename_string_heap_index : -1;
299
0
300
0
        /* Add name of code object. */
301
0
        MVM_repr_bind_key_o(tc, node_hash, pds->name,
302
0
            box_s(tc, pcn->sf->body.name));
303
0
304
0
        /* Add line number and file name. */
305
0
        if (fshi >= 0 && fshi < pcn->sf->body.cu->body.num_strings)
306
0
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
307
0
                box_s(tc, MVM_cu_string(tc, pcn->sf->body.cu, fshi)));
308
0
        else if (pcn->sf->body.cu->body.filename)
309
0
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
310
0
                box_s(tc, pcn->sf->body.cu->body.filename));
311
0
        else
312
0
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
313
0
                box_s(tc, tc->instance->str_consts.empty));
314
0
        MVM_repr_bind_key_o(tc, node_hash, pds->line,
315
0
            box_i(tc, annot ? (MVMint32)annot->line_number : -1));
316
0
        MVM_free(annot);
317
0
318
0
        /* Use static frame memory address to get a unique ID. */
319
0
        MVM_repr_bind_key_o(tc, node_hash, pds->id,
320
0
            box_i(tc, (MVMint64)pcn->sf));
321
0
    } else {
322
0
        MVMString *function_name_string =
323
0
            MVM_string_utf8_c8_decode(tc, tc->instance->VMString,
324
0
                                      pcn->native_target_name, strlen(pcn->native_target_name));
325
0
326
0
        MVM_repr_bind_key_o(tc, node_hash, pds->name,
327
0
            box_s(tc, function_name_string));
328
0
        MVM_repr_bind_key_o(tc, node_hash, pds->file,
329
0
            box_s(tc, pds->native_lib));
330
0
331
0
        MVM_repr_bind_key_o(tc, node_hash, pds->line,
332
0
            box_i(tc, -2));
333
0
334
0
        /* Use the address of the name string as unique ID. a hack, but oh well. */
335
0
        MVM_repr_bind_key_o(tc, node_hash, pds->id,
336
0
            box_i(tc, (MVMint64)pcn->native_target_name));
337
0
    }
338
0
339
0
    /* Entry counts. */
340
0
    if (pcn->total_entries)
341
0
        MVM_repr_bind_key_o(tc, node_hash, pds->entries,
342
0
            box_i(tc, pcn->total_entries));
343
0
    if (pcn->specialized_entries)
344
0
        MVM_repr_bind_key_o(tc, node_hash, pds->spesh_entries,
345
0
            box_i(tc, pcn->specialized_entries));
346
0
    if (pcn->jit_entries)
347
0
        MVM_repr_bind_key_o(tc, node_hash, pds->jit_entries,
348
0
            box_i(tc, pcn->jit_entries));
349
0
    if (pcn->inlined_entries)
350
0
        MVM_repr_bind_key_o(tc, node_hash, pds->inlined_entries,
351
0
            box_i(tc, pcn->inlined_entries));
352
0
353
0
    /* Total (inclusive) time. */
354
0
    MVM_repr_bind_key_o(tc, node_hash, pds->inclusive_time,
355
0
        box_i(tc, pcn->total_time / 1000));
356
0
357
0
    /* OSR and deopt counts. */
358
0
    if (pcn->osr_count)
359
0
        MVM_repr_bind_key_o(tc, node_hash, pds->osr,
360
0
            box_i(tc, pcn->osr_count));
361
0
    if (pcn->deopt_one_count)
362
0
        MVM_repr_bind_key_o(tc, node_hash, pds->deopt_one,
363
0
            box_i(tc, pcn->deopt_one_count));
364
0
    if (pcn->deopt_all_count)
365
0
        MVM_repr_bind_key_o(tc, node_hash, pds->deopt_all,
366
0
            box_i(tc, pcn->deopt_all_count));
367
0
368
0
    /* Visit successors in the call graph, dumping them and working out the
369
0
     * exclusive time. */
370
0
    if (pcn->num_succ) {
371
0
        MVMObject *callees        = new_array(tc);
372
0
        MVMuint64  exclusive_time = pcn->total_time;
373
0
        for (i = 0; i < pcn->num_succ; i++) {
374
0
            MVM_repr_push_o(tc, callees,
375
0
                dump_call_graph_node(tc, pds, pcn->succ[i]));
376
0
            exclusive_time -= pcn->succ[i]->total_time;
377
0
        }
378
0
        MVM_repr_bind_key_o(tc, node_hash, pds->exclusive_time,
379
0
            box_i(tc, exclusive_time / 1000));
380
0
        MVM_repr_bind_key_o(tc, node_hash, pds->callees, callees);
381
0
    }
382
0
    else {
383
0
        MVM_repr_bind_key_o(tc, node_hash, pds->exclusive_time,
384
0
            box_i(tc, pcn->total_time / 1000));
385
0
    }
386
0
387
0
    if (pcn->num_alloc) {
388
0
        /* Emit allocations. */
389
0
        MVMObject *alloc_list = new_array(tc);
390
0
        MVM_repr_bind_key_o(tc, node_hash, pds->allocations, alloc_list);
391
0
        for (i = 0; i < pcn->num_alloc; i++) {
392
0
            MVMObject *alloc_info = new_hash(tc);
393
0
            MVMProfileAllocationCount *alloc = &pcn->alloc[i];
394
0
395
0
            MVMObject *type       = pcn->alloc[i].type;
396
0
397
0
            MVM_repr_bind_key_o(tc, alloc_info, pds->id, box_i(tc, (MVMint64)type));
398
0
            MVM_repr_bind_key_o(tc, alloc_info, pds->type, type);
399
0
            if (alloc->allocations_spesh)
400
0
                MVM_repr_bind_key_o(tc, alloc_info, pds->spesh,
401
0
                    box_i(tc, alloc->allocations_spesh));
402
0
            if (alloc->allocations_jit)
403
0
                MVM_repr_bind_key_o(tc, alloc_info, pds->jit,
404
0
                    box_i(tc, alloc->allocations_jit));
405
0
            MVM_repr_bind_key_o(tc, alloc_info, pds->count,
406
0
                box_i(tc, alloc->allocations_interp
407
0
                          + alloc->allocations_spesh
408
0
                          + alloc->allocations_jit));
409
0
            MVM_repr_push_o(tc, alloc_list, alloc_info);
410
0
        }
411
0
    }
412
0
413
0
    return node_hash;
414
0
}
415
416
/* Dumps data from a single thread. */
417
static MVMObject * dump_thread_data(MVMThreadContext *tc, ProfDumpStrs *pds,
418
0
                                    const MVMProfileThreadData *ptd) {
419
0
    MVMObject *thread_hash = new_hash(tc);
420
0
    MVMObject *thread_gcs  = new_array(tc);
421
0
    MVMuint32  i;
422
0
423
0
    /* Add time. */
424
0
    MVM_repr_bind_key_o(tc, thread_hash, pds->total_time,
425
0
        box_i(tc, (ptd->end_time - ptd->start_time) / 1000));
426
0
427
0
    /* Add call graph. */
428
0
    if (ptd->call_graph)
429
0
        MVM_repr_bind_key_o(tc, thread_hash, pds->call_graph,
430
0
            dump_call_graph_node(tc, pds, ptd->call_graph));
431
0
432
0
    /* Add GCs. */
433
0
    for (i = 0; i < ptd->num_gcs; i++) {
434
0
        MVMObject *gc_hash = new_hash(tc);
435
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->time,
436
0
            box_i(tc, ptd->gcs[i].time / 1000));
437
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->full,
438
0
            box_i(tc, ptd->gcs[i].full));
439
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->cleared_bytes,
440
0
            box_i(tc, ptd->gcs[i].cleared_bytes));
441
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->retained_bytes,
442
0
            box_i(tc, ptd->gcs[i].retained_bytes));
443
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->promoted_bytes,
444
0
            box_i(tc, ptd->gcs[i].promoted_bytes));
445
0
        MVM_repr_bind_key_o(tc, gc_hash, pds->gen2_roots,
446
0
            box_i(tc, ptd->gcs[i].num_gen2roots));
447
0
        MVM_repr_push_o(tc, thread_gcs, gc_hash);
448
0
    }
449
0
    MVM_repr_bind_key_o(tc, thread_hash, pds->gcs, thread_gcs);
450
0
451
0
    /* Add spesh time. */
452
0
    MVM_repr_bind_key_o(tc, thread_hash, pds->spesh_time,
453
0
        box_i(tc, ptd->spesh_time / 1000));
454
0
455
0
    return thread_hash;
456
0
}
457
458
/* Dumps data from all threads into an array of per-thread data. */
459
0
static MVMObject * dump_data(MVMThreadContext *tc) {
460
0
    MVMObject *threads_array;
461
0
    ProfDumpStrs pds;
462
0
463
0
    /* We'll allocate the data in gen2, but as we want to keep it, but to be
464
0
     * sure we don't trigger a GC run. */
465
0
    MVM_gc_allocate_gen2_default_set(tc);
466
0
467
0
    /* Some string constants to re-use. */
468
0
    pds.total_time      = str(tc, "total_time");
469
0
    pds.call_graph      = str(tc, "call_graph");
470
0
    pds.name            = str(tc, "name");
471
0
    pds.id              = str(tc, "id");
472
0
    pds.file            = str(tc, "file");
473
0
    pds.line            = str(tc, "line");
474
0
    pds.entries         = str(tc, "entries");
475
0
    pds.spesh_entries   = str(tc, "spesh_entries");
476
0
    pds.jit_entries     = str(tc, "jit_entries");
477
0
    pds.inlined_entries = str(tc, "inlined_entries");
478
0
    pds.inclusive_time  = str(tc, "inclusive_time");
479
0
    pds.exclusive_time  = str(tc, "exclusive_time");
480
0
    pds.callees         = str(tc, "callees");
481
0
    pds.allocations     = str(tc, "allocations");
482
0
    pds.type            = str(tc, "type");
483
0
    pds.count           = str(tc, "count");
484
0
    pds.spesh           = str(tc, "spesh");
485
0
    pds.jit             = str(tc, "jit");
486
0
    pds.gcs             = str(tc, "gcs");
487
0
    pds.time            = str(tc, "time");
488
0
    pds.full            = str(tc, "full");
489
0
    pds.cleared_bytes   = str(tc, "cleared_bytes");
490
0
    pds.retained_bytes  = str(tc, "retained_bytes");
491
0
    pds.promoted_bytes  = str(tc, "promoted_bytes");
492
0
    pds.gen2_roots      = str(tc, "gen2_roots");
493
0
    pds.osr             = str(tc, "osr");
494
0
    pds.deopt_one       = str(tc, "deopt_one");
495
0
    pds.deopt_all       = str(tc, "deopt_all");
496
0
    pds.spesh_time      = str(tc, "spesh_time");
497
0
    pds.native_lib      = str(tc, "native library");
498
0
499
0
    /* Build up threads array. */
500
0
    /* XXX Only main thread for now. */
501
0
    threads_array = new_array(tc);
502
0
    if (tc->prof_data)
503
0
        MVM_repr_push_o(tc, threads_array, dump_thread_data(tc, &pds, tc->prof_data));
504
0
505
0
    /* Switch back to default allocation and return result; */
506
0
    MVM_gc_allocate_gen2_default_clear(tc);
507
0
    return threads_array;
508
0
}
509
510
/* Ends profiling, builds the result data structure, and returns it. */
511
0
MVMObject * MVM_profile_instrumented_end(MVMThreadContext *tc) {
512
0
    if (tc->prof_data) {
513
0
        /* If we have any call frames still on the profile stack, exit them. */
514
0
        while (tc->prof_data->current_call)
515
0
            MVM_profile_log_exit(tc);
516
0
517
0
        /* Record end time. */
518
0
        tc->prof_data->end_time = uv_hrtime();
519
0
    }
520
0
521
0
    /* Disable profiling. */
522
0
    /* XXX Needs to account for multiple threads. */
523
0
    tc->instance->profiling = 0;
524
0
    tc->instance->instrumentation_level++;
525
0
526
0
    /* Build and return result data structure. */
527
0
    return dump_data(tc);
528
0
}
529
530
531
/* Marks objects held in the profiling graph. */
532
0
static void mark_call_graph_node(MVMThreadContext *tc, MVMProfileCallNode *node, MVMGCWorklist *worklist) {
533
0
    MVMuint32 i;
534
0
    MVM_gc_worklist_add(tc, worklist, &(node->sf));
535
0
    for (i = 0; i < node->num_alloc; i++)
536
0
        MVM_gc_worklist_add(tc, worklist, &(node->alloc[i].type));
537
0
    for (i = 0; i < node->num_succ; i++)
538
0
        mark_call_graph_node(tc, node->succ[i], worklist);
539
0
}
540
146
void MVM_profile_instrumented_mark_data(MVMThreadContext *tc, MVMGCWorklist *worklist) {
541
146
    if (tc->prof_data)
542
0
        mark_call_graph_node(tc, tc->prof_data->call_graph, worklist);
543
146
}