[profiler] Implement call context introspection for enter/leave events.
authorAlex Rønne Petersen <alexrp@xamarin.com>
Tue, 25 Jul 2017 22:55:22 +0000 (00:55 +0200)
committerAlex Rønne Petersen <alexrp@xamarin.com>
Tue, 1 Aug 2017 04:15:06 +0000 (06:15 +0200)
When this feature is enabled for a method, the enter/leave event receives an
additional argument, a so-called 'call context'. This call context contains
enough information about the stack frame of the instrumented method to allow
the enter/leave callback to inspect the 'this' reference, method arguments,
local variables, and the return value (for non-void methods).

This feature enables some interesting scenarios that were not possible with
the regular enter/leave events. For example, a profiler could instrument
well-known methods in the managed thread pool code to get an idea of how an
application is using the thread pool, or it could instrument network-related
methods to gather statistics or even log all network traffic.

This is implemented by storing a MonoProfilerCallContext on the stack, whose
MonoContext field is populated by executing an OP_FILL_PROF_CALL_CTX opcode
which stores the stack pointer, frame pointer, and all callee-saved registers
to it. For the epilogue, a pointer to the return value (for non-void methods)
is also stored in the MonoProfilerCallContext. An address to this context is
then passed to mono_profiler_raise_method_enter/leave. Based on debug info,
all arguments and locals can then be looked up in the instrumented method's
stack frame.

For the interpreter, we just store an InterpFrame pointer on the
MonoProfilerCallContext and look everything up from that. We don't need debug
info in this case.

This feature is currently not supported with LLVM (for regular LLVM mode, it
will fall back to Mono's JIT, while for LLVM-only mode, it's not available).

I also refactored the interpreter code so that enter/leave events are generated
not only when interpreter debugging is enabled. Also, the interpreter will only
call mono_profiler_get_call_instrumentation_flags () once per method now.
Finally, I made the interpreter also generate exception leave events.

30 files changed:
CODEOWNERS
mono/metadata/profiler-events.h
mono/metadata/profiler-private.h
mono/metadata/profiler.c
mono/metadata/profiler.h
mono/mini/Makefile.am.in
mono/mini/cpu-amd64.md
mono/mini/cpu-arm.md
mono/mini/cpu-arm64.md
mono/mini/cpu-x86.md
mono/mini/debug-mini.c
mono/mini/interp/interp-internals.h
mono/mini/interp/interp.c
mono/mini/interp/mintops.def
mono/mini/interp/transform.c
mono/mini/method-to-ir.c
mono/mini/mini-amd64.c
mono/mini/mini-arm.c
mono/mini/mini-arm64.c
mono/mini/mini-ops.h
mono/mini/mini-profiler.c [new file with mode: 0644]
mono/mini/mini-runtime.c
mono/mini/mini-x86.c
mono/mini/mini.c
mono/mini/mini.h
mono/profiler/log.c
msvc/libmono-static.vcxproj
msvc/libmono-static.vcxproj.filters
msvc/mono.def
msvc/monosgen.def

index d835c00f0b24bb3565f087fed231fb43c1b5f2d9..4a0ef8ae08148a62b958c1d4174838ad14bf1b84 100644 (file)
@@ -33,6 +33,7 @@ mono/metadata/profiler* @alexrp
 mono/metadata/threads* @luhenry @kumpera
 mono/metadata/threadpool* @luhenry
 mono/metadata/w32* @luhenry
+mono/mini/profiler* @alexrp
 mono/profiler @alexrp
 mono/utils/atomic* @alexrp
 mono/utils/mono-hwcap* @alexrp
index 7b85f00fb0c1a5512fd4a29f4dfa8708bf6def33..1a7e0b25ef9e3cb2178565ca4e84b5cb4bf36318 100644 (file)
@@ -54,8 +54,8 @@ MONO_PROFILER_EVENT_1(assembly_loaded, AssemblyLLoaded, MonoAssembly *, assembly
 MONO_PROFILER_EVENT_1(assembly_unloading, AssemblyLUnloading, MonoAssembly *, assembly)
 MONO_PROFILER_EVENT_1(assembly_unloaded, AssemblyLUnloaded, MonoAssembly *, assembly)
 
-MONO_PROFILER_EVENT_1(method_enter, MethodEnter, MonoMethod *, method)
-MONO_PROFILER_EVENT_1(method_leave, MethodLeave, MonoMethod *, method)
+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_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)
index e087be07eb604fbfa0dc320637dfec8bd333998c..38e353b5b35b30f08789a6804df6fff176775f5e 100644 (file)
@@ -9,6 +9,7 @@
 
 #define MONO_PROFILER_UNSTABLE_GC_ROOTS
 #include <mono/metadata/profiler.h>
+#include <mono/utils/mono-context.h>
 #include <mono/utils/mono-lazy-init.h>
 #include <mono/utils/mono-os-mutex.h>
 #include <mono/utils/mono-os-semaphore.h>
