Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/instrument/crossthreadwrite.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Walk graph and insert write check instructions. */
4
static void prepend_ctw_check(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb,
5
                              MVMSpeshIns *before_ins, MVMSpeshOperand check_reg,
6
0
                              MVMint16 guilty) {
7
0
    MVMSpeshIns *ctw_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns));
8
0
    ctw_ins->info        = MVM_op_get_op(MVM_OP_ctw_check);
9
0
    ctw_ins->operands    = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand));
10
0
    ctw_ins->operands[0] = check_reg;
11
0
    ctw_ins->operands[1].lit_i16 = guilty;
12
0
    MVM_spesh_manipulate_insert_ins(tc, bb, before_ins->prev, ctw_ins);
13
0
}
14
0
static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) {
15
0
    MVMSpeshBB *bb = g->entry->linear_next;
16
0
    while (bb) {
17
0
        MVMSpeshIns *ins = bb->first_ins;
18
0
        while (ins) {
19
0
            switch (ins->info->opcode) {
20
0
                case MVM_OP_rebless:
21
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_REBLESS);
22
0
                case MVM_OP_bindattr_i:
23
0
                case MVM_OP_bindattr_n:
24
0
                case MVM_OP_bindattr_s:
25
0
                case MVM_OP_bindattr_o:
26
0
                case MVM_OP_bindattrs_i:
27
0
                case MVM_OP_bindattrs_n:
28
0
                case MVM_OP_bindattrs_s:
29
0
                case MVM_OP_bindattrs_o:
30
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_BIND_ATTR);
31
0
                    break;
32
0
                case MVM_OP_bindpos_i:
33
0
                case MVM_OP_bindpos_n:
34
0
                case MVM_OP_bindpos_s:
35
0
                case MVM_OP_bindpos_o:
36
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_BIND_POS);
37
0
                    break;
38
0
                case MVM_OP_push_i:
39
0
                case MVM_OP_push_n:
40
0
                case MVM_OP_push_s:
41
0
                case MVM_OP_push_o:
42
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_PUSH);
43
0
                    break;
44
0
                case MVM_OP_pop_i:
45
0
                case MVM_OP_pop_n:
46
0
                case MVM_OP_pop_s:
47
0
                case MVM_OP_pop_o:
48
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[1], MVM_CTW_POP);
49
0
                    break;
50
0
                case MVM_OP_shift_i:
51
0
                case MVM_OP_shift_n:
52
0
                case MVM_OP_shift_s:
53
0
                case MVM_OP_shift_o:
54
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[1], MVM_CTW_SHIFT);
55
0
                    break;
56
0
                case MVM_OP_unshift_i:
57
0
                case MVM_OP_unshift_n:
58
0
                case MVM_OP_unshift_s:
59
0
                case MVM_OP_unshift_o:
60
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_UNSHIFT);
61
0
                    break;
62
0
                case MVM_OP_slice:
63
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_SLICE);
64
0
                    break;
65
0
                case MVM_OP_splice:
66
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_SPLICE);
67
0
                    break;
68
0
                case MVM_OP_bindkey_i:
69
0
                case MVM_OP_bindkey_n:
70
0
                case MVM_OP_bindkey_s:
71
0
                case MVM_OP_bindkey_o:
72
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_BIND_KEY);
73
0
                    break;
74
0
                case MVM_OP_deletekey:
75
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_DELETE_KEY);
76
0
                    break;
77
0
                case MVM_OP_assign:
78
0
                case MVM_OP_assignunchecked:
79
0
                case MVM_OP_assign_i:
80
0
                case MVM_OP_assign_n:
81
0
                case MVM_OP_assign_s:
82
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_ASSIGN);
83
0
                    break;
84
0
                case MVM_OP_bindpos2d_i:
85
0
                case MVM_OP_bindpos2d_n:
86
0
                case MVM_OP_bindpos2d_s:
87
0
                case MVM_OP_bindpos2d_o:
88
0
                case MVM_OP_bindpos3d_i:
89
0
                case MVM_OP_bindpos3d_n:
90
0
                case MVM_OP_bindpos3d_s:
91
0
                case MVM_OP_bindpos3d_o:
92
0
                case MVM_OP_bindposnd_i:
93
0
                case MVM_OP_bindposnd_n:
94
0
                case MVM_OP_bindposnd_s:
95
0
                case MVM_OP_bindposnd_o:
96
0
                    prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_BIND_POS);
97
0
                    break;
98
0
            }
99
0
            ins = ins->next;
100
0
        }
101
0
        bb = bb->linear_next;
102
0
    }
