Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/profiler/heapsnapshot.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Check if we're currently taking heap snapshots. */
4
146
MVMint32 MVM_profile_heap_profiling(MVMThreadContext *tc) {
5
146
    return tc->instance->heap_snapshots != NULL;
6
146
}
7
8
/* Start heap profiling. */
9
0
void MVM_profile_heap_start(MVMThreadContext *tc, MVMObject *config) {
10
0
    tc->instance->heap_snapshots = MVM_calloc(1, sizeof(MVMHeapSnapshotCollection));
11
0
}
12
13
/* Grows storage if it's full, zeroing the extension. Assumes it's only being
14
 * grown for one more item. */
15
0
static void grow_storage(void *store_ptr, MVMuint64 *num, MVMuint64 *alloc, size_t size) {
16
0
    void **store = (void **)store_ptr;
17
0
    if (*num == *alloc) {
18
0
        *alloc = *alloc ? 2 * *alloc : 32;
19
0
        *store = MVM_realloc(*store, *alloc * size);
20
0
        memset(((char *)*store) + *num * size, 0, (*alloc - *num) * size);
21
0
    }
22
0
}
23
24
/* Get a string heap index for the specified C string, adding it if needed. */
25
0
#define STR_MODE_OWN    0
26
0
#define STR_MODE_CONST  1
27
0
#define STR_MODE_DUP    2
28
static MVMuint64 get_string_index(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
29
0
                                   char *str, char str_mode) {
30
0
     MVMuint64 i;
31
0
32
0
     /* Add a lookup hash here if it gets to be a hotspot. */
33
0
     MVMHeapSnapshotCollection *col = ss->col;
34
0
     for (i = 0; i < col->num_strings; i++) {
35
0
        if (strcmp(col->strings[i], str) == 0) {
36
0
            if (str_mode == STR_MODE_OWN)
37
0
                MVM_free(str);
38
0
            return i;
39
0
        }
40
0
    }
41
0
42
0
    grow_storage((void **)&(col->strings), &(col->num_strings),
43
0
        &(col->alloc_strings), sizeof(char *));
44
0
    grow_storage(&(col->strings_free), &(col->num_strings_free),
45
0
        &(col->alloc_strings_free), sizeof(char));
46
0
    col->strings_free[col->num_strings_free] = str_mode != STR_MODE_CONST;
47
0
    col->num_strings_free++;
48
0
    col->strings[col->num_strings] = str_mode == STR_MODE_DUP ? strdup(str) : str;
49
0
    return col->num_strings++;
50
0
 }
51
52
/* Gets a string index in the string heap for a VM string. */
53
0
static MVMuint64 get_vm_string_index(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMString *str) {
54
0
    return str
55
0
        ? get_string_index(tc, ss, MVM_string_utf8_encode_C_string(tc, str), STR_MODE_OWN)
56
0
        : get_string_index(tc, ss, "<null>", STR_MODE_CONST);
57
0
}
58
59
/* Push a collectable to the list of work items, allocating space for it and
60
 * returning the collectable index. */
61
static MVMuint64 push_workitem(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
62
0
                               MVMuint16 kind, void *target) {
63
0
    MVMHeapSnapshotWorkItem *wi;
64
0
    MVMuint64 col_idx;
65
0
66
0
    /* Mark space in collectables collection, and allocate an index. */
67
0
    grow_storage(&(ss->hs->collectables), &(ss->hs->num_collectables),
68
0
        &(ss->hs->alloc_collectables), sizeof(MVMHeapSnapshotCollectable));
69
0
    col_idx = ss->hs->num_collectables;
70
0
    ss->hs->num_collectables++;
71
0
72
0
    /* Add to the worklist. */
73
0
    grow_storage(&(ss->workitems), &(ss->num_workitems), &(ss->alloc_workitems),
74
0
        sizeof(MVMHeapSnapshotWorkItem));
75
0
    wi = &(ss->workitems[ss->num_workitems]);
76
0
    wi->kind = kind;
77
0
    wi->col_idx = col_idx;
78
0
    wi->target = target;
79
0
    ss->num_workitems++;
80
0
81
0
    return col_idx;
82
0
}
83
84
/* Pop a work item. */
85
0
static MVMHeapSnapshotWorkItem pop_workitem(MVMThreadContext *tc, MVMHeapSnapshotState *ss) {
86
0
    ss->num_workitems--;
87
0
    return ss->workitems[ss->num_workitems];
88
0
}
89
90
/* Sets the current reference "from" collectable. */
91
0
static void set_ref_from(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMuint64 col_idx) {
92
0
    /* The references should be contiguous, so if this collectable already
93
0
     * has any, something's wrong. */
94
0
    if (ss->hs->collectables[col_idx].num_refs)
95
0
        MVM_panic(1, "Heap snapshot corruption: can not add non-contiguous refs");
96
0
97
0
    ss->ref_from = col_idx;
98
0
    ss->hs->collectables[col_idx].refs_start = ss->hs->num_references;
99
0
}
100
101
/* Adds a reference. */
102
static void add_reference(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMuint16 ref_kind,
103
0
                          MVMuint64 index, MVMuint64 to) {
104
0
    /* Add to the references collection. */
105
0
    MVMHeapSnapshotReference *ref;
106
0
    MVMuint64 description = (index << MVM_SNAPSHOT_REF_KIND_BITS) | ref_kind;
107
0
    grow_storage(&(ss->hs->references), &(ss->hs->num_references),
108
0
        &(ss->hs->alloc_references), sizeof(MVMHeapSnapshotReference));
109
0
    ref = &(ss->hs->references[ss->hs->num_references]);
110
0
    ref->description = description;
111
0
    ref->collectable_index = to;
112
0
    ss->hs->num_references++;
113
0
114
0
    /* Increment collectable's number of references. */
115
0
    ss->hs->collectables[ss->ref_from].num_refs++;
116
0
}
117
118
/* Adds a reference with an integer description. */
119
static void add_reference_idx(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
120
0
                               MVMuint64 idx,  MVMuint64 to) {
121
0
    add_reference(tc, ss, MVM_SNAPSHOT_REF_KIND_INDEX, idx, to);
122
0
}
123
124
/* Adds a reference with a C string description. */
125
static void add_reference_cstr(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
126
0
                               char *cstr,  MVMuint64 to) {
127
0
    MVMuint64 str_idx = get_string_index(tc, ss, cstr, STR_MODE_OWN);
128
0
    add_reference(tc, ss, MVM_SNAPSHOT_REF_KIND_STRING, str_idx, to);
129
0
}
130
131
/* Adds a reference with a constant C string description. */
132
static void add_reference_const_cstr(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
133
0
                                     const char *cstr,  MVMuint64 to) {
134
0
    MVMuint64 str_idx = get_string_index(tc, ss, (char *)cstr, STR_MODE_CONST);
135
0
    add_reference(tc, ss, MVM_SNAPSHOT_REF_KIND_STRING, str_idx, to);
136
0
}
137
138
/* Adds a reference with a VM string description. */
139
static void add_reference_vm_str(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
140
0
                               MVMString *str,  MVMuint64 to) {
141
0
   MVMuint64 str_idx = get_vm_string_index(tc, ss, str);
142
0
   add_reference(tc, ss, MVM_SNAPSHOT_REF_KIND_STRING, str_idx, to);
143
0
}
144
145
/* Adds an entry to the seen hash. */
146
0
static void saw(MVMThreadContext *tc, MVMHeapSnapshotState *ss, void *addr, MVMuint64 idx) {
147
0
    MVMHeapSnapshotSeen *seen = MVM_calloc(1, sizeof(MVMHeapSnapshotSeen));
148
0
    seen->address = addr;
149
0
    seen->idx = idx;
150
0
    HASH_ADD_KEYPTR(hash_handle, ss->seen, (char *)&(seen->address), sizeof(void *), seen);
151
0
}
152
153
/* Checks for an entry in the seen hash. If we find an entry, write the index
154
 * into the index pointer passed. */
