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