103
0
}
104
105
/* Adds instrumented version of the unspecialized bytecode. */
106
0
static void add_instrumentation(MVMThreadContext *tc, MVMStaticFrame *sf) {
107
0
    MVMSpeshCode  *sc;
108
0
    MVMStaticFrameInstrumentation *ins;
109
0
    MVMSpeshGraph *sg = MVM_spesh_graph_create(tc, sf, 1, 0);
110
0
    instrument_graph(tc, sg);
111
0
    sc = MVM_spesh_codegen(tc, sg);
112
0
    ins = MVM_calloc(1, sizeof(MVMStaticFrameInstrumentation));
113
0
    ins->instrumented_bytecode        = sc->bytecode;
114
0
    ins->instrumented_handlers        = sc->handlers;
115
0
    ins->instrumented_bytecode_size   = sc->bytecode_size;
116
0
    ins->uninstrumented_bytecode      = sf->body.bytecode;
117
0
    ins->uninstrumented_handlers      = sf->body.handlers;
118
0
    ins->uninstrumented_bytecode_size = sf->body.bytecode_size;
119
0
    sf->body.instrumentation = ins;
120
0
    MVM_spesh_graph_destroy(tc, sg);
121
0
    MVM_free(sc);
122
0
}
123
124
/* Instruments code with detection and reporting of cross-thread writes. */
125
0
void MVM_cross_thread_write_instrument(MVMThreadContext *tc, MVMStaticFrame *sf) {
126
0
    if (!sf->body.instrumentation || sf->body.bytecode != sf->body.instrumentation->instrumented_bytecode) {
127
0
        /* Handle main, non-specialized, bytecode. */
128
0
        if (!sf->body.instrumentation)
129
0
            add_instrumentation(tc, sf);
130
0
        sf->body.bytecode      = sf->body.instrumentation->instrumented_bytecode;
131
0
        sf->body.handlers      = sf->body.instrumentation->instrumented_handlers;
132
0
        sf->body.bytecode_size = sf->body.instrumentation->instrumented_bytecode_size;
133
0
134
0
        /* Throw away any argument guard so we'll never resolve prior
135
0
         * specializations again. */
136
0
        MVM_spesh_arg_guard_discard(tc, sf);
137
0
    }
138
0
}
139
140
/* Filter out some special cases to reduce noise. */
141
0
static MVMint64 filtered_out(MVMThreadContext *tc, MVMObject *written) {
142
0
    /* If we're holding locks, exclude by default (unless we were asked to
143
0
     * also include these). */
144
0
    if (tc->num_locks && !tc->instance->cross_thread_write_logging_include_locked)
145
0
        return 1;
146
0
147
0
    /* Operations on a concurrent queue are fine 'cus it's concurrent. */
148
0
    if (REPR(written)->ID == MVM_REPR_ID_ConcBlockingQueue)
149
0
        return 1;
150
0
151
0
    /* Write on object from event loop thread is usually shift of invokable. */
152
0
    if (tc->instance->event_loop_thread)
153
0
        if (written->header.owner == tc->instance->event_loop_thread->thread_id)
154
0
            return 1;
155
0
156
0
    /* Filter out writes to Sub and Method, since these are almost always just
157
0
     * multi-dispatch caches. */
158
0
    if (strncmp( MVM_6model_get_stable_debug_name(tc, written->st), "Method", 6) == 0)
159
0
        return 1;
160
0
    if (strncmp( MVM_6model_get_stable_debug_name(tc, written->st), "Sub", 3) == 0)
161
0
        return 1;
162
0
163
0
    /* Otherwise, may be relevant. */
164
0
    return 0;
165
0
}
166
167
/* Squeal if the target of the write wasn't allocated by us. */
168
0
void MVM_cross_thread_write_check(MVMThreadContext *tc, MVMObject *written, MVMint16 guilty) {
169
0
    if (written->header.owner != tc->thread_id && !filtered_out(tc, written)) {
170
0
        char *guilty_desc = "did something to";
171
0
        switch (guilty) {
172
0
            case MVM_CTW_BIND_ATTR:
173
0
                guilty_desc = "bound to an attribute of";
174
0
                break;
175
0
            case MVM_CTW_BIND_POS:
176
0
                guilty_desc = "bound to an array slot of";
177
0
                break;
178
0
            case MVM_CTW_PUSH:
179
0
                guilty_desc = "pushed to";
180
0
                break;
181
0
            case MVM_CTW_POP:
182
0
                guilty_desc = "popped";
183
0
                break;
184
0
            case MVM_CTW_SHIFT:
185
0
                guilty_desc = "shifted";
186
0
                break;
187
0
            case MVM_CTW_UNSHIFT:
188
0
                guilty_desc = "unshifted to";
189
0
                break;
190
0
            case MVM_CTW_SLICE:
191
0
                guilty_desc = "sliced";
192
0
                break;
193
0
            case MVM_CTW_SPLICE:
194
0
                guilty_desc = "spliced";
195
0
                break;
196
0
            case MVM_CTW_BIND_KEY:
197
0
                guilty_desc = "bound to a hash key of";
198
0
                break;
199
0
            case MVM_CTW_DELETE_KEY:
200
0
                guilty_desc = "deleted a hash key of";
201
0
                break;
202
0
            case MVM_CTW_ASSIGN:
203
0
                guilty_desc = "assigned to";
204
0
                break;
205
0
            case MVM_CTW_REBLESS:
206
0
                guilty_desc = "reblessed";
207
0
                break;
208
0
        }
209
0
        uv_mutex_lock(&(tc->instance->mutex_cross_thread_write_logging));
210
0
        fprintf(stderr, "Thread %d %s an object (%s) allocated by thread %d\n",
211
0
            tc->thread_id, guilty_desc, MVM_6model_get_debug_name(tc, written), written->header.owner);
212
0
        MVM_dump_backtrace(tc);
213
0
        fprintf(stderr, "\n");
214
0
        uv_mutex_unlock(&(tc->instance->mutex_cross_thread_write_logging));
215
0
    }
216
0
}