155
0
static MVMuint32 seen(MVMThreadContext *tc, MVMHeapSnapshotState *ss, void *addr, MVMuint64 *idx) {
156
0
    MVMHeapSnapshotSeen *entry;
157
0
    HASH_FIND(hash_handle, ss->seen, (char *)&(addr), sizeof(void *), entry);
158
0
    if (entry) {
159
0
        *idx = entry->idx;
160
0
        return 1;
161
0
    }
162
0
    else {
163
0
        return 0;
164
0
    }
165
0
}
166
167
/* Gets the index of a collectable, either returning an existing index if we've
168
 * seen it before or adding it if not. */
169
static MVMuint64 get_collectable_idx(MVMThreadContext *tc,
170
0
        MVMHeapSnapshotState *ss, MVMCollectable *collectable) {
171
0
    MVMuint64 idx;
172
0
    if (!seen(tc, ss, collectable, &idx)) {
173
0
        if (collectable->flags & MVM_CF_STABLE)
174
0
            idx = push_workitem(tc, ss, MVM_SNAPSHOT_COL_KIND_STABLE, collectable);
175
0
        else if (collectable->flags & MVM_CF_TYPE_OBJECT)
176
0
            idx = push_workitem(tc, ss, MVM_SNAPSHOT_COL_KIND_TYPE_OBJECT, collectable);
177
0
        else if (collectable->flags & MVM_CF_FRAME)
178
0
            idx = push_workitem(tc, ss, MVM_SNAPSHOT_COL_KIND_FRAME, collectable);
179
0
        else
180
0
            idx = push_workitem(tc, ss, MVM_SNAPSHOT_COL_KIND_OBJECT, collectable);
181
0
        saw(tc, ss, collectable, idx);
182
0
    }
183
0
    return idx;
184
0
}
185
186
/* Gets the index of a frame, either returning an existing index if we've seen
187
 * it before or adding it if not. */
188
static MVMuint64 get_frame_idx(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
189
0
        MVMFrame *frame) {
190
0
    MVMuint64 idx;
191
0
    if (!seen(tc, ss, frame, &idx)) {
192
0
        idx = push_workitem(tc, ss, MVM_SNAPSHOT_COL_KIND_FRAME, frame);
193
0
        saw(tc, ss, frame, idx);
194
0
    }
195
0
    return idx;
196
0
}
197
198
/* Adds a type table index reference to the collectable snapshot entry, either
199
 * using an existing type table entry or adding a new one. */
200
static void set_type_index(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
201
0
       MVMHeapSnapshotCollectable *col, MVMSTable *st) {
202
0
    MVMuint64 repr_idx = get_string_index(tc, ss, (char *)st->REPR->name, STR_MODE_CONST);
203
0
    MVMuint64 type_idx = st->debug_name
204
0
        ? get_string_index(tc, ss, st->debug_name, STR_MODE_DUP)
205
0
        : get_string_index(tc, ss, "<anon>", STR_MODE_CONST);
206
0
207
0
    MVMuint64 i;
208
0
    MVMHeapSnapshotType *t;
209
0
    for (i = 0; i < ss->col->num_types; i++) {
210
0
        t = &(ss->col->types[i]);
211
0
        if (t->repr_name == repr_idx && t->type_name == type_idx) {
212
0
            col->type_or_frame_index = i;
213
0
            return;
214
0
        }
215
0
    }
216
0
217
0
    grow_storage(&(ss->col->types), &(ss->col->num_types),
218
0
        &(ss->col->alloc_types), sizeof(MVMHeapSnapshotType));
219
0
    t = &(ss->col->types[ss->col->num_types]);
220
0
    t->repr_name = repr_idx;
221
0
    t->type_name = type_idx;
222
0
    col->type_or_frame_index = ss->col->num_types;
223
0
    ss->col->num_types++;
224
0
}
225
226
/* Adds a static frame table index reference to the collectable snapshot entry,
227
 * either using an existing table entry or adding a new one. */