@@ -42,16 +43,28 @@ struct _MonoProfilerDesc {
 
 typedef struct {
        gboolean startup_done;
+
        MonoProfilerHandle profilers;
+
        mono_lazy_init_t coverage_status;
        mono_mutex_t coverage_mutex;
        GHashTable *coverage_hash;
+
        MonoProfilerHandle sampling_owner;
        MonoSemType sampling_semaphore;
        MonoProfilerSampleMode sample_mode;
        guint32 sample_freq;
+
        gboolean allocations;
 
+       gboolean call_contexts;
+       void (*context_enable) (void);
+       gpointer (*context_get_this) (MonoProfilerCallContext *);
+       gpointer (*context_get_argument) (MonoProfilerCallContext *, guint32);
+       gpointer (*context_get_local) (MonoProfilerCallContext *, guint32);
+       gpointer (*context_get_result) (MonoProfilerCallContext *);
+       gpointer (*context_free_buffer) (gpointer);
+
 #define _MONO_PROFILER_EVENT(name) \
        volatile gint32 name ## _count;
 #define MONO_PROFILER_EVENT_0(name, type) \
@@ -95,7 +108,25 @@ mono_profiler_installed (void)
 MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries);
 void mono_profiler_coverage_free (MonoMethod *method);
 
-gboolean mono_profiler_should_instrument_method (MonoMethod *method, gboolean entry);
+struct _MonoProfilerCallContext {
+       /*
+        * Must be the first field (the JIT relies on it). Only filled out if this
+        * is a JIT frame; otherwise, zeroed.
+        */
+       MonoContext context;
+       /*
+        * A non-NULL MonoInterpFrameHandle if this is an interpreter frame.
+        */
+       gpointer interp_frame;
+       MonoMethod *method;
+       /*
+        * Points to the return value for an epilogue context. For a prologue, this
+        * is set to NULL.
+        */
+       gpointer return_value;
+};
+
+MonoProfilerCallInstrumentationFlags mono_profiler_get_call_instrumentation_flags (MonoMethod *method);
 
 gboolean mono_profiler_sampling_enabled (void);
 void mono_profiler_sampling_thread_post (void);
index ccdb022a14fda51e6538da06f85329867d83e6eb..36b864a60e1284849cb371d681efda1aa37b4421 100644 (file)
@@ -361,9 +361,7 @@ mono_profiler_enable_allocations (void)
        if (mono_profiler_state.startup_done)
                return FALSE;
 
-       mono_profiler_state.allocations = TRUE;
-
-       return TRUE;
+       return mono_profiler_state.allocations = TRUE;
 }
 
 void
@@ -372,8 +370,61 @@ mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handl
        InterlockedWritePointer (&handle->call_instrumentation_filter, (gpointer) cb);
 }
 
-gboolean
-mono_profiler_should_instrument_method (MonoMethod *method, gboolean entry)
+mono_bool
+mono_profiler_enable_call_context_introspection (void)
+{
+       if (mono_profiler_state.startup_done)
+               return FALSE;
+
+       mono_profiler_state.context_enable ();
+
+       return mono_profiler_state.call_contexts = TRUE;
+}
+
+void *
+mono_profiler_call_context_get_this (MonoProfilerCallContext *context)
+{
+       if (!mono_profiler_state.call_contexts)
+               return NULL;
+
+       return mono_profiler_state.context_get_this (context);
+}
+
+void *
+mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position)
+{
+       if (!mono_profiler_state.call_contexts)
+               return NULL;
+
+       return mono_profiler_state.context_get_argument (context, position);
+}
+
+void *
+mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position)
+{
+       if (!mono_profiler_state.call_contexts)
+               return NULL;
+
+       return mono_profiler_state.context_get_local (context, position);
+}
+
+void *
+mono_profiler_call_context_get_result (MonoProfilerCallContext *context)
+{
+       if (!mono_profiler_state.call_contexts)
+               return NULL;
+
+       return mono_profiler_state.context_get_result (context);
+}
+
+void
+mono_profiler_call_context_free_buffer (void *buffer)
+{
+       mono_profiler_state.context_free_buffer (buffer);
+}
+
+MonoProfilerCallInstrumentationFlags
+mono_profiler_get_call_instrumentation_flags (MonoMethod *method)
 {
        MonoProfilerCallInstrumentationFlags flags = MONO_PROFILER_CALL_INSTRUMENTATION_NONE;
 
@@ -384,10 +435,7 @@ mono_profiler_should_instrument_method (MonoMethod *method, gboolean entry)
                        flags |= cb (handle->prof, method);
        }
 
-       if (entry)
-               return flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE;
-       else
-               return flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE;
+       return flags;
 }
 
 void
