Navigation Menu

Skip to content

Commit

Permalink
[profiler] Split method_leave callback into a method_tail_call callback.
Browse files Browse the repository at this point in the history
With this, profilers can distinguish between the two, and also know what the
target method of a tail call is.

The call instrumentation flags have been renamed accordingly and a new flag has
been added for instrumenting tail calls. I've also added a flag for exceptional
leave events, and method_exception_leave is only raised if this flag is given
for a method.

Also refactored the relevant JIT code a bit so it's easier to follow.
  • Loading branch information
Alex Rønne Petersen committed Aug 7, 2017
1 parent b40ae96 commit a5bc0ce
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 98 deletions.
1 change: 1 addition & 0 deletions mono/metadata/profiler-events.h
Expand Up @@ -56,6 +56,7 @@ MONO_PROFILER_EVENT_1(assembly_unloaded, AssemblyLUnloaded, MonoAssembly *, asse

MONO_PROFILER_EVENT_2(method_enter, MethodEnter, MonoMethod *, method, MonoProfilerCallContext *, context)
MONO_PROFILER_EVENT_2(method_leave, MethodLeave, MonoMethod *, method, MonoProfilerCallContext *, context)
MONO_PROFILER_EVENT_2(method_tail_call, MethodTailCall, MonoMethod *, method, MonoMethod *, target)
MONO_PROFILER_EVENT_2(method_exception_leave, MethodExceptionLeave, MonoMethod *, method, MonoObject *, exception)
MONO_PROFILER_EVENT_1(method_free, MethodFree, MonoMethod *, method)
MONO_PROFILER_EVENT_1(method_begin_invoke, MethodBeginInvoke, MonoMethod *, method)
Expand Down
20 changes: 12 additions & 8 deletions mono/metadata/profiler.h
Expand Up @@ -200,14 +200,18 @@ MONO_API mono_bool mono_profiler_enable_allocations (void);
typedef enum {
/* Do not instrument calls. */
MONO_PROFILER_CALL_INSTRUMENTATION_NONE = 0,
/* Instrument method prologues. */
MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE = 1 << 1,
/* Also capture a call context for prologues. */
MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE_CONTEXT = 1 << 2,
/* Instrument method epilogues. */
MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE = 1 << 3,
/* Also capture a call context for epilogues. */
MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE_CONTEXT = 1 << 4,
/* Instrument method entries. */
MONO_PROFILER_CALL_INSTRUMENTATION_ENTER = 1 << 1,
/* Also capture a call context for method entries. */
MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT = 1 << 2,
/* Instrument method exits. */
MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE = 1 << 3,
/* Also capture a call context for method exits. */
MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT = 1 << 4,
/* Instrument method exits as a result of a tail call. */
MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL = 1 << 5,
/* Instrument exceptional method exits. */
MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE = 1 << 6,
} MonoProfilerCallInstrumentationFlags;

typedef MonoProfilerCallInstrumentationFlags (*MonoProfilerCallInstrumentationFilterCallback) (MonoProfiler *prof, MonoMethod *method);
Expand Down
57 changes: 30 additions & 27 deletions mono/mini/interp/interp.c
Expand Up @@ -2436,6 +2436,10 @@ ves_exec_method_with_context (InterpFrame *frame, ThreadContext *context, unsign
}
MINT_IN_CASE(MINT_JMP) {
InterpMethod *new_method = rtm->data_items [* (guint16 *)(ip + 1)];

if (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL)
MONO_PROFILER_RAISE (method_tail_call, (frame->imethod->method, new_method->method));

if (!new_method->transformed) {
frame->ip = ip;
frame->ex = mono_interp_transform_method (new_method, context);
Expand Down Expand Up @@ -4769,7 +4773,7 @@ ves_exec_method_with_context (InterpFrame *frame, ThreadContext *context, unsign
if (MONO_PROFILER_ENABLED (method_enter)) {
MonoProfilerCallContext *prof_ctx = NULL;

if (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE_CONTEXT) {
if (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER_CONTEXT) {
prof_ctx = g_new0 (MonoProfilerCallContext, 1);
prof_ctx->interp_frame = frame;
prof_ctx->method = frame->imethod->method;
Expand Down Expand Up @@ -5140,35 +5144,34 @@ ves_exec_method_with_context (InterpFrame *frame, ThreadContext *context, unsign
}
exit_frame:

if (!frame->ex) {
if (MONO_PROFILER_ENABLED (method_leave) && frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE) {
MonoProfilerCallContext *prof_ctx = NULL;

if (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE_CONTEXT) {
prof_ctx = g_new0 (MonoProfilerCallContext, 1);
prof_ctx->interp_frame = frame;
prof_ctx->method = frame->imethod->method;

MonoType *rtype = mono_method_signature (frame->imethod->method)->ret;

switch (rtype->type) {
case MONO_TYPE_VOID:
break;
case MONO_TYPE_VALUETYPE:
prof_ctx->return_value = frame->retval->data.p;
break;
default:
prof_ctx->return_value = frame->retval;
break;
}
}
if (!frame->ex && MONO_PROFILER_ENABLED (method_leave) &&
frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE) {
MonoProfilerCallContext *prof_ctx = NULL;

MONO_PROFILER_RAISE (method_leave, (frame->imethod->method, prof_ctx));
if (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE_CONTEXT) {
prof_ctx = g_new0 (MonoProfilerCallContext, 1);
prof_ctx->interp_frame = frame;
prof_ctx->method = frame->imethod->method;

g_free (prof_ctx);
MonoType *rtype = mono_method_signature (frame->imethod->method)->ret;

switch (rtype->type) {
case MONO_TYPE_VOID:
break;
case MONO_TYPE_VALUETYPE:
prof_ctx->return_value = frame->retval->data.p;
break;
default:
prof_ctx->return_value = frame->retval;
break;
}
}
} else
MONO_PROFILER_RAISE (method_exception_leave, (frame->imethod->method, (MonoObject *) frame->ex));

MONO_PROFILER_RAISE (method_leave, (frame->imethod->method, prof_ctx));

g_free (prof_ctx);
} else if (frame->ex && frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE)
MONO_PROFILER_RAISE (method_exception_leave, (frame->imethod->method, &frame->ex->object));

DEBUG_LEAVE ();
}
Expand Down
3 changes: 2 additions & 1 deletion mono/mini/interp/transform.c
Expand Up @@ -1514,7 +1514,7 @@ generate (MonoMethod *method, InterpMethod *rtm, unsigned char *is_bb_start, Mon
}
}

if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE)
if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ENTER)
ADD_CODE (td, MINT_PROF_ENTER);

if (sym_seq_points) {
Expand Down Expand Up @@ -3753,6 +3753,7 @@ generate (MonoMethod *method, InterpMethod *rtm, unsigned char *is_bb_start, Mon
case CEE_TAIL_:
++td->ip;
/* FIX: should do something? */;
// TODO: This should raise a method_tail_call profiler event.
break;
case CEE_INITOBJ:
CHECK_STACK(td, 1);
Expand Down
18 changes: 9 additions & 9 deletions mono/mini/method-to-ir.c
Expand Up @@ -2217,7 +2217,7 @@ check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_

inline static MonoCallInst *
mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline)
MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline, MonoMethod *target)
{
MonoType *sig_ret;
MonoCallInst *call;
Expand All @@ -2229,7 +2229,7 @@ mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
tail = FALSE;

if (tail) {
mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE, NULL, NULL);
mini_profiler_emit_tail_call (cfg, target);

MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
} else
Expand Down Expand Up @@ -2362,7 +2362,7 @@ mini_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, Mo
MONO_ADD_INS (cfg->cbb, ins);
}

call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE, NULL);

call->inst.sreg1 = addr->dreg;

Expand Down Expand Up @@ -2456,7 +2456,7 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign

need_unbox_trampoline = method->klass == mono_defaults.object_class || mono_class_is_interface (method->klass);

call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline, method);

#ifndef DISABLE_REMOTING
if (might_be_remote)
Expand Down Expand Up @@ -2586,7 +2586,7 @@ mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature

g_assert (sig);

call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE, NULL);
call->fptr = func;

MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
Expand Down Expand Up @@ -8030,7 +8030,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (cfg->gshared && mono_method_check_context_used (cmethod))
GENERIC_SHARING_FAILURE (CEE_JMP);

mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE, NULL, NULL);
mini_profiler_emit_tail_call (cfg, cmethod);

fsig = mono_method_signature (cmethod);
n = fsig->param_count + fsig->hasthis;
Expand Down Expand Up @@ -8972,7 +8972,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
/* Handle tail calls similarly to normal calls */
tail_call = TRUE;
} else {
mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE, NULL, NULL);
mini_profiler_emit_tail_call (cfg, cmethod);

MONO_INST_NEW_CALL (cfg, call, OP_JMP);
call->tail_call = TRUE;
Expand Down Expand Up @@ -9083,7 +9083,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
break;
}
case CEE_RET:
mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE, sp - 1, sig->ret);
mini_profiler_emit_leave (cfg, sig->ret->type != MONO_TYPE_VOID ? sp [-1] : NULL);

if (cfg->method != method) {
/* return from inlined method */
Expand Down Expand Up @@ -12635,7 +12635,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
}

cfg->cbb = init_localsbb;
mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_enter, TRUE, NULL, NULL);
mini_profiler_emit_enter (cfg);

if (seq_points) {
MonoBasicBlock *bb;
Expand Down
9 changes: 6 additions & 3 deletions mono/mini/mini-exceptions.c
Expand Up @@ -2230,9 +2230,12 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
}
}

