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));
+    }
+}