index 0bbc0fe00612d7200d00f1516d5eb847827ce883..08929a5d23142639a349b3d5c8129a6920cf226b 100644 (file)
@@ -67,7 +67,7 @@ typedef mono_bool (*MonoProfilerCoverageFilterCallback) (MonoProfiler *prof, Mon
  * Sets a code coverage filter function. The profiler API will invoke filter
  * functions from all installed profilers. If any of them return TRUE, then the
  * given method will be instrumented for coverage analysis. All filters are
- * guaranteed to be called exactly once per method, even if an earlier filter
+ * guaranteed to be called at least once per method, even if an earlier filter
  * has already returned TRUE.
  *
  * Note that filter functions must be installed before a method is compiled in
@@ -170,8 +170,12 @@ typedef enum {
        MONO_PROFILER_CALL_INSTRUMENTATION_NONE = 1 << 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 << 2,
+       MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE = 1 << 3,
+       /* Also capture a call context for epilogues. */
+       MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE_CONTEXT = 1 << 4,
 } MonoProfilerCallInstrumentationFlags;
 
 typedef MonoProfilerCallInstrumentationFlags (*MonoProfilerCallInstrumentationFilterCallback) (MonoProfiler *prof, MonoMethod *method);
@@ -181,21 +185,119 @@ typedef MonoProfilerCallInstrumentationFlags (*MonoProfilerCallInstrumentationFi
  * filter functions from all installed profilers. If any of them return flags
  * other than MONO_PROFILER_CALL_INSTRUMENTATION_NONE, then the given method
  * will be instrumented as requested. All filters are guaranteed to be called
- * at least once (possibly more) per method entry and exit, even if earlier
- * filters have already specified all flags.
+ * exactly once per method, even if earlier filters have already specified all
+ * flags.
  *
  * Note that filter functions must be installed before a method is compiled in
  * order to have any effect, i.e. you should register your filter function in
- * your profiler's init function.
+ * your profiler's init function. Also, if you want to instrument a method
+ * that's going to be AOT-compiled, you must attach your profiler and install a
+ * call instrumentation filter function at AOT time. This can be done in
+ * exactly the same way as you would normally, i.e. by passing the --profile
+ * option on the command line, by calling mono_profiler_load, or simply by
+ * using the profiler API as an embedder.
  *
- * Keep in mind that method instrumentation is extremely heavy and will slow
- * down most applications to a crawl. Consider using sampling instead if it
- * would work for your use case.
+ * Keep in mind that indiscriminate method instrumentation is extremely heavy
+ * and will slow down most applications to a crawl. Consider using sampling
+ * instead if it would work for your use case.
  *
  * This function is async safe.
  */
 MONO_API void mono_profiler_set_call_instrumentation_filter_callback (MonoProfilerHandle handle, MonoProfilerCallInstrumentationFilterCallback cb);
 
+/*
+ * Enables support for retrieving stack frame data from a call context. At the
+ * moment, this means enabling the debug info subsystem. If you do not call
+ * this function, you will not be able to use the call context introspection
+ * functions (they will simply return NULL). Returns TRUE if call context
+ * introspection was enabled, or FALSE if the function was called too late for
+ * this to be possible.
+ *
+ * Please note: Mono's LLVM backend does not support this feature. This means
+ * that methods with call context instrumentation will be handled by Mono's
+ * JIT even in LLVM mode. There is also a special case when Mono is compiling
+ * in LLVM-only mode: Since LLVM does not provide a way to implement call
+ * contexts, a NULL context will always be passed to enter/leave events even
+ * though this method returns TRUE.
+ *
+ * This function may only be called from your profiler's init function.
+ *
+ * This function is not async safe.
+ */
+MONO_API mono_bool mono_profiler_enable_call_context_introspection (void);
+
+typedef struct _MonoProfilerCallContext MonoProfilerCallContext;
+
+/*
+ * Given a valid call context from an enter/leave event, retrieves a pointer to
+ * the this reference for the method. Returns NULL if none exists (i.e. it's a
+ * static method) or if call context introspection was not enabled.
+ *
+ * The buffer returned by this function must be freed with
+ * mono_profiler_call_context_free_buffer.
+ *
+ * Please note that a call context is only valid for the duration of the
+ * enter/leave callback it was passed to.
+ *
+ * This function is not async safe.
+ */
+MONO_API void *mono_profiler_call_context_get_this (MonoProfilerCallContext *context);
+
+/*
+ * Given a valid call context from an enter/leave event, retrieves a pointer to
+ * the method argument at the given position. Returns NULL if position is out
+ * of bounds or if call context introspection was not enabled.
+ *
+ * The buffer returned by this function must be freed with
+ * mono_profiler_call_context_free_buffer.
+ *
+ * Please note that a call context is only valid for the duration of the
+ * enter/leave callback it was passed to.
+ *
+ * This function is not async safe.
+ */
+MONO_API void *mono_profiler_call_context_get_argument (MonoProfilerCallContext *context, uint32_t position);
+
+/*
+ * Given a valid call context from an enter/leave event, retrieves a pointer to
+ * the local variable at the given position. Returns NULL if position is out of
+ * bounds or if call context introspection was not enabled.
+ *
+ * The buffer returned by this function must be freed with
+ * mono_profiler_call_context_free_buffer.
+ *
+ * Please note that a call context is only valid for the duration of the
+ * enter/leave callback it was passed to.
+ *
+ * This function is not async safe.
+ */
+MONO_API void *mono_profiler_call_context_get_local (MonoProfilerCallContext *context, uint32_t position);
+
+/*
+ * Given a valid call context from an enter/leave event, retrieves a pointer to
+ * return value of a method. Returns NULL if the method has no return value
+ * (i.e. it returns void), if the leave event was the result of a tail call, if
+ * the function is called on a context from an enter event, or if call context
+ * introspection was not enabled.
+ *
+ * The buffer returned by this function must be freed with
+ * mono_profiler_call_context_free_buffer.
+ *
+ * Please note that a call context is only valid for the duration of the
+ * enter/leave callback it was passed to.
+ *
+ * This function is not async safe.
+ */
+MONO_API void *mono_profiler_call_context_get_result (MonoProfilerCallContext *context);
+
+/*
+ * Frees a buffer returned by one of the call context introspection functions.
+ * Passing a NULL buffer is allowed, which makes this function a no-op.
+ *
+ * This function is not async safe.
+ */
+MONO_API void mono_profiler_call_context_free_buffer (void *buffer);
+
 #ifdef MONO_PROFILER_UNSTABLE_GC_ROOTS
 typedef enum {
        /* Upper 2 bytes. */
index 5e68273c645802bda1d003397f4fc15f0e7f8e28..848f75d3fe1544ed2dedbd459355d7c30a563280 100755 (executable)
@@ -488,7 +488,8 @@ common_sources = \
        type-checking.c \
        lldb.h                  \
        lldb.c  \
-       memory-access.c
+       memory-access.c \
+       mini-profiler.c
 
 test_sources =                         \
        basic-calls.cs          \
index f1dfac557bc157bf11c15acfe64b25f2242c580b..d35a867778fe49e99c74ab07296a3d58d6bc935b 100755 (executable)
@@ -792,3 +792,5 @@ gc_param_slot_liveness_def: len:0
 
 generic_class_init: src1:A len:32 clob:c
 get_last_error: dest:i len:32
+
+fill_prof_call_ctx: src1:i len:128
index 25e5f931118c65462120eb35e4050e4535ad5f88..e6a91fc176d13fc9474efdeb53841cdfb7097ec4 100644 (file)
@@ -404,3 +404,5 @@ atomic_store_r4: dest:b src1:f len:80
 atomic_store_r8: dest:b src1:f len:32
 
 generic_class_init: src1:a len:44 clob:c
+
+fill_prof_call_ctx: src1:i len:128
index c0651233eb50b02114232ec6ebf5e84ce6bcfea0..230a975dbe348bc43711c24276613744530c5e0f 100644 (file)
@@ -480,3 +480,5 @@ atomic_store_r8: dest:b src1:f len:24
 
 generic_class_init: src1:a len:44 clob:c
 gc_safe_point: src1:i len:12 clob:c
+
+fill_prof_call_ctx: src1:i len:128
index 280eda7c0ac58340e2b49a6bc15e289592bbf1b3..870b10b2397506dfdbfb087005e84c8e459018e7 100644 (file)
@@ -651,3 +651,5 @@ gc_spill_slot_liveness_def: len:0
 gc_param_slot_liveness_def: len:0
 get_sp: dest:i len:6
 set_sp: src1:i len:6
+
+fill_prof_call_ctx: src1:i len:128
index 8c1a71e147fe75ddbeaae4af024179139d90d1fd..30874eb3cf3572ab48f24d98b7a058e13ce5b8a0 100644 (file)
@@ -241,7 +241,7 @@ mono_debug_close_method (MonoCompile *cfg)
        jit->code_start = cfg->native_code;
        jit->epilogue_begin = cfg->epilog_begin;
        jit->code_size = cfg->code_len;
-       jit->has_var_info = debug_options.mdb_optimizations != 0;
+       jit->has_var_info = debug_options.mdb_optimizations || MONO_CFG_PROFILE_CALL_CONTEXT (cfg);
 
        if (jit->epilogue_begin)
                   record_line_number (info, jit->epilogue_begin, header->code_size);
index e566f535a6eeada977e7abffc27839e953a10125..894747e23d0880ad15f6705c40a2dde3d49d6a84 100644 (file)
@@ -96,6 +96,7 @@ typedef struct _InterpMethod
        MonoType **param_types;
        MonoJitInfo *jinfo;
        MonoDomain *domain;
+       MonoProfilerCallInstrumentationFlags prof_flags;
 } InterpMethod;
 
 struct _InterpFrame {
index e5d50d5aa5e98a0f17b0234f1c03fe5174f08c64..fdbbe262ba4f2bb6d3e0fb1b6617b0a84511abdf 100644 (file)
@@ -173,8 +173,6 @@ debug_enter (InterpFrame *frame, int *tracing)
                g_print  ("%s)\n", args);
                g_free (args);
        }
-       if (mono_profiler_should_instrument_method (frame->imethod->method, TRUE))
-               MONO_PROFILER_RAISE (method_enter, (frame->imethod->method));
 }
 
 
@@ -190,9 +188,7 @@ debug_enter (InterpFrame *frame, int *tracing)
                g_free (args);  \
                debug_indent_level--;   \
                if (tracing == 3) global_tracing = 0; \
-       }       \
-       if (mono_profiler_should_instrument_method (frame->imethod->method, FALSE)) \
-               MONO_PROFILER_RAISE (method_leave, (frame->imethod->method));
+       }
 
 #else
 
