Coverage Report

Created: 2018-07-03 15:31

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