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