@@ -297,6 +293,8 @@ mono_interp_get_imethod (MonoDomain *domain, MonoMethod *method, MonoError *erro
                mono_internal_hash_table_insert (&info->interp_code_hash, method, rtm);
        mono_domain_jit_code_hash_unlock (domain);
 
+       rtm->prof_flags = mono_profiler_get_call_instrumentation_flags (rtm->method);
+
        return rtm;
 }
 
@@ -603,6 +601,7 @@ fill_in_trace (MonoException *exception, InterpFrame *frame)
                if (!rethrow) { \
                        FILL_IN_TRACE(frame->ex, frame);        \
                } \
+               MONO_PROFILER_RAISE (exception_throw, ((MonoObject *) exception));      \
                goto handle_exception;  \
        } while (0)
 
@@ -4764,6 +4763,26 @@ array_constructed:
                        MINT_IN_BREAK;
                }
 
+               MINT_IN_CASE(MINT_PROF_ENTER) {
+                       ip += 1;
+
+                       if (MONO_PROFILER_ENABLED (method_enter)) {
+                               MonoProfilerCallContext *prof_ctx = NULL;
+
+                               if (frame->imethod->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE_CONTEXT) {
+                                       prof_ctx = g_new0 (MonoProfilerCallContext, 1);
+                                       prof_ctx->interp_frame = frame;
+                                       prof_ctx->method = frame->imethod->method;
+                               }
+
+                               MONO_PROFILER_RAISE (method_enter, (frame->imethod->method, prof_ctx));
+
+                               g_free (prof_ctx);
+                       }
+
+                       MINT_IN_BREAK;
+               }
+
                MINT_IN_CASE(MINT_LDARGA)
                        sp->data.p = frame->args + * (guint16 *)(ip + 1);
                        ip += 2;