228
static void set_static_frame_index(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
229
0
       MVMHeapSnapshotCollectable *col, MVMStaticFrame *sf) {
230
0
    MVMuint64 name_idx = get_vm_string_index(tc, ss, sf->body.name);
231
0
    MVMuint64 cuid_idx = get_vm_string_index(tc, ss, sf->body.cuuid);
232
0
233
0
    MVMCompUnit *cu = sf->body.cu;
234
0
    MVMBytecodeAnnotation *ann = MVM_bytecode_resolve_annotation(tc, &(sf->body), 0);
235
0
    MVMuint64 line = ann ? ann->line_number : 1;
236
0
    MVMuint64 file_idx = ann && ann->filename_string_heap_index < cu->body.num_strings
237
0
        ? get_vm_string_index(tc, ss, MVM_cu_string(tc, cu, ann->filename_string_heap_index))
238
0
        : get_vm_string_index(tc, ss, cu->body.filename);
239
0
240
0
    MVMuint64 i;
241
0
    MVMHeapSnapshotStaticFrame *s;
242
0
    for (i = 0; i < ss->col->num_static_frames; i++) {
243
0
        s = &(ss->col->static_frames[i]);
244
0
        if (s->name == name_idx && s->cuid == cuid_idx && s->line == line && s->file == file_idx) {
245
0
            col->type_or_frame_index = i;
246
0
            MVM_free(ann);
247
0
            return;
248
0
        }
249
0
    }
250
0
251
0
    MVM_free(ann);
252
0
253
0
    grow_storage(&(ss->col->static_frames), &(ss->col->num_static_frames),
254
0
        &(ss->col->alloc_static_frames), sizeof(MVMHeapSnapshotStaticFrame));
255
0
    s = &(ss->col->static_frames[ss->col->num_static_frames]);
256
0
    s->name = name_idx;
257
0
    s->cuid = cuid_idx;
258
0
    s->line = line;
259
0
    s->file = file_idx;
260
0
    col->type_or_frame_index = ss->col->num_static_frames;
261
0
    ss->col->num_static_frames++;
262
0
}
263
264
/* Processes the work items, until we've none left. */
265
static void process_collectable(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
266
0
        MVMHeapSnapshotCollectable *col, MVMCollectable *c) {
267
0
    MVMuint32 sc_idx = MVM_sc_get_idx_of_sc(c);
268
0
    if (sc_idx > 0)
269
0
        add_reference_const_cstr(tc, ss, "<SC>",
270
0
            get_collectable_idx(tc, ss,
271
0
                (MVMCollectable *)tc->instance->all_scs[sc_idx]->sc));
272
0
    col->collectable_size = c->size;
273
0
}
274
0
static void process_gc_worklist(MVMThreadContext *tc, MVMHeapSnapshotState *ss, char *desc) {
275
0
    MVMCollectable **c_ptr;
276
0
    MVMuint16 ref_kind = desc
277
0
        ? MVM_SNAPSHOT_REF_KIND_STRING
278
0
        : MVM_SNAPSHOT_REF_KIND_UNKNOWN;
279
0
    MVMuint16 ref_index = desc
280
0
        ? get_string_index(tc, ss, desc, STR_MODE_CONST)
281
0
        : 0;
282
0
    while (( c_ptr = MVM_gc_worklist_get(tc, ss->gcwl) )) {
283
0
        MVMCollectable *c = *c_ptr;
284
0
        if (c)
285
0
            add_reference(tc, ss, ref_kind, ref_index,
286
0
                get_collectable_idx(tc, ss, c));
287
0
    }
288
0
}
289
static void process_object(MVMThreadContext *tc, MVMHeapSnapshotState *ss,
290
0
        MVMHeapSnapshotCollectable *col, MVMObject *obj) {
291
0
    process_collectable(tc, ss, col, (MVMCollectable *)obj);
292
0
    set_type_index(tc, ss, col, obj->st);
293
0
    add_reference_const_cstr(tc, ss, "<STable>",
294
0
        get_collectable_idx(tc, ss, (MVMCollectable *)obj->st));
295
0
    if (IS_CONCRETE(obj)) {
296
0
        /* Use object's gc_mark function to find what it references. */
297
0
        /* XXX We'll also add an API for getting better information, e.g.
298
0
         * attribute names. */
299
0
        if (REPR(obj)->describe_refs)
300
0
            REPR(obj)->describe_refs(tc, ss, STABLE(obj), OBJECT_BODY(obj));
301
0
        else if (REPR(obj)->gc_mark) {
302
0
            REPR(obj)->gc_mark(tc, STABLE(obj), OBJECT_BODY(obj), ss->gcwl);
303
0
            process_gc_worklist(tc, ss, NULL);
304
0
        }
305
0
        if (REPR(obj)->unmanaged_size)
306
0
            col->unmanaged_size += REPR(obj)->unmanaged_size(tc, STABLE(obj),
307
0
                OBJECT_BODY(obj));
308
0
    }
309
0
}
310
0
static void process_workitems(MVMThreadContext *tc, MVMHeapSnapshotState *ss) {
311
0
    while (ss->num_workitems > 0) {
312
0
        MVMHeapSnapshotWorkItem item = pop_workitem(tc, ss);
313
0
314
0
        /* We take our own working copy of the collectable info, since the
315
0
         * collectables array can grow and be reallocated. */
316
0
        MVMHeapSnapshotCollectable col;
317
0
        set_ref_from(tc, ss, item.col_idx);
318
0
        col = ss->hs->collectables[item.col_idx];
319
0
        col.kind = item.kind;
320
0
321
0
        switch (item.kind) {
322
0
            case MVM_SNAPSHOT_COL_KIND_OBJECT:
323
0
            case MVM_SNAPSHOT_COL_KIND_TYPE_OBJECT:
324
0
                process_object(tc, ss, &col, (MVMObject *)item.target);
325
0
                break;
326
0
            case MVM_SNAPSHOT_COL_KIND_STABLE: {
327
0
                MVMuint16 i;
328
0
                MVMSTable *st = (MVMSTable *)item.target;
329
0
                process_collectable(tc, ss, &col, (MVMCollectable *)st);
330
0
                set_type_index(tc, ss, &col, st);
331
0
332
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
333
0
                    (MVMCollectable *)st->method_cache, "Method cache");
334
0
335
0
                for (i = 0; i < st->type_check_cache_length; i++)
336
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
337
0
                        (MVMCollectable *)st->type_check_cache[i], "Type cache entry");
338
0
339
0
                if (st->container_spec && st->container_spec->gc_mark_data) {
340
0
                    st->container_spec->gc_mark_data(tc, st, ss->gcwl);
341
0
                    process_gc_worklist(tc, ss, "Container spec data item");
342
0
                }
343
0
344
0
                if (st->boolification_spec)
345
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
346
0
                        (MVMCollectable *)st->boolification_spec->method,
347
0
                        "Boolification method");
348
0
349
0
                if (st->invocation_spec) {
350
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
351
0
                        (MVMCollectable *)st->invocation_spec->class_handle,
352
0
                        "Invocation spec class handle");
353
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
354
0
                        (MVMCollectable *)st->invocation_spec->attr_name,
355
0
                        "Invocation spec attribute name");
