Fix potential crash for Encoder.Convert (#20522)
[mono-project.git] / mono / profiler / aot.c
blobd919dda2737077c3b69134877ccf229c6378705c
1 /*
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.
7 */
9 #include <config.h>
11 #include "aot.h"
12 #include "helper.h"
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>
27 #include <string.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #ifndef HOST_WIN32
31 #include <sys/socket.h>
32 #endif
33 #include <glib.h>
35 struct _MonoProfiler {
36 GHashTable *classes;
37 GHashTable *images;
38 GPtrArray *methods;
39 FILE *outfile;
40 int id;
41 char *outfile_name;
42 mono_mutex_t mutex;
43 gboolean verbose;
44 int duration;
45 MonoMethodDesc *write_at;
46 MonoMethodDesc *send_to;
47 char *send_to_arg;
48 char *send_to_str;
49 guint8 *buf;
50 gboolean disable;
51 int buf_pos, buf_len;
52 int command_port;
53 int server_socket;
56 static MonoProfiler aot_profiler;
58 static void
59 prof_shutdown (MonoProfiler *prof);
61 static void
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)
67 return;
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));
71 prof_shutdown (prof);
72 return;
75 mono_os_mutex_lock (&prof->mutex);
76 if (prof->methods)
77 g_ptr_array_add (prof->methods, method);
78 mono_os_mutex_unlock (&prof->mutex);
81 static void
82 usage (void)
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");
96 exit (0);
99 static gboolean
100 match_option (const char *arg, const char *opt_name, const char **rval)
102 if (rval) {
103 const char *end = strchr (arg, '=');
105 *rval = NULL;
106 if (!end)
107 return !strcmp (arg, opt_name);
109 if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
110 return FALSE;
111 *rval = end + 1;
112 return TRUE;
113 } else {
114 //FIXME how should we handle passing a value to an arg that doesn't expect it?
115 return !strcmp (arg, opt_name);
119 static void
120 parse_arg (const char *arg)
122 const char *val;
124 if (match_option (arg, "help", NULL)) {
125 usage ();
126 } else if (match_option (arg, "duration", &val)) {
127 char *end;
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);
133 exit (1);
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);
139 exit (1);
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)) {
147 char *end;
148 aot_profiler.command_port = strtoul (val, &end, 10);
149 } else if (match_option (arg, "verbose", NULL)) {
150 aot_profiler.verbose = TRUE;
151 } else {
152 mono_profiler_printf_err ("Could not parse argument: %s", arg);
156 static void
157 parse_args (const char *desc)
159 const char *p;
160 gboolean in_quotes = FALSE;
161 char quote_char = '\0';
162 char *buffer = g_malloc (strlen (desc) + 1);
163 int buffer_pos = 0;
165 for (p = desc; *p; p++){
166 switch (*p){
167 case ',':
168 if (!in_quotes) {
169 if (buffer_pos != 0){
170 buffer [buffer_pos] = 0;
171 parse_arg (buffer);
172 buffer_pos = 0;
174 } else {
175 buffer [buffer_pos++] = *p;
177 break;
179 case '\\':
180 if (p [1]) {
181 buffer [buffer_pos++] = p[1];
182 p++;
184 break;
185 case '\'':
186 case '"':
187 if (in_quotes) {
188 if (quote_char == *p)
189 in_quotes = FALSE;
190 else
191 buffer [buffer_pos++] = *p;
192 } else {
193 in_quotes = TRUE;
194 quote_char = *p;
196 break;
197 default:
198 buffer [buffer_pos++] = *p;
199 break;
203 if (buffer_pos != 0) {
204 buffer [buffer_pos] = 0;
205 parse_arg (buffer);
208 g_free (buffer);
211 static void prof_save (MonoProfiler *prof, FILE* file);
213 static void *
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));
227 while (1) {
228 fd_set rfds;
229 int max_fd = -1;
230 int quit_command_received = 0;
232 FD_ZERO (&rfds);
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) {
243 if (errno == EINTR)
244 continue;
246 mono_profiler_printf_err ("Could not poll in aot profiler helper thread: %s", g_strerror (errno));
247 exit (1);
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))
254 continue;
256 char buf [64];
257 int len = read (fd, buf, sizeof (buf) - 1);
259 if (len == -1)
260 continue;
262 if (!len) {
263 // The other end disconnected.
264 g_array_remove_index (command_sockets, i);
265 i--;
266 close (fd);
268 continue;
271 buf [len] = 0;
273 if (!strcmp (buf, "save\n")) {
274 FILE* file = fdopen (fd, "w");
276 prof_save (&aot_profiler, file);
278 fclose (file);
280 mono_profiler_printf_err ("aot profiler data saved to the socket");
282 g_array_remove_index (command_sockets, i);
283 i--;
285 continue;
286 } else if (!strcmp (buf, "quit\n")) {
287 quit_command_received = 1;
291 if (quit_command_received)
292 break;
294 if (FD_ISSET (aot_profiler.server_socket, &rfds)) {
295 int fd = accept (aot_profiler.server_socket, NULL, NULL);
297 if (fd != -1) {
298 if (fd >= FD_SETSIZE)
299 close (fd);
300 else
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 ());
317 return NULL;
320 static void
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");
330 exit (1);
334 static void
335 runtime_initialized (MonoProfiler *profiler)
337 if (profiler->duration >= 0 || aot_profiler.command_port >= 0)
338 start_helper_thread ();
341 MONO_API void
342 mono_profiler_init_aot (const char *desc);
345 * mono_profiler_init_aot:
346 * the entry point
348 void
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.");
353 exit (1);
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 == '|') {
370 #ifdef HAVE_POPEN
371 aot_profiler.outfile = popen (aot_profiler.outfile_name + 1, "w");
372 #else
373 g_assert_not_reached ();
374 #endif
375 } else if (*aot_profiler.outfile_name == '#') {
376 aot_profiler.outfile = fdopen (strtol (aot_profiler.outfile_name + 1, NULL, 10), "a");
377 } else {
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));
383 exit (1);
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);
399 static void
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);
406 g_free (prof->buf);
407 prof->buf = new_buf;
408 prof->buf_len = new_len;
412 static void
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;
420 static void
421 emit_byte (MonoProfiler *prof, guint8 value)
423 emit_bytes (prof, &value, 1);
426 static void
427 emit_int32 (MonoProfiler *prof, gint32 value)
429 for (int i = 0; i < sizeof (gint32); ++i) {
430 guint8 b = value;
431 emit_bytes (prof, &b, 1);
432 value >>= 8;
436 static void
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);
445 static void
446 emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
448 emit_byte (prof, type);
449 emit_int32 (prof, id);
452 static int
453 add_image (MonoProfiler *prof, MonoImage *image)
455 int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
456 if (id)
457 return id - 1;
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?)
462 if (!image->guid)
463 return -1;
465 id = prof->id ++;
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));
470 return id;
473 static int
474 add_class (MonoProfiler *prof, MonoClass *klass);
476 static int
477 add_type (MonoProfiler *prof, MonoType *type)
479 switch (type->type) {
480 #if 0
481 case MONO_TYPE_SZARRAY: {
482 int eid = add_type (prof, m_class_get_byval_arg (type->data.klass));
483 if (eid == -1)
484 return -1;
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);
489 return id;
491 #endif
492 case MONO_TYPE_BOOLEAN:
493 case MONO_TYPE_CHAR:
494 case MONO_TYPE_I1:
495 case MONO_TYPE_U1:
496 case MONO_TYPE_I2:
497 case MONO_TYPE_U2:
498 case MONO_TYPE_I4:
499 case MONO_TYPE_U4:
500 case MONO_TYPE_I8:
501 case MONO_TYPE_U8:
502 case MONO_TYPE_R4:
503 case MONO_TYPE_R8:
504 case MONO_TYPE_I:
505 case MONO_TYPE_U:
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));
512 default:
513 return -1;
517 static int
518 add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
520 int i, id;
521 int *ids;
523 // FIXME: Cache
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);
528 if (ids [i] == -1) {
529 g_free (ids);
530 return -1;
533 id = prof->id ++;
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]);
538 g_free (ids);
540 return id;
543 static int
544 add_class (MonoProfiler *prof, MonoClass *klass)
546 int id, inst_id = -1, image_id;
547 char *name;
549 id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
550 if (id)
551 return id - 1;
553 image_id = add_image (prof, mono_class_get_image (klass));
554 if (image_id == -1)
555 return -1;
557 if (mono_class_is_ginst (klass)) {
558 MonoGenericContext *ctx = mono_class_get_context (klass);
559 inst_id = add_ginst (prof, ctx->class_inst);
560 if (inst_id == -1)
561 return -1;
564 MonoClass *klass_nested_in = mono_class_get_nesting_type (klass);
565 if (klass_nested_in)
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));
567 else
568 name = g_strdup_printf ("%s.%s", m_class_get_name_space (klass), m_class_get_name (klass));
570 id = prof->id ++;
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);
576 g_free (name);
577 g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
578 return id;
581 static void
582 add_method (MonoProfiler *prof, MonoMethod *m)
584 ERROR_DECL (error);
585 MonoMethodSignature *sig;
586 char *s;
588 sig = mono_method_signature_checked (m, error);
589 g_assert (is_ok (error));
591 int class_id = add_class (prof, m->klass);
592 if (class_id == -1)
593 return;
594 int inst_id = -1;
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);
609 g_free (s);
611 if (prof->verbose)
612 mono_profiler_printf ("%s %d", mono_method_full_name (m, 1), id);
615 static void
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)
623 return;
625 int mindex;
626 char magic [32];
628 prof->buf_len = 4096;
629 prof->buf = g_malloc0 (prof->buf_len);
630 prof->buf_pos = 0;
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))
643 continue;
645 if (g_hash_table_lookup (all_methods, m))
646 continue;
647 g_hash_table_insert (all_methods, m, m);
649 add_method (prof, m);
651 emit_record (prof, AOTPROF_RECORD_NONE, 0);
653 if (prof->send_to) {
654 GHashTableIter iter;
655 gpointer id;
656 MonoImage *image;
657 MonoMethod *send_method = NULL;
658 MonoMethodSignature *sig;
659 ERROR_DECL (error);
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);
664 if (send_method)
665 break;
667 if (!send_method) {
668 mono_profiler_printf_err ("Cannot find method in loaded assemblies: '%s'.", prof->send_to_str);
669 exit (1);
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);
676 exit (1);
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);
688 MonoObject *exc;
689 gpointer args [3];
690 int len = prof->buf_pos;
691 void *ptr = prof->buf;
692 args [0] = ptr;
693 args [1] = &len;
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);
700 } else {
701 g_assert (file);
702 fwrite (prof->buf, 1, prof->buf_pos, file);
703 fclose (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 */
717 static void
718 prof_shutdown (MonoProfiler *prof)
720 if (prof->outfile || prof->send_to) {
721 prof_save (prof, prof->outfile);
722 if (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);