@@ -5120,6 +5139,37 @@ die_on_ex:
                goto exit_frame;
        }
 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;
+                               }
+                       }
+
+                       MONO_PROFILER_RAISE (method_leave, (frame->imethod->method, prof_ctx));
+
+                       g_free (prof_ctx);
+               }
+       } else
+               MONO_PROFILER_RAISE (method_exception_leave, (frame->imethod->method, (MonoObject *) frame->ex));
+
        DEBUG_LEAVE ();
 }
 
index c270a2b18e47796337c5ce59b4807b4de8c31294..869c5f2df74e1102dce76f33694854b28f18719f 100644 (file)
@@ -526,3 +526,9 @@ OPDEF(MINT_JIT_CALL, "mono_jit_call", 2, MintOpNoArgs)
 OPDEF(MINT_SDB_INTR_LOC, "sdb_intr_loc", 1, MintOpNoArgs)
 OPDEF(MINT_SDB_SEQ_POINT, "sdb_seq_point", 1, MintOpNoArgs)
 OPDEF(MINT_SDB_BREAKPOINT, "sdb_breakpoint", 1, MintOpNoArgs)
+
+/*
+ * This needs to be an opcode because we need to trigger the enter event after
+ * the STINARG* opcodes have executed.
+ */
+OPDEF(MINT_PROF_ENTER, "prof_enter", 1, MintOpNoArgs)
index 1d98608c61ca84c4c12910b02bd66d932228f271..790f8b191d2ea048eeeaf1424f6d45477ea57212 100644 (file)
@@ -1514,6 +1514,9 @@ generate (MonoMethod *method, InterpMethod *rtm, unsigned char *is_bb_start, Mon
                }
        }
 
