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.
14 #include <mono/metadata/object-internals.h>
15 #include <mono/metadata/profiler.h>
16 #include <mono/metadata/tokentype.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/metadata/debug-helpers.h>
19 #include <mono/metadata/assembly.h>
20 #include <mono/metadata/class-internals.h>
21 #include <mono/metadata/debug-helpers.h>
22 #include <mono/utils/mono-publib.h>
23 #include <mono/mini/jit.h>
24 #include <mono/utils/mono-logger-internals.h>
25 #include <mono/utils/mono-os-mutex.h>
26 #include <mono/utils/mono-threads.h>
31 #include <sys/socket.h>
35 struct _MonoProfiler
{
45 MonoMethodDesc
*write_at
;
46 MonoMethodDesc
*send_to
;
56 static MonoProfiler aot_profiler
;
59 prof_shutdown (MonoProfiler
*prof
);
62 prof_jit_done (MonoProfiler
*prof
, MonoMethod
*method
, MonoJitInfo
*jinfo
)
64 MonoImage
*image
= mono_class_get_image (mono_method_get_class (method
));
66 if (!image
->assembly
|| method
->wrapper_type
|| !prof
->methods
|| prof
->disable
)
69 if (prof
->write_at
&& mono_method_desc_match (prof
->write_at
, method
)) {
70 printf ("aot-profiler | Writing data at: '%s'.\n", mono_method_full_name (method
, 1));
75 mono_os_mutex_lock (&prof
->mutex
);
77 g_ptr_array_add (prof
->methods
, method
);
78 mono_os_mutex_unlock (&prof
->mutex
);
84 mono_profiler_printf ("AOT profiler.");
85 mono_profiler_printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
86 mono_profiler_printf ("Options:");
87 mono_profiler_printf ("\tduration=NUM profile only NUM seconds of runtime and write the data");
88 mono_profiler_printf ("\thelp show this usage info");
89 mono_profiler_printf ("\toutput=FILENAME write the data to file FILENAME");
90 mono_profiler_printf ("\tport=PORT use PORT to listen for command server connections");
91 mono_profiler_printf ("\twrite-at-method=METHOD write the data when METHOD is compiled.");
92 mono_profiler_printf ("\tsend-to-method=METHOD call METHOD with the collected data.");
93 mono_profiler_printf ("\tsend-to-arg=STR extra argument to pass to METHOD.");
94 mono_profiler_printf ("\tverbose print diagnostic info");
100 match_option (const char *arg
, const char *opt_name
, const char **rval
)
103 const char *end
= strchr (arg
, '=');
107 return !strcmp (arg
, opt_name
);
109 if (strncmp (arg
, opt_name
, strlen (opt_name
)) || (end
- arg
) > strlen (opt_name
) + 1)
114 //FIXME how should we handle passing a value to an arg that doesn't expect it?
115 return !strcmp (arg
, opt_name
);
120 parse_arg (const char *arg
)
124 if (match_option (arg
, "help", NULL
)) {
126 } else if (match_option (arg
, "duration", &val
)) {
128 aot_profiler
.duration
= strtoul (val
, &end
, 10);
129 } else if (match_option (arg
, "write-at-method", &val
)) {
130 aot_profiler
.write_at
= mono_method_desc_new (val
, TRUE
);
131 if (!aot_profiler
.write_at
) {
132 mono_profiler_printf_err ("Could not parse method description: %s", val
);
135 } else if (match_option (arg
, "send-to-method", &val
)) {
136 aot_profiler
.send_to
= mono_method_desc_new (val
, TRUE
);
137 if (!aot_profiler
.send_to
) {
138 mono_profiler_printf_err ("Could not parse method description: %s", val
);
141 aot_profiler
.send_to_str
= strdup (val
);
142 } else if (match_option (arg
, "send-to-arg", &val
)) {
143 aot_profiler
.send_to_arg
= strdup (val
);
144 } else if (match_option (arg
, "output", &val
)) {
145 aot_profiler
.outfile_name
= g_strdup (val
);
146 } else if (match_option (arg
, "port", &val
)) {
148 aot_profiler
.command_port
= strtoul (val
, &end
, 10);
149 } else if (match_option (arg
, "verbose", NULL
)) {
150 aot_profiler
.verbose
= TRUE
;
152 mono_profiler_printf_err ("Could not parse argument: %s", arg
);
157 parse_args (const char *desc
)
160 gboolean in_quotes
= FALSE
;
161 char quote_char
= '\0';
162 char *buffer
= g_malloc (strlen (desc
) + 1);
165 for (p
= desc
; *p
; p
++){
169 if (buffer_pos
!= 0){
170 buffer
[buffer_pos
] = 0;
175 buffer
[buffer_pos
++] = *p
;
181 buffer
[buffer_pos
++] = p
[1];
188 if (quote_char
== *p
)
191 buffer
[buffer_pos
++] = *p
;
198 buffer
[buffer_pos
++] = *p
;
203 if (buffer_pos
!= 0) {
204 buffer
[buffer_pos
] = 0;
211 static void prof_save (MonoProfiler
*prof
, FILE* file
);
214 helper_thread (void *arg
)
216 mono_thread_attach (mono_get_root_domain ());
218 mono_thread_set_name_constant_ignore_error (mono_thread_internal_current (), "AOT Profiler Helper", MonoSetThreadNameFlag_None
);
220 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC
| MONO_THREAD_INFO_FLAGS_NO_SAMPLE
);
222 if (aot_profiler
.duration
>= 0) {
223 sleep (aot_profiler
.duration
);
224 } else if (aot_profiler
.command_port
>= 0) {
225 GArray
*command_sockets
= g_array_new (FALSE
, FALSE
, sizeof (int));
230 int quit_command_received
= 0;
234 mono_profhelper_add_to_fd_set (&rfds
, aot_profiler
.server_socket
, &max_fd
);
236 for (gint i
= 0; i
< command_sockets
->len
; i
++)
237 mono_profhelper_add_to_fd_set (&rfds
, g_array_index (command_sockets
, int, i
), &max_fd
);
239 struct timeval tv
= { .tv_sec
= 1, .tv_usec
= 0 };
241 // Sleep for 1sec or until a file descriptor has data.
242 if (select (max_fd
+ 1, &rfds
, NULL
, NULL
, &tv
) == -1) {
246 mono_profiler_printf_err ("Could not poll in aot profiler helper thread: %s", g_strerror (errno
));
250 for (gint i
= 0; i
< command_sockets
->len
; i
++) {
251 int fd
= g_array_index (command_sockets
, int, i
);
253 if (!FD_ISSET (fd
, &rfds
))
257 int len
= read (fd
, buf
, sizeof (buf
) - 1);
263 // The other end disconnected.
264 g_array_remove_index (command_sockets
, i
);
273 if (!strcmp (buf
, "save\n")) {
274 FILE* file
= fdopen (fd
, "w");
276 prof_save (&aot_profiler
, file
);
280 mono_profiler_printf_err ("aot profiler data saved to the socket");
282 g_array_remove_index (command_sockets
, i
);
286 } else if (!strcmp (buf
, "quit\n")) {
287 quit_command_received
= 1;
291 if (quit_command_received
)
294 if (FD_ISSET (aot_profiler
.server_socket
, &rfds
)) {
295 int fd
= accept (aot_profiler
.server_socket
, NULL
, NULL
);
298 if (fd
>= FD_SETSIZE
)
301 g_array_append_val (command_sockets
, fd
);
306 for (gint i
= 0; i
< command_sockets
->len
; i
++)
307 close (g_array_index (command_sockets
, int, i
));
309 g_array_free (command_sockets
, TRUE
);
312 prof_shutdown (&aot_profiler
);
314 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE
);
315 mono_thread_detach (mono_thread_current ());
321 start_helper_thread (void)
323 if (aot_profiler
.command_port
>= 0)
324 mono_profhelper_setup_command_server (&aot_profiler
.server_socket
, &aot_profiler
.command_port
, "aot");
326 MonoNativeThreadId thread_id
;
328 if (!mono_native_thread_create (&thread_id
, helper_thread
, NULL
)) {
329 mono_profiler_printf_err ("Could not start aot profiler helper thread");
335 runtime_initialized (MonoProfiler
*profiler
)
337 if (profiler
->duration
>= 0 || aot_profiler
.command_port
>= 0)
338 start_helper_thread ();
342 mono_profiler_init_aot (const char *desc
);
345 * mono_profiler_init_aot:
349 mono_profiler_init_aot (const char *desc
)
351 if (mono_jit_aot_compiling ()) {
352 mono_profiler_printf_err ("The AOT profiler is not meant to be run during AOT compilation.");
356 aot_profiler
.duration
= -1;
357 aot_profiler
.command_port
= -1;
358 aot_profiler
.outfile_name
= NULL
;
359 aot_profiler
.outfile
= NULL
;
361 parse_args (desc
[strlen ("aot")] == ':' ? desc
+ strlen ("aot") + 1 : "");
363 if (!aot_profiler
.send_to
) {
364 if (!aot_profiler
.outfile_name
)
365 aot_profiler
.outfile_name
= g_strdup ("output.aotprofile");
366 else if (*aot_profiler
.outfile_name
== '+')
367 aot_profiler
.outfile_name
= g_strdup_printf ("%s.%d", aot_profiler
.outfile_name
+ 1, getpid ());
369 if (*aot_profiler
.outfile_name
== '|') {
371 aot_profiler
.outfile
= popen (aot_profiler
.outfile_name
+ 1, "w");
373 g_assert_not_reached ();
375 } else if (*aot_profiler
.outfile_name
== '#') {
376 aot_profiler
.outfile
= fdopen (strtol (aot_profiler
.outfile_name
+ 1, NULL
, 10), "a");
378 aot_profiler
.outfile
= fopen (aot_profiler
.outfile_name
, "w");
381 if (!aot_profiler
.outfile
&& aot_profiler
.outfile_name
) {
382 mono_profiler_printf_err ("Could not create AOT profiler output file '%s': %s", aot_profiler
.outfile_name
, g_strerror (errno
));
387 aot_profiler
.images
= g_hash_table_new (NULL
, NULL
);
388 aot_profiler
.classes
= g_hash_table_new (NULL
, NULL
);
389 aot_profiler
.methods
= g_ptr_array_new ();
391 mono_os_mutex_init (&aot_profiler
.mutex
);
393 MonoProfilerHandle handle
= mono_profiler_create (&aot_profiler
);
394 mono_profiler_set_runtime_initialized_callback (handle
, runtime_initialized
);
395 mono_profiler_set_runtime_shutdown_end_callback (handle
, prof_shutdown
);
396 mono_profiler_set_jit_done_callback (handle
, prof_jit_done
);
400 make_room (MonoProfiler
*prof
, int n
)
402 if (prof
->buf_pos
+ n
>= prof
->buf_len
) {
403 int new_len
= prof
->buf_len
* 2;
404 guint8
*new_buf
= g_malloc0 (new_len
);
405 memcpy (new_buf
, prof
->buf
, prof
->buf_pos
);
408 prof
->buf_len
= new_len
;
413 emit_bytes (MonoProfiler
*prof
, guint8
*bytes
, int len
)
415 make_room (prof
, len
);
416 memcpy (prof
->buf
+ prof
->buf_pos
, bytes
, len
);
417 prof
->buf_pos
+= len
;
421 emit_byte (MonoProfiler
*prof
, guint8 value
)
423 emit_bytes (prof
, &value
, 1);
427 emit_int32 (MonoProfiler
*prof
, gint32 value
)
429 for (int i
= 0; i
< sizeof (gint32
); ++i
) {
431 emit_bytes (prof
, &b
, 1);
437 emit_string (MonoProfiler
*prof
, const char *str
)
439 int len
= strlen (str
);
441 emit_int32 (prof
, len
);
442 emit_bytes (prof
, (guint8
*)str
, len
);
446 emit_record (MonoProfiler
*prof
, AotProfRecordType type
, int id
)
448 emit_byte (prof
, type
);
449 emit_int32 (prof
, id
);
453 add_image (MonoProfiler
*prof
, MonoImage
*image
)
455 int id
= GPOINTER_TO_INT (g_hash_table_lookup (prof
->images
, image
));
459 // Dynamic images don't have a GUID set. Moreover, we won't
460 // have a chance to AOT them. (But perhaps they should be
461 // included in the profile, or logged, for diagnostic purposes?)
466 emit_record (prof
, AOTPROF_RECORD_IMAGE
, id
);
467 emit_string (prof
, image
->assembly
->aname
.name
);
468 emit_string (prof
, image
->guid
);
469 g_hash_table_insert (prof
->images
, image
, GINT_TO_POINTER (id
+ 1));
474 add_class (MonoProfiler
*prof
, MonoClass
*klass
);
477 add_type (MonoProfiler
*prof
, MonoType
*type
)
479 switch (type
->type
) {
481 case MONO_TYPE_SZARRAY
: {
482 int eid
= add_type (prof
, m_class_get_byval_arg (type
->data
.klass
));
485 int id
= prof
->id
++;
486 emit_record (prof
, AOTPROF_RECORD_TYPE
, id
);
487 emit_byte (prof
, MONO_TYPE_SZARRAY
);
488 emit_int32 (prof
, id
);
492 case MONO_TYPE_BOOLEAN
:
506 case MONO_TYPE_OBJECT
:
507 case MONO_TYPE_STRING
:
508 case MONO_TYPE_CLASS
:
509 case MONO_TYPE_VALUETYPE
:
510 case MONO_TYPE_GENERICINST
:
511 return add_class (prof
, mono_class_from_mono_type_internal (type
));
518 add_ginst (MonoProfiler
*prof
, MonoGenericInst
*inst
)
524 ids
= g_malloc0 (inst
->type_argc
* sizeof (int));
525 for (i
= 0; i
< inst
->type_argc
; ++i
) {
526 MonoType
*t
= inst
->type_argv
[i
];
527 ids
[i
] = add_type (prof
, t
);
534 emit_record (prof
, AOTPROF_RECORD_GINST
, id
);
535 emit_int32 (prof
, inst
->type_argc
);
536 for (i
= 0; i
< inst
->type_argc
; ++i
)
537 emit_int32 (prof
, ids
[i
]);
544 add_class (MonoProfiler
*prof
, MonoClass
*klass
)
546 int id
, inst_id
= -1, image_id
;
549 id
= GPOINTER_TO_INT (g_hash_table_lookup (prof
->classes
, klass
));
553 image_id
= add_image (prof
, mono_class_get_image (klass
));
557 if (mono_class_is_ginst (klass
)) {
558 MonoGenericContext
*ctx
= mono_class_get_context (klass
);
559 inst_id
= add_ginst (prof
, ctx
->class_inst
);
564 MonoClass
*klass_nested_in
= mono_class_get_nesting_type (klass
);
566 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
));
568 name
= g_strdup_printf ("%s.%s", m_class_get_name_space (klass
), m_class_get_name (klass
));
571 emit_record (prof
, AOTPROF_RECORD_TYPE
, id
);
572 emit_byte (prof
, MONO_TYPE_CLASS
);
573 emit_int32 (prof
, image_id
);
574 emit_int32 (prof
, inst_id
);
575 emit_string (prof
, name
);
577 g_hash_table_insert (prof
->classes
, klass
, GINT_TO_POINTER (id
+ 1));
582 add_method (MonoProfiler
*prof
, MonoMethod
*m
)
585 MonoMethodSignature
*sig
;
588 sig
= mono_method_signature_checked (m
, error
);
589 g_assert (is_ok (error
));
591 int class_id
= add_class (prof
, m
->klass
);
596 if (m
->is_inflated
) {
597 MonoGenericContext
*ctx
= mono_method_get_context (m
);
598 if (ctx
->method_inst
)
599 inst_id
= add_ginst (prof
, ctx
->method_inst
);
601 int id
= prof
->id
++;
602 emit_record (prof
, AOTPROF_RECORD_METHOD
, id
);
603 emit_int32 (prof
, class_id
);
604 emit_int32 (prof
, inst_id
);
605 emit_int32 (prof
, sig
->param_count
);
606 emit_string (prof
, m
->name
);
607 s
= mono_signature_full_name (sig
);
608 emit_string (prof
, s
);
612 mono_profiler_printf ("%s %d", mono_method_full_name (m
, 1), id
);
616 prof_save (MonoProfiler
*prof
, FILE* file
)
618 mono_os_mutex_lock (&prof
->mutex
);
619 int already_shutdown
= prof
->methods
== NULL
;
620 mono_os_mutex_unlock (&prof
->mutex
);
622 if (already_shutdown
)
628 prof
->buf_len
= 4096;
629 prof
->buf
= g_malloc0 (prof
->buf_len
);
632 gint32 version
= (AOT_PROFILER_MAJOR_VERSION
<< 16) | AOT_PROFILER_MINOR_VERSION
;
633 sprintf (magic
, AOT_PROFILER_MAGIC
);
634 emit_bytes (prof
, (guint8
*)magic
, strlen (magic
));
635 emit_int32 (prof
, version
);
637 GHashTable
*all_methods
= g_hash_table_new (NULL
, NULL
);
638 mono_os_mutex_lock (&prof
->mutex
);
639 for (mindex
= 0; mindex
< prof
->methods
->len
; ++mindex
) {
640 MonoMethod
*m
= (MonoMethod
*)g_ptr_array_index (prof
->methods
, mindex
);
642 if (!mono_method_get_token (m
))
645 if (g_hash_table_lookup (all_methods
, m
))
647 g_hash_table_insert (all_methods
, m
, m
);
649 add_method (prof
, m
);
651 emit_record (prof
, AOTPROF_RECORD_NONE
, 0);
657 MonoMethod
*send_method
= NULL
;
658 MonoMethodSignature
*sig
;
661 g_hash_table_iter_init (&iter
, prof
->images
);
662 while (g_hash_table_iter_next (&iter
, (void**)&image
, (void**)&id
)) {
663 send_method
= mono_method_desc_search_in_image (prof
->send_to
, image
);
668 mono_profiler_printf_err ("Cannot find method in loaded assemblies: '%s'.", prof
->send_to_str
);
672 sig
= mono_method_signature_checked (send_method
, error
);
673 mono_error_assert_ok (error
);
674 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
) {
675 mono_profiler_printf_err ("Method '%s' should have signature void (byte&,int,string).", prof
->send_to_str
);
679 // Don't collect data from the call
680 prof
->disable
= TRUE
;
682 MonoString
*extra_arg
= NULL
;
683 if (prof
->send_to_arg
) {
684 extra_arg
= mono_string_new_checked (mono_domain_get (), prof
->send_to_arg
, error
);
685 mono_error_assert_ok (error
);
690 int len
= prof
->buf_pos
;
691 void *ptr
= prof
->buf
;
694 args
[2] = extra_arg
;
696 printf ("aot-profiler | Passing data to '%s': %p %d %s\n", mono_method_full_name (send_method
, 1), args
[0], len
, prof
->send_to_arg
? prof
->send_to_arg
: "(null)");
697 mono_runtime_try_invoke (send_method
, NULL
, args
, &exc
, error
);
698 mono_error_assert_ok (error
);
699 g_assert (exc
== NULL
);
702 fwrite (prof
->buf
, 1, prof
->buf_pos
, file
);
705 mono_profiler_printf ("AOT profiler data written to '%s'", prof
->command_port
>= 0 ? "socket" : prof
->outfile_name
);
708 g_hash_table_destroy (all_methods
);
710 g_hash_table_remove_all (prof
->classes
);
711 g_hash_table_remove_all (prof
->images
);
713 mono_os_mutex_unlock (&prof
->mutex
);
716 /* called at the end of the program */
718 prof_shutdown (MonoProfiler
*prof
)
720 if (prof
->outfile
|| prof
->send_to
) {
721 prof_save (prof
, prof
->outfile
);
723 fclose (prof
->outfile
);
726 mono_os_mutex_lock (&prof
->mutex
);
728 g_hash_table_destroy (prof
->classes
);
729 g_hash_table_destroy (prof
->images
);
730 g_ptr_array_free (prof
->methods
, TRUE
);
731 g_free (prof
->outfile_name
);
733 prof
->methods
= NULL
;
734 mono_os_mutex_unlock (&prof
->mutex
);