356
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
357
0
                        (MVMCollectable *)st->invocation_spec->invocation_handler,
358
0
                        "Invocation spec invocation handler");
359
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
360
0
                        (MVMCollectable *)st->invocation_spec->md_class_handle,
361
0
                        "Invocation spec class handle (multi)");
362
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
363
0
                        (MVMCollectable *)st->invocation_spec->md_cache_attr_name,
364
0
                        "Invocation spec cache attribute name (multi)");
365
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
366
0
                        (MVMCollectable *)st->invocation_spec->md_valid_attr_name,
367
0
                        "Invocation spec valid attribute name (multi)");
368
0
                }
369
0
370
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
371
0
                    (MVMCollectable *)st->WHO, "WHO");
372
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
373
0
                    (MVMCollectable *)st->WHAT, "WHAT");
374
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
375
0
                    (MVMCollectable *)st->HOW, "HOW");
376
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
377
0
                    (MVMCollectable *)st->HOW_sc, "HOW serialization context");
378
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
379
0
                    (MVMCollectable *)st->method_cache_sc,
380
0
                    "Method cache serialization context");
381
0
382
0
                if (st->mode_flags & MVM_PARAMETRIC_TYPE) {
383
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
384
0
                        (MVMCollectable *)st->paramet.ric.parameterizer,
385
0
                        "Parametric type parameterizer");
386
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
387
0
                        (MVMCollectable *)st->paramet.ric.lookup,
388
0
                        "Parametric type intern table");
389
0
                }
390
0
                else if (st->mode_flags & MVM_PARAMETERIZED_TYPE) {
391
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
392
0
                        (MVMCollectable *)st->paramet.erized.parametric_type,
393
0
                        "Parameterized type's parametric type");
394
0
                    MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
395
0
                        (MVMCollectable *)st->paramet.erized.parameters,
396
0
                        "Parameterized type's parameters");
397
0
                }
398
0
399
0
                if (st->REPR->gc_mark_repr_data) {
400
0
                    st->REPR->gc_mark_repr_data(tc, st, ss->gcwl);
401
0
                    process_gc_worklist(tc, ss, "REPR data item");
402
0
                }
403
0
                break;
404
0
            }