+       if (rtm->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE)
+               ADD_CODE (td, MINT_PROF_ENTER);
+
        if (sym_seq_points) {
                InterpBasicBlock *cbb = td->offset_to_bb [0];
                g_assert (cbb);
index a74291148e889e440358f663f257094636ad6807..84c019460517374d6282b9245232d5c246680b22 100644 (file)
@@ -1764,24 +1764,6 @@ emit_pop_lmf (MonoCompile *cfg)
        EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
 }
 
-static void
-emit_instrumentation_call (MonoCompile *cfg, void *func, gboolean entry)
-{
-       MonoInst *iargs [1];
-
-       /*
-        * Avoid instrumenting inlined methods since it can
-        * distort profiling results.
-        */
-       if (cfg->method != cfg->current_method)
-               return;
-
-       if (mono_profiler_should_instrument_method (cfg->method, entry)) {
-               EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
-               mono_emit_jit_icall (cfg, func, iargs);
-       }
-}
-
 static int
 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt)
 {
@@ -2247,7 +2229,7 @@ mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
                tail = FALSE;
 
        if (tail) {
-               emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE);
+               mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE, NULL, NULL);
 
                MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
        } else
@@ -4362,6 +4344,9 @@ mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
        if (g_list_find (cfg->dont_inline, method))
                return FALSE;
 
+       if (mono_profiler_get_call_instrumentation_flags (method))
+               return FALSE;
+
        return TRUE;
 }
 
@@ -8045,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);
 
-                       emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE);
+                       mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE, NULL, NULL);
 
                        fsig = mono_method_signature (cmethod);
                        n = fsig->param_count + fsig->hasthis;
@@ -8987,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 {
-                                       emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE);
+                                       mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE, NULL, NULL);
 
                                        MONO_INST_NEW_CALL (cfg, call, OP_JMP);
                                        call->tail_call = TRUE;
@@ -9098,6 +9083,8 @@ 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);
+
                        if (cfg->method != method) {
                                /* return from inlined method */
                                /* 
@@ -9121,8 +9108,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        cfg->ret_var_set = TRUE;
                                } 
                        } else {
-                               emit_instrumentation_call (cfg, mono_profiler_raise_method_leave, FALSE);
-
                                if (cfg->lmf_var && cfg->cbb->in_count && !cfg->llvm_only)
                                        emit_pop_lmf (cfg);
 
@@ -12650,7 +12635,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        }
 
        cfg->cbb = init_localsbb;
-       emit_instrumentation_call (cfg, mono_profiler_raise_method_enter, TRUE);
+       mini_profiler_emit_instrumentation_call (cfg, mono_profiler_raise_method_enter, TRUE, NULL, NULL);
 
        if (seq_points) {
                MonoBasicBlock *bb;
index 5a376b8945017f49fa82fb49d444b81c8b19cc0e..d544ee34445c4d41e7f6bb05ab44c6a254480291 100644 (file)
@@ -6419,6 +6419,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_GET_LAST_ERROR:
                        emit_get_last_error(code, ins->dreg);
                        break;
+               case OP_FILL_PROF_CALL_CTX:
+                       for (int i = 0; i < AMD64_NREG; i++)
+                               if (AMD64_IS_CALLEE_SAVED_REG (i) || i == AMD64_RSP)
+                                       amd64_mov_membase_reg (code, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, gregs) + i * sizeof (mgreg_t), i, sizeof (mgreg_t));
+                       break;
                default:
                        g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
                        g_assert_not_reached ();
index e61e7b00c7123020d41e26f9b156f2e739882bf2..a304dbef08062a83bfc9b907a6840ee00e42cdcb 100644 (file)
@@ -5890,7 +5890,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        arm_patch (buf [0], code);
                        break;
                }
-
+               case OP_FILL_PROF_CALL_CTX:
+                       for (int i = 0; i < ARMREG_MAX; i++)
+                               if ((MONO_ARCH_CALLEE_SAVED_REGS & (1 << i)) || i == ARMREG_SP || i == ARMREG_FP)
+                                       ARM_STR_IMM (code, i, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, regs) + i * sizeof (mgreg_t));
+                       break;
                default:
                        g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
                        g_assert_not_reached ();
index aacdd599afcd8003aecb9cba419b6ec55a977172..46fb083b2823efaf6034aa894bf141418bb2b31d 100644 (file)
@@ -4329,7 +4329,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 #endif
                        break;
                }
-
+               case OP_FILL_PROF_CALL_CTX:
+                       for (int i = 0; i < MONO_MAX_IREGS; i++)
+                               if ((MONO_ARCH_CALLEE_SAVED_REGS & (1 << i)) || i == ARMREG_SP || i == ARMREG_FP)
+                                       arm_strx (code, i, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, regs) + i * sizeof (mgreg_t));
+                       break;
                default:
                        g_warning ("unknown opcode %s in %s()\n", mono_inst_name (ins->opcode), __FUNCTION__);
                        g_assert_not_reached ();
index ae9165e1174792376a3a602cdc75ba758692f012..29d8cb09fb77e0b2d84586309f48dc85243afc26 100644 (file)
@@ -1305,3 +1305,10 @@ MINI_OP(OP_GET_SP, "get_sp", IREG, NONE, NONE)
 MINI_OP(OP_SET_SP, "set_sp", NONE, IREG, NONE)
 
 MINI_OP(OP_GET_LAST_ERROR, "get_last_error", IREG, NONE, NONE)
+
+/*
+ * Fill out a MonoContext contained in a MonoProfilerCallContext. This only
+ * stores the stack pointer, frame pointer, and callee-saved registers. This
+ * should be enough to locate arguments and variables.
+ */
+MINI_OP(OP_FILL_PROF_CALL_CTX, "fill_prof_call_ctx", NONE, IREG, NONE)
diff --git a/mono/mini/mini-profiler.c b/mono/mini/mini-profiler.c
new file mode 100644 (file)
index 0000000..3b6149d
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * 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 <mono/metadata/abi-details.h>
+#include <mono/metadata/mono-debug.h>
+
+#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);
+}
index 7c22cc941575da3682a32aa27975058995aefc6f..af88efa4abd17461d5d55f6a8530c57bd737542f 100644 (file)
@@ -3938,6 +3938,13 @@ mini_init (const char *filename, const char *runtime_version)
        mono_install_get_class_from_name (mono_aot_get_class_from_name);
        mono_install_jit_info_find_in_aot (mono_aot_find_jit_info);
 
