Mercurial > hg > Members > anatofuz > MoarVM
diff src/instrument/crossthreadwrite.c @ 0:2cf249471370
convert mercurial for git
author | Takahiro SHIMIZU <anatofuz@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 08 May 2018 16:09:12 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/instrument/crossthreadwrite.c Tue May 08 16:09:12 2018 +0900 @@ -0,0 +1,210 @@ +#include "moar.h" + +/* Walk graph and insert write check instructions. */ +static void prepend_ctw_check(MVMThreadContext *tc, MVMSpeshGraph *g, MVMSpeshBB *bb, + MVMSpeshIns *before_ins, MVMSpeshOperand check_reg, + MVMint16 guilty) { + MVMSpeshIns *ctw_ins = MVM_spesh_alloc(tc, g, sizeof(MVMSpeshIns)); + ctw_ins->info = MVM_op_get_op(MVM_OP_ctw_check); + ctw_ins->operands = MVM_spesh_alloc(tc, g, 2 * sizeof(MVMSpeshOperand)); + ctw_ins->operands[0] = check_reg; + ctw_ins->operands[1].lit_i16 = guilty; + MVM_spesh_manipulate_insert_ins(tc, bb, before_ins->prev, ctw_ins); +} +static void instrument_graph(MVMThreadContext *tc, MVMSpeshGraph *g) { + MVMSpeshBB *bb = g->entry->linear_next; + while (bb) { + MVMSpeshIns *ins = bb->first_ins; + while (ins) { + switch (ins->info->opcode) { + case MVM_OP_rebless: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_REBLESS); + case MVM_OP_bindattr_i: + case MVM_OP_bindattr_n: + case MVM_OP_bindattr_s: + case MVM_OP_bindattr_o: + case MVM_OP_bindattrs_i: + case MVM_OP_bindattrs_n: + case MVM_OP_bindattrs_s: + case MVM_OP_bindattrs_o: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_BIND_ATTR); + break; + case MVM_OP_bindpos_i: + case MVM_OP_bindpos_n: + case MVM_OP_bindpos_s: + case MVM_OP_bindpos_o: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_BIND_POS); + break; + case MVM_OP_push_i: + case MVM_OP_push_n: + case MVM_OP_push_s: + case MVM_OP_push_o: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_PUSH); + break; + case MVM_OP_pop_i: + case MVM_OP_pop_n: + case MVM_OP_pop_s: + case MVM_OP_pop_o: + prepend_ctw_check(tc, g, bb, ins, ins->operands[1], MVM_CTW_POP); + break; + case MVM_OP_shift_i: + case MVM_OP_shift_n: + case MVM_OP_shift_s: + case MVM_OP_shift_o: + prepend_ctw_check(tc, g, bb, ins, ins->operands[1], MVM_CTW_SHIFT); + break; + case MVM_OP_unshift_i: + case MVM_OP_unshift_n: + case MVM_OP_unshift_s: + case MVM_OP_unshift_o: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_UNSHIFT); + break; + case MVM_OP_splice: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_SPLICE); + break; + case MVM_OP_bindkey_i: + case MVM_OP_bindkey_n: + case MVM_OP_bindkey_s: + case MVM_OP_bindkey_o: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_BIND_KEY); + break; + case MVM_OP_deletekey: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_DELETE_KEY); + break; + case MVM_OP_assign: + case MVM_OP_assignunchecked: + case MVM_OP_assign_i: + case MVM_OP_assign_n: + case MVM_OP_assign_s: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_ASSIGN); + break; + case MVM_OP_bindpos2d_i: + case MVM_OP_bindpos2d_n: + case MVM_OP_bindpos2d_s: + case MVM_OP_bindpos2d_o: + case MVM_OP_bindpos3d_i: + case MVM_OP_bindpos3d_n: + case MVM_OP_bindpos3d_s: + case MVM_OP_bindpos3d_o: + case MVM_OP_bindposnd_i: + case MVM_OP_bindposnd_n: + case MVM_OP_bindposnd_s: + case MVM_OP_bindposnd_o: + prepend_ctw_check(tc, g, bb, ins, ins->operands[0], MVM_CTW_BIND_POS); + break; + } + ins = ins->next; + } + bb = bb->linear_next; + } +} + +/* Adds instrumented version of the unspecialized bytecode. */ +static void add_instrumentation(MVMThreadContext *tc, MVMStaticFrame *sf) { + MVMSpeshCode *sc; + MVMStaticFrameInstrumentation *ins; + MVMSpeshGraph *sg = MVM_spesh_graph_create(tc, sf, 1, 0); + instrument_graph(tc, sg); + sc = MVM_spesh_codegen(tc, sg); + ins = MVM_calloc(1, sizeof(MVMStaticFrameInstrumentation)); + ins->instrumented_bytecode = sc->bytecode; + ins->instrumented_handlers = sc->handlers; + ins->instrumented_bytecode_size = sc->bytecode_size; + ins->uninstrumented_bytecode = sf->body.bytecode; + ins->uninstrumented_handlers = sf->body.handlers; + ins->uninstrumented_bytecode_size = sf->body.bytecode_size; + sf->body.instrumentation = ins; + MVM_spesh_graph_destroy(tc, sg); + MVM_free(sc); +} + +/* Instruments code with detection and reporting of cross-thread writes. */ +void MVM_cross_thread_write_instrument(MVMThreadContext *tc, MVMStaticFrame *sf) { + if (!sf->body.instrumentation || sf->body.bytecode != sf->body.instrumentation->instrumented_bytecode) { + /* Handle main, non-specialized, bytecode. */ + if (!sf->body.instrumentation) + add_instrumentation(tc, sf); + sf->body.bytecode = sf->body.instrumentation->instrumented_bytecode; + sf->body.handlers = sf->body.instrumentation->instrumented_handlers; + sf->body.bytecode_size = sf->body.instrumentation->instrumented_bytecode_size; + + /* Throw away any argument guard so we'll never resolve prior + * specializations again. */ + MVM_spesh_arg_guard_discard(tc, sf); + } +} + +/* Filter out some special cases to reduce noise. */ +static MVMint64 filtered_out(MVMThreadContext *tc, MVMObject *written) { + /* If we're holding locks, exclude by default (unless we were asked to + * also include these). */ + if (tc->num_locks && !tc->instance->cross_thread_write_logging_include_locked) + return 1; + + /* Operations on a concurrent queue are fine 'cus it's concurrent. */ + if (REPR(written)->ID == MVM_REPR_ID_ConcBlockingQueue) + return 1; + + /* Write on object from event loop thread is usually shift of invokable. */ + if (tc->instance->event_loop_thread) + if (written->header.owner == tc->instance->event_loop_thread->thread_id) + return 1; + + /* Filter out writes to Sub and Method, since these are almost always just + * multi-dispatch caches. */ + if (strncmp( MVM_6model_get_stable_debug_name(tc, written->st), "Method", 6) == 0) + return 1; + if (strncmp( MVM_6model_get_stable_debug_name(tc, written->st), "Sub", 3) == 0) + return 1; + + /* Otherwise, may be relevant. */ + return 0; +} + +/* Squeal if the target of the write wasn't allocated by us. */ +void MVM_cross_thread_write_check(MVMThreadContext *tc, MVMObject *written, MVMint16 guilty) { + if (written->header.owner != tc->thread_id && !filtered_out(tc, written)) { + char *guilty_desc = "did something to"; + switch (guilty) { + case MVM_CTW_BIND_ATTR: + guilty_desc = "bound to an attribute of"; + break; + case MVM_CTW_BIND_POS: + guilty_desc = "bound to an array slot of"; + break; + case MVM_CTW_PUSH: + guilty_desc = "pushed to"; + break; + case MVM_CTW_POP: + guilty_desc = "popped"; + break; + case MVM_CTW_SHIFT: + guilty_desc = "shifted"; + break; + case MVM_CTW_UNSHIFT: + guilty_desc = "unshifted to"; + break; + case MVM_CTW_SPLICE: + guilty_desc = "spliced"; + break; + case MVM_CTW_BIND_KEY: + guilty_desc = "bound to a hash key of"; + break; + case MVM_CTW_DELETE_KEY: + guilty_desc = "deleted a hash key of"; + break; + case MVM_CTW_ASSIGN: + guilty_desc = "assigned to"; + break; + case MVM_CTW_REBLESS: + guilty_desc = "reblessed"; + break; + } + uv_mutex_lock(&(tc->instance->mutex_cross_thread_write_logging)); + fprintf(stderr, "Thread %d %s an object (%s) allocated by thread %d\n", + tc->thread_id, guilty_desc, MVM_6model_get_debug_name(tc, written), written->header.owner); + MVM_dump_backtrace(tc); + fprintf(stderr, "\n"); + uv_mutex_unlock(&(tc->instance->mutex_cross_thread_write_logging)); + } +}