405
0
            case MVM_SNAPSHOT_COL_KIND_FRAME: {
406
0
                MVMFrame *frame = (MVMFrame *)item.target;
407
0
                col.collectable_size = sizeof(MVMFrame);
408
0
                set_static_frame_index(tc, ss, &col, frame->static_info);
409
0
410
0
                if (frame->caller && !MVM_FRAME_IS_ON_CALLSTACK(tc, frame))
411
0
                    add_reference_const_cstr(tc, ss, "Caller",
412
0
                        get_frame_idx(tc, ss, frame->caller));
413
0
                if (frame->outer)
414
0
                    add_reference_const_cstr(tc, ss, "Outer",
415
0
                        get_frame_idx(tc, ss, frame->outer));
416
0
417
0
                MVM_gc_root_add_frame_registers_to_worklist(tc, ss->gcwl, frame);
418
0
                process_gc_worklist(tc, ss, "Register");
419
0
                if (frame->work)
420
0
                    col.unmanaged_size += frame->allocd_work;
421
0
422
0
                if (frame->env) {
423
0
                    MVMuint16  i, count;
424
0
                    MVMuint16 *type_map;
425
0
                    MVMuint16  name_count = frame->static_info->body.num_lexicals;
426
0
                    MVMLexicalRegistry **names = frame->static_info->body.lexical_names_list;
427
0
                    if (frame->spesh_cand && frame->spesh_log_idx == -1 && frame->spesh_cand->lexical_types) {
428
0
                        type_map = frame->spesh_cand->lexical_types;
429
0
                        count    = frame->spesh_cand->num_lexicals;
430
0
                    }
431
0
                    else {
432
0
                        type_map = frame->static_info->body.lexical_types;
433
0
                        count    = frame->static_info->body.num_lexicals;
434
0
                    }
435
0
                    for (i = 0; i < count; i++) {
436
0
                        if (type_map[i] == MVM_reg_str || type_map[i] == MVM_reg_obj) {
437
0
                            if (i < name_count)
438
0
                                MVM_profile_heap_add_collectable_rel_vm_str(tc, ss,
439
0
                                    (MVMCollectable *)frame->env[i].o, names[i]->key);
440
0
                            else
441
0
                                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
442
0
                                    (MVMCollectable *)frame->env[i].o, "Lexical (inlined)");
443
0
                        }
444
0
                    }
445
0
                    col.unmanaged_size += frame->allocd_env;
446
0
                }
447
0
448
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
449
0
                    (MVMCollectable *)frame->code_ref, "Code reference");
450
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
451
0
                    (MVMCollectable *)frame->static_info, "Static frame");
452
0
                MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
453
0
                    (MVMCollectable *)frame->dynlex_cache_name,
454
0
                    "Dynamic lexical cache name");
455
0
456
0
                if (frame->special_return_data && frame->mark_special_return_data) {
457
0
                    frame->mark_special_return_data(tc, frame, ss->gcwl);
458
0
                    process_gc_worklist(tc, ss, "Special return data");
459
0
                }
460
0
461
0
                if (frame->continuation_tags) {
462
0
                    MVMContinuationTag *tag = frame->continuation_tags;
463
0
                    while (tag) {
464
0
                        MVM_profile_heap_add_collectable_rel_const_cstr(tc, ss,
465
0
                            (MVMCollectable *)tag->tag, "Continuation tag");
466
0
                        col.unmanaged_size += sizeof(MVMContinuationTag);
467
0
                        tag = tag->next;
468
0
                    }
469
0
                }
470
0
471
0
                break;
472
0
            }
473
0
            case MVM_SNAPSHOT_COL_KIND_PERM_ROOTS:
474
0
                MVM_gc_root_add_permanents_to_worklist(tc, NULL, ss);
475
0
                break;
476
0
            case MVM_SNAPSHOT_COL_KIND_INSTANCE_ROOTS:
477
0
                MVM_gc_root_add_instance_roots_to_worklist(tc, NULL, ss);
478
0
                break;
479
0
            case MVM_SNAPSHOT_COL_KIND_CSTACK_ROOTS:
480
0
                MVM_gc_root_add_temps_to_worklist((MVMThreadContext *)item.target, NULL, ss);
481
0
                break;
482
0
            case MVM_SNAPSHOT_COL_KIND_THREAD_ROOTS: {
483
0
                MVMThreadContext *thread_tc = (MVMThreadContext *)item.target;
484
0
                MVM_gc_root_add_tc_roots_to_worklist(thread_tc, NULL, ss);
485
0
                if (thread_tc->cur_frame && !MVM_FRAME_IS_ON_CALLSTACK(thread_tc, tc->cur_frame))
486
0
                    add_reference_const_cstr(tc, ss, "Current frame",
487
0
                        get_frame_idx(tc, ss, thread_tc->cur_frame));
488
0
                 break;
489
0
            }
490
0
            case MVM_SNAPSHOT_COL_KIND_ROOT: {
491
0
                MVMThread *cur_thread;
492
0
493
0
                add_reference_const_cstr(tc, ss, "Permanent Roots",
494
0
                    push_workitem(tc, ss, MVM_SNAPSHOT_COL_KIND_PERM_ROOTS, NULL));
495
0
                add_reference_const_cstr(tc, ss, "VM Instance Roots",
496
0
                    push_workitem(tc, ss, MVM_SNAPSHOT_COL_KIND_INSTANCE_ROOTS, NULL));
497
0
498
0
                cur_thread = tc->instance->threads;
499
0
                while (cur_thread) {
500
0
                    if (cur_thread->body.tc) {
501
0
                        add_reference_const_cstr(tc, ss, "C Stack Roots",
502
0
                            push_workitem(tc, ss,
503
0
                                MVM_SNAPSHOT_COL_KIND_CSTACK_ROOTS,
504
0
                                cur_thread->body.tc));
505
0
                        add_reference_const_cstr(tc, ss, "Thread Roots",
506
0
                            push_workitem(tc, ss,
507
0
                                MVM_SNAPSHOT_COL_KIND_THREAD_ROOTS,
508
0
                                cur_thread->body.tc));
509
0
                        add_reference_const_cstr(tc, ss, "Inter-generational Roots",
510
0
                            push_workitem(tc, ss,
511
0
                                MVM_SNAPSHOT_COL_KIND_INTERGEN_ROOTS,
512
0
                                cur_thread->body.tc));
513
0
                        add_reference_const_cstr(tc, ss, "Thread Call Stack Roots",
514
0
                            push_workitem(tc, ss,
515
0
                                MVM_SNAPSHOT_COL_KIND_CALLSTACK_ROOTS,
516
0
                                cur_thread->body.tc));
517
0
                    }
518
0
                    cur_thread = cur_thread->body.next;
519
0
                }
520
0
521
0
                break;
522
0
            }
