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 *
emit_fill_call_ctx(MonoCompile * cfg,MonoInst * method,MonoInst * ret)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
mini_profiler_emit_enter(MonoCompile * cfg)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
mini_profiler_emit_leave(MonoCompile * cfg,MonoInst * ret)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
mini_profiler_emit_tail_call(MonoCompile * cfg,MonoMethod * target)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
mini_profiler_context_enable(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
memdup_with_type(gpointer data,MonoType * t)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 *
get_int_reg(MonoContext * ctx,guint32 reg)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
get_variable_buffer(MonoDebugMethodJitInfo * jit,MonoDebugVarInfo * var,MonoContext * ctx)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
mini_profiler_context_get_this(MonoProfilerCallContext * ctx)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 (mini_get_interp_callbacks ()->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
mini_profiler_context_get_argument(MonoProfilerCallContext * ctx,guint32 pos)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 (mini_get_interp_callbacks ()->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
mini_profiler_context_get_local(MonoProfilerCallContext * ctx,guint32 pos)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 (mini_get_interp_callbacks ()->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
mini_profiler_context_get_result(MonoProfilerCallContext * ctx)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
mini_profiler_context_free_buffer(void * buffer)273 mini_profiler_context_free_buffer (void *buffer)
274 {
275 	g_free (buffer);
276 }
277