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