[profiler] Implement call context introspection for enter/leave events.
[mono.git] / mono / mini / mini-profiler.c
1 /*
2  * Licensed to the .NET Foundation under one or more agreements.
3  * The .NET Foundation licenses this file to you under the MIT license.
4  * See the LICENSE file in the project root for more information.
5  */
6
7 #include <mono/metadata/abi-details.h>
8 #include <mono/metadata/mono-debug.h>
9
10 #include "interp/interp.h"
11 #include "ir-emit.h"
12 #include "mini.h"
13
14 void
15 mini_profiler_emit_instrumentation_call (MonoCompile *cfg, void *func, gboolean entry, MonoInst **ret, MonoType *rtype)
16 {
17         gboolean instrument, capture;
18
19         if (entry) {
20                 instrument = cfg->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE;
21                 capture = cfg->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE_CONTEXT;
22         } else {
23                 instrument = cfg->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE;
24                 capture = cfg->prof_flags & MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE_CONTEXT;
25         }
26
27         if (!instrument)
28                 return;
29
30         g_assert (cfg->current_method == cfg->method);
31
32         MonoInst *iargs [2];
33
34         EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
35
36         if (capture && !cfg->llvm_only) {
37                 cfg->flags |= MONO_CFG_HAS_ALLOCA;
38
39                 MonoInst *size, *fill_ctx;
40
41                 EMIT_NEW_ICONST (cfg, size, sizeof (MonoProfilerCallContext));
42                 MONO_INST_NEW (cfg, iargs [1], OP_LOCALLOC);
43                 iargs [1]->dreg = alloc_preg (cfg);
44                 iargs [1]->sreg1 = size->dreg;
45                 iargs [1]->flags |= MONO_INST_INIT;
46                 MONO_ADD_INS (cfg->cbb, iargs [1]);
47                 MONO_INST_NEW (cfg, fill_ctx, OP_FILL_PROF_CALL_CTX);
48                 fill_ctx->sreg1 = iargs [1]->dreg;
49                 MONO_ADD_INS (cfg->cbb, fill_ctx);
50                 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, iargs [1]->dreg, MONO_STRUCT_OFFSET (MonoProfilerCallContext, method), iargs [0]->dreg);
51
52                 if (rtype && rtype->type != MONO_TYPE_VOID) {
53                         MonoInst *var = mono_compile_create_var (cfg, rtype, OP_LOCAL);
54
55                         MonoInst *store, *addr;
56
57                         EMIT_NEW_TEMPSTORE (cfg, store, var->inst_c0, *ret);
58                         EMIT_NEW_VARLOADA (cfg, addr, var, NULL);
59                         MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, iargs [1]->dreg, MONO_STRUCT_OFFSET (MonoProfilerCallContext, return_value), addr->dreg);
60                 }
61         } else
62                 EMIT_NEW_PCONST (cfg, iargs [1], NULL);
63
64         mono_emit_jit_icall (cfg, func, iargs);
65 }
66
67 void
68 mini_profiler_context_enable (void)
69 {
70         if (!mono_debug_enabled ())
71                 mono_debug_init (MONO_DEBUG_FORMAT_MONO);
72 }
73
74 static gpointer
75 memdup_with_type (gpointer data, MonoType *t)
76 {
77         int dummy;
78
79         return g_memdup (data, mono_type_size (t, &dummy));
80 }
81
82 static guint8 *
83 get_int_reg (MonoContext *ctx, guint32 reg)
84 {
85         return (guint8 *) mono_arch_context_get_int_reg (ctx, reg);
86 }
87
88 static gpointer
89 get_variable_buffer (MonoDebugMethodJitInfo *jit, MonoDebugVarInfo *var, MonoContext *ctx)
90 {
91         guint32 flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
92         guint32 reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
93
94         switch (flags) {
95         case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER: {
96                 /*
97                  * This is kind of a special case: All other address modes ultimately
98                  * produce an address to where the actual value is located, but this
99                  * address mode gets us the value itself as an mgreg_t value.
100                  */
101                 mgreg_t value = (mgreg_t) get_int_reg (ctx, reg);
102
103                 return memdup_with_type (&value, var->type);
104         }
105         case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
106                 return memdup_with_type (get_int_reg (ctx, reg) + (gint32) var->offset, var->type);
107         case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR:
108         case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR:
109                 return memdup_with_type (*(guint8 **) (get_int_reg (ctx, reg) + (gint32) var->offset), var->type);
110         case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: {
111                 guint32 idx = reg;
112
113                 MonoDebugVarInfo *info_var = jit->gsharedvt_info_var;
114
115                 flags = info_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
116                 reg = info_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
117
118                 MonoGSharedVtMethodRuntimeInfo *info;
119
120                 switch (flags) {
121                 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
122                         info = (MonoGSharedVtMethodRuntimeInfo *) get_int_reg (ctx, reg);
123                         break;
124                 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
125                         info = *(MonoGSharedVtMethodRuntimeInfo **) (get_int_reg (ctx, reg) + (gint32) info_var->offset);
126                         break;
127                 default:
128                         g_assert_not_reached ();
129                 }
130
131                 MonoDebugVarInfo *locals_var = jit->gsharedvt_locals_var;
132
133                 flags = locals_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
134                 reg = locals_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
135
136                 guint8 *locals;
137
138                 switch (flags) {
139                 case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
140                         locals = get_int_reg (ctx, reg);
141                         break;
142                 case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
143                         locals = *(guint8 **) (get_int_reg (ctx, reg) + (gint32) info_var->offset);
144                         break;
145                 default:
146                         g_assert_not_reached ();
147                 }
148
149                 return memdup_with_type (locals + (gsize) info->entries [idx], var->type);
150         }
151         default:
152                 g_assert_not_reached ();
153                 return NULL;
154         }
155 }
156
157 gpointer
158 mini_profiler_context_get_this (MonoProfilerCallContext *ctx)
159 {
160         if (!mono_method_signature (ctx->method)->hasthis)
161                 return NULL;
162
163         if (ctx->interp_frame)
164                 return memdup_with_type (mono_interp_frame_get_this (ctx->interp_frame), &ctx->method->klass->this_arg);
165
166         MonoDebugMethodJitInfo *info = mono_debug_find_method (ctx->method, mono_domain_get ());
167
168         if (!info)
169                 return NULL;
170
171         return get_variable_buffer (info, info->this_var, &ctx->context);
172 }
173
174 gpointer
175 mini_profiler_context_get_argument (MonoProfilerCallContext *ctx, guint32 pos)
176 {
177         MonoMethodSignature *sig = mono_method_signature (ctx->method);
178
179         if (pos >= sig->param_count)
180                 return NULL;
181
182         if (ctx->interp_frame)
183                 return memdup_with_type (mono_interp_frame_get_arg (ctx->interp_frame, pos), sig->params [pos]);
184
185         MonoDebugMethodJitInfo *info = mono_debug_find_method (ctx->method, mono_domain_get ());
186
187         if (!info)
188                 return NULL;
189
190         return get_variable_buffer (info, &info->params [pos], &ctx->context);
191 }
192
193 gpointer
194 mini_profiler_context_get_local (MonoProfilerCallContext *ctx, guint32 pos)
195 {
196         MonoError error;
197         MonoMethodHeader *header = mono_method_get_header_checked (ctx->method, &error);
198         mono_error_assert_ok (&error); // Must be a valid method at this point.
199
200         if (pos >= header->num_locals) {
201                 mono_metadata_free_mh (header);
202                 return NULL;
203         }
204
205         MonoType *t = header->locals [pos];
206
207         mono_metadata_free_mh (header);
208
209         if (ctx->interp_frame)
210                 return memdup_with_type (mono_interp_frame_get_local (ctx->interp_frame, pos), t);
211
212         MonoDebugMethodJitInfo *info = mono_debug_find_method (ctx->method, mono_domain_get ());
213
214         if (!info)
215                 return NULL;
216
217         return get_variable_buffer (info, &info->locals [pos], &ctx->context);
218 }
219
220 gpointer
221 mini_profiler_context_get_result (MonoProfilerCallContext *ctx)
222 {
223         if (!ctx->return_value)
224                 return NULL;
225
226         return memdup_with_type (ctx->return_value, mono_method_signature (ctx->method)->ret);
227 }
228
229 void
230 mini_profiler_context_free_buffer (void *buffer)
231 {
232         g_free (buffer);
233 }