Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/instrument/line_coverage.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
#ifdef _WIN32
4
#define snprintf _snprintf
5
#endif
6
7
0
static void instrument_graph_with_breakpoints(MVMThreadContext *tc, MVMSpeshGraph *g) {
8
0
    MVMSpeshBB *bb = g->entry->linear_next;
9
0
    MVMuint16 array_slot = 0;
10
0
11
0
    MVMint32 last_line_number = -2;
12
0
    MVMint32 last_filename = -1;
13
0
14
0
    char *filename_buf = NULL;
15
0
16
0
    while (bb) {
17
0
        MVMSpeshIns *ins = bb->first_ins;
18
0
        MVMSpeshIns *breakpoint_ins;
19
0
20
0
        MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc);
21
0
        MVMint64 line_number = -1;
22
0
        MVMint64 filename_string_index = -1;
23
0
24
0
        MVMuint32 file_bp_idx;
25
0
26
0
        if (bbba) {
27
0
            line_number = bbba->line_number;
28
0
            filename_string_index = bbba->filename_string_heap_index;
29
0
            MVM_free(bbba);
30
0
        } else {
31
0
            line_number = -1;
32
0
            bb = bb->linear_next;
33
0
            continue;
34
0
        }
35
0
36
0
        /* skip PHI instructions, to make sure PHI only occur uninterrupted after start-of-bb */
37
0
        while (ins && ins->info->opcode == MVM_SSA_PHI) {
38
0
            ins = ins->next;
39
0
        }
40
0
        if (!ins) ins = bb->last_ins;
41
0
42
0
        /* Jumplists require the target BB to start in the goto op.
43
0
         * We must not break this, or we cause the interpreter to derail */
44
0
        if (bb->last_ins->info->opcode == MVM_OP_jumplist) {
45
0
            MVMint16 to_skip = bb->num_succ;
46
0
            for (; to_skip > 0; to_skip--) {
47
0
                bb = bb->linear_next;
48
0
            }
49
0
            continue;
50
0
        }
51
0
52
0
        if (line_number >= 0) {
53
0
            breakpoint_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
54
0
            breakpoint_ins->info        = MVM_op_get_op(MVM_OP_breakpoint);
55
0
            breakpoint_ins->operands    = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
56
0
57
0
            if (last_filename != filename_string_index) {
58
0
                if (filename_buf)
59
0
                    MVM_free(filename_buf);
60
0
                filename_buf = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, filename_string_index));
61
0
            }
62
0
63
0
            MVM_debugserver_register_line(tc, filename_buf, strlen(filename_buf), line_number, &file_bp_idx);
64
0
65
0
            breakpoint_ins->operands[0].lit_i32 = file_bp_idx;
66
0
            breakpoint_ins->operands[1].lit_i32 = line_number;
67
0
68
0
            last_filename = filename_string_index;
69
0
70
0
            MVM_spesh_manipulate_insert_ins(tc, bb, ins->prev, breakpoint_ins);
71
0
        }
72
0
73
0
        /* Now go through instructions to see if any are annotated with a
74
0
         * precise filename/lineno as well. */
75
0
        while (ins) {
76
0
            MVMSpeshAnn *ann = ins->annotations;
77
0
78
0
            while (ann) {
79
0
                if (ann->type == MVM_SPESH_ANN_LINENO) {
80
0
                    /* We are very likely to have one instruction here that has
81
0
                     * the same annotation as the bb itself. We skip that one.*/
82
0
                    if (ann->data.lineno.line_number == line_number && ann->data.lineno.filename_string_index == filename_string_index) {
83
0
                        break;
84
0
                    }
85
0
86
0
                    line_number = ann->data.lineno.line_number;
87
0
                    filename_string_index = ann->data.lineno.filename_string_index;
88
0
89
0
                    /*breakpoint_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));*/
90
0
                    /*breakpoint_ins->info        = MVM_op_get_op(MVM_OP_breakpoint);*/
91
0
                    /*breakpoint_ins->operands    = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));*/
92
0
93
0
                    if (last_filename != filename_string_index) {
94
0
                        if (filename_buf)
95
0
                            MVM_free(filename_buf);
96
0
                        filename_buf = MVM_string_utf8_encode_C_string(tc, MVM_cu_string(tc, g->sf->body.cu, filename_string_index));
97
0
                    }
98
0
99
0
                    MVM_debugserver_register_line(tc, filename_buf, strlen(filename_buf), line_number, &file_bp_idx);
100
0
101
0
                    /*breakpoint_ins->operands[0].lit_i32 = file_bp_idx;*/
102
0
                    /*breakpoint_ins->operands[1].lit_i32 = ann->data.lineno.line_number;*/
103
0
104
0
                    /* XXX insert breakpoint op here, too, maybe? */
105
0
106
0
                    break;
107
0
                }
108
0
109
0
                ann = ann->next;
110
0
            }
111
0
112
0
            ins = ins->next;
113
0
        }
114
0
115
0
        bb = bb->linear_next;
116
0
    }
117
0
118
0
    if (filename_buf)
119
0
        MVM_free(filename_buf);
