/home/travis/build/MoarVM/MoarVM/src/instrument/line_coverage.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | 0 | static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) { |
4 | 0 | MVMSpeshBB *bb = g->entry->linear_next; |
5 | 0 | MVMuint16 array_slot = 0; |
6 | 0 |
|
7 | 0 | MVMint32 last_line_number; |
8 | 0 | MVMint32 last_filename; |
9 | 0 |
|
10 | 0 | char *line_report_store = MVM_calloc(g->num_bbs, sizeof(char)); |
11 | 0 | MVMuint16 allocd_slots = g->num_bbs; |
12 | 0 |
|
13 | 0 | while (bb) { |
14 | 0 | MVMSpeshIns *ins = bb->first_ins; |
15 | 0 | MVMSpeshIns *log_ins; |
16 | 0 |
|
17 | 0 | MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc); |
18 | 0 | MVMuint32 line_number; |
19 | 0 | MVMuint32 filename_string_index; |
20 | 0 | if (bbba) { |
21 | 0 | line_number = bbba->line_number; |
22 | 0 | filename_string_index = bbba->filename_string_heap_index; |
23 | 0 | MVM_free(bbba); |
24 | 0 | } else { |
25 | 0 | line_number = -1; |
26 | 0 | bb = bb->linear_next; |
27 | 0 | continue; |
28 | 0 | } |
29 | 0 |
|
30 | 0 | /* skip PHI instructions, to make sure PHI only occur uninterrupted after start-of-bb */ |
31 | 0 | while (ins && ins->info->opcode == MVM_SSA_PHI) { |
32 | 0 | ins = ins->next; |
33 | 0 | } |
34 | 0 | if (!ins) ins = bb->last_ins; |
35 | 0 |
|
36 | 0 | /* Jumplists require the target BB to start in the goto op. |
37 | 0 | * We must not break this, or we cause the interpreter to derail */ |
38 | 0 | if (bb->last_ins->info->opcode == MVM_OP_jumplist) { |
39 | 0 | MVMint16 to_skip = bb->num_succ; |
40 | 0 | for (; to_skip > 0; to_skip--) { |
41 | 0 | bb = bb->linear_next; |
42 | 0 | } |
43 | 0 | continue; |
44 | 0 | } |
45 | 0 |
|
46 | 0 | log_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); |
47 | 0 | log_ins->info = MVM_op_get_op(MVM_OP_coverage_log); |
48 | 0 | log_ins->operands = MVM_spesh_alloc(tc, g, 4 * sizeof(MVMSpeshOperand)); |
49 | 0 |
|
50 | 0 | log_ins->operands[0].lit_str_idx = filename_string_index; |
51 | 0 | log_ins->operands[1].lit_i32 = line_number; |
52 | 0 |
|
53 | 0 | if (last_line_number == line_number && last_filename == filename_string_index) { |
54 | 0 | /* Consecutive BBs with the same line number and filename should |
55 | 0 | * share one "already reported" slot. */ |
56 | 0 | log_ins->operands[2].lit_i32 = array_slot; |
57 | 0 | } else { |
58 | 0 | log_ins->operands[2].lit_i32 = array_slot++; |
59 | 0 | last_line_number = line_number; |
60 | 0 | last_filename = filename_string_index; |
61 | 0 | } |
62 | 0 |
|
63 | 0 | log_ins->operands[3].lit_i64 = (MVMint64)line_report_store; |
64 | 0 |
|
65 | 0 | MVM_spesh_manipulate_insert_ins(tc, bb, ins, log_ins); |
66 | 0 |
|
67 | 0 | bb = bb->linear_next; |
68 | 0 | } |
69 | 0 |
|
70 | 0 | if (array_slot == 0) { |
71 | 0 | MVM_free(line_report_store); |
72 | 0 | } else if (array_slot > g->num_bbs) { |
73 | 0 | MVM_panic(99, "we've allocated %d slots for coverage reporting, but we've used up to %d!", g->num_bbs, array_slot); |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | /* Adds instrumented version of the unspecialized bytecode. */ |
78 | 0 | static void add_instrumentation(MVMThreadContext *tc, MVMStaticFrame *sf) { |
79 | 0 | MVMSpeshCode *sc; |
80 | 0 | MVMStaticFrameInstrumentation *ins; |
81 | 0 | MVMSpeshGraph *sg = MVM_spesh_graph_create(tc, sf, 1, 0); |
82 | 0 | instrument_graph(tc, sg); |
83 | 0 | sc = MVM_spesh_codegen(tc, sg); |
84 | 0 | ins = MVM_calloc(1, sizeof(MVMStaticFrameInstrumentation)); |
85 | 0 | ins->instrumented_bytecode = sc->bytecode; |
86 | 0 | ins->instrumented_handlers = sc->handlers; |
87 | 0 | ins->instrumented_bytecode_size = sc->bytecode_size; |
88 | 0 | ins->uninstrumented_bytecode = sf->body.bytecode; |
89 | 0 | ins->uninstrumented_handlers = sf->body.handlers; |
90 | 0 | ins->uninstrumented_bytecode_size = sf->body.bytecode_size; |
91 | 0 | sf->body.instrumentation = ins; |
92 | 0 | MVM_spesh_graph_destroy(tc, sg); |
93 | 0 | MVM_free(sc); |
94 | 0 | } |
95 | | |
96 | | |
97 | | /* Instruments code with per-line logging of code coverage */ |
98 | 0 | void MVM_line_coverage_instrument(MVMThreadContext *tc, MVMStaticFrame *sf) { |
99 | 0 | if (!sf->body.instrumentation || sf->body.bytecode != sf->body.instrumentation->instrumented_bytecode) { |
100 | 0 | /* Handle main, non-specialized, bytecode. */ |
101 | 0 | if (!sf->body.instrumentation) |
102 | 0 | add_instrumentation(tc, sf); |
103 | 0 | sf->body.bytecode = sf->body.instrumentation->instrumented_bytecode; |
104 | 0 | sf->body.handlers = sf->body.instrumentation->instrumented_handlers; |
105 | 0 | sf->body.bytecode_size = sf->body.instrumentation->instrumented_bytecode_size; |
106 | 0 |
|
107 | 0 | /* Throw away any specializations; we'll need to reproduce them as |
108 | 0 | * instrumented versions. */ |
109 | 0 | sf->body.num_spesh_candidates = 0; |
110 | 0 | sf->body.spesh_candidates = NULL; |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | 0 | void MVM_line_coverage_report(MVMThreadContext *tc, MVMString *filename, MVMuint32 line_number, MVMuint16 cache_slot, char *cache) { |
115 | 0 | if (cache[cache_slot] == 0) { |
116 | 0 | char *encoded_filename; |
117 | 0 | char composed_line[256]; |
118 | 0 | size_t length; |
119 | 0 |
|
120 | 0 | cache[cache_slot] = 1; |
121 | 0 |
|
122 | 0 | encoded_filename = MVM_string_utf8_encode_C_string(tc, filename); |
123 | 0 | if ((length = snprintf(composed_line, 255, "HIT %s %d\n", encoded_filename, line_number)) > 0) { |
124 | 0 | fputs(composed_line, tc->instance->coverage_log_fh); |
125 | 0 | } |
126 | 0 | MVM_free(encoded_filename); |
127 | 0 | } |
128 | 0 | } |