Skip to content

Commit

Permalink
Introduce vmevent, "VM Event Subscription"
Browse files Browse the repository at this point in the history
Lets a ConcBlockingQueue be configured to receive
a set of events from inside the VM.

Currently there's only "gcevent", which gives you
an object in the queue every time the GC runs. It's
an int64 array containing GC sequence number, absolute
start time, start time since vm started, time taken,
minor or major collection, bytes promoted to the old
generation, and thread id of the thread that coordinated
(almost equivalent to the thread that caused GC to
run in the first pace).
  • Loading branch information
timo committed Aug 27, 2019
1 parent 127d783 commit 3faf198
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 15 deletions.
8 changes: 4 additions & 4 deletions lib/MAST/Ops.nqp
Expand Up @@ -2968,7 +2968,7 @@ BEGIN {
57,
58,
65,
66,
65,
65,
66,
65,
Expand Down Expand Up @@ -4276,7 +4276,7 @@ BEGIN {
'link', 547,
'gethostname', 548,
'exreturnafterunwind', 549,
'DEPRECATED_13', 550,
'vmeventsubscribe', 550,
'continuationreset', 551,
'continuationcontrol', 552,
'continuationinvoke', 553,
Expand Down Expand Up @@ -5099,7 +5099,7 @@ BEGIN {
'link',
'gethostname',
'exreturnafterunwind',
'DEPRECATED_13',
'vmeventsubscribe',
'continuationreset',
'continuationcontrol',
'continuationinvoke',
Expand Down Expand Up @@ -9453,7 +9453,7 @@ BEGIN {
nqp::writeuint($bytecode, $elems, 549, 5);
my uint $index0 := nqp::unbox_u($op0); nqp::writeuint($bytecode, nqp::add_i($elems, 2), $index0, 5);
},
'DEPRECATED_13', sub ($op0, $op1) {
'vmeventsubscribe', sub ($op0, $op1) {
my $bytecode := $*MAST_FRAME.bytecode;
my uint $elems := nqp::elems($bytecode);
nqp::writeuint($bytecode, $elems, 550, 5);
Expand Down
4 changes: 2 additions & 2 deletions src/6model/reprs/MVMHash.h
Expand Up @@ -27,7 +27,7 @@ const MVMREPROps * MVMHash_initialize(MVMThreadContext *tc);
HASH_ADD_KEYPTR_VM_STR(tc, hash_handle, hash, key, value); \
} \
else { \
MVM_exception_throw_adhoc(tc, "Hash keys must be concrete strings"); \
MVM_exception_throw_adhoc(tc, "Hash keys must be concrete strings (got %s)", MVM_6model_get_debug_name(tc, (MVMObject *)key)); \
} \
} while (0);

Expand All @@ -38,7 +38,7 @@ const MVMREPROps * MVMHash_initialize(MVMThreadContext *tc);
HASH_FIND_VM_STR(tc, hash_handle, hash, key, value); \
} \
else { \
MVM_exception_throw_adhoc(tc, "Hash keys must be concrete strings"); \
MVM_exception_throw_adhoc(tc, "Hash keys must be concrete strings (got %s)", MVM_6model_get_debug_name(tc, (MVMObject *)key)); \
} \
} while (0);

Expand Down
16 changes: 16 additions & 0 deletions src/core/instance.h
Expand Up @@ -115,6 +115,16 @@ struct MVMObjectId {
UT_hash_handle hash_handle;
};

struct MVMEventSubscriptions {
uv_mutex_t mutex_event_subscription;

MVMObject *subscription_queue;

MVMObject *GCEvent;

MVMuint64 vm_startup_time;
};

/* Represents a MoarVM instance. */
struct MVMInstance {
/************************************************************************
Expand Down Expand Up @@ -530,4 +540,10 @@ struct MVMInstance {
/* Hash Secrets which is used as the hash seed. This is to avoid denial of
* service type attacks. */
MVMuint64 hashSecrets[2];

/************************************************************************
* VM Event subscription
************************************************************************/

MVMEventSubscriptions subscriptions;
};
9 changes: 7 additions & 2 deletions src/core/interp.c
Expand Up @@ -3934,6 +3934,13 @@ void MVM_interp_run(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContex
cur_op += 2;
goto NEXT;
}
OP(vmeventsubscribe): {
MVMObject *queue = GET_REG(cur_op, 0).o;
MVMObject *config = GET_REG(cur_op, 2).o;
MVM_vm_event_subscription_configure(tc, queue, config);
cur_op += 4;
goto NEXT;
}
OP(continuationreset): {
MVMRegister *res = &GET_REG(cur_op, 0);
MVMObject *tag = GET_REG(cur_op, 2).o;
Expand Down Expand Up @@ -6517,8 +6524,6 @@ void MVM_interp_run(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContex
OP(DEPRECATED_11):
OP(DEPRECATED_12):
MVM_exception_throw_adhoc(tc, "The getregref_* ops were removed in MoarVM 2017.01.");
OP(DEPRECATED_13):
MVM_exception_throw_adhoc(tc, "The continuationclone op was removed in MoarVM 2017.01.");
OP(DEPRECATED_14):
MVM_exception_throw_adhoc(tc, "The asyncwritestr op was removed in MoarVM 2017.05.");
OP(DEPRECATED_15):
Expand Down
2 changes: 1 addition & 1 deletion src/core/oplabels.h
Expand Up @@ -551,7 +551,7 @@ static const void * const LABELS[] = {
&&OP_link,
&&OP_gethostname,
&&OP_exreturnafterunwind,
&&OP_DEPRECATED_13,
&&OP_vmeventsubscribe,
&&OP_continuationreset,
&&OP_continuationcontrol,
&&OP_continuationinvoke,
Expand Down
2 changes: 1 addition & 1 deletion src/core/oplist
Expand Up @@ -607,7 +607,7 @@ symlink r(str) r(str)
link r(str) r(str)
gethostname w(str)
exreturnafterunwind r(obj)
DEPRECATED_13 w(obj) r(obj)
vmeventsubscribe r(obj) r(obj)
continuationreset w(obj) r(obj) r(obj) :invokish :maycausedeopt
# this op isn't actually invokish, but it requires the cur_op to be set before doing its work
continuationcontrol w(obj) r(int64) r(obj) r(obj) :invokish :maycausedeopt
Expand Down
6 changes: 3 additions & 3 deletions src/core/ops.c
Expand Up @@ -7698,8 +7698,8 @@ static const MVMOpInfo MVM_op_infos[] = {
{ MVM_operand_read_reg | MVM_operand_obj }
},
{
MVM_OP_DEPRECATED_13,
"DEPRECATED_13",
MVM_OP_vmeventsubscribe,
"vmeventsubscribe",
2,
0,
0,
Expand All @@ -7709,7 +7709,7 @@ static const MVMOpInfo MVM_op_infos[] = {
0,
0,
0,
{ MVM_operand_write_reg | MVM_operand_obj, MVM_operand_read_reg | MVM_operand_obj }
{ MVM_operand_read_reg | MVM_operand_obj, MVM_operand_read_reg | MVM_operand_obj }
},
{
MVM_OP_continuationreset,
Expand Down
2 changes: 1 addition & 1 deletion src/core/ops.h
Expand Up @@ -551,7 +551,7 @@
#define MVM_OP_link 547
#define MVM_OP_gethostname 548
#define MVM_OP_exreturnafterunwind 549
#define MVM_OP_DEPRECATED_13 550
#define MVM_OP_vmeventsubscribe 550
#define MVM_OP_continuationreset 551
#define MVM_OP_continuationcontrol 552
#define MVM_OP_continuationinvoke 553
Expand Down
42 changes: 41 additions & 1 deletion src/gc/orchestrate.c
Expand Up @@ -392,8 +392,16 @@ static void run_gc(MVMThreadContext *tc, MVMuint8 what_to_do) {
MVMuint8 gen;
MVMuint32 i, n;

MVMuint8 is_coordinator;

MVMuint64 start_time, end_time;

unsigned int interval_id;

MVMObject *subscription_queue = NULL;

is_coordinator = what_to_do == MVMGCWhatToDo_All;

#if MVM_GC_DEBUG
if (tc->in_spesh)
MVM_panic(1, "Must not GC when in the specializer/JIT\n");
Expand All @@ -408,6 +416,9 @@ static void run_gc(MVMThreadContext *tc, MVMuint8 what_to_do) {
interval_id = MVM_telemetry_interval_start(tc, "start minor collection");
}

if (is_coordinator)
start_time = uv_hrtime();

/* Do GC work for ourselves and any work threads. */
for (i = 0, n = tc->gc_work_count ; i < n; i++) {
MVMThreadContext *other = tc->gc_work[i].tc;
Expand All @@ -419,7 +430,36 @@ static void run_gc(MVMThreadContext *tc, MVMuint8 what_to_do) {
}

/* Wait for everybody to agree we're done. */
finish_gc(tc, gen, what_to_do == MVMGCWhatToDo_All);
finish_gc(tc, gen, is_coordinator);

if (is_coordinator)
end_time = uv_hrtime();

/* Finally, as the very last thing ever, the coordinator pushes a bit of
* info into the subscription queue (if it is set) */

subscription_queue = tc->instance->subscriptions.subscription_queue;

if (is_coordinator && subscription_queue && tc->instance->subscriptions.GCEvent) {
MVMObject *instance = MVM_repr_alloc(tc, tc->instance->subscriptions.GCEvent);

MVMArray *arrobj = (MVMArray *)instance;
MVMuint64 *data;

MVM_repr_pos_set_elems(tc, instance, 7);

data = arrobj->body.slots.u64;

data[0] = MVM_load(&tc->instance->gc_seq_number);
data[1] = start_time;
data[2] = start_time - tc->instance->subscriptions.vm_startup_time;
data[3] = end_time - start_time;
data[4] = gen == MVMGCGenerations_Both;
data[5] = tc->gc_promoted_bytes;
data[6] = tc->thread_id;

MVM_repr_push_o(tc, tc->instance->subscriptions.subscription_queue, instance);
}

MVM_telemetry_interval_stop(tc, interval_id, "finished run_gc");
}
Expand Down
6 changes: 6 additions & 0 deletions src/gc/roots.c
Expand Up @@ -134,6 +134,12 @@ void MVM_gc_root_add_instance_roots_to_worklist(MVMThreadContext *tc, MVMGCWorkl
if (tc->instance->confprog)
MVM_confprog_mark(tc, worklist, snapshot);

add_collectable(tc, worklist, snapshot, tc->instance->subscriptions.subscription_queue,
"VM Event Subscription Queue");

add_collectable(tc, worklist, snapshot, tc->instance->subscriptions.GCEvent,
"VM Event GCEvent type");

MVM_debugserver_mark_handles(tc, worklist, snapshot);
}

Expand Down
45 changes: 45 additions & 0 deletions src/moar.c
Expand Up @@ -85,6 +85,8 @@ MVMInstance * MVM_vm_create_instance(void) {
/* Set up instance data structure. */
instance = MVM_calloc(1, sizeof(MVMInstance));

instance->subscriptions.vm_startup_time = uv_hrtime();

/* Create the main thread's ThreadContext and stash it. */
instance->main_thread = MVM_tc_create(NULL, instance);
/* Get the 128-bit hashSecret */
Expand Down Expand Up @@ -392,6 +394,8 @@ MVMInstance * MVM_vm_create_instance(void) {
/* Back to nursery allocation, now we're set up. */
MVM_gc_allocate_gen2_default_clear(instance->main_thread);

init_mutex(instance->subscriptions.mutex_event_subscription, "vm event subscription mutex");

return instance;
}

Expand Down Expand Up @@ -661,6 +665,8 @@ void MVM_vm_destroy_instance(MVMInstance *instance) {
/* Clean up fixed size allocator */
MVM_fixed_size_destroy(instance->fsa);

uv_mutex_destroy(&instance->subscriptions.mutex_event_subscription);

/* Clear up VM instance memory. */
MVM_free(instance);
}
Expand All @@ -678,6 +684,45 @@ void MVM_vm_set_prog_name(MVMInstance *instance, const char *prog_name) {
instance->prog_name = prog_name;
}

void MVM_vm_event_subscription_configure(MVMThreadContext *tc, MVMObject *queue, MVMObject *config) {
MVMString *gcevent;

MVMROOT2(tc, queue, config, {
if (!IS_CONCRETE(config)) {
MVM_exception_throw_adhoc(tc, "vmeventsubscribe requires a concrete configuration hash (got a %s type object)", MVM_6model_get_debug_name(tc, config));
}

if (REPR(queue)->ID != MVM_REPR_ID_ConcBlockingQueue && !MVM_is_null(tc, queue) || !IS_CONCRETE(queue)) {
MVM_exception_throw_adhoc(tc, "vmeventsubscribe requires a concrete ConcBlockingQueue (got a %s)", MVM_6model_get_debug_name(tc, queue));
}

uv_mutex_lock(&tc->instance->subscriptions.mutex_event_subscription);

if (REPR(queue)->ID == MVM_REPR_ID_ConcBlockingQueue && IS_CONCRETE(queue)) {
tc->instance->subscriptions.subscription_queue = queue;
}

gcevent = MVM_string_utf8_decode(tc, tc->instance->VMString, "gcevent", 7);

if (MVM_repr_exists_key(tc, config, gcevent)) {
MVMObject *value = MVM_repr_at_key_o(tc, config, gcevent);

if (MVM_is_null(tc, value)) {
tc->instance->subscriptions.GCEvent = NULL;
}
else if (REPR(value)->ID == MVM_REPR_ID_VMArray && !IS_CONCRETE(value) && ((MVMArrayREPRData *)STABLE(value)->REPR_data)->slot_type == MVM_ARRAY_I64) {
tc->instance->subscriptions.GCEvent = value;
}
else {
uv_mutex_unlock(&tc->instance->subscriptions.mutex_event_subscription);
MVM_exception_throw_adhoc(tc, "vmeventsubscribe expects value at 'gcevent' key to be null (to unsubscribe) or a VMArray of int64 type object, got a %s%s%s (%s)", IS_CONCRETE(value) ? "concrete " : "", MVM_6model_get_debug_name(tc, value), IS_CONCRETE(value) ? "" : " type object", REPR(value)->name);
}
}
});

uv_mutex_unlock(&tc->instance->subscriptions.mutex_event_subscription);
}

void MVM_vm_set_lib_path(MVMInstance *instance, int count, const char **lib_path) {
enum { MAX_COUNT = sizeof instance->lib_path / sizeof *instance->lib_path };

Expand Down
2 changes: 2 additions & 0 deletions src/moar.h
Expand Up @@ -231,6 +231,8 @@ MVM_PUBLIC void MVM_vm_set_exec_name(MVMInstance *instance, const char *exec_nam
MVM_PUBLIC void MVM_vm_set_prog_name(MVMInstance *instance, const char *prog_name);
MVM_PUBLIC void MVM_vm_set_lib_path(MVMInstance *instance, int count, const char **lib_path);

MVM_PUBLIC void MVM_vm_event_subscription_configure(MVMThreadContext *tc, MVMObject *queue, MVMObject *config);

/* Returns absolute executable path. */
MVM_PUBLIC int MVM_exepath(char* buffer, size_t* size);

Expand Down
1 change: 1 addition & 0 deletions src/types.h
Expand Up @@ -12,6 +12,7 @@ typedef struct MVMAsyncTaskOps MVMAsyncTaskOps;
typedef struct MVMAttributeIdentifier MVMAttributeIdentifier;
typedef struct MVMBoolificationSpec MVMBoolificationSpec;
typedef struct MVMBootTypes MVMBootTypes;
typedef struct MVMEventSubscriptions MVMEventSubscriptions;
typedef struct MVMBytecodeAnnotation MVMBytecodeAnnotation;
typedef struct MVMCallCapture MVMCallCapture;
typedef struct MVMCallCaptureBody MVMCallCaptureBody;
Expand Down

0 comments on commit 3faf198

Please sign in to comment.