523
0
            case MVM_SNAPSHOT_COL_KIND_INTERGEN_ROOTS: {
524
0
                MVMThreadContext *thread_tc = (MVMThreadContext *)item.target;
525
0
                MVM_gc_root_add_gen2s_to_snapshot(thread_tc, ss);
526
0
                break;
527
0
            }
528
0
            case MVM_SNAPSHOT_COL_KIND_CALLSTACK_ROOTS: {
529
0
                MVMThreadContext *thread_tc = (MVMThreadContext *)item.target;
530
0
                if (thread_tc->cur_frame && MVM_FRAME_IS_ON_CALLSTACK(tc, thread_tc->cur_frame)) {
531
0
                    MVMFrame *cur_frame = thread_tc->cur_frame;
532
0
                    MVMint32 idx = 0;
533
0
                    while (cur_frame && MVM_FRAME_IS_ON_CALLSTACK(tc, cur_frame)) {
534
0
                        add_reference_idx(tc, ss, idx,
535
0
                            push_workitem(tc, ss, MVM_SNAPSHOT_COL_KIND_FRAME,
536
0
                                (MVMCollectable *)cur_frame));
537
0
                        idx++;
538
0
                        cur_frame = cur_frame->caller;
539
0
                    }
540
0
                }
541
0
                break;
542
0
            }
543
0
            default:
544
0
                MVM_panic(1, "Unknown heap snapshot worklist item kind %d", item.kind);
545
0
        }
546
0
547
0
        /* Store updated collectable info into array. Note that num_refs was
548
0
         * updated "at a distance". */
549
0
        col.num_refs = ss->hs->collectables[item.col_idx].num_refs;
550
0
        ss->hs->collectables[item.col_idx] = col;
551
0
    }
552
0
}
553
554
/* API function for adding a collectable to the snapshot, describing its
555
 * relation to the current collectable with a constant C string that we
556
 * should not free. */
557
void MVM_profile_heap_add_collectable_rel_const_cstr(MVMThreadContext *tc,
558
0
        MVMHeapSnapshotState *ss, MVMCollectable *collectable, char *desc) {
559
0
    if (collectable)
560
0
        add_reference_const_cstr(tc, ss, desc,
561
0
            get_collectable_idx(tc, ss, collectable));
562
0
}
563
564
/* API function for adding a collectable to the snapshot, describing its
565
 * relation to the current collectable with an MVMString. */
566
void MVM_profile_heap_add_collectable_rel_vm_str(MVMThreadContext *tc,
567
0
        MVMHeapSnapshotState *ss, MVMCollectable *collectable, MVMString *desc) {
568
0
    if (collectable)
569
0
        add_reference_vm_str(tc, ss, desc,
570
0
            get_collectable_idx(tc, ss, collectable));
571
0
}
572
573
/* API function for adding a collectable to the snapshot, describing its
574
 * relation to the current collectable with an integer index. */
575
void MVM_profile_heap_add_collectable_rel_idx(MVMThreadContext *tc,
576
0
        MVMHeapSnapshotState *ss, MVMCollectable *collectable, MVMuint64 idx) {
577
0
    if (collectable)
578
0
        add_reference_idx(tc, ss, idx,
579
0
            get_collectable_idx(tc, ss, collectable));
580
0
}
581
582
/* Drives the overall process of recording a snapshot of the heap. */
583
0
static void record_snapshot(MVMThreadContext *tc, MVMHeapSnapshotCollection *col, MVMHeapSnapshot *hs) {
584
0
    /* Initialize state for taking a snapshot. */
585
0
    MVMHeapSnapshotState ss;
586
0
    memset(&ss, 0, sizeof(MVMHeapSnapshotState));
587
0
    ss.col = col;
588
0
    ss.hs = hs;
589
0
    ss.gcwl = MVM_gc_worklist_create(tc, 1);
590
0
591
0
    /* We push the ultimate "root of roots" onto the worklist to get things
592
0
     * going, then set off on our merry way. */
593
0
    push_workitem(tc, &ss, MVM_SNAPSHOT_COL_KIND_ROOT, NULL);
594
0
    process_workitems(tc, &ss);
595
0
596
0
    /* Clean up temporary state. */
597
0
    MVM_free(ss.workitems);
598
0
    MVM_HASH_DESTROY(hash_handle, MVMHeapSnapshotSeen, ss.seen);
599
0
    MVM_gc_worklist_destroy(tc, ss.gcwl);
600
0
}
601
602
/* Takes a snapshot of the heap, adding it to the current heap snapshot
603
 * collection. */
604
146
void MVM_profile_heap_take_snapshot(MVMThreadContext *tc) {
605
146
    if (MVM_profile_heap_profiling(tc)) {
606
0
        MVMHeapSnapshotCollection *col = tc->instance->heap_snapshots;
607
0
        grow_storage(&(col->snapshots), &(col->num_snapshots), &(col->alloc_snapshots),
608
0
            sizeof(MVMHeapSnapshot));
609
0
        record_snapshot(tc, col, &(col->snapshots[col->num_snapshots]));
610
0
        col->num_snapshots++;
611
0
    }
612
146
}
613
614
/* Frees all memory associated with the heap snapshot. */
615
0
static void destroy_heap_snapshot_collection(MVMThreadContext *tc) {
616
0
    MVMHeapSnapshotCollection *col = tc->instance->heap_snapshots;
617
0
    MVMuint64 i;
618
0
619
0
    for (i = 0; i < col->num_snapshots; i++) {
620
0
        MVMHeapSnapshot *hs = &(col->snapshots[i]);
621
0
        MVM_free(hs->collectables);
622
0
        MVM_free(hs->references);
623
0
    }
624
0
    MVM_free(col->snapshots);
625
0
626
0
    for (i = 0; i < col->num_strings; i++)
627
0
        if (col->strings_free[i])
628
0
            MVM_free(col->strings[i]);
629
0
    MVM_free(col->strings);
630
0
    MVM_free(col->strings_free);
631
0
632
0
    MVM_free(col->types);
633
0
    MVM_free(col->static_frames);
634
0
635
0
    MVM_free(col);
636
0
    tc->instance->heap_snapshots = NULL;
637
0
}
638
639
/* Turns the collected data into MoarVM objects. */
640
0
#define vmstr(tc, cstr) MVM_string_utf8_decode(tc, tc->instance->VMString, cstr, strlen(cstr))
641
0
#define box_s(tc, str) MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, str)
642
0
MVMObject * string_heap_array(MVMThreadContext *tc, MVMHeapSnapshotCollection *col) {
643
0
    MVMObject *arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTStrArray);
