2 * aot.c: Ahead of Time Compiler Profiler for Mono.
5 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
6 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <mono/metadata/object-internals.h>
14 #include <mono/metadata/profiler.h>
15 #include <mono/metadata/tokentype.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/debug-helpers.h>
18 #include <mono/metadata/assembly.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/mini/jit.h>
22 #include <mono/utils/mono-logger-internals.h>
23 #include <mono/utils/mono-os-mutex.h>
24 #include <mono/utils/mono-threads.h>
29 #include <mono/utils/mono-error-internals.h>
31 struct _MonoProfiler
{
41 MonoMethodDesc
*write_at
;
42 MonoMethodDesc
*send_to
;
50 static MonoProfiler aot_profiler
;
53 prof_shutdown (MonoProfiler
*prof
);
56 prof_jit_done (MonoProfiler
*prof
, MonoMethod
*method
, MonoJitInfo
*jinfo
)
58 MonoImage
*image
= mono_class_get_image (mono_method_get_class (method
));
60 if (!image
->assembly
|| method
->wrapper_type
|| !prof
->methods
|| prof
->disable
)
63 if (prof
->write_at
&& mono_method_desc_match (prof
->write_at
, method
)) {
64 printf ("aot-profiler | Writing data at: '%s'.\n", mono_method_full_name (method
, 1));
69 mono_os_mutex_lock (&prof
->mutex
);
71 g_ptr_array_add (prof
->methods
, method
);
72 mono_os_mutex_unlock (&prof
->mutex
);
78 mono_profiler_printf ("AOT profiler.");
79 mono_profiler_printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
80 mono_profiler_printf ("Options:");
81 mono_profiler_printf ("\tduration=NUM profile only NUM seconds of runtime and write the data");
82 mono_profiler_printf ("\thelp show this usage info");
83 mono_profiler_printf ("\toutput=FILENAME write the data to file FILENAME");
84 mono_profiler_printf ("\twrite-at-method=METHOD write the data when METHOD is compiled.");
85 mono_profiler_printf ("\tsend-to-method=METHOD call METHOD with the collected data.");
86 mono_profiler_printf ("\tsend-to-arg=STR extra argument to pass to METHOD.");
87 mono_profiler_printf ("\tverbose print diagnostic info");
93 match_option (const char *arg
, const char *opt_name
, const char **rval
)
96 const char *end
= strchr (arg
, '=');
100 return !strcmp (arg
, opt_name
);
102 if (strncmp (arg
, opt_name
, strlen (opt_name
)) || (end
- arg
) > strlen (opt_name
) + 1)
107 //FIXME how should we handle passing a value to an arg that doesn't expect it?
108 return !strcmp (arg
, opt_name
);
113 parse_arg (const char *arg
)
117 if (match_option (arg
, "help", NULL
)) {
119 } else if (match_option (arg
, "duration", &val
)) {
121 aot_profiler
.duration
= strtoul (val
, &end
, 10);
122 } else if (match_option (arg
, "write-at-method", &val
)) {
123 aot_profiler
.write_at
= mono_method_desc_new (val
, TRUE
);
124 if (!aot_profiler
.write_at
) {
125 mono_profiler_printf_err ("Could not parse method description: %s", val
);
128 } else if (match_option (arg
, "send-to-method", &val
)) {
129 aot_profiler
.send_to
= mono_method_desc_new (val
, TRUE
);
130 if (!aot_profiler
.send_to
) {
131 mono_profiler_printf_err ("Could not parse method description: %s", val
);
134 aot_profiler
.send_to_str
= strdup (val
);
135 } else if (match_option (arg
, "send-to-arg", &val
)) {
136 aot_profiler
.send_to_arg
= strdup (val
);
137 } else if (match_option (arg
, "output", &val
)) {
138 aot_profiler
.outfile_name
= g_strdup (val
);
139 } else if (match_option (arg
, "verbose", NULL
)) {
140 aot_profiler
.verbose
= TRUE
;
142 mono_profiler_printf_err ("Could not parse argument: %s", arg
);
147 parse_args (const char *desc
)
150 gboolean in_quotes
= FALSE
;
151 char quote_char
= '\0';
152 char *buffer
= g_malloc (strlen (desc
) + 1);
155 for (p
= desc
; *p
; p
++){
159 if (buffer_pos
!= 0){
160 buffer
[buffer_pos
] = 0;
165 buffer
[buffer_pos
++] = *p
;
171 buffer
[buffer_pos
++] = p
[1];
178 if (quote_char
== *p
)
181 buffer
[buffer_pos
++] = *p
;
188 buffer
[buffer_pos
++] = *p
;
193 if (buffer_pos
!= 0) {
194 buffer
[buffer_pos
] = 0;
202 helper_thread (void *arg
)
204 mono_thread_attach (mono_get_root_domain ());
206 MonoInternalThread
*internal
= mono_thread_internal_current ();
210 MonoString
*name_str
= mono_string_new_checked (mono_get_root_domain (), "AOT Profiler Helper", error
);
211 mono_error_assert_ok (error
);
212 mono_thread_set_name_internal (internal
, name_str
, FALSE
, FALSE
, error
);
213 mono_error_assert_ok (error
);
215 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC
| MONO_THREAD_INFO_FLAGS_NO_SAMPLE
);
217 sleep (aot_profiler
.duration
);
219 prof_shutdown (&aot_profiler
);
221 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE
);
222 mono_thread_detach (mono_thread_current ());
228 start_helper_thread (void)
230 MonoNativeThreadId thread_id
;
232 if (!mono_native_thread_create (&thread_id
, helper_thread
, NULL
)) {
233 mono_profiler_printf_err ("Could not start aot profiler helper thread");
239 runtime_initialized (MonoProfiler
*profiler
)
241 if (profiler
->duration
>= 0)
242 start_helper_thread ();
246 mono_profiler_init_aot (const char *desc
);
249 * mono_profiler_init_aot:
253 mono_profiler_init_aot (const char *desc
)
255 if (mono_jit_aot_compiling ()) {
256 mono_profiler_printf_err ("The AOT profiler is not meant to be run during AOT compilation.");
260 aot_profiler
.duration
= -1;
262 parse_args (desc
[strlen ("aot")] == ':' ? desc
+ strlen ("aot") + 1 : "");
264 if (!aot_profiler
.send_to
) {
265 if (!aot_profiler
.outfile_name
)
266 aot_profiler
.outfile_name
= g_strdup ("output.aotprofile");
267 else if (*aot_profiler
.outfile_name
== '+')
268 aot_profiler
.outfile_name
= g_strdup_printf ("%s.%d", aot_profiler
.outfile_name
+ 1, getpid ());
270 if (*aot_profiler
.outfile_name
== '|') {
272 aot_profiler
.outfile
= popen (aot_profiler
.outfile_name
+ 1, "w");
274 g_assert_not_reached ();
276 } else if (*aot_profiler
.outfile_name
== '#') {
277 aot_profiler
.outfile
= fdopen (strtol (aot_profiler
.outfile_name
+ 1, NULL
, 10), "a");
279 aot_profiler
.outfile
= fopen (aot_profiler
.outfile_name
, "w");
282 if (!aot_profiler
.outfile
) {
283 mono_profiler_printf_err ("Could not create AOT profiler output file '%s': %s", aot_profiler
.outfile_name
, g_strerror (errno
));
288 aot_profiler
.images
= g_hash_table_new (NULL
, NULL
);
289 aot_profiler
.classes
= g_hash_table_new (NULL
, NULL
);
290 aot_profiler
.methods
= g_ptr_array_new ();
292 mono_os_mutex_init (&aot_profiler
.mutex
);
294 MonoProfilerHandle handle
= mono_profiler_create (&aot_profiler
);
295 mono_profiler_set_runtime_initialized_callback (handle
, runtime_initialized
);
296 mono_profiler_set_runtime_shutdown_end_callback (handle
, prof_shutdown
);
297 mono_profiler_set_jit_done_callback (handle
, prof_jit_done
);
301 make_room (MonoProfiler
*prof
, int n
)
303 if (prof
->buf_pos
+ n
>= prof
->buf_len
) {
304 int new_len
= prof
->buf_len
* 2;
305 guint8
*new_buf
= g_malloc0 (new_len
);
306 memcpy (new_buf
, prof
->buf
, prof
->buf_pos
);
309 prof
->buf_len
= new_len
;
314 emit_bytes (MonoProfiler
*prof
, guint8
*bytes
, int len
)
316 make_room (prof
, len
);
317 memcpy (prof
->buf
+ prof
->buf_pos
, bytes
, len
);
318 prof
->buf_pos
+= len
;
322 emit_byte (MonoProfiler
*prof
, guint8 value
)
324 emit_bytes (prof
, &value
, 1);
328 emit_int32 (MonoProfiler
*prof
, gint32 value
)
330 for (int i
= 0; i
< sizeof (gint32
); ++i
) {
332 emit_bytes (prof
, &b
, 1);
338 emit_string (MonoProfiler
*prof
, const char *str
)
340 int len
= strlen (str
);
342 emit_int32 (prof
, len
);
343 emit_bytes (prof
, (guint8
*)str
, len
);
347 emit_record (MonoProfiler
*prof
, AotProfRecordType type
, int id
)
349 emit_byte (prof
, type
);
350 emit_int32 (prof
, id
);
354 add_image (MonoProfiler
*prof
, MonoImage
*image
)
356 int id
= GPOINTER_TO_INT (g_hash_table_lookup (prof
->images
, image
));
360 // Dynamic images don't have a GUID set. Moreover, we won't
361 // have a chance to AOT them. (But perhaps they should be
362 // included in the profile, or logged, for diagnostic purposes?)
367 emit_record (prof
, AOTPROF_RECORD_IMAGE
, id
);
368 emit_string (prof
, image
->assembly
->aname
.name
);
369 emit_string (prof
, image
->guid
);
370 g_hash_table_insert (prof
->images
, image
, GINT_TO_POINTER (id
+ 1));
375 add_class (MonoProfiler
*prof
, MonoClass
*klass
);
378 add_type (MonoProfiler
*prof
, MonoType
*type
)
380 switch (type
->type
) {
382 case MONO_TYPE_SZARRAY
: {
383 int eid
= add_type (prof
, m_class_get_byval_arg (type
->data
.klass
));
386 int id
= prof
->id
++;
387 emit_record (prof
, AOTPROF_RECORD_TYPE
, id
);
388 emit_byte (prof
, MONO_TYPE_SZARRAY
);
389 emit_int32 (prof
, id
);
393 case MONO_TYPE_BOOLEAN
:
407 case MONO_TYPE_OBJECT
:
408 case MONO_TYPE_STRING
:
409 case MONO_TYPE_CLASS
:
410 case MONO_TYPE_VALUETYPE
:
411 case MONO_TYPE_GENERICINST
:
412 return add_class (prof
, mono_class_from_mono_type_internal (type
));
419 add_ginst (MonoProfiler
*prof
, MonoGenericInst
*inst
)
425 ids
= g_malloc0 (inst
->type_argc
* sizeof (int));
426 for (i
= 0; i
< inst
->type_argc
; ++i
) {
427 MonoType
*t
= inst
->type_argv
[i
];
428 ids
[i
] = add_type (prof
, t
);
435 emit_record (prof
, AOTPROF_RECORD_GINST
, id
);
436 emit_int32 (prof
, inst
->type_argc
);
437 for (i
= 0; i
< inst
->type_argc
; ++i
)
438 emit_int32 (prof
, ids
[i
]);
445 add_class (MonoProfiler
*prof
, MonoClass
*klass
)
447 int id
, inst_id
= -1, image_id
;
450 id
= GPOINTER_TO_INT (g_hash_table_lookup (prof
->classes
, klass
));
454 image_id
= add_image (prof
, mono_class_get_image (klass
));
458 if (mono_class_is_ginst (klass
)) {
459 MonoGenericContext
*ctx
= mono_class_get_context (klass
);
460 inst_id
= add_ginst (prof
, ctx
->class_inst
);
465 MonoClass
*klass_nested_in
= mono_class_get_nesting_type (klass
);
467 name
= g_strdup_printf ("%s.%s/%s", m_class_get_name_space (klass_nested_in
), m_class_get_name (klass_nested_in
), m_class_get_name (klass
));
469 name
= g_strdup_printf ("%s.%s", m_class_get_name_space (klass
), m_class_get_name (klass
));
472 emit_record (prof
, AOTPROF_RECORD_TYPE
, id
);
473 emit_byte (prof
, MONO_TYPE_CLASS
);
474 emit_int32 (prof
, image_id
);
475 emit_int32 (prof
, inst_id
);
476 emit_string (prof
, name
);
478 g_hash_table_insert (prof
->classes
, klass
, GINT_TO_POINTER (id
+ 1));
483 add_method (MonoProfiler
*prof
, MonoMethod
*m
)
486 MonoMethodSignature
*sig
;
489 sig
= mono_method_signature_checked (m
, error
);
490 g_assert (mono_error_ok (error
));
492 int class_id
= add_class (prof
, m
->klass
);
497 if (m
->is_inflated
) {
498 MonoGenericContext
*ctx
= mono_method_get_context (m
);
499 if (ctx
->method_inst
)
500 inst_id
= add_ginst (prof
, ctx
->method_inst
);
502 int id
= prof
->id
++;
503 emit_record (prof
, AOTPROF_RECORD_METHOD
, id
);
504 emit_int32 (prof
, class_id
);
505 emit_int32 (prof
, inst_id
);
506 emit_int32 (prof
, sig
->param_count
);
507 emit_string (prof
, m
->name
);
508 s
= mono_signature_full_name (sig
);
509 emit_string (prof
, s
);
513 mono_profiler_printf ("%s %d", mono_method_full_name (m
, 1), id
);
516 /* called at the end of the program */
518 prof_shutdown (MonoProfiler
*prof
)
520 mono_os_mutex_lock (&prof
->mutex
);
521 int already_shutdown
= prof
->methods
== NULL
;
522 mono_os_mutex_unlock (&prof
->mutex
);
524 if (already_shutdown
)
530 prof
->buf_len
= 4096;
531 prof
->buf
= g_malloc0 (prof
->buf_len
);
534 gint32 version
= (AOT_PROFILER_MAJOR_VERSION
<< 16) | AOT_PROFILER_MINOR_VERSION
;
535 sprintf (magic
, AOT_PROFILER_MAGIC
);
536 emit_bytes (prof
, (guint8
*)magic
, strlen (magic
));
537 emit_int32 (prof
, version
);
539 GHashTable
*all_methods
= g_hash_table_new (NULL
, NULL
);
540 mono_os_mutex_lock (&prof
->mutex
);
541 for (mindex
= 0; mindex
< prof
->methods
->len
; ++mindex
) {
542 MonoMethod
*m
= (MonoMethod
*)g_ptr_array_index (prof
->methods
, mindex
);
544 if (!mono_method_get_token (m
))
547 if (g_hash_table_lookup (all_methods
, m
))
549 g_hash_table_insert (all_methods
, m
, m
);
551 add_method (prof
, m
);
553 emit_record (prof
, AOTPROF_RECORD_NONE
, 0);
559 MonoMethod
*send_method
= NULL
;
560 MonoMethodSignature
*sig
;
563 g_hash_table_iter_init (&iter
, prof
->images
);
564 while (g_hash_table_iter_next (&iter
, (void**)&image
, (void**)&id
)) {
565 send_method
= mono_method_desc_search_in_image (prof
->send_to
, image
);
570 mono_profiler_printf_err ("Cannot find method in loaded assemblies: '%s'.", prof
->send_to_str
);
574 sig
= mono_method_signature_checked (send_method
, error
);
575 mono_error_assert_ok (error
);
576 if (sig
->param_count
!= 3 || !sig
->params
[0]->byref
|| sig
->params
[0]->type
!= MONO_TYPE_U1
|| sig
->params
[1]->type
!= MONO_TYPE_I4
|| sig
->params
[2]->type
!= MONO_TYPE_STRING
) {
577 mono_profiler_printf_err ("Method '%s' should have signature void (byte&,int,string).", prof
->send_to_str
);
581 // Don't collect data from the call
582 prof
->disable
= TRUE
;
584 MonoString
*extra_arg
= NULL
;
585 if (prof
->send_to_arg
) {
586 extra_arg
= mono_string_new_checked (mono_domain_get (), prof
->send_to_arg
, error
);
587 mono_error_assert_ok (error
);
592 int len
= prof
->buf_pos
;
593 void *ptr
= prof
->buf
;
596 args
[2] = extra_arg
;
598 printf ("aot-profiler | Passing data to '%s': %p %d %s\n", mono_method_full_name (send_method
, 1), args
[0], len
, extra_arg
);
599 mono_runtime_try_invoke (send_method
, NULL
, args
, &exc
, error
);
600 mono_error_assert_ok (error
);
601 g_assert (exc
== NULL
);
603 g_assert (prof
->outfile
);
604 fwrite (prof
->buf
, 1, prof
->buf_pos
, prof
->outfile
);
605 fclose (prof
->outfile
);
607 mono_profiler_printf ("AOT profiler data written to '%s'", prof
->outfile_name
);
610 g_hash_table_destroy (all_methods
);
611 g_hash_table_destroy (prof
->classes
);
612 g_hash_table_destroy (prof
->images
);
613 g_ptr_array_free (prof
->methods
, TRUE
);
614 g_free (prof
->outfile_name
);
616 prof
->methods
= NULL
;
617 mono_os_mutex_unlock (&prof
->mutex
);