/* * Licensed to the .NET Foundation under one or more agreements. * The .NET Foundation licenses this file to you under the MIT license. * See the LICENSE file in the project root for more information. */ #include #include #include "interp/interp.h" #include "ir-emit.h" #include "mini.h" void mini_profiler_emit_instrumentation_call (MonoCompile *cfg, void *func, gboolean entry, MonoInst **ret, MonoType *rtype) { gboolean instrument, capture; 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; } if (!instrument) return; g_assert (cfg->current_method == cfg->method); MonoInst *iargs [2]; EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method); if (capture && !cfg->llvm_only) { cfg->flags |= MONO_CFG_HAS_ALLOCA; MonoInst *size, *fill_ctx; 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); if (rtype && rtype->type != MONO_TYPE_VOID) { MonoInst *var = mono_compile_create_var (cfg, rtype, OP_LOCAL); 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, iargs [1]->dreg, MONO_STRUCT_OFFSET (MonoProfilerCallContext, return_value), addr->dreg); } } else EMIT_NEW_PCONST (cfg, iargs [1], NULL); mono_emit_jit_icall (cfg, func, iargs); } void mini_profiler_context_enable (void) { if (!mono_debug_enabled ()) mono_debug_init (MONO_DEBUG_FORMAT_MONO); } static gpointer memdup_with_type (gpointer data, MonoType *t) { int dummy; return g_memdup (data, mono_type_size (t, &dummy)); } static guint8 * get_int_reg (MonoContext *ctx, guint32 reg) { return (guint8 *) mono_arch_context_get_int_reg (ctx, reg); } static gpointer get_variable_buffer (MonoDebugMethodJitInfo *jit, MonoDebugVarInfo *var, MonoContext *ctx) { guint32 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; guint32 reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; switch (flags) { case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: { /* * This is kind of a special case: All other address modes ultimately * produce an address to where the actual value is located, but this * address mode gets us the value itself as an mgreg_t value. */ mgreg_t value = (mgreg_t) get_int_reg (ctx, reg); return memdup_with_type (&value, var->type); } case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: return memdup_with_type (get_int_reg (ctx, reg) + (gint32) var->offset, var->type); case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR: case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR: return memdup_with_type (*(guint8 **) (get_int_reg (ctx, reg) + (gint32) var->offset), var->type); case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: { guint32 idx = reg; MonoDebugVarInfo *info_var = jit->gsharedvt_info_var; flags = info_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; reg = info_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; MonoGSharedVtMethodRuntimeInfo *info; switch (flags) { case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: info = (MonoGSharedVtMethodRuntimeInfo *) get_int_reg (ctx, reg); break; case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: info = *(MonoGSharedVtMethodRuntimeInfo **) (get_int_reg (ctx, reg) + (gint32) info_var->offset); break; default: g_assert_not_reached (); } MonoDebugVarInfo *locals_var = jit->gsharedvt_locals_var; flags = locals_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; reg = locals_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS; guint8 *locals; switch (flags) { case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: locals = get_int_reg (ctx, reg); break; case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET: locals = *(guint8 **) (get_int_reg (ctx, reg) + (gint32) info_var->offset); break; default: g_assert_not_reached (); } return memdup_with_type (locals + (gsize) info->entries [idx], var->type); } default: g_assert_not_reached (); return NULL; } } gpointer mini_profiler_context_get_this (MonoProfilerCallContext *ctx) { if (!mono_method_signature (ctx->method)->hasthis) return NULL; if (ctx->interp_frame) return memdup_with_type (mono_interp_frame_get_this (ctx->interp_frame), &ctx->method->klass->this_arg); MonoDebugMethodJitInfo *info = mono_debug_find_method (ctx->method, mono_domain_get ()); if (!info) return NULL; return get_variable_buffer (info, info->this_var, &ctx->context); } gpointer mini_profiler_context_get_argument (MonoProfilerCallContext *ctx, guint32 pos) { MonoMethodSignature *sig = mono_method_signature (ctx->method); if (pos >= sig->param_count) return NULL; if (ctx->interp_frame) return memdup_with_type (mono_interp_frame_get_arg (ctx->interp_frame, pos), sig->params [pos]); MonoDebugMethodJitInfo *info = mono_debug_find_method (ctx->method, mono_domain_get ()); if (!info) return NULL; return get_variable_buffer (info, &info->params [pos], &ctx->context); } gpointer mini_profiler_context_get_local (MonoProfilerCallContext *ctx, guint32 pos) { MonoError error; MonoMethodHeader *header = mono_method_get_header_checked (ctx->method, &error); mono_error_assert_ok (&error); // Must be a valid method at this point. if (pos >= header->num_locals) { mono_metadata_free_mh (header); return NULL; } MonoType *t = header->locals [pos]; mono_metadata_free_mh (header); if (ctx->interp_frame) return memdup_with_type (mono_interp_frame_get_local (ctx->interp_frame, pos), t); MonoDebugMethodJitInfo *info = mono_debug_find_method (ctx->method, mono_domain_get ()); if (!info) return NULL; return get_variable_buffer (info, &info->locals [pos], &ctx->context); } gpointer mini_profiler_context_get_result (MonoProfilerCallContext *ctx) { if (!ctx->return_value) return NULL; return memdup_with_type (ctx->return_value, mono_method_signature (ctx->method)->ret); } void mini_profiler_context_free_buffer (void *buffer) { g_free (buffer); }