3 * Tracing facilities for the Mono Runtime.
6 * Paolo Molaro (lupus@ximian.com)
7 * Dietmar Maurer (dietmar@ximian.com)
9 * (C) 2002 Ximian, Inc.
10 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
23 #include "mini-runtime.h"
24 #include <mono/metadata/debug-helpers.h>
25 #include <mono/utils/mono-time.h>
26 #include <mono/utils/mono-memory-model.h>
28 #include <mono/metadata/callspec.h>
30 #if defined (HOST_ANDROID) || (defined (TARGET_IOS) && defined (TARGET_IOS))
32 # define printf(...) g_log("mono", G_LOG_LEVEL_MESSAGE, __VA_ARGS__)
34 # define fprintf(__ignore, ...) g_log ("mono-gc", G_LOG_LEVEL_MESSAGE, __VA_ARGS__)
37 static MonoCallSpec trace_spec
;
39 static volatile gint32 output_lock
= 0;
41 gboolean
mono_trace_eval_exception (MonoClass
*klass
)
43 return mono_callspec_eval_exception (klass
, &trace_spec
);
46 gboolean
mono_trace_eval (MonoMethod
*method
)
48 return mono_callspec_eval (method
, &trace_spec
);
51 MonoCallSpec
*mono_trace_set_options (const char *options
)
54 if (!mono_callspec_parse (options
, &trace_spec
, &errstr
)) {
55 fprintf (stderr
, "%s\n", errstr
);
64 #ifdef MONO_KEYWORD_THREAD
68 static guint64 start_time
= 0;
70 static double seconds_since_start (void)
72 guint64 diff
= mono_100ns_ticks () - start_time
;
73 return diff
/10000000.0;
76 static void indent (int diff
) {
80 start_time
= mono_100ns_ticks ();
81 printf ("[%p: %.5f %d] ", (void*)mono_native_thread_id_get (), seconds_since_start (), indent_level
);
87 string_to_utf8 (MonoString
*s
)
90 GError
*gerror
= NULL
;
97 as
= g_utf16_to_utf8 (mono_string_chars_internal (s
), s
->length
, NULL
, NULL
, &gerror
);
99 /* Happens with StringBuilders */
100 g_error_free (gerror
);
101 return g_strdup ("<INVALID UTF8>");
108 * This used to be endianness sensitive due to the stack, but since the change
109 * to using the profiler to get an argument, it can be dereferenced as a
110 * pointer of the specified type, regardless of endian.
112 #define arg_in_stack_slot(cpos, type) ((type *)(cpos))
115 is_gshared_vt_wrapper (MonoMethod
*m
)
117 if (m
->wrapper_type
!= MONO_WRAPPER_OTHER
)
119 return !strcmp (m
->name
, "interp_in") || !strcmp (m
->name
, "gsharedvt_out_sig");
123 * ENTER:c <- compiled (JIT or AOT)
124 * ENTER:u <- no JitInfo available
127 frame_kind (MonoJitInfo
*ji
)
139 mono_trace_enter_method (MonoMethod
*method
, MonoJitInfo
*ji
, MonoProfilerCallContext
*ctx
)
144 MonoMethodSignature
*sig
;
146 MonoGenericSharingContext
*gsctx
= NULL
;
148 if (!trace_spec
.enabled
)
151 fname
= mono_method_full_name (method
, TRUE
);
154 while (output_lock
!= 0 || mono_atomic_cas_i32 (&output_lock
, 1, 0) != 0)
155 mono_thread_info_yield ();
158 /* FIXME: Might be better to pass the ji itself */
160 ji
= mini_jit_info_table_find (mono_domain_get (), (char *)MONO_RETURN_ADDRESS (), NULL
);
162 printf ("ENTER:%c %s(", frame_kind (ji
), fname
);
165 sig
= mono_method_signature_internal (method
);
167 if (method
->is_inflated
&& ji
) {
168 gsctx
= mono_jit_info_get_generic_sharing_context (ji
);
169 if (gsctx
&& gsctx
->is_gsharedvt
) {
170 /* Needs a ctx to get precise method */
171 printf (") <gsharedvt>\n");
172 mono_atomic_store_release (&output_lock
, 0);
178 void *this_buf
= mini_profiler_context_get_this (ctx
);
179 if (m_class_is_valuetype (method
->klass
) || is_gshared_vt_wrapper (method
)) {
180 printf ("value:%p", this_buf
);
182 MonoObject
*o
= *(MonoObject
**)this_buf
;
185 klass
= o
->vtable
->klass
;
187 if (klass
== mono_defaults
.string_class
) {
188 MonoString
*s
= (MonoString
*)o
;
189 char *as
= string_to_utf8 (s
);
191 printf ("this:[STRING:%p:%s]", o
, as
);
193 } else if (klass
== mono_defaults
.runtimetype_class
) {
194 printf ("[this:[TYPE:%p:%s]]", o
, mono_type_full_name (((MonoReflectionType
*)o
)->type
));
196 printf ("this:%p[%s.%s %s]", o
, m_class_get_name_space (klass
), m_class_get_name (klass
), o
->vtable
->domain
->friendly_name
);
199 printf ("this:NULL");
202 if (sig
->param_count
)
204 mini_profiler_context_free_buffer (this_buf
);
207 for (i
= 0; i
< sig
->param_count
; ++i
) {
208 gpointer buf
= mini_profiler_context_get_argument (ctx
, i
);
210 MonoType
*type
= sig
->params
[i
];
213 printf ("[BYREF:%p]", *(gpointer
*)buf
);
214 mini_profiler_context_free_buffer (buf
);
218 switch (mini_get_underlying_type (type
)->type
) {
221 printf ("%p", *arg_in_stack_slot(buf
, gpointer
*));
223 case MONO_TYPE_BOOLEAN
:
227 printf ("%d", *arg_in_stack_slot(buf
, gint8
));
231 printf ("%d", *arg_in_stack_slot(buf
, gint16
));
235 printf ("%d", *arg_in_stack_slot(buf
, int));
237 case MONO_TYPE_STRING
: {
238 MonoString
*s
= *arg_in_stack_slot(buf
, MonoString
*);
242 g_assert (((MonoObject
*)s
)->vtable
->klass
== mono_defaults
.string_class
);
243 as
= string_to_utf8 (s
);
245 printf ("[STRING:%p:%s]", s
, as
);
248 printf ("[STRING:null]");
251 case MONO_TYPE_CLASS
:
252 case MONO_TYPE_OBJECT
: {
253 o
= *arg_in_stack_slot(buf
, MonoObject
*);
255 klass
= o
->vtable
->klass
;
257 gpointer data
= mono_object_get_data (o
);
258 if (klass
== mono_defaults
.string_class
) {
259 char *as
= string_to_utf8 ((MonoString
*)o
);
261 printf ("[STRING:%p:%s]", o
, as
);
263 } else if (klass
== mono_defaults
.int32_class
) {
264 printf ("[INT32:%p:%d]", o
, *(gint32
*)data
);
265 } else if (klass
== mono_defaults
.runtimetype_class
) {
266 printf ("[TYPE:%s]", mono_type_full_name (((MonoReflectionType
*)o
)->type
));
267 } else if (m_class_get_rank (klass
)) {
268 MonoArray
*arr
= (MonoArray
*)o
;
269 printf ("[%s.%s:[%d]%p]", m_class_get_name_space (klass
), m_class_get_name (klass
), mono_array_length_internal (arr
), o
);
271 printf ("[%s.%s:%p]", m_class_get_name_space (klass
), m_class_get_name (klass
), o
);
273 printf ("%p", *arg_in_stack_slot(buf
, gpointer
));
278 case MONO_TYPE_FNPTR
:
279 case MONO_TYPE_ARRAY
:
280 case MONO_TYPE_SZARRAY
:
281 printf ("%p", *arg_in_stack_slot(buf
, gpointer
));
285 printf ("0x%016llx", (long long)*arg_in_stack_slot(buf
, gint64
));
288 printf ("%f", *arg_in_stack_slot(buf
, float));
291 printf ("%f", *arg_in_stack_slot(buf
, double));
293 case MONO_TYPE_VALUETYPE
: {
295 size
= mono_type_size (type
, &align
);
297 for (j
= 0; j
< size
; j
++)
298 printf ("%02x,", *((guint8
*)buf
+j
));
305 if (i
+ 1 < sig
->param_count
)
307 mini_profiler_context_free_buffer (buf
);
313 mono_atomic_store_release (&output_lock
, 0);
317 mono_trace_leave_method (MonoMethod
*method
, MonoJitInfo
*ji
, MonoProfilerCallContext
*ctx
)
321 MonoGenericSharingContext
*gsctx
;
323 if (!trace_spec
.enabled
)
326 fname
= mono_method_full_name (method
, TRUE
);
329 while (output_lock
!= 0 || mono_atomic_cas_i32 (&output_lock
, 1, 0) != 0)
330 mono_thread_info_yield ();
332 /* FIXME: Might be better to pass the ji itself from the JIT */
334 ji
= mini_jit_info_table_find (mono_domain_get (), (char *)MONO_RETURN_ADDRESS (), NULL
);
336 printf ("LEAVE:%c %s(", frame_kind (ji
), fname
);
339 if (method
->is_inflated
&& ji
) {
340 gsctx
= mono_jit_info_get_generic_sharing_context (ji
);
341 if (gsctx
&& gsctx
->is_gsharedvt
) {
342 /* Needs a ctx to get precise method */
343 printf (") <gsharedvt>\n");
344 mono_atomic_store_release (&output_lock
, 0);
349 type
= mini_get_underlying_type (mono_method_signature_internal (method
)->ret
);
351 gpointer buf
= mini_profiler_context_get_result (ctx
);
352 switch (type
->type
) {
357 gint8 res
= *arg_in_stack_slot (buf
, gint8
);
358 printf ("result=%d", res
);
363 gint16 res
= *arg_in_stack_slot (buf
, gint16
);
364 printf ("result=%d", res
);
369 int res
= *arg_in_stack_slot (buf
, int);
370 printf ("result=%d", res
);
376 gpointer res
= *arg_in_stack_slot (buf
, gpointer
);
377 printf ("result=%p", res
);
380 case MONO_TYPE_OBJECT
: {
381 MonoObject
*o
= *arg_in_stack_slot (buf
, MonoObject
*);
384 gpointer data
= mono_object_get_data (o
);
385 if (o
->vtable
->klass
== mono_defaults
.boolean_class
) {
386 printf ("[BOOLEAN:%p:%d]", o
, *(guint8
*)data
);
387 } else if (o
->vtable
->klass
== mono_defaults
.int32_class
) {
388 printf ("[INT32:%p:%d]", o
, *(gint32
*)data
);
389 } else if (o
->vtable
->klass
== mono_defaults
.int64_class
) {
390 printf ("[INT64:%p:%lld]", o
, (long long)*(gint64
*)data
);
391 } else if (o
->vtable
->klass
== mono_defaults
.string_class
) {
393 as
= string_to_utf8 ((MonoString
*)o
);
394 printf ("[STRING:%p:%s]", o
, as
);
396 printf ("[%s.%s:%p]", m_class_get_name_space (mono_object_class (o
)), m_class_get_name (mono_object_class (o
)), o
);
399 printf ("[OBJECT:%p]", o
);
404 gint64 l
= *arg_in_stack_slot (buf
, gint64
);
405 printf ("lresult=0x%16llx", (long long)l
);
409 gint64 l
= *arg_in_stack_slot (buf
, gint64
);
410 printf ("lresult=0x%16llx", (long long)l
);
415 double f
= *arg_in_stack_slot (buf
, double);
419 case MONO_TYPE_VALUETYPE
: {
420 guint8
*p
= (guint8
*)buf
;
422 size
= mono_type_size (type
, &align
);
424 for (j
= 0; p
&& j
< size
; j
++)
425 printf ("%02x,", p
[j
]);
430 printf ("(unknown return type %x)", mono_method_signature_internal (method
)->ret
->type
);
432 mini_profiler_context_free_buffer (buf
);
434 //printf (" ip: %p\n", MONO_RETURN_ADDRESS_N (1));
438 mono_atomic_store_release (&output_lock
, 0);
442 mono_trace_enable (gboolean enable
)
444 trace_spec
.enabled
= enable
;
448 mono_trace_is_enabled ()
450 return trace_spec
.enabled
;