120
0
}
121
122
0
static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) {
123
0
    MVMSpeshBB *bb = g->entry->linear_next;
124
0
    MVMuint16 array_slot = 0;
125
0
126
0
    MVMint32 last_line_number = -2;
127
0
    MVMint32 last_filename = -1;
128
0
129
0
    MVMuint16 allocd_slots  = g->num_bbs * 2;
130
0
    char *line_report_store = MVM_calloc(allocd_slots, sizeof(char));
131
0
132
0
    /* Since we don't know the right size for the line report store
133
0
     * up front, we will have to realloc it along the way. After that
134
0
     * we havee to fix up the arguments to the coverage log instructions */
135
0
    MVMuint32 fixup_alloc = g->num_bbs * 2;
136
0
    MVMuint32 fixup_elems = 0;
137
0
    MVMuint32 fixup_idx; /* for iterating over the fixup array */
138
0
    MVMSpeshIns **to_fixup = MVM_malloc(fixup_alloc * sizeof(MVMSpeshIns*));
139
0
140
0
    while (bb) {
141
0
        MVMSpeshIns *ins = bb->first_ins;
142
0
        MVMSpeshIns *log_ins;
143
0
144
0
        MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc);
145
0
        MVMint64 line_number;
146
0
        MVMint64 filename_string_index;
147
0
        if (bbba) {
148
0
            line_number = bbba->line_number;
149
0
            filename_string_index = bbba->filename_string_heap_index;
150
0
            MVM_free(bbba);
151
0
        } else {
152
0
            line_number = -1;
153
0
            bb = bb->linear_next;
154
0
            continue;
155
0
        }
156
0
157
0
        /* skip PHI instructions, to make sure PHI only occur uninterrupted after start-of-bb */
158
0
        while (ins && ins->info->opcode == MVM_SSA_PHI) {
159
0
            ins = ins->next;
160
0
        }
161
0
        if (!ins) ins = bb->last_ins;
162
0
163
0
        /* Jumplists require the target BB to start in the goto op.
164
0
         * We must not break this, or we cause the interpreter to derail */
165
0
        if (bb->last_ins->info->opcode == MVM_OP_jumplist) {
166
0
            MVMint16 to_skip = bb->num_succ;
167
0
            for (; to_skip > 0; to_skip--) {
168
0
                bb = bb->linear_next;
169
0
            }
170
0
            continue;
171
0
        }
172
0
173
0
        log_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
174
0
        log_ins->info        = MVM_op_get_op(MVM_OP_coverage_log);
175
0
        log_ins->operands    = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));
176
0
177
0
        log_ins->operands[0].lit_str_idx = filename_string_index;
178
0
        log_ins->operands[1].lit_i32 = line_number;
179
0
180
0
        if (last_line_number == line_number && last_filename == filename_string_index) {
181
0
            /* Consecutive BBs with the same line number and filename should
182
0
             * share one "already reported" slot. */
183
0
            log_ins->operands[2].lit_i32 = array_slot;
184
0
        } else {
185
0
            log_ins->operands[2].lit_i32 = array_slot++;
186
0
            last_line_number = line_number;
187
0
            last_filename = filename_string_index;
188
0
189
0
            if (array_slot == allocd_slots) {
190
0
                allocd_slots *= 2;
191
0
                line_report_store = MVM_realloc(line_report_store, sizeof(char) * allocd_slots);
192
0
            }
193
0
        }
194
0
195
0
        to_fixup[fixup_elems++] = log_ins;
196
0
        if (fixup_elems == fixup_alloc) {
197
0
            fixup_alloc *= 2;
198
0
            to_fixup = MVM_realloc(to_fixup, sizeof(MVMSpeshIns*) * fixup_alloc);
199
0
        }
200
0
        MVM_spesh_manipulate_insert_ins(tc, bb, ins, log_ins);
201
0
202
0
        /* Now go through instructions to see if any are annotated with a
203
0
         * precise filename/lineno as well. */
204
0
        while (ins) {
205
0
            MVMSpeshAnn *ann = ins->annotations;
206
0
207
0
            while (ann) {
208
0
                if (ann->type == MVM_SPESH_ANN_LINENO) {
209
0
                    /* We are very likely to have one instruction here that has
210
0
                     * the same annotation as the bb itself. We skip that one.*/
211
0
                    if (ann->data.lineno.line_number == line_number && ann->data.lineno.filename_string_index == filename_string_index) {
212
0
                        break;
213
0
                    }
214
0
215
0
                    log_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
216
0
                    log_ins->info        = MVM_op_get_op(MVM_OP_coverage_log);
217
0
                    log_ins->operands    = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand));
218
0
219
0
                    log_ins->operands[0].lit_str_idx = ann->data.lineno.filename_string_index;
220
0
                    log_ins->operands[1].lit_i32 = ann->data.lineno.line_number;
221
0
                    log_ins->operands[2].lit_i32 = array_slot++;
222
0
223
0
                    if (array_slot == allocd_slots) {
224
0
                        allocd_slots *= 2;
225
0
                        line_report_store = MVM_realloc(line_report_store, sizeof(char) * allocd_slots);
226
0
                    }
227
0
228
0
                    to_fixup[fixup_elems++] = log_ins;
229
0
                    if (fixup_elems == fixup_alloc) {
230
0
                        fixup_alloc *= 2;
231
0
                        to_fixup = MVM_realloc(to_fixup, sizeof(MVMSpeshIns*) * fixup_alloc);
232
0
                    }
233
0
                    break;
234
0
                }
