*/
#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);
+}