+       mono_profiler_state.context_enable = mini_profiler_context_enable;
+       mono_profiler_state.context_get_this = mini_profiler_context_get_this;
+       mono_profiler_state.context_get_argument = mini_profiler_context_get_argument;
+       mono_profiler_state.context_get_local = mini_profiler_context_get_local;
+       mono_profiler_state.context_get_result = mini_profiler_context_get_result;
+       mono_profiler_state.context_free_buffer = mini_profiler_context_free_buffer;
+
        if (profile_options)
                for (guint i = 0; i < profile_options->len; i++)
                        mono_profiler_load ((const char *) g_ptr_array_index (profile_options, i));
@@ -4056,8 +4063,8 @@ register_icalls (void)
         * the wrapper would call the icall which would call the wrapper and
         * so on.
         */
-       register_icall (mono_profiler_raise_method_enter, "mono_profiler_raise_method_enter", "void ptr", TRUE);
-       register_icall (mono_profiler_raise_method_leave, "mono_profiler_raise_method_leave", "void ptr", TRUE);
+       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_trace_enter_method, "mono_trace_enter_method", NULL, TRUE);
        register_icall (mono_trace_leave_method, "mono_trace_leave_method", NULL, TRUE);
index 54966b1dc8f39bbbafc7fe04dcfe581c4781e90d..a3e893a1e2b79a4d8ca8f10dd559b9a592b64ba3 100644 (file)
@@ -4927,6 +4927,13 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_SET_SP:
                        x86_mov_reg_reg (code, X86_ESP, ins->sreg1, sizeof (mgreg_t));
                        break;
+               case OP_FILL_PROF_CALL_CTX:
+                       x86_mov_membase_reg (code, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, esp), X86_ESP, sizeof (mgreg_t));
+                       x86_mov_membase_reg (code, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, ebp), X86_EBP, sizeof (mgreg_t));
+                       x86_mov_membase_reg (code, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, ebx), X86_EBX, sizeof (mgreg_t));
+                       x86_mov_membase_reg (code, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, esi), X86_ESI, sizeof (mgreg_t));
+                       x86_mov_membase_reg (code, ins->sreg1, MONO_STRUCT_OFFSET (MonoContext, edi), X86_EDI, sizeof (mgreg_t));
+                       break;
                default:
                        g_warning ("unknown opcode %s\n", mono_inst_name (ins->opcode));
                        g_assert_not_reached ();
index 5374f9a3653704f39cb982c8ea524013044de012..5e6e718486f8b7c29b519017b2acf237b41b8e58 100644 (file)
@@ -3294,8 +3294,10 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
        }
 #endif
 
+       cfg->prof_flags = mono_profiler_get_call_instrumentation_flags (cfg->method);
+
        /* The debugger has no liveness information, so avoid sharing registers/stack slots */