jit_tls->orig_ex_ctx_set = TRUE;
MONO_PROFILER_RAISE (method_exception_leave, (method, ex_obj));
jit_tls->orig_ex_ctx_set = FALSE;
if (MONO_PROFILER_ENABLED (method_exception_leave) &&
mono_profiler_get_call_instrumentation_flags (method) & MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE) {
jit_tls->orig_ex_ctx_set = TRUE;
MONO_PROFILER_RAISE (method_exception_leave, (method, ex_obj));
jit_tls->orig_ex_ctx_set = FALSE;
}

*ctx = new_ctx;
}
Expand Down
108 changes: 70 additions & 38 deletions mono/mini/mini-profiler.c
Expand Up @@ -15,62 +15,94 @@

#ifndef DISABLE_JIT

void
mini_profiler_emit_instrumentation_call (MonoCompile *cfg, void *func, gboolean entry, MonoInst **ret, MonoType *rtype)
static MonoInst *
emit_fill_call_ctx (MonoCompile *cfg, MonoInst *method, MonoInst *ret)
{
gboolean instrument, capture;
cfg->flags |= MONO_CFG_HAS_ALLOCA;

/*
* Do not instrument an inlined method - it becomes
* part of the current method.
*/
if (cfg->current_method != cfg->method)
return;
MonoInst *alloc, *size, *fill_ctx;

EMIT_NEW_ICONST (cfg, size, sizeof (MonoProfilerCallContext));
MONO_INST_NEW (cfg, alloc, OP_LOCALLOC);
alloc->dreg = alloc_preg (cfg);
alloc->sreg1 = size->dreg;
alloc->flags |= MONO_INST_INIT;
MONO_ADD_INS (cfg->cbb, alloc);
MONO_INST_NEW (cfg, fill_ctx, OP_FILL_PROF_CALL_CTX);
fill_ctx->sreg1 = alloc->dreg;
MONO_ADD_INS (cfg->cbb, fill_ctx);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, alloc->dreg, MONO_STRUCT_OFFSET (MonoProfilerCallContext, method), method->dreg);

if (ret) {
MonoInst *var = mono_compile_create_var (cfg, mono_method_signature (cfg->method)->ret, OP_LOCAL);

if (entry) {
instrument = cfg->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE;
capture = cfg->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE_CONTEXT;
} else {
instrument = cfg->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE;
capture = cfg->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE_CONTEXT;
MonoInst *store, *addr;

EMIT_NEW_TEMPSTORE (cfg, store, var->inst_c0, ret);
EMIT_NEW_VARLOADA (cfg, addr, var, NULL);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, alloc->dreg, MONO_STRUCT_OFFSET (MonoProfilerCallContext, return_value), addr->dreg);
}

if (!instrument)
return alloc;
}

void
mini_profiler_emit_enter (MonoCompile *cfg)
{
if (!MONO_CFG_PROFILE (cfg, ENTER) || cfg->current_method != cfg->method)
return;

MonoInst *iargs [2];

EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);

if (MONO_CFG_PROFILE (cfg, ENTER_CONTEXT) && !cfg->llvm_only)
iargs [1] = emit_fill_call_ctx (cfg, iargs [0], NULL);
else
EMIT_NEW_PCONST (cfg, iargs [1], NULL);

/* void mono_profiler_raise_method_enter (MonoMethod *method, MonoProfilerCallContext *ctx) */
mono_emit_jit_icall (cfg, mono_profiler_raise_method_enter, iargs);
}

void
mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret)
{
if (!MONO_CFG_PROFILE (cfg, LEAVE) || cfg->current_method != cfg->method)
return;

MonoInst *iargs [2];

EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);

if (capture && !cfg->llvm_only) {
cfg->flags |= MONO_CFG_HAS_ALLOCA;
if (MONO_CFG_PROFILE (cfg, LEAVE_CONTEXT) && !cfg->llvm_only)
iargs [1] = emit_fill_call_ctx (cfg, iargs [0], ret);
else
EMIT_NEW_PCONST (cfg, iargs [1], NULL);

MonoInst *size, *fill_ctx;
/* void mono_profiler_raise_method_leave (MonoMethod *method, MonoProfilerCallContext *ctx) */
mono_emit_jit_icall (cfg, mono_profiler_raise_method_leave, iargs);
}

