[aot] Rewrite the AOT profiler. (#4176)
[mono.git] / mono / profiler / mono-profiler-aot.c
index 4cf48bbd1b2491a54e3155c2c793b1934d5bb441..05ed0758ed2cfff00dea1280523e4e3abc7495c9 100644 (file)
  */
 
 #include <config.h>
+
+#include "mono-profiler-aot.h"
+
 #include <mono/metadata/profiler.h>
 #include <mono/metadata/tokentype.h>
 #include <mono/metadata/tabledefs.h>
 #include <mono/metadata/debug-helpers.h>
 #include <mono/metadata/assembly.h>
+#include <mono/metadata/class-internals.h>
+#include <mono/utils/mono-os-mutex.h>
 #include <string.h>
 #include <errno.h>
 #include <stdlib.h>
 #endif
 
 struct _MonoProfiler {
+       GHashTable *classes;
        GHashTable *images;
+       GPtrArray *methods;
+       FILE *outfile;
+       int id;
+       char *outfile_name;
 };
 
-typedef struct {
-       GList *methods;
-} PerImageData;
+static mono_mutex_t mutex;
+static gboolean verbose;
 
-typedef struct ForeachData {
-       MonoProfiler *prof;
-       FILE *outfile;
-       MonoImage *image;
-       MonoMethod *method;
-} ForeachData;
+static void
+prof_jit_enter (MonoProfiler *prof, MonoMethod *method)
+{
+}
 
 static void
-foreach_method (gpointer data, gpointer user_data)
+prof_jit_leave (MonoProfiler *prof, MonoMethod *method, int result)
 {
-       ForeachData *udata = (ForeachData*)user_data;
-       MonoMethod *method = (MonoMethod*)data;
-       char *name;
+       MonoImage *image = mono_class_get_image (mono_method_get_class (method));
 
-       if (!mono_method_get_token (method) || mono_class_get_image (mono_method_get_class (method)) != udata->image)
+       if (!image->assembly || method->wrapper_type)
                return;
 
-       name = mono_method_full_name (method, TRUE);
-       fprintf (udata->outfile, "%s\n", name);
-       g_free (name);
+       mono_os_mutex_lock (&mutex);
+       g_ptr_array_add (prof->methods, method);
+       mono_os_mutex_unlock (&mutex);
 }
 
 static void
-output_image (gpointer key, gpointer value, gpointer user_data)
-{
-       MonoImage *image = (MonoImage*)key;
-       PerImageData *image_data = (PerImageData*)value;
-       MonoProfiler *prof = (MonoProfiler*)user_data;
-       char *tmp, *outfile_name;
-       FILE *outfile;
-       int i, err;
-       ForeachData data;
+prof_shutdown (MonoProfiler *prof);
 
-       tmp = g_strdup_printf ("%s/.mono/aot-profile-data", g_get_home_dir ());
+static void
+usage (int do_exit)
+{
+       printf ("AOT profiler.\n");
+       printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
+       printf ("Options:\n");
+       printf ("\thelp                 show this usage info\n");
+       printf ("\toutput=FILENAME      write the data to file FILENAME (required)\n");
+       printf ("\tverbose              print diagnostic info\n");
+       if (do_exit)
+               exit (1);
+}
 
-       if (!g_file_test (tmp, G_FILE_TEST_IS_DIR)) {
-#ifdef HOST_WIN32
-               err = mkdir (tmp);
-#else
-               err = mkdir (tmp, 0777);
-#endif
-               if (err) {
-                       fprintf (stderr, "mono-profiler-aot: Unable to create output directory '%s': %s\n", tmp, g_strerror (errno));
-                       exit (1);
+static const char*
+match_option (const char* p, const char *opt, char **rval)
+{
+       int len = strlen (opt);
+       if (strncmp (p, opt, len) == 0) {
+               if (rval) {
+                       if (p [len] == '=' && p [len + 1]) {
+                               const char *opt = p + len + 1;
+                               const char *end = strchr (opt, ',');
+                               char *val;
+                               int l;
+                               if (end == NULL) {
+                                       l = strlen (opt);
+                               } else {
+                                       l = end - opt;
+                               }
+                               val = (char *) g_malloc (l + 1);
+                               memcpy (val, opt, l);
+                               val [l] = 0;
+                               *rval = val;
+                               return opt + l;
+                       }
+                       if (p [len] == 0 || p [len] == ',') {
+                               *rval = NULL;
+                               return p + len + (p [len] == ',');
+                       }
+                       usage (1);
+               } else {
+                       if (p [len] == 0)
+                               return p + len;
+                       if (p [len] == ',')
+                               return p + len + 1;
                }
        }
+       return p;
+}
 
-       i = 0;
-       while (TRUE) {
-               outfile_name = g_strdup_printf ("%s/%s-%d", tmp, mono_image_get_name (image), i);
+void
+mono_profiler_startup (const char *desc);
 
-               if (!g_file_test (outfile_name, G_FILE_TEST_IS_REGULAR))
-                       break;
+/* the entry point */
+void
+mono_profiler_startup (const char *desc)
+{
+       MonoProfiler *prof;
+       const char *p;
+       const char *opt;
+       char *outfile_name;
 
-               i ++;
+       p = desc;
+       if (strncmp (p, "aot", 3))
+               usage (1);
+       p += 3;
+       if (*p == ':')
+               p++;
+       for (; *p; p = opt) {
+               char *val;
+               if (*p == ',') {
+                       opt = p + 1;
+                       continue;
+               }
+               if ((opt = match_option (p, "help", NULL)) != p) {
+                       usage (0);
+                       continue;
+               }
+               if ((opt = match_option (p, "verbose", NULL)) != p) {
+                       verbose = TRUE;
+                       continue;
+               }
+               if ((opt = match_option (p, "output", &val)) != p) {
+                       outfile_name = val;
+                       continue;
+               }
+               fprintf (stderr, "mono-profiler-aot: Unknown option: '%s'.\n", p);
+               exit (1);
        }
 
-       printf ("Creating output file: %s\n", outfile_name);
+       if (!outfile_name) {
+               fprintf (stderr, "mono-profiler-aot: The 'output' argument is required.\n");
+               exit (1);
+       }
 
-       outfile = fopen (outfile_name, "w+");
-       g_assert (outfile);
+       prof = g_new0 (MonoProfiler, 1);
+       prof->images = g_hash_table_new (NULL, NULL);
+       prof->classes = g_hash_table_new (NULL, NULL);
+       prof->methods = g_ptr_array_new ();
+       prof->outfile_name = outfile_name;
+
+       mono_os_mutex_init (&mutex);
 
-       fprintf (outfile, "#VER:%d\n", 2);
+       mono_profiler_install (prof, prof_shutdown);
+
+       mono_profiler_install_jit_compile (prof_jit_enter, prof_jit_leave);
 
-       data.prof = prof;
-       data.outfile = outfile;
-       data.image = image;
+       mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
+}
 
-       g_list_foreach (image_data->methods, foreach_method, &data);
+static void
+emit_byte (MonoProfiler *prof, guint8 value)
+{
+       fwrite (&value, 1, 1, prof->outfile);
 }
 
-/* called at the end of the program */
 static void
-prof_shutdown (MonoProfiler *prof)
+emit_int32 (MonoProfiler *prof, int value)
 {
-       g_hash_table_foreach (prof->images, output_image, prof);
+       // FIXME: Endianness
+       fwrite (&value, 4, 1, prof->outfile);
 }
 
 static void
-prof_jit_enter (MonoProfiler *prof, MonoMethod *method)
+emit_string (MonoProfiler *prof, const char *str)
 {
+       int len = strlen (str);
+
+       emit_int32 (prof, len);
+       fwrite (str, len, 1, prof->outfile);
 }
 
 static void
-prof_jit_leave (MonoProfiler *prof, MonoMethod *method, int result)
+emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
 {
-       MonoImage *image = mono_class_get_image (mono_method_get_class (method));
-       PerImageData *data;
+       emit_byte (prof, type);
+       emit_int32 (prof, id);
+}
+
+static int
+add_image (MonoProfiler *prof, MonoImage *image)
+{
+       int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
+       if (id)
+               return id - 1;
 
-       data = (PerImageData *)g_hash_table_lookup (prof->images, image);
-       if (!data) {
-               data = g_new0 (PerImageData, 1);
-               g_hash_table_insert (prof->images, image, data);
+       id = prof->id ++;
+       emit_record (prof, AOTPROF_RECORD_IMAGE, id);
+       emit_string (prof, image->assembly->aname.name);
+       emit_string (prof, image->guid);
+       g_hash_table_insert (prof->images, image, GINT_TO_POINTER (id + 1));
+       return id;
+}
+
+static int
+add_class (MonoProfiler *prof, MonoClass *klass);
+
+static int
+add_type (MonoProfiler *prof, MonoType *type)
+{
+       switch (type->type) {
+#if 0
+       case MONO_TYPE_SZARRAY: {
+               int eid = add_type (prof, &type->data.klass->byval_arg);
+               if (eid == -1)
+                       return -1;
+               int id = prof->id ++;
+               emit_record (prof, AOTPROF_RECORD_TYPE, id);
+               emit_byte (prof, MONO_TYPE_SZARRAY);
+               emit_int32 (prof, id);
+               return id;
+       }
+#endif
+       case MONO_TYPE_BOOLEAN:
+       case MONO_TYPE_CHAR:
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+       case MONO_TYPE_R4:
+       case MONO_TYPE_R8:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_VALUETYPE:
+       case MONO_TYPE_GENERICINST:
+               return add_class (prof, mono_class_from_mono_type (type));
+       default:
+               return -1;
+       }
+}
+
+static int
+add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
+{
+       int i, id;
+       int *ids;
+
+       // FIXME: Cache
+       ids = g_malloc0 (inst->type_argc * sizeof (int));
+       for (i = 0; i < inst->type_argc; ++i) {
+               MonoType *t = inst->type_argv [i];
+               ids [i] = add_type (prof, t);
+               if (ids [i] == -1) {
+                       g_free (ids);
+                       return -1;
+               }
        }
+       id = prof->id ++;
+       emit_record (prof, AOTPROF_RECORD_GINST, id);
+       emit_int32 (prof, inst->type_argc);
+       for (i = 0; i < inst->type_argc; ++i)
+               emit_int32 (prof, ids [i]);
+       g_free (ids);
 
-       data->methods = g_list_append (data->methods, method);
+       return id;
 }
 
-void
-mono_profiler_startup (const char *desc);
+static int
+add_class (MonoProfiler *prof, MonoClass *klass)
+{
+       int id, inst_id = -1, image_id;
+       char *name;
 
-/* the entry point */
-void
-mono_profiler_startup (const char *desc)
+       id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
+       if (id)
+               return id - 1;
+
+       image_id = add_image (prof, klass->image);
+
+       if (mono_class_is_ginst (klass)) {
+               MonoGenericContext *ctx = mono_class_get_context (klass);
+               inst_id = add_ginst (prof, ctx->class_inst);
+               if (inst_id == -1)
+                       return -1;
+       }
+
+       if (klass->nested_in)
+               name = g_strdup_printf ("%s.%s/%s", klass->nested_in->name_space, klass->nested_in->name, klass->name);
+       else
+               name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
+
+       id = prof->id ++;
+       emit_record (prof, AOTPROF_RECORD_TYPE, id);
+       emit_byte (prof, MONO_TYPE_CLASS);
+       emit_int32 (prof, image_id);
+       emit_int32 (prof, inst_id);
+       emit_string (prof, name);
+       g_free (name);
+       g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
+       return id;
+}
+
+static void
+add_method (MonoProfiler *prof, MonoMethod *m)
 {
-       MonoProfiler *prof;
+       MonoError error;
+       MonoMethodSignature *sig;
+       char *s;
 
-       prof = g_new0 (MonoProfiler, 1);
-       prof->images = g_hash_table_new (NULL, NULL);
+       sig = mono_method_signature_checked (m, &error);
+       g_assert (mono_error_ok (&error));
 
-       mono_profiler_install (prof, prof_shutdown);
-       
-       mono_profiler_install_jit_compile (prof_jit_enter, prof_jit_leave);
+       int class_id = add_class (prof, m->klass);
+       if (class_id == -1)
+               return;
+       int inst_id = -1;
 
-       mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
+       if (m->is_inflated) {
+               MonoGenericContext *ctx = mono_method_get_context (m);
+               if (ctx->method_inst)
+                       inst_id = add_ginst (prof, ctx->method_inst);
+       }
+       int id = prof->id ++;
+       emit_record (prof, AOTPROF_RECORD_METHOD, id);
+       emit_int32 (prof, class_id);
+       emit_int32 (prof, inst_id);
+       emit_int32 (prof, sig->param_count);
+       emit_string (prof, m->name);
+       s = mono_signature_full_name (sig);
+       emit_string (prof, s);
+       g_free (s);
+       if (verbose)
+               printf ("%s %d\n", mono_method_full_name (m, 1), id);
 }
 
+/* called at the end of the program */
+static void
+prof_shutdown (MonoProfiler *prof)
+{
+       FILE *outfile;
+       int mindex;
+       char magic [32];
+
+       printf ("Creating output file: %s\n", prof->outfile_name);
+
+       outfile = fopen (prof->outfile_name, "w+");
+       if (!outfile) {
+               fprintf (stderr, "Unable to create output file '%s': %s.\n", prof->outfile_name, strerror (errno));
+               return;
+       }
+       prof->outfile = outfile;
+
+       gint32 version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
+       sprintf (magic, AOT_PROFILER_MAGIC);
+       fwrite (magic, strlen (magic), 1, outfile);
+       emit_int32 (prof, version);
+
+       GHashTable *all_methods = g_hash_table_new (NULL, NULL);
+       for (mindex = 0; mindex < prof->methods->len; ++mindex) {
+           MonoMethod *m = (MonoMethod*)g_ptr_array_index (prof->methods, mindex);
 
+               if (!mono_method_get_token (m))
+                       continue;
+
+               if (g_hash_table_lookup (all_methods, m))
+                       continue;
+               g_hash_table_insert (all_methods, m, m);
+
+               add_method (prof, m);
+       }
+       emit_record (prof, AOTPROF_RECORD_NONE, 0);
+
+       fclose (outfile);
+
+       g_hash_table_destroy (all_methods);
+       g_hash_table_destroy (prof->classes);
+       g_hash_table_destroy (prof->images);
+       g_ptr_array_free (prof->methods, TRUE);
+       g_free (prof->outfile_name);
+}