235
0
236
0
                ann = ann->next;
237
0
            }
238
0
239
0
            ins = ins->next;
240
0
        }
241
0
242
0
        bb = bb->linear_next;
243
0
    }
244
0
245
0
    line_report_store = MVM_realloc(line_report_store, sizeof(char) * (array_slot + 1));
246
0
247
0
    for (fixup_idx = 0; fixup_idx < fixup_elems; fixup_idx++) {
248
0
        MVMSpeshIns *ins = to_fixup[fixup_idx];
249
0
250
0
        ins->operands[3].lit_i64 = (MVMint64)line_report_store;
251
0
    }
252
0
253
0
    if (array_slot == 0) {
254
0
        MVM_free(line_report_store);
255
0
    }
256
0
    MVM_free(to_fixup);
257
0
}
258
259
/* Adds instrumented version of the unspecialized bytecode. */
260
0
static void add_instrumentation(MVMThreadContext *tc, MVMStaticFrame *sf, MVMuint8 want_coverage) {
261
0
    MVMSpeshCode  *sc;
262
0
    MVMStaticFrameInstrumentation *ins;
263
0
    MVMSpeshGraph *sg = MVM_spesh_graph_create(tc, sf, 1, 0);
264
0
    if (want_coverage)
265
0
        instrument_graph(tc, sg);
266
0
    else
267
0
        instrument_graph_with_breakpoints(tc, sg);
268
0
    sc = MVM_spesh_codegen(tc, sg);
269
0
    ins = MVM_calloc(1, sizeof(MVMStaticFrameInstrumentation));
270
0
    ins->instrumented_bytecode        = sc->bytecode;
271
0
    ins->instrumented_handlers        = sc->handlers;
272
0
    ins->instrumented_bytecode_size   = sc->bytecode_size;
273
0
    ins->uninstrumented_bytecode      = sf->body.bytecode;
274
0
    ins->uninstrumented_handlers      = sf->body.handlers;
275
0
    ins->uninstrumented_bytecode_size = sf->body.bytecode_size;
276
0
    sf->body.instrumentation = ins;
277
0
    MVM_spesh_graph_destroy(tc, sg);
278
0
    MVM_free(sc);
279
0
}
280
281
282
/* Instruments code with per-line logging of code coverage */
283
0
static void line_numbers_instrument(MVMThreadContext *tc, MVMStaticFrame *sf, MVMuint8 want_coverage) {
284
0
    if (!sf->body.instrumentation || sf->body.bytecode != sf->body.instrumentation->instrumented_bytecode) {
285
0
        /* Handle main, non-specialized, bytecode. */
286
0
        if (!sf->body.instrumentation)
287
0
            add_instrumentation(tc, sf, want_coverage);
288
0
        sf->body.bytecode      = sf->body.instrumentation->instrumented_bytecode;
289
0
        sf->body.handlers      = sf->body.instrumentation->instrumented_handlers;
290
0
        sf->body.bytecode_size = sf->body.instrumentation->instrumented_bytecode_size;
291
0
292
0
        /* Throw away any argument guard so we'll never resolve prior
293
0
         * specializations again. */
294
0
        MVM_spesh_arg_guard_discard(tc, sf);
295
0
    }
296
0
}
297
298
/* Instruments code with per-line logging of code coverage */
299
0
void MVM_line_coverage_instrument(MVMThreadContext *tc, MVMStaticFrame *sf) {
300
0
    line_numbers_instrument(tc, sf, 1);
301
0
}
302
303
/* Instruments code with a breakpoint check instruction af every line number change */
304
0
void MVM_breakpoint_instrument(MVMThreadContext *tc, MVMStaticFrame *sf) {
305
0
    line_numbers_instrument(tc, sf, 0);
306
0
}
307
308
0
void MVM_line_coverage_report(MVMThreadContext *tc, MVMString *filename, MVMuint32 line_number, MVMuint16 cache_slot, char *cache) {
309
0
    if (tc->instance->coverage_control == 2 || (!tc->instance->coverage_control && cache[cache_slot] == 0)) {
310
0
        char *encoded_filename;
311
0
        char composed_line[256];
312
0
        size_t length;
313
0
314
0
        cache[cache_slot] = 1;
315
0
316
0
        encoded_filename = MVM_string_utf8_encode_C_string(tc, filename);
317
0
        if ((length = snprintf(composed_line, 255, "HIT  %s  %d\n", encoded_filename, line_number)) > 0) {
318
0
            fputs(composed_line, tc->instance->coverage_log_fh);
319
0
        }
320
0
        MVM_free(encoded_filename);
321
0
    }
322
0
}