EMIT_NEW_ICONST (cfg, size, sizeof (MonoProfilerCallContext));
MONO_INST_NEW (cfg, iargs [1], OP_LOCALLOC);
iargs [1]->dreg = alloc_preg (cfg);
iargs [1]->sreg1 = size->dreg;
iargs [1]->flags |= MONO_INST_INIT;
MONO_ADD_INS (cfg->cbb, iargs [1]);
MONO_INST_NEW (cfg, fill_ctx, OP_FILL_PROF_CALL_CTX);
fill_ctx->sreg1 = iargs [1]->dreg;
MONO_ADD_INS (cfg->cbb, fill_ctx);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, iargs [1]->dreg, MONO_STRUCT_OFFSET (MonoProfilerCallContext, method), iargs [0]->dreg);
void
mini_profiler_emit_tail_call (MonoCompile *cfg, MonoMethod *target)
{
if (!MONO_CFG_PROFILE (cfg, TAIL_CALL) || cfg->current_method != cfg->method)
return;

if (rtype && rtype->type != MONO_TYPE_VOID) {
MonoInst *var = mono_compile_create_var (cfg, rtype, OP_LOCAL);
g_assert (cfg->current_method == cfg->method);

MonoInst *store, *addr;
MonoInst *iargs [2];

EMIT_NEW_TEMPSTORE (cfg, store, var->inst_c0, *ret);
EMIT_NEW_VARLOADA (cfg, addr, var, NULL);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, iargs [1]->dreg, MONO_STRUCT_OFFSET (MonoProfilerCallContext, return_value), addr->dreg);
}
} else
EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);

if (target)
EMIT_NEW_METHODCONST (cfg, iargs [1], target);
else
EMIT_NEW_PCONST (cfg, iargs [1], NULL);

mono_emit_jit_icall (cfg, func, iargs);
/* void mono_profiler_raise_method_tail_call (MonoMethod *method, MonoMethod *target) */
mono_emit_jit_icall (cfg, mono_profiler_raise_method_tail_call, iargs);
}

#endif
Expand Down
1 change: 1 addition & 0 deletions mono/mini/mini-runtime.c
Expand Up @@ -4059,6 +4059,7 @@ register_icalls (void)
*/
register_icall (mono_profiler_raise_method_enter, "mono_profiler_raise_method_enter", "void ptr ptr", TRUE);
register_icall (mono_profiler_raise_method_leave, "mono_profiler_raise_method_leave", "void ptr ptr", TRUE);
register_icall (mono_profiler_raise_method_tail_call, "mono_profiler_raise_method_tail_call", "void ptr ptr", TRUE);

register_icall (mono_trace_enter_method, "mono_trace_enter_method", NULL, TRUE);
register_icall (mono_trace_leave_method, "mono_trace_leave_method", NULL, TRUE);
Expand Down
11 changes: 9 additions & 2 deletions mono/mini/mini.h
Expand Up @@ -1920,8 +1920,11 @@ typedef struct {
MonoProfilerCallInstrumentationFlags prof_flags;
} MonoCompile;

#define MONO_CFG_PROFILE(cfg, flag) \
G_UNLIKELY ((cfg)->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_ ## flag)

#define MONO_CFG_PROFILE_CALL_CONTEXT(cfg) \
((cfg)->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE_CONTEXT | MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE_CONTEXT))
(MONO_CFG_PROFILE (cfg, ENTER_CONTEXT) || MONO_CFG_PROFILE (cfg, LEAVE_CONTEXT))

typedef enum {
MONO_CFG_HAS_ALLOCA = 1 << 0,
Expand Down Expand Up @@ -2357,8 +2360,12 @@ MonoDomain* mini_init (const char *filename, const char *ru
void mini_cleanup (MonoDomain *domain);
MONO_API MonoDebugOptions *mini_get_debug_options (void);
MONO_API gboolean mini_parse_debug_option (const char *option);

/* profiler support */
void mini_add_profiler_argument (const char *desc);
void mini_profiler_emit_instrumentation_call (MonoCompile *cfg, void *func, gboolean entry, MonoInst **ret, MonoType *rtype);
void mini_profiler_emit_enter (MonoCompile *cfg);
void mini_profiler_emit_leave (MonoCompile *cfg, MonoInst *ret);
void mini_profiler_emit_tail_call (MonoCompile *cfg, MonoMethod *target);
void mini_profiler_context_enable (void);
gpointer mini_profiler_context_get_this (MonoProfilerCallContext *ctx);
gpointer mini_profiler_context_get_argument (MonoProfilerCallContext *ctx, guint32 pos);
Expand Down

0 comments on commit a5bc0ce

Please sign in to comment.