-       if (debug_options.mdb_optimizations) {
+       if (debug_options.mdb_optimizations || MONO_CFG_PROFILE_CALL_CONTEXT (cfg)) {
                cfg->disable_reuse_registers = TRUE;
                cfg->disable_reuse_stack_slots = TRUE;
                /* 
index a8bd6f70221f2d606c6589a2756eda6430432ec6..7d681122d43c3b3aa212a18cc74e77f265966a02 100644 (file)
@@ -1916,8 +1916,13 @@ typedef struct {
        int stat_inlineable_methods;
        int stat_inlined_methods;
        int stat_code_reallocs;
+
+       MonoProfilerCallInstrumentationFlags prof_flags;
 } MonoCompile;
 
+#define MONO_CFG_PROFILE_CALL_CONTEXT(cfg) \
+       ((cfg)->prof_flags & (MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE_CONTEXT | MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE_CONTEXT))
+
 typedef enum {
        MONO_CFG_HAS_ALLOCA = 1 << 0,
        MONO_CFG_HAS_CALLS  = 1 << 1,
@@ -2353,6 +2358,13 @@ void        mini_cleanup                   (MonoDomain *domain);
 MONO_API MonoDebugOptions *mini_get_debug_options   (void);
 MONO_API gboolean    mini_parse_debug_option (const char *option);
 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_context_enable (void);
+gpointer    mini_profiler_context_get_this (MonoProfilerCallContext *ctx);
+gpointer    mini_profiler_context_get_argument (MonoProfilerCallContext *ctx, guint32 pos);
+gpointer    mini_profiler_context_get_local (MonoProfilerCallContext *ctx, guint32 pos);
+gpointer    mini_profiler_context_get_result (MonoProfilerCallContext *ctx);
+void        mini_profiler_context_free_buffer (gpointer buffer);
 
 /* graph dumping */
 void mono_cfg_dump_create_context (MonoCompile *cfg);
index ed7a21226593e44c5ad4137d27df819c8b10f031..90f7309884796a55ac57dfb19aa89a76a192bffd 100644 (file)
@@ -1760,7 +1760,7 @@ class_loaded (MonoProfiler *prof, MonoClass *klass)
 }
 
 static void
-method_enter (MonoProfiler *prof, MonoMethod *method)
+method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx)
 {
        if (get_thread ()->call_depth++ <= log_config.max_call_depth) {
                ENTER_LOG (&method_entries_ctr, logbuffer,
@@ -1776,7 +1776,7 @@ method_enter (MonoProfiler *prof, MonoMethod *method)
 }
 
 static void
-method_leave (MonoProfiler *prof, MonoMethod *method)
+method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx)
 {
        if (--get_thread ()->call_depth <= log_config.max_call_depth) {
                ENTER_LOG (&method_exits_ctr, logbuffer,
index 70dc4b0554c6eb8d780d1a2c8dcd7feb348ab91a..b23daec250520a61e23c30e04a16991f9cb7426b 100644 (file)
     <ClCompile Include="..\mono\mini\type-checking.c" />\r
     <ClCompile Include="..\mono\mini\lldb.c" />\r
     <ClCompile Include="..\mono\mini\interp\interp-stubs.c" />\r
+    <ClCompile Include="..\mono\mini\mini-profiler.c" />\r
   </ItemGroup>\r
   <PropertyGroup Label="Globals">\r
     <ProjectGuid>{CB0D9E92-293C-439C-9AC7-C5F59B6E0772}</ProjectGuid>\r
index 029b9d162c15144f2740162e2726bb56fb2572ac..672e6052f41a69606d0d2f407964fa33ee1a5dd1 100644 (file)
     <ClCompile Include="..\mono\mini\interp\interp-stubs.c">\r
       <Filter>Source Files</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\mono\mini\mini-profiler.c">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\mono\mini\abcremoval.h">\r
index 884457efa69cda149667fdb60977632946687a3f..acf9e59d1b4bf8c64de4aec4425b8817984a5076 100644 (file)
@@ -704,8 +704,14 @@ mono_print_method_from_ip
 mono_print_thread_dump
 mono_print_thread_dump_from_ctx
 mono_print_unhandled_exception
+mono_profiler_call_context_free_buffer
+mono_profiler_call_context_get_argument
+mono_profiler_call_context_get_local
+mono_profiler_call_context_get_result
+mono_profiler_call_context_get_this
 mono_profiler_create
 mono_profiler_enable_allocations
+mono_profiler_enable_call_context_introspection
 mono_profiler_enable_sampling
 mono_profiler_get_coverage_data
 mono_profiler_get_sample_mode
index 64c8c16f7b3ae7d6fb4565578e1fca8ca6c259f7..513e2eda72ede1b1ecfbce1ade7d77bd7f090909 100644 (file)
@@ -706,8 +706,14 @@ mono_print_method_from_ip
 mono_print_thread_dump
 mono_print_thread_dump_from_ctx
 mono_print_unhandled_exception
+mono_profiler_call_context_free_buffer
+mono_profiler_call_context_get_argument
+mono_profiler_call_context_get_local
+mono_profiler_call_context_get_result
+mono_profiler_call_context_get_this
 mono_profiler_create
 mono_profiler_enable_allocations
+mono_profiler_enable_call_context_introspection
 mono_profiler_enable_sampling
 mono_profiler_get_coverage_data
 mono_profiler_get_sample_mode