644
0
    MVMuint64 i;
645
0
    for (i = 0; i < col->num_strings; i++)
646
0
        MVM_repr_bind_pos_s(tc, arr, i, vmstr(tc, col->strings[i]));
647
0
    return arr;
648
0
}
649
0
MVMObject * types_str(MVMThreadContext *tc, MVMHeapSnapshotCollection *col) {
650
0
    /* Produces ; separated sequences of:
651
0
     *   repr_string_index,type_name_string_index
652
0
     * Both of which are integers.
653
0
     */
654
0
     MVMObject *result;
655
0
     size_t buffer_size = 10 * col->num_types;
656
0
     size_t buffer_pos  = 0;
657
0
     char *buffer       = MVM_malloc(buffer_size);
658
0
659
0
     MVMuint64 i;
660
0
     for (i = 0; i < col->num_types; i++) {
661
0
         char tmp[256];
662
0
         int item_chars = snprintf(tmp, 256,
663
0
            "%"PRIu64",%"PRIu64";",
664
0
            col->types[i].repr_name,
665
0
            col->types[i].type_name);
666
0
         if (item_chars < 0)
667
0
             MVM_panic(1, "Failed to save type in heap snapshot");
668
0
         if (buffer_pos + item_chars >= buffer_size) {
669
0
             buffer_size += 4096;
670
0
             buffer = MVM_realloc(buffer, buffer_size);
671
0
         }
672
0
         memcpy(buffer + buffer_pos, tmp, item_chars);
673
0
         buffer_pos += item_chars;
674
0
     }
675
0
    if (buffer_pos > 1)
676
0
        buffer[buffer_pos - 1] = 0; /* Cut off the trailing ; for ease of parsing */
677
0
     buffer[buffer_pos] = 0;
678
0
679
0
     result = box_s(tc, vmstr(tc, buffer));
680
0
     MVM_free(buffer);
681
0
     return result;
682
0
}
683
0
MVMObject * static_frames_str(MVMThreadContext *tc, MVMHeapSnapshotCollection *col) {
684
0
    /* Produces ; separated sequences of:
685
0
     *   name_string_index,cuid_string_index,line_number,file_string_index
686
0
     * All of which are integers.
687
0
     */
688
0
     MVMObject *result;
689
0
     size_t buffer_size = 16 * col->num_static_frames;
690
0
     size_t buffer_pos  = 0;
691
0
     char *buffer       = MVM_malloc(buffer_size);
692
0
693
0
     MVMuint64 i;
694
0
     for (i = 0; i < col->num_static_frames; i++) {
695
0
         char tmp[256];
696
0
         int item_chars = snprintf(tmp, 256,
697
0
            "%"PRId64",%"PRId64",%"PRId64",%"PRId64";",
698
0
            col->static_frames[i].name,
699
0
            col->static_frames[i].cuid,
700
0
            col->static_frames[i].line,
701
0
            col->static_frames[i].file);
702
0
         if (item_chars < 0)
703
0
             MVM_panic(1, "Failed to save static frame in heap snapshot");
704
0
         if (buffer_pos + item_chars >= buffer_size) {
705
0
             buffer_size += 4096;
706
0
             buffer = MVM_realloc(buffer, buffer_size);
707
0
         }
708
0
         memcpy(buffer + buffer_pos, tmp, item_chars);
709
0
         buffer_pos += item_chars;
710
0
     }
711
0
    if (buffer_pos > 1)
712
0
        buffer[buffer_pos - 1] = 0; /* Cut off the trailing ; for ease of parsing */
713
0
     buffer[buffer_pos] = 0;
714
0
715
0
     result = box_s(tc, vmstr(tc, buffer));
716
0
     MVM_free(buffer);
717
0
     return result;
718
0
}
719
0
MVMObject * collectables_str(MVMThreadContext *tc, MVMHeapSnapshot *s) {
720
0
    /* Produces ; separated sequences of:
721
0
     *   kind,type_or_frame_index,collectable_size,unmanaged_size,refs_start,num_refs
722
0
     * All of which are integers.
723
0
     */
724
0
     MVMObject *result;
725
0
     size_t buffer_size = 20 * s->num_collectables;
726
0
     size_t buffer_pos  = 0;
727
0
     char *buffer       = MVM_malloc(buffer_size);
728
0
729
0
     MVMuint64 i;
730
0
     for (i = 0; i < s->num_collectables; i++) {
731
0
         char tmp[256];
732
0
         int item_chars = snprintf(tmp, 256,
733
0
            "%"PRIu16",%"PRId32",%"PRIu16",%"PRIu64",%"PRIu64",%"PRIu32";",
734
0
            s->collectables[i].kind,
735
0
            s->collectables[i].type_or_frame_index,
736
0
            s->collectables[i].collectable_size,
737
0
            s->collectables[i].unmanaged_size,
738
0
            s->collectables[i].num_refs ? s->collectables[i].refs_start : (MVMuint64)0,
739
0
            s->collectables[i].num_refs);
740
0
         if (item_chars < 0)
741
0
             MVM_panic(1, "Failed to save collectable in heap snapshot");
742
0
         if (buffer_pos + item_chars >= buffer_size) {
743
0
             buffer_size += 4096;
744
0
             buffer = MVM_realloc(buffer, buffer_size);
745
0
         }
746
0
         memcpy(buffer + buffer_pos, tmp, item_chars);
747
0
         buffer_pos += item_chars;
748
0
     }
749
0
    if (buffer_pos > 1)
750
0
        buffer[buffer_pos - 1] = 0; /* Cut off the trailing ; for ease of parsing */
751
0
     buffer[buffer_pos] = 0;
752
0
753
0
     result = box_s(tc, vmstr(tc, buffer));
754
0
     MVM_free(buffer);
755
0
     return result;
756
0
}
757
0
MVMObject * references_str(MVMThreadContext *tc, MVMHeapSnapshot *s) {
758
0
    /* Produces ; separated sequences of:
759
0
     *   kind,idx,to
760
0
     * All of which are integers.
761
0
     */
762
0
    MVMObject *result;
763
0
    size_t buffer_size = 10 * s->num_references;
764
0
    size_t buffer_pos  = 0;
765
0
    char *buffer       = MVM_malloc(buffer_size);
766
0
767
0
    MVMuint64 i;
768
0
    for (i = 0; i < s->num_references; i++) {
769
0
        char tmp[128];
770
0
        int item_chars = snprintf(tmp, 128, "%"PRIu64",%"PRIu64",%"PRIu64";",
771
0
            s->references[i].description & ((1 << MVM_SNAPSHOT_REF_KIND_BITS) - 1),
772
0
            s->references[i].description >> MVM_SNAPSHOT_REF_KIND_BITS,
773
0
            s->references[i].collectable_index);
774
0
        if (item_chars < 0)
775
0
            MVM_panic(1, "Failed to save reference in heap snapshot");
776
0
        if (buffer_pos + item_chars >= buffer_size) {
777
0
            buffer_size += 4096;
778
0
            buffer = MVM_realloc(buffer, buffer_size);
779
0
        }
780
0
        memcpy(buffer + buffer_pos, tmp, item_chars);
781
0
        buffer_pos += item_chars;
782
0
    }
783
0
    if (buffer_pos > 1)
784
0
        buffer[buffer_pos - 1] = 0; /* Cut off the trailing ; for ease of parsing */
785
0
    buffer[buffer_pos] = 0;
786
0
787
0
    result = box_s(tc, vmstr(tc, buffer));
788
0
    MVM_free(buffer);
789
0
    return result;
790
0
}
791
0
MVMObject * snapshot_to_mvm_object(MVMThreadContext *tc, MVMHeapSnapshot *s) {
792
0
    MVMObject *snapshot = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type);
