From 2ba6bcbc48f8b4e145f9ad450fef8d411b728a8e Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Mon, 9 Jan 2017 17:37:57 -0500 Subject: [PATCH] [aot] Rewrite the AOT profiler. (#4176) * [aot] Rewrite the AOT profiler. The AOT profiler is used to collect the methods/instances used at runtime. Usage: * mono --profile=aot:output=out.aotprof * mono --aot=profile=out.aotprof The first command will run , collecting the list of methods it uses into the out.aotprof file. The second command passes the profile file to the AOT compiler to have it generate additional generic instances. * [docs] Add a section for AOT profiling to the mono(1) man page. * [build] Collect AOT profile info from building mscorlib in the build profile, and use it when aot-ing the csc assemblies. * [runtime] Add missing mono-profiler-aot.h file. * [runtime] Tidy up usage of MONO_PROFILER_API. * [aot] Improve error handling in the aot profiler. * [aot] Improve error handling, add more diagnostics. --- man/mono.1 | 20 ++ mcs/class/aot-compiler/Makefile | 19 +- mcs/class/corlib/Makefile | 4 + mono/metadata/class-internals.h | 10 +- mono/metadata/domain-internals.h | 2 + mono/metadata/domain.c | 21 ++ mono/mini/aot-compiler.c | 576 +++++++++++++++++++++++++----- mono/profiler/mono-profiler-aot.c | 397 ++++++++++++++++---- mono/profiler/mono-profiler-aot.h | 33 ++ mono/utils/mono-compiler.h | 3 + 10 files changed, 907 insertions(+), 178 deletions(-) create mode 100644 mono/profiler/mono-profiler-aot.h diff --git a/man/mono.1 b/man/mono.1 index bea38842eb3..ef13ea04f68 100644 --- a/man/mono.1 +++ b/man/mono.1 @@ -853,6 +853,26 @@ The offsets displayed are IL offsets. .PP A more powerful coverage tool is available in the module `monocov'. See the monocov(1) man page for details. +.SH AOT PROFILING +You can improve startup performance by using the AOT profiler. +.PP +Typically the AOT compiler (\fBmono --aot\fR) will not generate code +for generic instantiations. To solve this, you can run Mono with the +AOT profiler to find out all the generic instantiations that are used, +and then instructing the AOT compiler to produce code for these. +.PP +This command will run the specified app.exe and produce the +\fBout.aotprof\fR file with the data describing the generic +instantiations that are needed: +.nf + $ mono --profile=aot:output=out.aotprof app.exe +.fi +.PP +Once you have this data, you can pass this to Mono's AOT compiler to +instruct it to generate code for it: +.nf + $ mono --aot=profile=out.aotprof +.fi .SH DEBUGGING AIDS To debug managed applications, you can use the .B mdb diff --git a/mcs/class/aot-compiler/Makefile b/mcs/class/aot-compiler/Makefile index 505f91591ec..bcf7c1ba5ba 100644 --- a/mcs/class/aot-compiler/Makefile +++ b/mcs/class/aot-compiler/Makefile @@ -40,29 +40,34 @@ LIBRARY_INSTALL_DIR = $(mono_libdir)/mono/$(FRAMEWORK_VERSION) ifndef SKIP_AOT +profile_file=$(wildcard $(topdir)/class/lib/build/csc.aotprofile) +ifneq ($(profile_file),) +profile_arg=,profile=$(profile_file) +endif + ifdef PLATFORM_AOT_SUFFIX $(mcs_aot_image): $(mcs_exe) $(mscorlib_dll) $(runtime_dep) - $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version,outfile=$(mcs_aot_image) --debug $(mcs_exe) || cat $(PROFILE)_aot.log || (cat $(PROFILE)_aot.log; exit 1) + $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version$(profile_arg),outfile=$(mcs_aot_image) --debug $(mcs_exe) || cat $(PROFILE)_aot.log || (cat $(PROFILE)_aot.log; exit 1) $(csc_aot_image): $(csc_exe) $(mscorlib_dll) $(runtime_dep) - $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version,outfile=$(csc_aot_image) --debug $(csc_exe) || cat $(PROFILE)_aot.log || (cat $(PROFILE)_aot.log; exit 1) + $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version$(profile_arg),outfile=$(csc_aot_image) --debug $(csc_exe) || cat $(PROFILE)_aot.log || (cat $(PROFILE)_aot.log; exit 1) $(mscorlib_aot_image): $(mscorlib_dll) $(runtime_dep) - $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version --debug $(mscorlib_dll) || (cat $(PROFILE)_aot.log; exit 1) + $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version$(profile_arg) --debug $(mscorlib_dll) || (cat $(PROFILE)_aot.log; exit 1) # Disabled as it hits AOT too big limit $(csc_MC_image): $(csc_MC_dll) $(runtime_dep) - $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version,outfile=$(csc_MC_image) --debug $(csc_MC_dll) || (cat $(PROFILE)_aot.log; exit 1) + $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version$(profile_arg),outfile=$(csc_MC_image) --debug $(csc_MC_dll) || (cat $(PROFILE)_aot.log; exit 1) # Disabled as it hits AOT too big limit $(csc_MCS_image): $(csc_MCS_dll) $(runtime_dep) - $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version,outfile=$(csc_MCS_image) --debug $(csc_MCS_dll) || (cat $(PROFILE)_aot.log; exit 1) + $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version$(profile_arg),outfile=$(csc_MCS_image) --debug $(csc_MCS_dll) || (cat $(PROFILE)_aot.log; exit 1) $(csc_SRM_image): $(csc_SRM_dll) $(runtime_dep) - $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version,outfile=$(csc_SRM_image) --debug $(csc_SRM_dll) || (cat $(PROFILE)_aot.log; exit 1) + $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version$(profile_arg),outfile=$(csc_SRM_image) --debug $(csc_SRM_dll) || (cat $(PROFILE)_aot.log; exit 1) $(csc_SCI_image): $(csc_SCI_dll) $(runtime_dep) - $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version,outfile=$(csc_SCI_image) --debug $(csc_SCI_dll) || (cat $(PROFILE)_aot.log; exit 1) + $(Q_AOT) MONO_PATH='$(the_libdir)' > $(PROFILE)_aot.log 2>&1 $(RUNTIME) --aot=bind-to-runtime-version$(profile_arg),outfile=$(csc_SCI_image) --debug $(csc_SCI_dll) || (cat $(PROFILE)_aot.log; exit 1) ifdef ENABLE_AOT diff --git a/mcs/class/corlib/Makefile b/mcs/class/corlib/Makefile index 4d40d337125..e466678a17f 100644 --- a/mcs/class/corlib/Makefile +++ b/mcs/class/corlib/Makefile @@ -20,6 +20,10 @@ $(error Unknown framework version) endif endif +ifeq ($(PROFILE),build) +CSC_RUNTIME_FLAGS=--profile=aot:output=$(topdir)/class/lib/build/csc.aotprofile +endif + RESOURCE_STRINGS = ../referencesource/mscorlib/mscorlib.txt RESX_RESOURCE_STRING = ../../../external/corefx/src/System.ValueTuple/src/Resources/Strings.resx diff --git a/mono/metadata/class-internals.h b/mono/metadata/class-internals.h index dab2cc743c9..efb4c62874f 100644 --- a/mono/metadata/class-internals.h +++ b/mono/metadata/class-internals.h @@ -999,16 +999,16 @@ mono_install_get_cached_class_info (MonoGetCachedClassInfo func); void mono_install_get_class_from_name (MonoGetClassFromName func); -MonoGenericContext* +MONO_PROFILER_API MonoGenericContext* mono_class_get_context (MonoClass *klass); -MonoMethodSignature* +MONO_PROFILER_API MonoMethodSignature* mono_method_signature_checked (MonoMethod *m, MonoError *err); MonoGenericContext* mono_method_get_context_general (MonoMethod *method, gboolean uninflated); -MonoGenericContext* +MONO_PROFILER_API MonoGenericContext* mono_method_get_context (MonoMethod *method); /* Used by monodis, thus cannot be MONO_INTERNAL */ @@ -1286,7 +1286,7 @@ MONO_API void mono_class_describe_statics (MonoClass* klass); /* method debugging functions, for use inside gdb */ MONO_API void mono_method_print_code (MonoMethod *method); -char *mono_signature_full_name (MonoMethodSignature *sig); +MONO_PROFILER_API char *mono_signature_full_name (MonoMethodSignature *sig); /*Enum validation related functions*/ MONO_API gboolean @@ -1295,7 +1295,7 @@ mono_type_is_valid_enum_basetype (MonoType * type); MONO_API gboolean mono_class_is_valid_enum (MonoClass *klass); -gboolean +MONO_PROFILER_API gboolean mono_type_is_primitive (MonoType *type); MonoType * diff --git a/mono/metadata/domain-internals.h b/mono/metadata/domain-internals.h index a083d1ccc88..5e8282ccdf6 100644 --- a/mono/metadata/domain-internals.h +++ b/mono/metadata/domain-internals.h @@ -602,5 +602,7 @@ mono_context_init_checked (MonoDomain *domain, MonoError *error); gboolean mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error); +GPtrArray* +mono_domain_get_assemblies (MonoDomain *domain, gboolean refonly); #endif /* __MONO_METADATA_DOMAIN_INTERNALS_H__ */ diff --git a/mono/metadata/domain.c b/mono/metadata/domain.c index 26ee185cac7..76cfe9979db 100644 --- a/mono/metadata/domain.c +++ b/mono/metadata/domain.c @@ -2013,3 +2013,24 @@ mono_domain_unlock (MonoDomain *domain) { mono_locks_coop_release (&domain->lock, DomainLock); } + +GPtrArray* +mono_domain_get_assemblies (MonoDomain *domain, gboolean refonly) +{ + GSList *tmp; + GPtrArray *assemblies; + MonoAssembly *ass; + + assemblies = g_ptr_array_new (); + mono_domain_assemblies_lock (domain); + for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) { + ass = (MonoAssembly *)tmp->data; + if (refonly != ass->ref_only) + continue; + if (ass->corlib_internal) + continue; + g_ptr_array_add (assemblies, ass); + } + mono_domain_assemblies_unlock (domain); + return assemblies; +} diff --git a/mono/mini/aot-compiler.c b/mono/mini/aot-compiler.c index 3b4470fc3c6..b91dfaedb24 100644 --- a/mono/mini/aot-compiler.c +++ b/mono/mini/aot-compiler.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include "aot-compiler.h" @@ -94,6 +95,40 @@ #define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1))) #define ROUND_DOWN(VALUE,SIZE) ((VALUE) & ~((SIZE) - 1)) +typedef struct { + char *name; + MonoImage *image; +} ImageProfileData; + +typedef struct ClassProfileData ClassProfileData; + +typedef struct { + int argc; + ClassProfileData **argv; + MonoGenericInst *inst; +} GInstProfileData; + +struct ClassProfileData { + ImageProfileData *image; + char *ns, *name; + GInstProfileData *inst; + MonoClass *klass; +}; + +typedef struct { + ClassProfileData *klass; + int id; + char *name; + int param_count; + char *signature; + GInstProfileData *inst; + MonoMethod *method; +} MethodProfileData; + +typedef struct { + GHashTable *images, *classes, *ginsts, *methods; +} ProfileData; + /* predefined values for static readonly fields without needed to run the .cctor */ typedef struct _ReadOnlyValue ReadOnlyValue; struct _ReadOnlyValue { @@ -114,6 +149,7 @@ typedef struct MonoAotOptions { char *outfile; char *llvm_outfile; char *data_outfile; + char *profile_file; gboolean save_temps; gboolean write_symbols; gboolean metadata_only; @@ -274,6 +310,7 @@ typedef struct MonoAotCompile { int objc_selector_index, objc_selector_index_2; GPtrArray *objc_selectors; GHashTable *objc_selector_to_index; + ProfileData *profile_data; FILE *logfile; FILE *instances_logfile; FILE *data_outfile; @@ -343,6 +380,9 @@ get_plt_entry_debug_sym (MonoAotCompile *acfg, MonoJumpInfo *ji, GHashTable *cac static void add_gsharedvt_wrappers (MonoAotCompile *acfg, MonoMethodSignature *sig, gboolean gsharedvt_in, gboolean gsharedvt_out); +static void +add_profile_instances (MonoAotCompile *acfg); + static void aot_printf (MonoAotCompile *acfg, const gchar *format, ...) { @@ -7181,6 +7221,8 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) opts->llvm_only = TRUE; } else if (str_begins_with (arg, "data-outfile=")) { opts->data_outfile = g_strdup (arg + strlen ("data-outfile=")); + } else if (str_begins_with (arg, "profile=")) { + opts->profile_file = g_strdup (arg + strlen ("profile=")); } else if (str_begins_with (arg, "help") || str_begins_with (arg, "?")) { printf ("Supported options for --aot:\n"); printf (" outfile=\n"); @@ -7852,95 +7894,6 @@ compile_thread_main (gpointer user_data) return 0; } - -static void -load_profile_files (MonoAotCompile *acfg) -{ - FILE *infile; - char *tmp; - int file_index, res, method_index, i; - char ver [256]; - guint32 token; - GList *unordered, *l; - gboolean found; - - file_index = 0; - while (TRUE) { - tmp = g_strdup_printf ("%s/.mono/aot-profile-data/%s-%d", g_get_home_dir (), acfg->image->assembly_name, file_index); - - if (!g_file_test (tmp, G_FILE_TEST_IS_REGULAR)) { - g_free (tmp); - break; - } - - infile = fopen (tmp, "r"); - g_assert (infile); - - printf ("Using profile data file '%s'\n", tmp); - g_free (tmp); - - file_index ++; - - res = fscanf (infile, "%32s\n", ver); - if ((res != 1) || strcmp (ver, "#VER:2") != 0) { - printf ("Profile file has wrong version or invalid.\n"); - fclose (infile); - continue; - } - - while (TRUE) { - char name [1024]; - MonoMethodDesc *desc; - MonoMethod *method; - - if (fgets (name, 1023, infile) == NULL) - break; - - /* Kill the newline */ - if (strlen (name) > 0) - name [strlen (name) - 1] = '\0'; - - desc = mono_method_desc_new (name, TRUE); - - method = mono_method_desc_search_in_image (desc, acfg->image); - - if (method && mono_method_get_token (method)) { - token = mono_method_get_token (method); - method_index = mono_metadata_token_index (token) - 1; - - found = FALSE; - for (i = 0; i < acfg->method_order->len; ++i) { - if (g_ptr_array_index (acfg->method_order, i) == GUINT_TO_POINTER (method_index)) { - found = TRUE; - break; - } - } - if (!found) - g_ptr_array_add (acfg->method_order, GUINT_TO_POINTER (method_index)); - } else { - //printf ("No method found matching '%s'.\n", name); - } - } - fclose (infile); - } - - /* Add missing methods */ - unordered = NULL; - for (method_index = 0; method_index < acfg->image->tables [MONO_TABLE_METHOD].rows; ++method_index) { - found = FALSE; - for (i = 0; i < acfg->method_order->len; ++i) { - if (g_ptr_array_index (acfg->method_order, i) == GUINT_TO_POINTER (method_index)) { - found = TRUE; - break; - } - } - if (!found) - unordered = g_list_prepend (unordered, GUINT_TO_POINTER (method_index)); - } - unordered = g_list_reverse (unordered); - for (l = unordered; l; l = l->next) - g_ptr_array_add (acfg->method_order, l->data); -} /* Used by the LLVM backend */ guint32 @@ -9931,7 +9884,7 @@ compile_methods (MonoAotCompile *acfg) /* Compile methods added by compile_method () or all methods if nthreads == 0 */ for (i = methods_len; i < acfg->methods->len; ++i) { - /* This can new methods to acfg->methods */ + /* This can add new methods to acfg->methods */ compile_method (acfg, (MonoMethod *)g_ptr_array_index (acfg->methods, i)); } } @@ -10172,7 +10125,433 @@ compile_asm (MonoAotCompile *acfg) return 0; } -static void init_got_info (GotInfo *info) +static guint8 +profread_byte (FILE *infile) +{ + guint8 i; + int res; + + res = fread (&i, 1, 1, infile); + g_assert (res == 1); + return i; +} + +static int +profread_int (FILE *infile) +{ + int i, res; + + res = fread (&i, 4, 1, infile); + g_assert (res == 1); + return i; +} + +static char* +profread_string (FILE *infile) +{ + int len, res; + char buf [1024]; + char *pbuf; + + len = profread_int (infile); + if (len + 1 > 1024) + pbuf = g_malloc (len + 1); + else + pbuf = buf; + res = fread (pbuf, 1, len, infile); + g_assert (res == len); + pbuf [len] = '\0'; + if (pbuf == buf) + return g_strdup (buf); + else + return pbuf; +} + +static void +load_profile_file (MonoAotCompile *acfg, char *filename) +{ + FILE *infile; + char buf [1024]; + int res, len, version; + char magic [32]; + + infile = fopen (filename, "r"); + if (!infile) { + fprintf (stderr, "Unable to open file '%s': %s.\n", filename, strerror (errno)); + exit (1); + } + + printf ("Using profile data file '%s'\n", filename); + + sprintf (magic, AOT_PROFILER_MAGIC); + len = strlen (magic); + res = fread (buf, 1, len, infile); + magic [len] = '\0'; + buf [len] = '\0'; + if ((res != len) || strcmp (buf, magic) != 0) { + printf ("Profile file has wrong header: '%s'.\n", buf); + fclose (infile); + exit (1); + } + guint32 expected_version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION; + version = profread_int (infile); + if (version != expected_version) { + printf ("Profile file has wrong version 0x%4x, expected 0x%4x.\n", version, expected_version); + fclose (infile); + exit (1); + } + + ProfileData *data = g_new0 (ProfileData, 1); + data->images = g_hash_table_new (NULL, NULL); + data->classes = g_hash_table_new (NULL, NULL); + data->ginsts = g_hash_table_new (NULL, NULL); + data->methods = g_hash_table_new (NULL, NULL); + + while (TRUE) { + int type = profread_byte (infile); + int id = profread_int (infile); + + if (type == AOTPROF_RECORD_NONE) + break; + + switch (type) { + case AOTPROF_RECORD_IMAGE: { + ImageProfileData *idata = g_new0 (ImageProfileData, 1); + idata->name = profread_string (infile); + char *mvid = profread_string (infile); + g_free (mvid); + g_hash_table_insert (data->images, GINT_TO_POINTER (id), idata); + break; + } + case AOTPROF_RECORD_GINST: { + int i; + int len = profread_int (infile); + + GInstProfileData *gdata = g_new0 (GInstProfileData, 1); + gdata->argc = len; + gdata->argv = g_new0 (ClassProfileData*, len); + + for (i = 0; i < len; ++i) { + int class_id = profread_int (infile); + + gdata->argv [i] = g_hash_table_lookup (data->classes, GINT_TO_POINTER (class_id)); + g_assert (gdata->argv [i]); + } + g_hash_table_insert (data->ginsts, GINT_TO_POINTER (id), gdata); + break; + } + case AOTPROF_RECORD_TYPE: { + int type = profread_byte (infile); + + switch (type) { + case MONO_TYPE_CLASS: { + int image_id = profread_int (infile); + int ginst_id = profread_int (infile); + char *class_name = profread_string (infile); + + ImageProfileData *image = g_hash_table_lookup (data->images, GINT_TO_POINTER (image_id)); + g_assert (image); + + char *p = strrchr (class_name, '.'); + g_assert (p); + *p = '\0'; + + ClassProfileData *cdata = g_new0 (ClassProfileData, 1); + cdata->image = image; + cdata->ns = g_strdup (class_name); + cdata->name = g_strdup (p + 1); + + if (ginst_id != -1) { + cdata->inst = g_hash_table_lookup (data->ginsts, GINT_TO_POINTER (ginst_id)); + g_assert (cdata->inst); + } + g_free (class_name); + + g_hash_table_insert (data->classes, GINT_TO_POINTER (id), cdata); + break; + } +#if 0 + case MONO_TYPE_SZARRAY: { + int elem_id = profread_int (infile); + // FIXME: + break; + } +#endif + default: + g_assert_not_reached (); + break; + } + break; + } + case AOTPROF_RECORD_METHOD: { + int class_id = profread_int (infile); + int ginst_id = profread_int (infile); + int param_count = profread_int (infile); + char *method_name = profread_string (infile); + char *sig = profread_string (infile); + + ClassProfileData *klass = g_hash_table_lookup (data->classes, GINT_TO_POINTER (class_id)); + g_assert (klass); + + MethodProfileData *mdata = g_new0 (MethodProfileData, 1); + mdata->id = id; + mdata->klass = klass; + mdata->name = method_name; + mdata->signature = sig; + mdata->param_count = param_count; + + if (ginst_id != -1) { + mdata->inst = g_hash_table_lookup (data->ginsts, GINT_TO_POINTER (ginst_id)); + g_assert (mdata->inst); + } + g_hash_table_insert (data->methods, GINT_TO_POINTER (id), mdata); + break; + } + default: + printf ("%d\n", type); + g_assert_not_reached (); + break; + } + } + + acfg->profile_data = data; +} + +static void +resolve_class (ClassProfileData *cdata); + +static void +resolve_ginst (GInstProfileData *inst_data) +{ + int i; + + if (inst_data->inst) + return; + + for (i = 0; i < inst_data->argc; ++i) { + resolve_class (inst_data->argv [i]); + if (!inst_data->argv [i]->klass) + return; + } + MonoType **args = g_new0 (MonoType*, inst_data->argc); + for (i = 0; i < inst_data->argc; ++i) + args [i] = &inst_data->argv [i]->klass->byval_arg; + + inst_data->inst = mono_metadata_get_generic_inst (inst_data->argc, args); +} + +static void +resolve_class (ClassProfileData *cdata) +{ + MonoError error; + MonoClass *klass; + + if (!cdata->image->image) + return; + + klass = mono_class_from_name_checked (cdata->image->image, cdata->ns, cdata->name, &error); + if (!klass) { + //printf ("[%s] %s.%s\n", cdata->image->name, cdata->ns, cdata->name); + return; + } + if (cdata->inst) { + resolve_ginst (cdata->inst); + if (!cdata->inst->inst) + return; + MonoGenericContext ctx; + + memset (&ctx, 0, sizeof (ctx)); + ctx.class_inst = cdata->inst->inst; + cdata->klass = mono_class_inflate_generic_class_checked (klass, &ctx, &error); + } else { + cdata->klass = klass; + } +} + +/* + * Resolve the profile data to the corresponding loaded classes/methods etc. if possible. + */ +static void +resolve_profile_data (MonoAotCompile *acfg) +{ + ProfileData *data = acfg->profile_data; + GHashTableIter iter; + gpointer key, value; + int i; + + if (!data) + return; + + /* Images */ + GPtrArray *assemblies = mono_domain_get_assemblies (mono_get_root_domain (), FALSE); + g_hash_table_iter_init (&iter, data->images); + while (g_hash_table_iter_next (&iter, &key, &value)) { + ImageProfileData *idata = (ImageProfileData*)value; + + for (i = 0; i < assemblies->len; ++i) { + MonoAssembly *ass = g_ptr_array_index (assemblies, i); + + if (!strcmp (ass->aname.name, idata->name)) { + idata->image = ass->image; + break; + } + } + } + g_ptr_array_free (assemblies, TRUE); + + /* Classes */ + g_hash_table_iter_init (&iter, data->classes); + while (g_hash_table_iter_next (&iter, &key, &value)) { + ClassProfileData *cdata = (ClassProfileData*)value; + + if (!cdata->image->image) + continue; + + resolve_class (cdata); + /* + if (cdata->klass) + printf ("%s %s %s\n", cdata->ns, cdata->name, mono_class_full_name (cdata->klass)); + */ + } + + /* Methods */ + g_hash_table_iter_init (&iter, data->methods); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MethodProfileData *mdata = (MethodProfileData*)value; + MonoClass *klass; + MonoMethod *m; + gpointer miter; + + resolve_class (mdata->klass); + klass = mdata->klass->klass; + if (!klass) + continue; + miter = NULL; + while ((m = mono_class_get_methods (klass, &miter))) { + MonoError error; + + if (strcmp (m->name, mdata->name)) + continue; + MonoMethodSignature *sig = mono_method_signature (m); + if (!sig) + continue; + if (sig->param_count != mdata->param_count) + continue; + if (mdata->inst) { + resolve_ginst (mdata->inst); + if (!mdata->inst->inst) + continue; + MonoGenericContext ctx; + + memset (&ctx, 0, sizeof (ctx)); + ctx.method_inst = mdata->inst->inst; + + m = mono_class_inflate_generic_method_checked (m, &ctx, &error); + if (!m) + continue; + sig = mono_method_signature_checked (m, &error); + if (!is_ok (&error)) { + mono_error_cleanup (&error); + continue; + } + } + char *sig_str = mono_signature_full_name (sig); + gboolean match = !strcmp (sig_str, mdata->signature); + g_free (sig_str); + if (!match) + continue; + //printf ("%s\n", mono_method_full_name (m, 1)); + mdata->method = m; + break; + } + } +} + +static gboolean +inst_references_image (MonoGenericInst *inst, MonoImage *image) +{ + int i; + + for (i = 0; i < inst->type_argc; ++i) { + MonoClass *k = mono_class_from_mono_type (inst->type_argv [i]); + if (k->image == image) + return TRUE; + if (mono_class_is_ginst (k)) { + MonoGenericInst *kinst = mono_class_get_context (k)->class_inst; + if (inst_references_image (kinst, image)) + return TRUE; + } + } + return FALSE; +} + +static gboolean +is_local_inst (MonoGenericInst *inst, MonoImage *image) +{ + int i; + + for (i = 0; i < inst->type_argc; ++i) { + MonoClass *k = mono_class_from_mono_type (inst->type_argv [i]); + if (!MONO_TYPE_IS_PRIMITIVE (inst->type_argv [i]) && k->image != image) + return FALSE; + } + return TRUE; +} + +static void +add_profile_instances (MonoAotCompile *acfg) +{ + ProfileData *data = acfg->profile_data; + GHashTableIter iter; + gpointer key, value; + int count = 0; + + if (!data) + return; + + /* + * Add method instances 'related' to this assembly to the AOT image. + */ + g_hash_table_iter_init (&iter, data->methods); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MethodProfileData *mdata = (MethodProfileData*)value; + MonoMethod *m = mdata->method; + MonoGenericContext *ctx; + + if (!m) + continue; + if (!m->is_inflated) + continue; + + ctx = mono_method_get_context (m); + /* For simplicity, add instances which reference the assembly we are compiling */ + if (((ctx->class_inst && inst_references_image (ctx->class_inst, acfg->image)) || + (ctx->method_inst && inst_references_image (ctx->method_inst, acfg->image))) && + !mono_method_is_generic_sharable_full (m, FALSE, FALSE, FALSE)) { + //printf ("%s\n", mono_method_full_name (m, TRUE)); + add_extra_method (acfg, m); + count ++; + } else if (m->klass->image == acfg->image && + ((ctx->class_inst && is_local_inst (ctx->class_inst, acfg->image)) || + (ctx->method_inst && is_local_inst (ctx->method_inst, acfg->image))) && + !mono_method_is_generic_sharable_full (m, FALSE, FALSE, FALSE)) { + /* Add instances where the gtd is in the assembly and its inflated with types from this assembly or corlib */ + //printf ("%s\n", mono_method_full_name (m, TRUE)); + add_extra_method (acfg, m); + count ++; + } + /* + * FIXME: We might skip some instances, for example: + * Foo won't be compiled when compiling Foo's assembly since it doesn't match the first case, + * and it won't be compiled when compiling Bar's assembly if Foo's assembly is not loaded. + */ + } + + printf ("Added %d methods from profile.\n", count); +} + +static void +init_got_info (GotInfo *info) { int i; @@ -10652,7 +11031,16 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) } } - load_profile_files (acfg); + if (acfg->aot_opts.profile_file) + load_profile_file (acfg, acfg->aot_opts.profile_file); + + { + int method_index; + + for (method_index = 0; method_index < acfg->image->tables [MONO_TABLE_METHOD].rows; ++method_index) { + g_ptr_array_add (acfg->method_order,GUINT_TO_POINTER (method_index)); + } + } acfg->num_trampolines [MONO_AOT_TRAMP_SPECIFIC] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.ntrampolines : 0; #ifdef MONO_ARCH_GSHARED_SUPPORTED @@ -10713,6 +11101,10 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) if (!res) return 1; + resolve_profile_data (acfg); + + add_profile_instances (acfg); + acfg->cfgs_size = acfg->methods->len + 32; acfg->cfgs = g_new0 (MonoCompile*, acfg->cfgs_size); diff --git a/mono/profiler/mono-profiler-aot.c b/mono/profiler/mono-profiler-aot.c index 4cf48bbd1b2..05ed0758ed2 100644 --- a/mono/profiler/mono-profiler-aot.c +++ b/mono/profiler/mono-profiler-aot.c @@ -13,11 +13,16 @@ */ #include + +#include "mono-profiler-aot.h" + #include #include #include #include #include +#include +#include #include #include #include @@ -29,128 +34,372 @@ #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); +} diff --git a/mono/profiler/mono-profiler-aot.h b/mono/profiler/mono-profiler-aot.h new file mode 100644 index 00000000000..7663cab70fe --- /dev/null +++ b/mono/profiler/mono-profiler-aot.h @@ -0,0 +1,33 @@ +#ifndef __MONO_PROFILER_AOT_H__ +#define __MONO_PROFILER_AOT_H__ + +#include + +/* + * File format: + * - magic + * - major/minor version as an int, i.e. 0x00010001 + * - sequence of records terminated by a record with type TYPE_NONE + * Record format: + * - 1 byte record type (AotProfRecordType) + * - 1 int record id + * - record specific data + * Encoding rules: + * - int - 4 bytes little endian + * - string - int length followed by data + */ + +typedef enum { + AOTPROF_RECORD_NONE, + AOTPROF_RECORD_IMAGE, + AOTPROF_RECORD_TYPE, + AOTPROF_RECORD_GINST, + AOTPROF_RECORD_METHOD +} AotProfRecordType; + +#define AOT_PROFILER_MAGIC "AOTPROFILE" + +#define AOT_PROFILER_MAJOR_VERSION 1 +#define AOT_PROFILER_MINOR_VERSION 0 + +#endif /* __MONO_PROFILER_AOT_H__ */ diff --git a/mono/utils/mono-compiler.h b/mono/utils/mono-compiler.h index 2a7849f101a..8d83f5d672e 100644 --- a/mono/utils/mono-compiler.h +++ b/mono/utils/mono-compiler.h @@ -80,6 +80,9 @@ typedef SSIZE_T ssize_t; #define MONO_LLVM_INTERNAL #endif +/* Used to mark internal functions used by the profiler modules */ +#define MONO_PROFILER_API MONO_API + #ifdef __GNUC__ #define MONO_ALWAYS_INLINE __attribute__((always_inline)) #elif defined(_MSC_VER) -- 2.25.1