793
0
    MVM_repr_bind_key_o(tc, snapshot, vmstr(tc, "collectables"),
794
0
        collectables_str(tc, s));
795
0
    MVM_repr_bind_key_o(tc, snapshot, vmstr(tc, "references"),
796
0
        references_str(tc, s));
797
0
    return snapshot;
798
0
}
799
0
MVMObject * snapshots_to_mvm_objects(MVMThreadContext *tc, MVMHeapSnapshotCollection *col) {
800
0
    MVMObject *arr = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type);
801
0
    MVMuint64 i;
802
0
    for (i = 0; i < col->num_snapshots; i++)
803
0
        MVM_repr_bind_pos_o(tc, arr, i,
804
0
            snapshot_to_mvm_object(tc, &(col->snapshots[i])));
805
0
    return arr;
806
0
}
807
0
MVMObject * collection_to_mvm_objects(MVMThreadContext *tc, MVMHeapSnapshotCollection *col) {
808
0
    MVMObject *results;
809
0
810
0
    /* Allocate in gen2, so as not to trigger GC. */
811
0
    MVM_gc_allocate_gen2_default_set(tc);
812
0
813
0
    /* Top-level results is a hash. */
814
0
    results = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type);
815
0
    MVM_repr_bind_key_o(tc, results, vmstr(tc, "strings"),
816
0
        string_heap_array(tc, col));
817
0
    MVM_repr_bind_key_o(tc, results, vmstr(tc, "types"),
818
0
        types_str(tc, col));
819
0
    MVM_repr_bind_key_o(tc, results, vmstr(tc, "static_frames"),
820
0
        static_frames_str(tc, col));
821
0
    MVM_repr_bind_key_o(tc, results, vmstr(tc, "snapshots"),
822
0
        snapshots_to_mvm_objects(tc, col));
823
0
824
0
    /* Switch off gen2 allocations now we're done. */
825
0
    MVM_gc_allocate_gen2_default_clear(tc);
826
0
827
0
    return results;
828
0
}
829
830
/* Finishes heap profiling, getting the data. */
831
0
MVMObject * MVM_profile_heap_end(MVMThreadContext *tc) {
832
0
    MVMObject *dataset;
833
0
834
0
    /* Trigger a GC run, to ensure we get at least one heap snapshot. */
835
0
    MVM_gc_enter_from_allocator(tc);
836
0
837
0
    /* Process and return the data. */
838
0
    dataset = collection_to_mvm_objects(tc, tc->instance->heap_snapshots);
839
0
    destroy_heap_snapshot_collection(tc);
840
0
    return dataset;
841
0
}