From ecf447d3b6a8ac1ffcdf0cf6e5c43f1397097537 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 2 Nov 2020 15:56:07 -0500 Subject: [PATCH 01/31] Initial metadata-update prototype Co-Authored-By: Bernhard Urban-Forster --- src/mono/m4/mono-output.m4 | 1 + src/mono/mono/cil/tables.def | 13 + src/mono/mono/metadata/Makefile.am | 2 + src/mono/mono/metadata/class.c | 2 +- src/mono/mono/metadata/domain.c | 3 +- src/mono/mono/metadata/icall-def-netcore.h | 3 + src/mono/mono/metadata/icall-def.h | 1 + src/mono/mono/metadata/icall.c | 16 + src/mono/mono/metadata/image.c | 63 +- src/mono/mono/metadata/loader.c | 26 +- src/mono/mono/metadata/metadata-internals.h | 22 + src/mono/mono/metadata/metadata-update.c | 731 ++++++++++++++++++ src/mono/mono/metadata/metadata-update.h | 36 + src/mono/mono/metadata/metadata.c | 58 +- src/mono/mono/metadata/metadata.h | 2 + src/mono/mono/metadata/object-internals.h | 2 + src/mono/mono/mini/ee.h | 1 + src/mono/mono/mini/interp-stubs.c | 5 + src/mono/mono/mini/interp/interp.c | 48 ++ src/mono/mono/mini/mini-runtime.c | 20 + src/mono/mono/tests/Makefile.am | 2 +- src/mono/mono/tests/enc/.gitignore | 4 + src/mono/mono/tests/enc/AddClass.cs | 36 + src/mono/mono/tests/enc/AddClass_v1.cs | 38 + src/mono/mono/tests/enc/AddStaticField.cs | 29 + src/mono/mono/tests/enc/AddStaticField_v1.cs | 30 + src/mono/mono/tests/enc/AddStaticMethod.cs | 31 + src/mono/mono/tests/enc/AddStaticMethod_v1.cs | 36 + src/mono/mono/tests/enc/CallExisting.cs | 36 + src/mono/mono/tests/enc/CallExisting_v1.cs | 36 + src/mono/mono/tests/enc/EncHelper.cs | 59 ++ src/mono/mono/tests/enc/LambdaFunc.cs | 38 + src/mono/mono/tests/enc/LambdaFunc_v1.cs | 38 + src/mono/mono/tests/enc/Makefile.am | 82 ++ src/mono/mono/tests/enc/OnStackMethod.cs | 43 ++ .../mono/tests/enc/OnStackMethodThread.cs | 55 ++ .../tests/enc/OnStackMethodThreadThread.cs | 82 ++ .../tests/enc/OnStackMethodThreadThread_v1.cs | 77 ++ .../mono/tests/enc/OnStackMethodThread_v1.cs | 52 ++ src/mono/mono/tests/enc/OnStackMethod_v1.cs | 40 + src/mono/mono/tests/enc/ReplaceMethod.cs | 36 + src/mono/mono/tests/enc/ReplaceMethodOften.cs | 54 ++ .../mono/tests/enc/ReplaceMethodOften_v1.cs | 54 ++ .../mono/tests/enc/ReplaceMethodOften_v2.cs | 54 ++ .../mono/tests/enc/ReplaceMethodOften_v3.cs | 54 ++ .../mono/tests/enc/ReplaceMethodOften_v4.cs | 54 ++ .../mono/tests/enc/ReplaceMethodOften_v5.cs | 54 ++ .../mono/tests/enc/ReplaceMethodOften_v6.cs | 54 ++ src/mono/mono/tests/enc/ReplaceMethod_v1.cs | 34 + .../tests/enc/ReplacePrivateVirtualMethod.cs | 54 ++ .../enc/ReplacePrivateVirtualMethod_v1.cs | 54 ++ src/mono/mono/tests/enc/TypeTokenSwap.cs | 29 + src/mono/mono/tests/enc/TypeTokenSwap_v1.cs | 29 + src/mono/mono/tests/enc/UserStringSwap.cs | 31 + src/mono/mono/tests/enc/UserStringSwap_v1.cs | 31 + src/mono/mono/utils/mono-logger-internals.h | 2 +- src/mono/mono/utils/mono-logger.c | 2 +- 57 files changed, 2557 insertions(+), 22 deletions(-) create mode 100644 src/mono/mono/metadata/metadata-update.c create mode 100644 src/mono/mono/metadata/metadata-update.h create mode 100644 src/mono/mono/tests/enc/.gitignore create mode 100644 src/mono/mono/tests/enc/AddClass.cs create mode 100644 src/mono/mono/tests/enc/AddClass_v1.cs create mode 100644 src/mono/mono/tests/enc/AddStaticField.cs create mode 100644 src/mono/mono/tests/enc/AddStaticField_v1.cs create mode 100644 src/mono/mono/tests/enc/AddStaticMethod.cs create mode 100644 src/mono/mono/tests/enc/AddStaticMethod_v1.cs create mode 100644 src/mono/mono/tests/enc/CallExisting.cs create mode 100644 src/mono/mono/tests/enc/CallExisting_v1.cs create mode 100644 src/mono/mono/tests/enc/EncHelper.cs create mode 100644 src/mono/mono/tests/enc/LambdaFunc.cs create mode 100644 src/mono/mono/tests/enc/LambdaFunc_v1.cs create mode 100644 src/mono/mono/tests/enc/Makefile.am create mode 100644 src/mono/mono/tests/enc/OnStackMethod.cs create mode 100644 src/mono/mono/tests/enc/OnStackMethodThread.cs create mode 100644 src/mono/mono/tests/enc/OnStackMethodThreadThread.cs create mode 100644 src/mono/mono/tests/enc/OnStackMethodThreadThread_v1.cs create mode 100644 src/mono/mono/tests/enc/OnStackMethodThread_v1.cs create mode 100644 src/mono/mono/tests/enc/OnStackMethod_v1.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethod.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v1.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v2.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v3.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v4.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v5.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v6.cs create mode 100644 src/mono/mono/tests/enc/ReplaceMethod_v1.cs create mode 100644 src/mono/mono/tests/enc/ReplacePrivateVirtualMethod.cs create mode 100644 src/mono/mono/tests/enc/ReplacePrivateVirtualMethod_v1.cs create mode 100644 src/mono/mono/tests/enc/TypeTokenSwap.cs create mode 100644 src/mono/mono/tests/enc/TypeTokenSwap_v1.cs create mode 100644 src/mono/mono/tests/enc/UserStringSwap.cs create mode 100644 src/mono/mono/tests/enc/UserStringSwap_v1.cs diff --git a/src/mono/m4/mono-output.m4 b/src/mono/m4/mono-output.m4 index 40d72ea7d6d14..1456be63e2d20 100644 --- a/src/mono/m4/mono-output.m4 +++ b/src/mono/m4/mono-output.m4 @@ -28,5 +28,6 @@ AC_DEFUN([AC_MONO_OUTPUT], [ mono/eglib/Makefile mono/eglib/eglib-config.h mono/eglib/test/Makefile + mono/tests/enc/Makefile ]) ]) diff --git a/src/mono/mono/cil/tables.def b/src/mono/mono/cil/tables.def index d28447a1ada4f..1a2888fb05584 100644 --- a/src/mono/mono/cil/tables.def +++ b/src/mono/mono/cil/tables.def @@ -44,3 +44,16 @@ TABLEDEF(MONO_TABLE_GENERICPARAM, "GenericParam") /* 0x2a */ TABLEDEF(MONO_TABLE_METHODSPEC, "MethodSpec") TABLEDEF(MONO_TABLE_GENERICPARAMCONSTRAINT, "GenericParamConstraint") +TABLEDEF(MONO_TABLE_UNUSED8, "Unused8") +TABLEDEF(MONO_TABLE_UNUSED9, "Unused9") +TABLEDEF(MONO_TABLE_UNUSED10, "Unused10") + +/* Portable PDB tables */ +TABLEDEF(MONO_TABLE_DOCUMENT, "Document") +TABLEDEF(MONO_TABLE_METHODBODY, "Methodbody") +TABLEDEF(MONO_TABLE_LOCALSCOPE, "LocalScope") +TABLEDEF(MONO_TABLE_LOCALVARIABLE, "LocalVariable") +TABLEDEF(MONO_TABLE_LOCALCONSTANT, "LocalConstant") +TABLEDEF(MONO_TABLE_IMPORTSCOPE, "ImportScope") +TABLEDEF(MONO_TABLE_STATEMACHINEMETHOD, "StateMachineMethod") +TABLEDEF(MONO_TABLE_CUSTOMDEBUGINFORMATION, "CustomDebugInformation") diff --git a/src/mono/mono/metadata/Makefile.am b/src/mono/mono/metadata/Makefile.am index 26c0b992aeedf..45392bbed42a6 100644 --- a/src/mono/mono/metadata/Makefile.am +++ b/src/mono/mono/metadata/Makefile.am @@ -311,6 +311,8 @@ common_sources = \ metadata.c \ metadata-verify.c \ metadata-internals.h \ + metadata-update.h \ + metadata-update.c \ method-builder.h \ method-builder-internals.h \ method-builder.c \ diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c index 02406e46e76a4..e52274df2f009 100644 --- a/src/mono/mono/metadata/class.c +++ b/src/mono/mono/metadata/class.c @@ -197,7 +197,7 @@ mono_class_from_typeref_checked (MonoImage *image, guint32 type_token, MonoError break; } - if (idx > image->tables [MONO_TABLE_ASSEMBLYREF].rows) { + if (mono_metadata_table_bounds_check (image, MONO_TABLE_ASSEMBLYREF, idx)) { mono_error_set_bad_image (error, image, "Image with invalid assemblyref token %08x.", idx); return NULL; } diff --git a/src/mono/mono/metadata/domain.c b/src/mono/mono/metadata/domain.c index 9c6927efc4c98..1b62a5213c0a3 100644 --- a/src/mono/mono/metadata/domain.c +++ b/src/mono/mono/metadata/domain.c @@ -919,7 +919,8 @@ mono_cleanup (void) void mono_close_exe_image (void) { - if (exe_image) + /* EnC: shutdown hack. We mess something up and try to double-close/free it. */ + if (exe_image && !exe_image->delta_image) mono_image_close (exe_image); } diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 0a6e7c6debd3e..9e55858d05f04 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -326,6 +326,9 @@ HANDLES_REUSE_WRAPPER(MPROP_3, "get_metadata_token", ves_icall_reflection_get_to HANDLES(MPROP_4, "get_property_info", ves_icall_RuntimePropertyInfo_get_property_info, void, 3, (MonoReflectionProperty, MonoPropertyInfo_ref, PInfo)) HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_RuntimePropertyInfo_internal_from_handle_type, MonoReflectionProperty, 2, (MonoProperty_ptr, MonoType_ptr)) +ICALL_TYPE(RUNF, "System.Runtime.CompilerServices.RuntimeFeature", RUNF_1) +HANDLES(RUNT_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate, void, 5, (MonoReflectionAssembly, gconstpointer, gint32, gconstpointer, gint32)) + ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1) HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject)) HANDLES(RUNH_2, "GetUninitializedObjectInternal", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetUninitializedObjectInternal, MonoObject, 1, (MonoType_ptr)) diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 56dd99bccb57e..a77e69c75982c 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -152,6 +152,7 @@ HANDLES(RUNTIME_2, "EnableMicrosoftTelemetry_internal", ves_icall_Mono_Runtime_E HANDLES(RUNTIME_3, "ExceptionToState_internal", ves_icall_Mono_Runtime_ExceptionToState, MonoString, 3, (MonoException, guint64_ref, guint64_ref)) HANDLES(RUNTIME_4, "GetDisplayName", ves_icall_Mono_Runtime_GetDisplayName, MonoString, 0, ()) HANDLES(RUNTIME_12, "GetNativeStackTrace", ves_icall_Mono_Runtime_GetNativeStackTrace, MonoString, 1, (MonoException)) +HANDLES(RUNTIME_22, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate, void, 5, (MonoReflectionAssembly, gconstpointer, gint32, gconstpointer, gint32)) NOHANDLES(ICALL(RUNTIME_21, "RegisterReportingForAllNativeLibs_internal", ves_icall_Mono_Runtime_RegisterReportingForAllNativeLibs)) NOHANDLES(ICALL(RUNTIME_17, "RegisterReportingForNativeLib_internal", ves_icall_Mono_Runtime_RegisterReportingForNativeLib)) HANDLES(RUNTIME_13, "SendMicrosoftTelemetry_internal", ves_icall_Mono_Runtime_SendMicrosoftTelemetry, void, 3, (const_char_ptr, guint64, guint64)) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 1a5e4e57a34db..e1d8c1626dc2b 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -6777,6 +6777,22 @@ ves_icall_Mono_Runtime_DumpStateTotal (guint64 *portable_hash, guint64 *unportab return result; } +void +ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoReflectionAssemblyHandle refassm, + gconstpointer dmeta_bytes, int32_t dmeta_len, + gconstpointer dil_bytes, int32_t dil_len, + MonoError *error) +{ + g_assert (MONO_HANDLE_BOOL (refassm)); + g_assert (dmeta_len >= 0); + MonoAssembly *assm = MONO_HANDLE_GETVAL (refassm, assembly); + MonoImage *image_base = assm->image; + g_assert (image_base); + + MonoDomain *domain = mono_domain_get (); + mono_image_load_enc_delta (domain, image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error); +} + MonoBoolean ves_icall_System_Reflection_AssemblyName_ParseAssemblyName (const char *name, MonoAssemblyName *aname, MonoBoolean *is_version_defined_arg, MonoBoolean *is_token_defined_arg) { diff --git a/src/mono/mono/metadata/image.c b/src/mono/mono/metadata/image.c index fe5ec7d9eb103..9c1d068adc145 100644 --- a/src/mono/mono/metadata/image.c +++ b/src/mono/mono/metadata/image.c @@ -24,6 +24,7 @@ #include "tabledefs.h" #include "tokentype.h" #include "metadata-internals.h" +#include "metadata-update.h" #include "profiler-private.h" #include "loader.h" #include "marshal.h" @@ -45,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -598,7 +600,7 @@ load_metadata_ptrs (MonoImage *image, MonoCLIImageInfo *iinfo) } /* - * Load representation of logical metadata tables, from the "#~" stream + * Load representation of logical metadata tables, from the "#~" or "#-" stream */ static gboolean load_tables (MonoImage *image) @@ -613,6 +615,13 @@ load_tables (MonoImage *image) image->idx_string_wide = ((heap_sizes & 0x01) == 1); image->idx_guid_wide = ((heap_sizes & 0x02) == 2); image->idx_blob_wide = ((heap_sizes & 0x04) == 4); + + if (image->minimal_delta) { + /* sanity check */ + g_assert (image->idx_string_wide); + g_assert (image->idx_guid_wide); + g_assert (image->idx_blob_wide); + } valid_mask = read64 (heap_tables + 8); rows = (const guint32 *) (heap_tables + 24); @@ -628,6 +637,7 @@ load_tables (MonoImage *image) g_warning("bits in valid must be zero above 0x37 (II - 23.1.6)"); } else { image->tables [table].rows = read32 (rows); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "found %s in image %s with rows = %d", mono_meta_table_name (table), image->assembly_name, image->tables [table].rows); } rows++; valid++; @@ -1462,6 +1472,22 @@ mono_is_problematic_image (MonoImage *image) return FALSE; } +static void +dump_encmap (MonoImage *image) +{ + MonoTableInfo *encmap = &image->tables [MONO_TABLE_ENCMAP]; + if (!encmap || !encmap->rows) + return; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "ENCMAP for %s", image->filename); + for (int i = 0; i < encmap->rows; ++i) { + guint32 cols [MONO_ENCMAP_SIZE]; + mono_metadata_decode_row (encmap, i, cols, MONO_ENCMAP_SIZE); + int token = cols [MONO_ENCMAP_TOKEN]; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "\t0x%08x: 0x%08x table: %s", i+1, token, mono_meta_table_name (mono_metadata_token_table (token))); + } +} + static MonoImage * do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status, gboolean care_about_cli, gboolean care_about_pecoff) @@ -1526,6 +1552,8 @@ do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status, if (image->loader == &pe_loader && !image->metadata_only && !mono_verifier_verify_table_data (image, error)) goto invalid_image; + dump_encmap (image); + mono_image_load_names (image); mono_image_load_time_date_stamp (image); @@ -2513,6 +2541,18 @@ mono_image_close_except_pools_all (MonoImage**images, int image_count) } } +static void +mono_image_close_except_pools_all_list (GSList *images) +{ + for (GSList *ptr = images; ptr; ptr = ptr->next) { + MonoImage *image = (MonoImage *)ptr->data; + if (image) { + if (!mono_image_close_except_pools (image)) + ptr->data = NULL; + } + } +} + /* * Returns whether mono_image_close_finish() must be called as well. * We must unload images in two steps because clearing the domain in @@ -2552,6 +2592,8 @@ mono_image_close_except_pools (MonoImage *image) mono_metadata_clean_for_image (image); + mono_metadata_update_cleanup_on_close (image); + /* * The caches inside a MonoImage might refer to metadata which is stored in referenced * assemblies, so we can't release these references in mono_assembly_close () since the @@ -2679,6 +2721,9 @@ mono_image_close_except_pools (MonoImage *image) mono_image_close_except_pools_all (image->modules, image->module_count); g_free (image->modules_loaded); + if (image->delta_image) + mono_image_close_except_pools_all_list (image->delta_image); + mono_os_mutex_destroy (&image->szarray_cache_lock); mono_os_mutex_destroy (&image->lock); @@ -2705,6 +2750,18 @@ mono_image_close_all (MonoImage**images, int image_count) g_free (images); } +static void +mono_image_close_all_list (GSList *images) +{ + for (GSList *ptr = images; ptr; ptr = ptr->next) { + MonoImage *image = (MonoImage *)ptr->data; + if (image) + mono_image_close_finish (image); + } + + g_slist_free (images); +} + void mono_image_close_finish (MonoImage *image) { @@ -2723,6 +2780,8 @@ mono_image_close_finish (MonoImage *image) mono_image_close_all (image->files, image->file_count); mono_image_close_all (image->modules, image->module_count); + mono_image_close_all_list (image->delta_image); + #ifndef DISABLE_PERFCOUNTERS /* FIXME: use an explicit subtraction method as soon as it's available */ mono_atomic_fetch_add_i32 (&mono_perfcounters->loader_bytes, -1 * mono_mempool_get_allocated (image->mempool)); @@ -3425,5 +3484,3 @@ mono_image_append_class_to_reflection_info_set (MonoClass *klass) mono_image_unlock (image); } - - diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index 1d5405e14bf45..e97c578a22b6a 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -864,7 +864,7 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp error_init (error); - mono_metadata_decode_row (&tables [MONO_TABLE_MEMBERREF], idx-1, cols, 3); + mono_metadata_decode_row (&tables [MONO_TABLE_MEMBERREF], idx-1, cols, MONO_MEMBERREF_SIZE); nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; class_index = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; /*g_print ("methodref: 0x%x 0x%x %s\n", class, nindex, @@ -1073,7 +1073,7 @@ mono_get_method_from_token (MonoImage *image, guint32 token, MonoClass *klass, if (used_context) *used_context = FALSE; - if (idx > image->tables [MONO_TABLE_METHOD].rows) { + if (mono_metadata_table_bounds_check (image, MONO_TABLE_METHOD, idx)) { mono_error_set_bad_image (error, image, "Bad method token 0x%08x (out of bounds).", token); return NULL; } @@ -2024,8 +2024,9 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error) { int idx; guint32 rva; + gboolean from_dmeta_image = FALSE; MonoImage* img; - gpointer loc; + gpointer loc = NULL; MonoGenericContainer *container; error_init (error); @@ -2070,12 +2071,23 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error) */ g_assert (mono_metadata_token_table (method->token) == MONO_TABLE_METHOD); idx = mono_metadata_token_index (method->token); - rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA); - if (!mono_verifier_verify_method_header (img, rva, error)) - return NULL; + /* EnC case */ + if (G_UNLIKELY (img->method_table_delta_index)) { + /* pre-computed rva pointer into delta IL image */ + loc = g_hash_table_lookup (img->method_table_delta_index, GUINT_TO_POINTER (idx)); + from_dmeta_image = !!loc; + } + + if (!loc) { + rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA); + + if (!mono_verifier_verify_method_header (img, rva, error)) + return NULL; + + loc = mono_image_rva_map (img, rva); + } - loc = mono_image_rva_map (img, rva); if (!loc) { mono_error_set_bad_image (error, img, "Method has zero rva"); return NULL; diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 329f839f7fc33..76095c436b255 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -587,6 +587,16 @@ struct _MonoImage { /* Contains 1 based indexes */ GHashTable *weak_field_indexes; + GHashTable *method_table_delta_index; /* EnC index for method updates */ + + /* List of MonoImages of deltas. Parent image owns 1 refcount ref of the delta image */ + GSList *delta_image; + /* Tail of delta_image for fast appends */ + GSList *delta_image_last; + + /* Metadata delta images only */ + uint32_t generation; + /* * No other runtime locks must be taken while holding this lock. * It's meant to be used only to mutate and query structures part of this image. @@ -887,6 +897,15 @@ mono_install_image_loader (const MonoImageLoader *loader); void mono_image_append_class_to_reflection_info_set (MonoClass *klass); +void +mono_image_effective_table (const MonoTableInfo **t, int *idx); + +int +mono_image_relative_delta_index (MonoImage *image_dmeta, int token); + +void +mono_image_load_enc_delta (MonoDomain *domain, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); + gpointer mono_image_set_alloc (MonoImageSet *set, guint size); @@ -943,6 +962,9 @@ mono_metadata_clean_generic_classes_for_image (MonoImage *image); MONO_API void mono_metadata_cleanup (void); +gboolean +mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index); + const char * mono_meta_table_name (int table); void mono_metadata_compute_table_bases (MonoImage *meta); diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c new file mode 100644 index 0000000000000..a5fabe45c6265 --- /dev/null +++ b/src/mono/mono/metadata/metadata-update.c @@ -0,0 +1,731 @@ +/** + * \file + * Routines for publishing metadata updates + * + * Copyright 2020 Microsoft + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + +#include +#include +#include "mono/metadata/metadata-internals.h" +#include "mono/metadata/metadata-update.h" +#include "mono/metadata/object-internals.h" +#include "mono/metadata/tokentype.h" +#include "mono/utils/mono-coop-mutex.h" +#include "mono/utils/mono-error-internals.h" +#include "mono/utils/mono-lazy-init.h" +#include "mono/utils/mono-logger-internals.h" +#include "mono/utils/mono-path.h" + +#if 1 +#define UPDATE_DEBUG(stmt) do { stmt; } while (0) +#else +#define UPDATE_DEBUG(stmt) /*empty */ +#endif + +typedef struct _EncRecs { + // for each table, the row in the EncMap table that has the first token for remapping it? + uint32_t enc_recs [MONO_TABLE_NUM + 1]; +} EncRecs; + + +/* Maps each MonoTableInfo* to the MonoImage that it belongs to. This is + * mapping the base image MonoTableInfos to the base MonoImage. We don't need + * this for deltas. + */ +static GHashTable *table_to_image, *delta_image_to_encrecs; +static MonoCoopMutex table_to_image_mutex; + +static void +table_to_image_lock (void) +{ + mono_coop_mutex_lock (&table_to_image_mutex); +} + +static void +table_to_image_unlock (void) +{ + mono_coop_mutex_unlock (&table_to_image_mutex); +} + +static void +table_to_image_init (void) +{ + mono_coop_mutex_init (&table_to_image_mutex); + table_to_image = g_hash_table_new (NULL, NULL); + delta_image_to_encrecs = g_hash_table_new (NULL, NULL); +} + +static gboolean +remove_base_image (gpointer key, gpointer value, gpointer user_data) +{ + MonoImage *base_image = (MonoImage*)user_data; + MonoImage *value_image = (MonoImage*)value; + return (value_image == base_image); +} + +void +mono_metadata_update_cleanup_on_close (MonoImage *base_image) +{ + /* remove all keys that map to the given image */ + table_to_image_lock (); + g_hash_table_foreach_remove (table_to_image, remove_base_image, (gpointer)base_image); + table_to_image_unlock (); +} + +static void +table_to_image_add (MonoImage *base_image) +{ + /* If at least one table from this image is already here, they all are */ + if (g_hash_table_contains (table_to_image, &base_image->tables[MONO_TABLE_MODULE])) + return; + table_to_image_lock (); + if (g_hash_table_contains (table_to_image, &base_image->tables[MONO_TABLE_MODULE])) { + table_to_image_unlock (); + return; + } + for (int idx = 0; idx < MONO_TABLE_NUM; ++idx) { + MonoTableInfo *table = &base_image->tables[idx]; + g_hash_table_insert (table_to_image, table, base_image); + } + table_to_image_unlock (); +} + +MonoImage * +mono_table_info_get_base_image (const MonoTableInfo *t) +{ + MonoImage *image = (MonoImage *) g_hash_table_lookup (table_to_image, t); + return image; +} + +static MonoImage* +mono_image_open_dmeta_from_data (MonoImage *base_image, uint32_t generation, gconstpointer dmeta_bytes, uint32_t dmeta_length, MonoImageOpenStatus *status); + +static void +mono_image_append_delta (MonoImage *base, MonoImage *delta); + + +void +mono_metadata_update_init (void) +{ + table_to_image_init (); + if (mono_get_runtime_callbacks ()->metadata_update_init) + mono_get_runtime_callbacks ()->metadata_update_init (); +} + + +static +void +mono_metadata_update_invoke_hook (MonoDomain *domain, MonoAssemblyLoadContext *alc, uint32_t generation) +{ + if (mono_get_runtime_callbacks ()->metadata_update_published) + mono_get_runtime_callbacks ()->metadata_update_published (domain, alc, generation); +} + +static uint32_t update_published, update_alloc_frontier; +static MonoCoopMutex publish_mutex; + +static void +publish_lock (void) +{ + mono_coop_mutex_lock (&publish_mutex); +} + +static void +publish_unlock (void) +{ + mono_coop_mutex_unlock (&publish_mutex); +} + +static mono_lazy_init_t metadata_update_lazy_init; + +static void +initialize (void) +{ + mono_coop_mutex_init (&publish_mutex); +} + +uint32_t +mono_metadata_update_prepare (MonoDomain *domain) { + mono_lazy_initialize (&metadata_update_lazy_init, initialize); + /* + * TODO: assert that the updater isn't depending on current metadata, else publishing might block. + */ + publish_lock (); + return ++update_alloc_frontier; +} + +gboolean +mono_metadata_update_available (void) { + return update_published < update_alloc_frontier; +} + +gboolean +mono_metadata_wait_for_update (uint32_t timeout_ms) +{ + /* TODO: give threads a way to voluntarily wait for an update to be published. */ + g_assert_not_reached (); +} + +void +mono_metadata_update_publish (MonoDomain *domain, MonoAssemblyLoadContext *alc, uint32_t generation) { + g_assert (update_published < generation && generation <= update_alloc_frontier); + /* TODO: wait for all threads that are using old metadata to update. */ + mono_metadata_update_invoke_hook (domain, alc, generation); + update_published = update_alloc_frontier; + publish_unlock (); +} + +void +mono_metadata_update_cancel (uint32_t generation) +{ + g_assert (update_alloc_frontier == generation); + g_assert (update_alloc_frontier > 0); + g_assert (update_alloc_frontier - 1 >= update_published); + --update_alloc_frontier; + publish_unlock (); +} + +/** + * LOCKING: Assumes the publish_lock is held + */ +void +mono_image_append_delta (MonoImage *base, MonoImage *delta) +{ + if (!base->delta_image) { + base->delta_image = base->delta_image_last = g_slist_prepend (NULL, delta); + return; + } + g_assert (((MonoImage*)base->delta_image_last->data)->generation < delta->generation); + base->delta_image_last = g_slist_append (base->delta_image_last, delta); +} + +/** + * LOCKING: assumes the publish_lock is held + */ +MonoImage* +mono_image_open_dmeta_from_data (MonoImage *base_image, uint32_t generation, gconstpointer dmeta_bytes, uint32_t dmeta_length, MonoImageOpenStatus *status) +{ + MonoAssemblyLoadContext *alc = mono_image_get_alc (base_image); + MonoImage *dmeta_image = mono_image_open_from_data_internal (alc, (char*)dmeta_bytes, dmeta_length, TRUE, status, FALSE, TRUE, NULL, NULL); + + dmeta_image->generation = generation; + + /* base_image takes ownership of 1 refcount ref of dmeta_image */ + mono_image_append_delta (base_image, dmeta_image); + + return dmeta_image; +} + +static void +dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) +{ + int rows; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "dmeta tables:"); + for (int idx = 0; idx < MONO_TABLE_NUM; ++idx) { + if (image_dmeta->tables [idx].base) + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "\t0x%02x \"%s\"", idx, mono_meta_table_name (idx)); + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "================================"); + + rows = mono_image_get_table_rows (image_base, MONO_TABLE_TYPEREF); + for (int i = 1; i <= rows; ++i) { + guint32 cols [MONO_TYPEREF_SIZE]; + mono_metadata_decode_row (&image_base->tables [MONO_TABLE_TYPEREF], i - 1, cols, MONO_TYPEREF_SIZE); + const char *scope = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_SCOPE]); + const char *name = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_NAME]); + const char *nspace = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_NAMESPACE]); + + if (!scope) + scope = ""; + if (!name) + name = ""; + if (!nspace) + nspace = ""; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base typeref i=%d (token=0x%08x) -> scope=%s, namespace=%s, name=%s", i, MONO_TOKEN_TYPE_REF | i, scope, nspace, name); + } + if (!image_dmeta->minimal_delta) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "--------------------------------"); + + rows = mono_image_get_table_rows (image_dmeta, MONO_TABLE_TYPEREF); + for (int i = 1; i <= rows; ++i) { + guint32 cols [MONO_TYPEREF_SIZE]; + mono_metadata_decode_row (&image_dmeta->tables [MONO_TABLE_TYPEREF], i - 1, cols, MONO_TYPEREF_SIZE); + const char *scope = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_SCOPE]); + const char *name = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_NAME]); + const char *nspace = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_NAMESPACE]); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "dmeta typeref i=%d (token=0x%08x) -> scope=%s, nspace=%s, name=%s", i, MONO_TOKEN_TYPE_REF | i, scope, nspace, name); + } + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "================================"); + + rows = mono_image_get_table_rows (image_base, MONO_TABLE_METHOD); + for (int i = 1; i <= rows ; ++i) { + guint32 cols [MONO_METHOD_SIZE]; + mono_metadata_decode_row_raw (&image_base->tables [MONO_TABLE_METHOD], i - 1, cols, MONO_METHOD_SIZE); + const char *name = mono_metadata_string_heap (image_base, cols [MONO_METHOD_NAME]); + guint32 rva = cols [MONO_METHOD_RVA]; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base method i=%d (token=0x%08x), rva=%d/0x%04x, name=%s", i, MONO_TOKEN_METHOD_DEF | i, rva, rva, name); + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "--------------------------------"); + + rows = mono_image_get_table_rows (image_dmeta, MONO_TABLE_METHOD); + for (int i = 1; i <= rows ; ++i) { + guint32 cols [MONO_METHOD_SIZE]; + mono_metadata_decode_row_raw (&image_dmeta->tables [MONO_TABLE_METHOD], i - 1, cols, MONO_METHOD_SIZE); + const char *name = mono_metadata_string_heap (image_base, cols [MONO_METHOD_NAME]); + guint32 rva = cols [MONO_METHOD_RVA]; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "dmeta method i=%d (token=0x%08x), rva=%d/0x%04x, name=%s", i, MONO_TOKEN_METHOD_DEF | i, rva, rva, name); + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "================================"); + + rows = mono_image_get_table_rows (image_base, MONO_TABLE_STANDALONESIG); + for (int i = 1; i <= rows; ++i) { + guint32 cols [MONO_STAND_ALONE_SIGNATURE_SIZE]; + mono_metadata_decode_row (&image_base->tables [MONO_TABLE_STANDALONESIG], i - 1, cols, MONO_STAND_ALONE_SIGNATURE_SIZE); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base standalonesig i=%d (token=0x%08x) -> 0x%08x", i, MONO_TOKEN_SIGNATURE | i, cols [MONO_STAND_ALONE_SIGNATURE]); + } + + if (!image_dmeta->minimal_delta) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "--------------------------------"); + + rows = mono_image_get_table_rows (image_dmeta, MONO_TABLE_STANDALONESIG); + for (int i = 1; i <= rows; ++i) { + guint32 cols [MONO_STAND_ALONE_SIGNATURE_SIZE]; + mono_metadata_decode_row_raw (&image_dmeta->tables [MONO_TABLE_STANDALONESIG], i - 1, cols, MONO_STAND_ALONE_SIGNATURE_SIZE); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "dmeta standalonesig i=%d (token=0x%08x) -> 0x%08x", i, MONO_TOKEN_SIGNATURE | i, cols [MONO_STAND_ALONE_SIGNATURE]); + } + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "================================"); + +} + +void +mono_image_effective_table (const MonoTableInfo **t, int *idx) +{ + if (*idx < (*t)->rows) + return; + + MonoImage *base = mono_table_info_get_base_image (*t); + if (!base || !base->delta_image) + return; + + GSList *list = base->delta_image; + MonoImage *dmeta; + int ridx; + MonoTableInfo *table; + + /* Invariant: `*t` must be a `MonoTableInfo` of the base image. */ + g_assert (base->tables < *t && *t < &base->tables [MONO_TABLE_LAST]); + + size_t s = ALIGN_TO (sizeof (MonoTableInfo), sizeof (gpointer)); + int tbl_index = ((intptr_t) *t - (intptr_t) base->tables) / s; + + do { + g_assertf (list, "couldn't find idx=0x%08x in assembly=%s", idx, dmeta && dmeta->name ? dmeta->name : "unknown image"); + dmeta = list->data; + list = list->next; + table = &dmeta->tables [tbl_index]; + ridx = mono_image_relative_delta_index (dmeta, mono_metadata_make_token (tbl_index, *idx + 1)) - 1; + } while (ridx < 0 || ridx >= table->rows); + + *t = table; + *idx = ridx; +} + +/* + * The ENCMAP table contains the base of the relative offset. + * + * Example: + * Say you have a base image with a METHOD table having 5 entries. The minimal + * delta image adds another one, so it would be indexed with token + * `MONO_TOKEN_METHOD_DEF | 6`. However, the minimal delta image only has this + * single entry, and thus this would be an out-of-bounds access. That's where + * the ENCMAP table comes into play: It will have an entry + * `MONO_TOKEN_METHOD_DEF | 5`, so before accessing the new entry in the + * minimal delta image, it has to be substracted. Thus the new relative index + * is `1`, and no out-of-bounds acccess anymore. + * + * One can assume that ENCMAP is sorted (todo: verify this claim). + * + * BTW, `enc_recs` is just a pre-computed map to make the lookup for the + * relative index faster. + */ +int +mono_image_relative_delta_index (MonoImage *image_dmeta, int token) +{ + MonoTableInfo *encmap = &image_dmeta->tables [MONO_TABLE_ENCMAP]; + int table = mono_metadata_token_table (token); + int index = mono_metadata_token_index (token); + + /* this helper expects and returns as "index origin = 1" */ + g_assert (index > 0); + + if (!encmap->rows || !image_dmeta->minimal_delta) + return mono_metadata_token_index (token); + + EncRecs *enc_recs = g_hash_table_lookup (delta_image_to_encrecs, image_dmeta); + g_assert (enc_recs); + + int index_map = enc_recs->enc_recs [table]; + guint32 cols[MONO_ENCMAP_SIZE]; + mono_metadata_decode_row (encmap, index_map - 1, cols, MONO_ENCMAP_SIZE); + int map_entry = cols [MONO_ENCMAP_TOKEN]; + + while (mono_metadata_token_table (map_entry) == table && mono_metadata_token_index (map_entry) < index && index_map < encmap->rows) { + mono_metadata_decode_row (encmap, ++index_map - 1, cols, MONO_ENCMAP_SIZE); + map_entry = cols [MONO_ENCMAP_TOKEN]; + } + + if (mono_metadata_token_table (map_entry) == table) { +#if 0 + g_assert (mono_metadata_token_index (map_entry) == index); +#endif + if (mono_metadata_token_index (map_entry) != index) + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) + g_print ("warning: map_entry=0x%08x != index=0x%08x. is this a problem?\n", map_entry, index); + } + + int return_val = index_map - enc_recs->enc_recs [table] + 1; + g_assert (return_val > 0); + return return_val; +} + +static void +start_encmap (MonoImage *image_dmeta) +{ + MonoTableInfo *encmap = &image_dmeta->tables [MONO_TABLE_ENCMAP]; + int table, prev_table = -1, idx; + + g_assert (!g_hash_table_lookup (delta_image_to_encrecs, image_dmeta)); + + if (!encmap->rows) + return; + + EncRecs *enc_recs = g_malloc0 (sizeof (EncRecs)); + + for (idx = 1; idx <= encmap->rows; ++idx) { + guint32 cols[MONO_ENCMAP_SIZE]; + mono_metadata_decode_row (encmap, idx - 1, cols, MONO_ENCMAP_SIZE); + uint32_t tok = cols [MONO_ENCMAP_TOKEN]; + table = mono_metadata_token_table (tok); + g_assert (table >= 0 && table < MONO_TABLE_NUM); + g_assert (table != MONO_TABLE_ENCLOG); + g_assert (table != MONO_TABLE_ENCMAP); + g_assert (table >= prev_table); + if (table == prev_table) + continue; + while (prev_table < table) { + prev_table++; + enc_recs->enc_recs [prev_table] = idx; + } + } + g_assert (prev_table < MONO_TABLE_NUM); + while (prev_table < MONO_TABLE_NUM) { + prev_table++; + enc_recs->enc_recs [prev_table] = idx; + } + + for (int i = 0 ; i < MONO_TABLE_NUM; ++i) { + if (!image_dmeta->tables [i].base) + continue; + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "enc_recs [%02x] / %s = 0x%02x", i, mono_meta_table_name (i), enc_recs->enc_recs[i]); + } + + g_hash_table_insert (delta_image_to_encrecs, image_dmeta, enc_recs); +} + +static const char* +funccode_to_str (int func_code) +{ + switch (func_code) { + case 0: return "Func default"; + case 1: return "Method Create"; + case 2: return "Field Create"; + case 3: return "Param Create"; + case 4: return "Property Create"; + case 5: return "Event Create"; + default: g_assert_not_reached (); + } + return NULL; +} + +/* Run some sanity checks first. If we detect unsupported scenarios, this + * function will fail and the metadata update should be aborted. This should + * run before anything in the metadata world is updated. */ +static gboolean +apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer dil_data, uint32_t dil_length, MonoError *error) +{ + MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG]; + int rows = table_enclog->rows; + + /* hack: make a pass over it, looking only for table method updates, in + * order to give more meaningful error messages first */ + + for (int i = 0; i < rows ; ++i) { + guint32 cols [MONO_ENCLOG_SIZE]; + mono_metadata_decode_row (table_enclog, i, cols, MONO_ENCLOG_SIZE); + + int log_token = cols [MONO_ENCLOG_TOKEN]; + int func_code = cols [MONO_ENCLOG_FUNC_CODE]; + + int token_table = mono_metadata_token_table (log_token); + int token_index = mono_metadata_token_index (log_token); + + if (token_table != MONO_TABLE_METHOD) + continue; + + if (token_index > image_base->tables [token_table].rows) { + mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: cannot add new method with token 0x%08x", log_token); + return FALSE; + } + + g_assert (func_code == 0); /* anything else doesn't make sense here */ + } + + for (int i = 0; i < rows ; ++i) { + guint32 cols [MONO_ENCLOG_SIZE]; + mono_metadata_decode_row (table_enclog, i, cols, MONO_ENCLOG_SIZE); + + int log_token = cols [MONO_ENCLOG_TOKEN]; + int func_code = cols [MONO_ENCLOG_FUNC_CODE]; + + int token_table = mono_metadata_token_table (log_token); + int token_index = mono_metadata_token_index (log_token); + + if (token_table == MONO_TABLE_ASSEMBLYREF) { + /* okay, supported */ + } else if (token_table == MONO_TABLE_METHOD) { + /* handled above */ + } else { + if (token_index <= image_base->tables [token_table].rows) { + mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing table cols. token=0x%08x", log_token); + return FALSE; + } + } + + + switch (func_code) { + case 0: /* default */ + break; + default: + mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: FuncCode %d (%s) not supported (token=0x%08x)", func_code, funccode_to_str (func_code), log_token); + return FALSE; + } + } + return TRUE; +} + +/* do actuall enclog application */ +static gboolean +apply_enclog_pass2 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer dil_data, uint32_t dil_length, MonoError *error) +{ + MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG]; + int rows = table_enclog->rows; + + gboolean assemblyref_updated = FALSE; + for (int i = 0; i < rows ; ++i) { + guint32 cols [MONO_ENCLOG_SIZE]; + mono_metadata_decode_row (table_enclog, i, cols, MONO_ENCLOG_SIZE); + + int log_token = cols [MONO_ENCLOG_TOKEN]; + int func_code = cols [MONO_ENCLOG_FUNC_CODE]; + + int token_table = mono_metadata_token_table (log_token); + int token_index = mono_metadata_token_index (log_token); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "enclog i=%d: token=0x%08x (table=%s): %d", i, log_token, mono_meta_table_name (token_table), func_code); + + switch (func_code) { + case 0: /* default */ + break; + default: + g_error ("EnC: unsupported FuncCode, should be caught by pass1"); + break; + } + + if (token_table == MONO_TABLE_ASSEMBLYREF) { + g_assert (token_index > image_base->tables [token_table].rows); + + if (assemblyref_updated) + continue; + + assemblyref_updated = TRUE; + + int old_rows = image_base->tables [MONO_TABLE_ASSEMBLYREF].rows; + for (GSList *l = image_base->delta_image; l; l = l->next) { + MonoImage *delta_child = l->data; + old_rows += delta_child->tables [MONO_TABLE_ASSEMBLYREF].rows; + } + int new_rows = image_dmeta->tables [MONO_TABLE_ASSEMBLYREF].rows; + + old_rows -= new_rows; + g_assert (new_rows > 0); + g_assert (old_rows > 0); + + /* TODO: this can end bad with code around assembly.c:mono_assembly_load_reference */ + mono_image_lock (image_base); + MonoAssembly **old_array = image_base->references; + g_assert (image_base->nreferences == old_rows); + + image_base->references = g_new0 (MonoAssembly *, old_rows + new_rows + 1); + memcpy (image_base->references, old_array, sizeof (gpointer) * (old_rows + 1)); + image_base->nreferences = old_rows + new_rows; + mono_image_unlock (image_base); + + g_free (old_array); + } else if (token_table == MONO_TABLE_METHOD) { + if (token_index > image_base->tables [token_table].rows) { + g_error ("EnC: new method added, should be caught by pass1"); + } + + if (!image_base->method_table_delta_index) + image_base->method_table_delta_index = g_hash_table_new (g_direct_hash, g_direct_equal); + + int mapped_token = mono_image_relative_delta_index (image_dmeta, mono_metadata_make_token (token_table, token_index)); + int rva = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_METHOD], mapped_token - 1, MONO_METHOD_RVA); + if (rva < dil_length) { + char *il_address = ((char *) dil_data) + rva; + g_hash_table_insert (image_base->method_table_delta_index, GUINT_TO_POINTER (token_index), (gpointer) il_address); + } else { + /* rva points probably into image_base IL stream. can this ever happen? */ + g_print ("TODO: this case is still a bit WTF. token=0x%08x with rva=0x%04x\n", log_token, rva); + } + } else if (token_table == MONO_TABLE_TYPEDEF) { + /* TODO: throw? */ + /* TODO: happens changing the class (adding field or method). we ignore it, but dragons are here */ + + /* existing entries are supposed to be patched */ + g_assert (token_index <= image_base->tables [token_table].rows); + } else { + g_assert (token_index > image_base->tables [token_table].rows); + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) + g_print ("todo: do something about this table index: 0x%02x\n", token_table); + } + } + return TRUE; +} + +static void +append_heap (MonoStreamHeader *base, MonoStreamHeader *appendix) +{ + int size = base->size + appendix->size; + char *data = (char *) g_malloc (size * sizeof (char)); + memcpy (data, base->data, base->size); + memcpy (data + base->size, appendix->data, appendix->size); + base->data = data; + base->size = size; +} + +/** + * + * LOCKING: Takes the publish_lock + */ +void +mono_image_load_enc_delta (MonoDomain *domain, MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes, uint32_t dil_length, MonoError *error) +{ + const char *basename = image_base->filename; + /* FIXME: + * (1) do we need to memcpy dmeta_bytes ? (maybe) + * (2) do we need to memcpy dil_bytes ? (pretty sure, yes) + */ + + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) { + g_print ("LOADING basename=%s delta update.\ndelta image=%p & dil=%p\n", basename, dmeta_bytes, dil_bytes); + /* TODO: add a non-async version of mono_dump_mem */ + mono_dump_mem (dmeta_bytes, dmeta_length); + mono_dump_mem (dil_bytes, dil_length); + } + + uint32_t generation = mono_metadata_update_prepare (domain); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image string size: 0x%08x", image_base->heap_strings.size); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image user string size: 0x%08x", image_base->heap_us.size); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image blob heap addr: %p", image_base->heap_blob.data); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image blob heap size: 0x%08x", image_base->heap_blob.size); + + MonoImageOpenStatus status; + MonoImage *image_dmeta = mono_image_open_dmeta_from_data (image_base, generation, dmeta_bytes, dmeta_length, &status); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "delta image string size: 0x%08x", image_dmeta->heap_strings.size); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "delta image user string size: 0x%08x", image_dmeta->heap_us.size); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "delta image blob heap addr: %p", image_dmeta->heap_blob.data); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "delta image blob heap size: 0x%08x", image_dmeta->heap_blob.size); + g_assert (image_dmeta); + g_assert (status == MONO_IMAGE_OK); + + if (image_dmeta->minimal_delta) { + guint32 idx = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_MODULE], 0, MONO_MODULE_NAME); + + /* TODO: hmm. */ + const char *module_name = NULL; + if (idx >= image_base->heap_strings.size) { + module_name = mono_metadata_string_heap (image_dmeta, idx - image_base->heap_strings.size); + } else { + module_name = mono_metadata_string_heap (image_base, idx); + } + + /* Set the module name now that we know the base String heap size */ + g_assert (!image_dmeta->module_name); + image_dmeta->module_name = module_name; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "applied dmeta name: '%s'\n", module_name); + } + + MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG]; + MonoTableInfo *table_encmap = &image_dmeta->tables [MONO_TABLE_ENCMAP]; + + if (!table_enclog->rows && !table_encmap->rows) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "No enclog or encmap in delta image for base=%s, nothing to do", basename); + mono_metadata_update_cancel (generation); + return; + } + + if (!apply_enclog_pass1 (image_base, image_dmeta, dil_bytes, dil_length, error)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error on sanity-checking delta image to base=%s, due to: %s", basename, mono_error_get_message (error)); + mono_metadata_update_cancel (generation); + return; + } + + /* if there are updates, start tracking the tables of the base image, if we weren't already. */ + if (table_enclog->rows) + table_to_image_add (image_base); + + start_encmap (image_dmeta); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base guid: %s", image_base->guid); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "dmeta guid: %s", image_dmeta->guid); + + if (image_dmeta->minimal_delta) { + append_heap (&image_base->heap_strings, &image_dmeta->heap_strings); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image strings heap addr (merged): %p", image_base->heap_strings.data); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image strings heap size (merged): 0x%08x", image_base->heap_strings.size); + + append_heap (&image_base->heap_us, &image_dmeta->heap_us); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image user string heap addr (merged): %p", image_base->heap_us.data); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image user string heap size (merged): 0x%08x", image_base->heap_us.size); + + append_heap (&image_base->heap_blob, &image_dmeta->heap_blob); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image blob heap addr (merged): %p", image_base->heap_blob.data); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image blob heap size (merged): 0x%08x", image_base->heap_blob.size); + } else { + g_error ("implement me for regular delta images"); + } + + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) + dump_update_summary (image_base, image_dmeta); + + if (!apply_enclog_pass2 (image_base, image_dmeta, dil_bytes, dil_length, error)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error applying delta image to base=%s, due to: %s", basename, mono_error_get_message (error)); + mono_metadata_update_cancel (generation); + return; + } + mono_error_assert_ok (error); + + MonoAssemblyLoadContext *alc = mono_image_get_alc (image_base); + mono_metadata_update_publish (domain, alc, generation); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, ">>> EnC delta for base=%s (generation %d) applied", basename, generation); +} diff --git a/src/mono/mono/metadata/metadata-update.h b/src/mono/mono/metadata/metadata-update.h new file mode 100644 index 0000000000000..6bab202798329 --- /dev/null +++ b/src/mono/mono/metadata/metadata-update.h @@ -0,0 +1,36 @@ +/** + * \file + */ + +#ifndef __MONO_METADATA_UPDATE_H__ +#define __MONO_METADATA_UPDATE_H__ + +#include "mono/utils/mono-forward.h" +#include "mono/metadata/loader-internals.h" +#include "mono/metadata/metadata-internals.h" + +void +mono_metadata_update_init (void); + +gboolean +mono_metadata_update_available (void); + +gboolean +mono_metadata_wait_for_update (uint32_t timeout_ms); + +uint32_t +mono_metadata_update_prepare (MonoDomain *domain); + +void +mono_metadata_update_publish (MonoDomain *domain, MonoAssemblyLoadContext *alc, uint32_t generation); + +void +mono_metadata_update_cancel (uint32_t generation); + +void +mono_metadata_update_cleanup_on_close (MonoImage *base_image); + +MonoImage * +mono_table_info_get_base_image (const MonoTableInfo *t); + +#endif /*__MONO_METADATA_UPDATE_H__*/ diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index 73a6978c988e9..869dded8e17f3 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -24,6 +24,7 @@ #include "class-internals.h" #include "metadata-internals.h" #include "reflection-internals.h" +#include "metadata-update.h" #include "verify-internals.h" #include "class.h" #include "marshal.h" @@ -989,6 +990,31 @@ mono_metadata_compute_size (MonoImage *meta, int tableindex, guint32 *result_bit return size; } +/* returns true if given index is not in bounds with provided table/index pair */ +gboolean +mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index) +{ + if (token_index <= image->tables [table_index].rows) + return FALSE; + + GSList *list = image->delta_image; + MonoImage *dmeta; + MonoTableInfo *table; + int ridx; + + /* FIXME: Only lookup upto the latest published image, not all images. */ + do { + if (!list) + return TRUE; + dmeta = list->data; + list = list->next; + table = &dmeta->tables [table_index]; + ridx = mono_image_relative_delta_index (dmeta, mono_metadata_make_token (table_index, token_index + 1)) - 1; + } while (ridx < 0 || ridx >= table->rows); + + return FALSE; +} + /** * mono_metadata_compute_table_bases: * \param meta metadata context to compute table values @@ -1201,6 +1227,17 @@ dword_align (const unsigned char *ptr) */ void mono_metadata_decode_row (const MonoTableInfo *t, int idx, guint32 *res, int res_size) +{ + mono_image_effective_table (&t, &idx); + + mono_metadata_decode_row_raw (t, idx, res, res_size); +} + +/** + * same as mono_metadata_decode_row, but ignores potential delta images + */ +void +mono_metadata_decode_row_raw (const MonoTableInfo *t, int idx, guint32 *res, int res_size) { guint32 bitfield = t->size_bitfield; int i, count = mono_metadata_table_count (bitfield); @@ -1246,11 +1283,13 @@ mono_metadata_decode_row (const MonoTableInfo *t, int idx, guint32 *res, int res gboolean mono_metadata_decode_row_checked (const MonoImage *image, const MonoTableInfo *t, int idx, guint32 *res, int res_size, MonoError *error) { + const char *image_name = image && image->name ? image->name : "unknown image"; + + mono_image_effective_table (&t, &idx); + guint32 bitfield = t->size_bitfield; int i, count = mono_metadata_table_count (bitfield); - const char *image_name = image && image->name ? image->name : "unknown image"; - if (G_UNLIKELY (! (idx < t->rows && idx >= 0))) { mono_error_set_bad_image_by_name (error, image_name, "row index %d out of bounds: %d rows: %s", idx, t->rows, image_name); return FALSE; @@ -1320,10 +1359,13 @@ mono_metadata_decode_row_dynamic_checked (const MonoDynamicImage *image, const M guint32 mono_metadata_decode_row_col (const MonoTableInfo *t, int idx, guint col) { - guint32 bitfield = t->size_bitfield; int i; const char *data; int n; + + mono_image_effective_table (&t, &idx); + + guint32 bitfield = t->size_bitfield; g_assert (idx < t->rows); g_assert (col < mono_metadata_table_count (bitfield)); @@ -1836,6 +1878,8 @@ mono_metadata_init (void) mono_counters_register ("ImgSet Cache Hit", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_hit); mono_counters_register ("ImgSet Cache Miss", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_miss); mono_counters_register ("ImgSet Count", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_count); + + mono_metadata_update_init (); } /** @@ -4503,12 +4547,12 @@ mono_metadata_parse_mh_full (MonoImage *m, MonoGenericContainer *container, cons } if (local_var_sig_tok) { - int idx = (local_var_sig_tok & 0xffffff)-1; - if (idx >= t->rows || idx < 0) { - mono_error_set_bad_image (error, m, "Invalid method header local vars signature token 0x%8x", idx); + int idx = mono_metadata_token_index (local_var_sig_tok) - 1; + if (mono_metadata_table_bounds_check (m, MONO_TABLE_STANDALONESIG, idx)) { + mono_error_set_bad_image (error, m, "Invalid method header local vars signature token 0x%08x", idx); goto fail; } - mono_metadata_decode_row (t, idx, cols, 1); + mono_metadata_decode_row (t, idx, cols, MONO_STAND_ALONE_SIGNATURE_SIZE); if (!mono_verifier_verify_standalone_signature (m, cols [MONO_STAND_ALONE_SIGNATURE], error)) goto fail; diff --git a/src/mono/mono/metadata/metadata.h b/src/mono/mono/metadata/metadata.h index c44931d7328d8..7870adbdf88b8 100644 --- a/src/mono/mono/metadata/metadata.h +++ b/src/mono/mono/metadata/metadata.h @@ -205,6 +205,8 @@ typedef struct { MONO_API void mono_metadata_init (void); +void mono_metadata_decode_row_raw (const MonoTableInfo *t, int idx, uint32_t *res, int res_size); + MONO_API void mono_metadata_decode_row (const MonoTableInfo *t, int idx, uint32_t *res, diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index a5d6e8afa73fd..8e17b48befacb 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -841,6 +841,8 @@ typedef struct { GHashTable *(*get_weak_field_indexes) (MonoImage *image); void (*install_state_summarizer) (void); gboolean (*is_interpreter_enabled) (void); + void (*metadata_update_init) (void); + void (*metadata_update_published) (MonoDomain *domain, MonoAssemblyLoadContext *alc, uint32_t generation); } MonoRuntimeCallbacks; typedef gboolean (*MonoInternalStackWalk) (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data); diff --git a/src/mono/mono/mini/ee.h b/src/mono/mono/mini/ee.h index a74b444e95fd8..e4c2ee7b7cd0b 100644 --- a/src/mono/mono/mini/ee.h +++ b/src/mono/mono/mini/ee.h @@ -57,6 +57,7 @@ typedef gpointer MonoInterpFrameHandle; MONO_EE_CALLBACK (void, stop_single_stepping, (void)) \ MONO_EE_CALLBACK (void, free_context, (gpointer)) \ MONO_EE_CALLBACK (void, set_optimizations, (guint32)) \ + MONO_EE_CALLBACK (void, metadata_update_init, (void)) \ MONO_EE_CALLBACK (void, invalidate_transformed, (MonoDomain *domain)) \ MONO_EE_CALLBACK (void, cleanup, (void)) \ MONO_EE_CALLBACK (void, mark_stack, (gpointer thread_info, GcScanFunc func, gpointer gc_data, gboolean precise)) \ diff --git a/src/mono/mono/mini/interp-stubs.c b/src/mono/mono/mini/interp-stubs.c index 0c06044553cf5..7adba1f63fd72 100644 --- a/src/mono/mono/mini/interp-stubs.c +++ b/src/mono/mono/mini/interp-stubs.c @@ -79,6 +79,11 @@ stub_set_optimizations (guint32 i) { } +static void +stub_metadata_update_init (void) +{ +} + static void stub_invalidate_transformed (MonoDomain *domain) { diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 24cc42c9d2ebc..d71fbb6e4128d 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -25,7 +25,9 @@ #include #include #include +#include #include +#include #include #ifdef HAVE_ALLOCA_H @@ -7553,13 +7555,59 @@ invalidate_transform (gpointer imethod_) imethod->transformed = FALSE; } +static void +copy_imethod_for_frame (MonoDomain *domain, InterpFrame *frame) +{ + InterpMethod *copy = (InterpMethod *) mono_domain_alloc0 (domain, sizeof (InterpMethod)); + memcpy (copy, frame->imethod, sizeof (InterpMethod)); + copy->next_jit_code_hash = NULL; /* we don't want that in our copy */ + frame->imethod = copy; + /* Note: The copy will be around until the domain is unloading. Ideally we + * would reclaim its memory when the corresponding InterpFrame is popped. + */ +} + +static void +interp_metadata_update_init (void) +{ + g_assertf ((mono_interp_opt & INTERP_OPT_INLINE) == 0, "Interpreter inlining must be turned off for metadata updates"); +} + static void interp_invalidate_transformed (MonoDomain *domain) { + mono_gc_stop_world (); + /* (1) make a copy of imethod for every interpframe that is on the stack, + * so we do not invalidate currently running methods */ + + FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { + if (!info) + continue; + + MonoLMF *lmf = info->jit_data->lmf; + while (lmf) { + if (((gsize) lmf->previous_lmf) & 2) { + MonoLMFExt *ext = (MonoLMFExt *) lmf; + if (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX) { + InterpFrame *frame = ext->interp_exit_data; + while (frame) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "threadinfo=%p, copy imethod for method=%s", info, mono_method_full_name (frame->imethod->method, 1)); + copy_imethod_for_frame (domain, frame); + frame = frame->parent; + } + } + } + lmf = (MonoLMF *)(((gsize) lmf->previous_lmf) & ~3); + } + } FOREACH_THREAD_END + + /* (2) invalidate all the registered imethods */ MonoJitDomainInfo *info = domain_jit_info (domain); mono_domain_jit_code_hash_lock (domain); mono_internal_hash_table_apply (&info->interp_code_hash, invalidate_transform); mono_domain_jit_code_hash_unlock (domain); + + mono_gc_restart_world (); } static void diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index ca227b46961d2..e8ab558966377 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -169,6 +169,10 @@ GSList *mono_interp_only_classes; static void register_icalls (void); static void runtime_cleanup (MonoDomain *domain, gpointer user_data); +static void mini_metadata_update_init (void); +static void mini_invalidate_transformed_interp_methods (MonoDomain *domain, MonoAssemblyLoadContext *alc, uint32_t generation); + + gboolean mono_running_on_valgrind (void) @@ -4429,6 +4433,8 @@ mini_init (const char *filename, const char *runtime_version) #ifndef DISABLE_CRASH_REPORTING callbacks.install_state_summarizer = mini_register_sigterm_handler; #endif + callbacks.metadata_update_init = mini_metadata_update_init; + callbacks.metadata_update_published = mini_invalidate_transformed_interp_methods; mono_install_callbacks (&callbacks); @@ -5322,3 +5328,17 @@ mono_runtime_install_custom_handlers_usage (void) "No handlers supported on current platform.\n"); } #endif /* HOST_WIN32 */ + +void +mini_metadata_update_init (void) +{ + mini_get_interp_callbacks ()->metadata_update_init (); +} + +void +mini_invalidate_transformed_interp_methods (MonoDomain *domain, MonoAssemblyLoadContext *alc G_GNUC_UNUSED, uint32_t generation G_GNUC_UNUSED) +{ + mini_get_interp_callbacks ()->invalidate_transformed (domain); +} + + diff --git a/src/mono/mono/tests/Makefile.am b/src/mono/mono/tests/Makefile.am index 52f0c42e08601..9771c11dc0dc8 100755 --- a/src/mono/mono/tests/Makefile.am +++ b/src/mono/mono/tests/Makefile.am @@ -1,6 +1,6 @@ MAKEFLAGS := $(MAKEFLAGS) --no-builtin-rules -SUBDIRS = gc-descriptors . testing_gac assembly-load-reference llvmonly-mixed fullaot-mixed +SUBDIRS = gc-descriptors . testing_gac assembly-load-reference llvmonly-mixed fullaot-mixed enc check-local: ok=:; \ diff --git a/src/mono/mono/tests/enc/.gitignore b/src/mono/mono/tests/enc/.gitignore new file mode 100644 index 0000000000000..3fe5e125b6c99 --- /dev/null +++ b/src/mono/mono/tests/enc/.gitignore @@ -0,0 +1,4 @@ +*.dil +*.dmeta +*.dpdb +Makefile diff --git a/src/mono/mono/tests/enc/AddClass.cs b/src/mono/mono/tests/enc/AddClass.cs new file mode 100644 index 0000000000000..24e362dc18326 --- /dev/null +++ b/src/mono/mono/tests/enc/AddClass.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class AddClass { + public static int Main (string []args) { + Assembly assm = typeof (AddClass).Assembly; + var replacer = EncHelper.Make (); + + var secondClassType = ReplaceMe (); + if (secondClassType != typeof (AddClass)) + return 1; + + try { + replacer.Update (assm); + /* doesn't work yet, thus should throw exception */ + return 2; + } catch (TargetInvocationException e) { + Console.WriteLine ("e: " + e); + if (e.InnerException.Message.Contains ("cannot add new class")) + return 0; + } + /* we should not get here, yet */ + secondClassType = ReplaceMe (); + if (secondClassType != typeof (AddClass)) { + /* would be expected if it would work */ + return 4; + } + return 3; + } + + public static System.Type ReplaceMe () { + return typeof (AddClass); + } +} diff --git a/src/mono/mono/tests/enc/AddClass_v1.cs b/src/mono/mono/tests/enc/AddClass_v1.cs new file mode 100644 index 0000000000000..ada9324b39411 --- /dev/null +++ b/src/mono/mono/tests/enc/AddClass_v1.cs @@ -0,0 +1,38 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class AddClass { + public static int Main (string []args) { + Assembly assm = typeof (AddClass).Assembly; + var replacer = EncHelper.Make (); + + var secondClassType = ReplaceMe (); + if (secondClassType != typeof (AddClass)) + return 1; + + try { + replacer.Update (assm); + /* doesn't work yet, thus should throw exception */ + return 2; + } catch (TargetInvocationException e) { + Console.WriteLine ("e: " + e); + if (e.InnerException.Message.Contains ("cannot add new class")) + return 0; + } + /* we should not get here, yet */ + secondClassType = ReplaceMe (); + if (secondClassType != typeof (AddClass)) { + /* would be expected if it would work */ + return 4; + } + return 3; + } + + public static System.Type ReplaceMe () { + return typeof (SecondClass); + } +} + +class SecondClass { } diff --git a/src/mono/mono/tests/enc/AddStaticField.cs b/src/mono/mono/tests/enc/AddStaticField.cs new file mode 100644 index 0000000000000..9ffba39a0b4b8 --- /dev/null +++ b/src/mono/mono/tests/enc/AddStaticField.cs @@ -0,0 +1,29 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class AddStaticField { + public static int field1 = 1; + + public static int Main (string []args) { + Assembly assm = typeof (AddStaticField).Assembly; + var replacer = EncHelper.Make (); + + if (AddStaticField.field1 != 1) + return 1; + + try { + replacer.Update (assm); + /* doesn't work yet, thus should throw exception */ + return 2; + } catch (TargetInvocationException e) { + Console.WriteLine ("e: " + e); +#if false + if (e.InnerException.Message.Contains ("cannot add new class")) + return 0; +#endif + } + return 3; + } +} diff --git a/src/mono/mono/tests/enc/AddStaticField_v1.cs b/src/mono/mono/tests/enc/AddStaticField_v1.cs new file mode 100644 index 0000000000000..c082ecc065d53 --- /dev/null +++ b/src/mono/mono/tests/enc/AddStaticField_v1.cs @@ -0,0 +1,30 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class AddStaticField { + public static int field1 = 1; + public static int field2 = 2; + + public static int Main (string []args) { + Assembly assm = typeof (AddStaticField).Assembly; + var replacer = EncHelper.Make (); + + if (AddStaticField.field1 != 1) + return 1; + + try { + replacer.Update (assm); + /* doesn't work yet, thus should throw exception */ + return 2; + } catch (TargetInvocationException e) { + Console.WriteLine ("e: " + e); +#if false + if (e.InnerException.Message.Contains ("cannot add new class")) + return 0; +#endif + } + return 3; + } +} diff --git a/src/mono/mono/tests/enc/AddStaticMethod.cs b/src/mono/mono/tests/enc/AddStaticMethod.cs new file mode 100644 index 0000000000000..04528fccccb70 --- /dev/null +++ b/src/mono/mono/tests/enc/AddStaticMethod.cs @@ -0,0 +1,31 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class AddStaticMethod { + public static int Main (string []args) { + Assembly assm = typeof (AddStaticMethod).Assembly; + var replacer = EncHelper.Make (); + + int res = TargetMethod (); + if (res != 1) + return 1; + + try { + replacer.Update (assm); + /* doesn't work yet, thus should throw exception */ + } catch (TargetInvocationException e) { + if (e.InnerException.Message.Contains ("cannot add new method")) + return 0; + } + return 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TargetMethod () { + Console.WriteLine ("Hello old World"); + return 1; + } +} + diff --git a/src/mono/mono/tests/enc/AddStaticMethod_v1.cs b/src/mono/mono/tests/enc/AddStaticMethod_v1.cs new file mode 100644 index 0000000000000..cd386b5d311d7 --- /dev/null +++ b/src/mono/mono/tests/enc/AddStaticMethod_v1.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class AddStaticMethod { + public static int Main (string []args) { + Assembly assm = typeof (AddStaticMethod).Assembly; + var replacer = EncHelper.Make (); + + int res = TargetMethod (); + if (res != 1) + return 1; + + try { + replacer.Update (assm); + /* doesn't work yet, thus should throw exception */ + } catch (TargetInvocationException e) { + if (e.InnerException.Message.Contains ("cannot add new method")) + return 0; + } + return 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TargetMethod () { + return NewMethod (); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int NewMethod () { + Console.WriteLine ("Hello NEW World"); + return 2; + } +} + diff --git a/src/mono/mono/tests/enc/CallExisting.cs b/src/mono/mono/tests/enc/CallExisting.cs new file mode 100644 index 0000000000000..51b1d6d7a705e --- /dev/null +++ b/src/mono/mono/tests/enc/CallExisting.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class CallExisting { + public static int Main (string []args) { + Assembly assm = typeof (CallExisting).Assembly; + var replacer = EncHelper.Make (); + + int res = TargetMethod (); + if (res != 2) + return 1; + + replacer.Update (assm); + + res = TargetMethod (); + if (res != 4) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TargetMethod () { + Console.WriteLine ("Hello old World"); + return 2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int SecondMethod () { + Console.WriteLine ("SecondMethod"); + return 1; + } +} + diff --git a/src/mono/mono/tests/enc/CallExisting_v1.cs b/src/mono/mono/tests/enc/CallExisting_v1.cs new file mode 100644 index 0000000000000..60d2faeef8fe6 --- /dev/null +++ b/src/mono/mono/tests/enc/CallExisting_v1.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class CallExisting { + public static int Main (string []args) { + Assembly assm = typeof (CallExisting).Assembly; + var replacer = EncHelper.Make (); + + int res = TargetMethod (); + if (res != 2) + return 1; + + replacer.Update (assm); + + res = TargetMethod (); + if (res != 4) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TargetMethod () { + Console.WriteLine ("Hello new World"); + return 3 + SecondMethod (); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int SecondMethod () { + Console.WriteLine ("SecondMethod"); + return 1; + } +} + diff --git a/src/mono/mono/tests/enc/EncHelper.cs b/src/mono/mono/tests/enc/EncHelper.cs new file mode 100644 index 0000000000000..af7eb2d2fd55a --- /dev/null +++ b/src/mono/mono/tests/enc/EncHelper.cs @@ -0,0 +1,59 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Collections.Generic; + +namespace MonoEnc { + public class EncHelper { +#if false + const string name = "System.Runtime.CompilerServices.RuntimeFeature"; +#else + const string name = "Mono.Runtime"; +#endif + private static MethodBase _updateMethod; + + private static MethodBase UpdateMethod => _updateMethod ?? InitUpdateMethod(); + + private static MethodBase InitUpdateMethod () + { + var monoType = Type.GetType (name, false); + _updateMethod = monoType.GetMethod ("LoadMetadataUpdate"); + if (_updateMethod == null) + throw new Exception ($"Couldn't get LoadMetadataUpdate from {name}"); + return _updateMethod; + } + + private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) + { + UpdateMethod.Invoke (null, new object [] { assm, dmeta_data, dil_data}); + } + + EncHelper () { } + + public static EncHelper Make () + { + return new EncHelper (); + } + + private Dictionary assembly_count = new Dictionary (); + + public void Update (Assembly assm) { + int count; + if (!assembly_count.TryGetValue (assm, out count)) + count = 1; + else + count++; + assembly_count [assm] = count; + + string basename = assm.Location; + Console.WriteLine ($"Apply Delta Update for {basename}, revision {count}"); + + string dmeta_name = $"{basename}.{count}.dmeta"; + string dil_name = $"{basename}.{count}.dil"; + byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name); + byte[] dil_data = System.IO.File.ReadAllBytes (dil_name); + + LoadMetadataUpdate (assm, dmeta_data, dil_data); + } + } +} diff --git a/src/mono/mono/tests/enc/LambdaFunc.cs b/src/mono/mono/tests/enc/LambdaFunc.cs new file mode 100644 index 0000000000000..dd8845e31e500 --- /dev/null +++ b/src/mono/mono/tests/enc/LambdaFunc.cs @@ -0,0 +1,38 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class LambdaFunc { + public delegate int TestDelegate (int a); + + public static int Main (string []args) { + Assembly assm = typeof (LambdaFunc).Assembly; + var replacer = EncHelper.Make (); + + TestDelegate del = CreateDelegate (); + + int res = del (1); + if (res != 2) { + Console.WriteLine ("#1: " + res); + return 1; + } + + replacer.Update (assm); + + res = del (1); + if (res != 3) { + Console.WriteLine ("#2: " + res); + return 2; + } + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static TestDelegate CreateDelegate () { + TestDelegate ret = delegate (int a) { return a + 1; }; + return ret; + } +} + diff --git a/src/mono/mono/tests/enc/LambdaFunc_v1.cs b/src/mono/mono/tests/enc/LambdaFunc_v1.cs new file mode 100644 index 0000000000000..5f290c24c3882 --- /dev/null +++ b/src/mono/mono/tests/enc/LambdaFunc_v1.cs @@ -0,0 +1,38 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class LambdaFunc { + public delegate int TestDelegate (int a); + + public static int Main (string []args) { + Assembly assm = typeof (LambdaFunc).Assembly; + var replacer = EncHelper.Make (); + + TestDelegate del = CreateDelegate (); + + int res = del (1); + if (res != 2) { + Console.WriteLine ("#1: " + res); + return 1; + } + + replacer.Update (assm); + + res = del (1); + if (res != 3) { + Console.WriteLine ("#2: " + res); + return 2; + } + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static TestDelegate CreateDelegate () { + TestDelegate ret = delegate (int a) { return a + 2; }; + return ret; + } +} + diff --git a/src/mono/mono/tests/enc/Makefile.am b/src/mono/mono/tests/enc/Makefile.am new file mode 100644 index 0000000000000..835e1dd53f966 --- /dev/null +++ b/src/mono/mono/tests/enc/Makefile.am @@ -0,0 +1,82 @@ +include ../common_mixed.mk + +#ROSLYNILDIFF_KIND=Mono +ROSLYNILDIFF_KIND=Net5 + +ROSLYNILDIFF ?=$(HOME)/work/roslynildiff/bin/Debug/roslynildiff.$(if $(findstring Net5,$(ROSLYNILDIFF_KIND)),dll,exe) + +ROSLYNILDIFF_CMD =$(if $(findstring Mono,$(ROSLYNILDIFF_KIND)),mono --debug,dotnet) +ROSLYNILDIFF_RUN =$(ROSLYNILDIFF_CMD) $(ROSLYNILDIFF) + +ROSLYNILDIFF_ARGS =$(if $(findstring Mono,$(ROSLYNILDIFF_KIND)),,-mono -bcl:$(CLASS)) + +DISABLED_TESTS := \ + AddClass.dll \ + AddStaticField.dll \ + LambdaFunc.dll + +TESTS_REGULAR := \ + AddClass.dll \ + AddStaticField.dll \ + AddStaticMethod.dll \ + CallExisting.dll \ + LambdaFunc.dll \ + OnStackMethod.dll \ + OnStackMethodThread.dll \ + OnStackMethodThreadThread.dll \ + ReplaceMethod.dll \ + ReplaceMethodOften.dll \ + ReplacePrivateVirtualMethod.dll \ + TypeTokenSwap.dll \ + UserStringSwap.dll + +TESTS_ACTUAL=$(filter-out $(DISABLED_TESTS),$(TESTS_REGULAR)) + +# DEBUG_FLAGS=MONO_LOG_MASK=metadata-update MONO_LOG_LEVEL=debug MONO_VERBOSE_METHOD=DiffTestMethod1 + +TESTS_BASE_SRCS:= $(TESTS_REGULAR:.dll=.cs) + +TESTS_UPDATE_SRCS:= $(foreach b,$(basename $(TESTS_REGULAR)),$(wildcard $(b)_v?.cs)) + +TESTS_SRCS:= $(TEST_BASE_SRCS) $(TEST_UPDATE_SRCS) + +EXTRA_DIST= $(TESTS_SRCS) + +.PHONY: names + +check-local: $(TESTS_REGULAR) + @failed=0; \ + passed=0; \ + failed_tests=""; \ + for i in $(TESTS_ACTUAL); do \ + echo "======== $$i start ======== "; \ + if $(DEBUG_FLAGS) $(RUNTIME) $(TEST_RUNTIME_ARGS) --interp=-inline $$i ; \ + then \ + passed=`expr $${passed} + 1`; \ + echo "======== $$i end (passed) ======== "; \ + else \ + failed=`expr $${failed} + 1`; \ + failed_tests="$${failed_tests} $$i"; \ + echo "======== $$i end (failed) ======== "; \ + fi; \ + done; \ + echo "$${passed} test(s) passed. $${failed} test(s) did not pass."; \ + exit $${failed} + +EXTRA_DIST+= EncHelper.cs + +.PHONY: run-demo1 run-demo2 + +run-demo1: UserStringSwap.dll + $(DEBUG_FLAGS) $(RUNTIME) $(TEST_RUNTIME_ARGS) --interp=-inline $< + +run-demo2: ReplaceMethodOften.dll + $(DEBUG_FLAGS) $(RUNTIME) $(TEST_RUNTIME_ARGS) --interp=-inline $< + +EncHelper.dll: EncHelper.cs + $(MCS) -target:library -out:$@ $< + +%.dll: %.cs EncHelper.dll + bash -x -c "$(ROSLYNILDIFF_RUN) $(ROSLYNILDIFF_ARGS) -l:EncHelper.dll $< $(basename $<)_v?.cs" + +CLEANFILES += *.dll.*.dil *.dll.*.dmeta *.dll.*.dpdb diff --git a/src/mono/mono/tests/enc/OnStackMethod.cs b/src/mono/mono/tests/enc/OnStackMethod.cs new file mode 100644 index 0000000000000..5a84b4cb173d1 --- /dev/null +++ b/src/mono/mono/tests/enc/OnStackMethod.cs @@ -0,0 +1,43 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class OnStackMethod { + public static EncHelper replacer = null; + public static Assembly assm = null; + public static int state = 0; + + public static int Main (string []args) { + assm = typeof (OnStackMethod).Assembly; + replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (); + if (res != 1) + return 1; + + res = DiffTestMethod1 (); + if (res != 2) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 () { + Console.WriteLine ("Hello old World"); + DoTheUpdate (); + Console.WriteLine ("Hello old World #2"); + Console.WriteLine ("Hello old World #3"); + return 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoTheUpdate () { + if (state == 0) { + replacer.Update (assm); + state++; + } + } +} + diff --git a/src/mono/mono/tests/enc/OnStackMethodThread.cs b/src/mono/mono/tests/enc/OnStackMethodThread.cs new file mode 100644 index 0000000000000..ea6bd45cfc451 --- /dev/null +++ b/src/mono/mono/tests/enc/OnStackMethodThread.cs @@ -0,0 +1,55 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using MonoEnc; + +public class OnStackMethod { + public static EncHelper replacer = null; + public static Assembly assm = null; + public static int state = 0; + + public static int Main (string []args) { + assm = typeof (OnStackMethod).Assembly; + replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (); + if (res != 1) + return 1; + + res = DiffTestMethod1 (); + if (res != 2) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 () { + Console.WriteLine ("Hello old World"); + DoTheUpdate (); + Console.WriteLine ("Hello old World #2"); + Console.WriteLine ("Hello old World #3"); + return 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Wrapper () { + int ret = DiffTestMethod1 (); + if (ret != 2) + Environment.Exit (5); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoTheUpdate () { + if (state == 0) { + replacer.Update (assm); + state++; + + Thread thread = new Thread(new ThreadStart(Wrapper)); + thread.Start(); + thread.Join (); + } + } +} + diff --git a/src/mono/mono/tests/enc/OnStackMethodThreadThread.cs b/src/mono/mono/tests/enc/OnStackMethodThreadThread.cs new file mode 100644 index 0000000000000..30c23d490a2f5 --- /dev/null +++ b/src/mono/mono/tests/enc/OnStackMethodThreadThread.cs @@ -0,0 +1,82 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using MonoEnc; + +public class OnStackMethodThreadThread { + public static EncHelper replacer = null; + public static Assembly assm = null; + public static int state = 0; + public static Semaphore sem = null; + + public static int Main (string []args) { + assm = typeof (OnStackMethodThreadThread).Assembly; + replacer = EncHelper.Make (); + sem = new Semaphore (0, 1); + + int res = DiffTestMethod1 (0); + if (res != 1) + return 1; + + res = DiffTestMethod1 (0); + if (res != 2) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int worker) { + if (worker == 1) { + Console.WriteLine ("Hello from Wrapper1"); + sem.WaitOne (); + Console.WriteLine ("Hello from Wrapper1: Done waiting"); + return 0x10001; + } else if (worker == 2) { + throw new Exception ("should not happen"); + } else { + Console.WriteLine ("Hello old World"); + DoTheUpdate (); + Console.WriteLine ("Hello old World #2"); + Console.WriteLine ("Hello old World #3"); + return 1; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Wrapper1 () { + int ret = DiffTestMethod1 (1); + if (ret != 0x10001) + Environment.Exit (5); + ret = DiffTestMethod1 (1); + if (ret != 0x10002) + Environment.Exit (6); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Wrapper2 () { + int ret = DiffTestMethod1 (2); + if (ret != 0x20002) + Environment.Exit (7); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoTheUpdate () { + if (state == 0) { + Thread thread1 = new Thread(new ThreadStart(Wrapper1)); + thread1.Start(); + + replacer.Update (assm); + state++; + + Thread thread2 = new Thread(new ThreadStart(Wrapper2)); + thread2.Start(); + thread2.Join (); + + sem.Release (1); + thread1.Join (); + } + } +} + diff --git a/src/mono/mono/tests/enc/OnStackMethodThreadThread_v1.cs b/src/mono/mono/tests/enc/OnStackMethodThreadThread_v1.cs new file mode 100644 index 0000000000000..c3e74872a7b6a --- /dev/null +++ b/src/mono/mono/tests/enc/OnStackMethodThreadThread_v1.cs @@ -0,0 +1,77 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using MonoEnc; + +public class OnStackMethodThreadThread { + public static EncHelper replacer = null; + public static Assembly assm = null; + public static int state = 0; + public static Semaphore sem = null; + + public static int Main (string []args) { + assm = typeof (OnStackMethodThreadThread).Assembly; + replacer = EncHelper.Make (); + sem = new Semaphore (0, 1); + + int res = DiffTestMethod1 (0); + if (res != 1) + return 1; + + res = DiffTestMethod1 (0); + if (res != 2) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int worker) { + if (worker == 1) { + Console.WriteLine ("Hello from Wrapper1: NEW"); + return 0x10002; + } else if (worker == 2) { + return 0x20002; + } else { + Console.WriteLine ("Hello NEW World"); + return 2; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Wrapper1 () { + int ret = DiffTestMethod1 (1); + if (ret != 0x10001) + Environment.Exit (5); + ret = DiffTestMethod1 (1); + if (ret != 0x10002) + Environment.Exit (6); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Wrapper2 () { + int ret = DiffTestMethod1 (2); + if (ret != 0x20002) + Environment.Exit (7); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoTheUpdate () { + if (state == 0) { + Thread thread1 = new Thread(new ThreadStart(Wrapper1)); + thread1.Start(); + + replacer.Update (assm); + state++; + + Thread thread2 = new Thread(new ThreadStart(Wrapper2)); + thread2.Start(); + thread2.Join (); + + sem.Release (1); + thread1.Join (); + } + } +} + diff --git a/src/mono/mono/tests/enc/OnStackMethodThread_v1.cs b/src/mono/mono/tests/enc/OnStackMethodThread_v1.cs new file mode 100644 index 0000000000000..6db05b2576275 --- /dev/null +++ b/src/mono/mono/tests/enc/OnStackMethodThread_v1.cs @@ -0,0 +1,52 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using MonoEnc; + +public class OnStackMethod { + public static EncHelper replacer = null; + public static Assembly assm = null; + public static int state = 0; + + public static int Main (string []args) { + assm = typeof (OnStackMethod).Assembly; + replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (); + if (res != 1) + return 1; + + res = DiffTestMethod1 (); + if (res != 2) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 () { + Console.WriteLine ("Hello NEW World"); + return 2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Wrapper () { + int ret = DiffTestMethod1 (); + if (ret != 2) + Environment.Exit (5); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoTheUpdate () { + if (state == 0) { + replacer.Update (assm); + state++; + + Thread thread = new Thread(new ThreadStart(Wrapper)); + thread.Start(); + thread.Join (); + } + } +} + diff --git a/src/mono/mono/tests/enc/OnStackMethod_v1.cs b/src/mono/mono/tests/enc/OnStackMethod_v1.cs new file mode 100644 index 0000000000000..1314745f18da3 --- /dev/null +++ b/src/mono/mono/tests/enc/OnStackMethod_v1.cs @@ -0,0 +1,40 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class OnStackMethod { + public static EncHelper replacer = null; + public static Assembly assm = null; + public static int state = 0; + + public static int Main (string []args) { + assm = typeof (OnStackMethod).Assembly; + replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (); + if (res != 1) + return 1; + + res = DiffTestMethod1 (); + if (res != 2) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 () { + Console.WriteLine ("Hello new World"); + return 2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void DoTheUpdate () { + if (state == 0) { + replacer.Update (assm); + state++; + } + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethod.cs b/src/mono/mono/tests/enc/ReplaceMethod.cs new file mode 100644 index 0000000000000..8855af301bdd2 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethod.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != (3 + 1)) + return 1; + + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != (8 + 2)) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + Console.WriteLine ("Hello old World"); + return x + SecondMethod (); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int SecondMethod () { + Console.WriteLine ("Print something old-worldish"); + return 1; + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften.cs b/src/mono/mono/tests/enc/ReplaceMethodOften.cs new file mode 100644 index 0000000000000..0cbc382b447d2 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethodOften.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != 0) + return 0; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 1) + return 1; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 2) + return 2; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 3) + return 3; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 4) + return 4; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 5) + return 5; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 6) + return 6; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + // Console.WriteLine ("Version 0"); + return 0; + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v1.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v1.cs new file mode 100644 index 0000000000000..806ff3cc052c8 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethodOften_v1.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != 0) + return 0; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 1) + return 1; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 2) + return 2; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 3) + return 3; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 4) + return 4; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 5) + return 5; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 6) + return 6; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + // Console.WriteLine ("Version 1"); + return 1; + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v2.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v2.cs new file mode 100644 index 0000000000000..9dca89f345b77 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethodOften_v2.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != 0) + return 0; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 1) + return 1; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 2) + return 2; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 3) + return 3; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 4) + return 4; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 5) + return 5; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 6) + return 6; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + // Console.WriteLine ("Version 2"); + return 2; + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v3.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v3.cs new file mode 100644 index 0000000000000..0fb7525aaf585 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethodOften_v3.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != 0) + return 0; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 1) + return 1; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 2) + return 2; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 3) + return 3; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 4) + return 4; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 5) + return 5; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 6) + return 6; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + // Console.WriteLine ("Version 3"); + return 3; + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v4.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v4.cs new file mode 100644 index 0000000000000..3f38bc6bfd478 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethodOften_v4.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != 0) + return 0; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 1) + return 1; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 2) + return 2; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 3) + return 3; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 4) + return 4; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 5) + return 5; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 6) + return 6; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + // Console.WriteLine ("Version 4"); + return 4; + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v5.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v5.cs new file mode 100644 index 0000000000000..4223f73d9dc7d --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethodOften_v5.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != 0) + return 0; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 1) + return 1; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 2) + return 2; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 3) + return 3; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 4) + return 4; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 5) + return 5; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 6) + return 6; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + // Console.WriteLine ("Version 5"); + return 5; + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v6.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v6.cs new file mode 100644 index 0000000000000..3ebc4bc7c7139 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethodOften_v6.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != 0) + return 0; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 1) + return 1; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 2) + return 2; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 3) + return 3; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 4) + return 4; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 5) + return 5; + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != 6) + return 6; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + // Console.WriteLine ("Version 6"); + return 6; + } +} + diff --git a/src/mono/mono/tests/enc/ReplaceMethod_v1.cs b/src/mono/mono/tests/enc/ReplaceMethod_v1.cs new file mode 100644 index 0000000000000..7f3ab94ad5aa4 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplaceMethod_v1.cs @@ -0,0 +1,34 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class Sample3 { + public static int Main (string []args) { + Assembly assm = typeof (Sample3).Assembly; + var replacer = EncHelper.Make (); + + int res = DiffTestMethod1 (3, 8); + if (res != (3 + 1)) + return 1; + + replacer.Update (assm); + + res = DiffTestMethod1 (3, 8); + if (res != (8 + 2)) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int DiffTestMethod1 (int x, int y) { + return y + SecondMethod ();; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int SecondMethod () { + Console.WriteLine ("HELLO NEW WORLD"); + return 2; + } +} diff --git a/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod.cs b/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod.cs new file mode 100644 index 0000000000000..6c8946bbc1605 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +class Container { + private Thickness _margin; + + public Thickness Margin + { + get { return _margin; } + set { _margin = value; } + } +} + +class Thickness { + public int val; + + public Thickness (int val) + { + this.val = val; + } +} + +public class Sample { + private Container listView; + + public static int Main (string []args) { + Assembly assm = typeof (Sample).Assembly; + var replacer = EncHelper.Make (); + + Sample s = new Sample (); + s.listView = new Container (); + + s.OnItemSelected (null, null); + if (s.listView.Margin.val != 30) + return 1; + + replacer.Update (assm); + + s.OnItemSelected (null, null); + if (s.listView.Margin.val != 40) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void OnItemSelected (object sender, object s) + { + listView.Margin = new Thickness (30); + } +} + diff --git a/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod_v1.cs b/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod_v1.cs new file mode 100644 index 0000000000000..ed40ab374c1e7 --- /dev/null +++ b/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod_v1.cs @@ -0,0 +1,54 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +class Container { + private Thickness _margin; + + public Thickness Margin + { + get { return _margin; } + set { _margin = value; } + } +} + +class Thickness { + public int val; + + public Thickness (int val) + { + this.val = val; + } +} + +public class Sample { + private Container listView; + + public static int Main (string []args) { + Assembly assm = typeof (Sample).Assembly; + var replacer = EncHelper.Make (); + + Sample s = new Sample (); + s.listView = new Container (); + + s.OnItemSelected (null, null); + if (s.listView.Margin.val != 30) + return 1; + + replacer.Update (assm); + + s.OnItemSelected (null, null); + if (s.listView.Margin.val != 40) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void OnItemSelected (object sender, object s) + { + listView.Margin = new Thickness (40); + } +} + diff --git a/src/mono/mono/tests/enc/TypeTokenSwap.cs b/src/mono/mono/tests/enc/TypeTokenSwap.cs new file mode 100644 index 0000000000000..8e6d75555536e --- /dev/null +++ b/src/mono/mono/tests/enc/TypeTokenSwap.cs @@ -0,0 +1,29 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class TypeTokenSwap { + public static int Main (string []args) { + Assembly assm = typeof (TypeTokenSwap).Assembly; + var replacer = EncHelper.Make (); + + var res = TargetMethod (); + if (res != typeof (string)) + return 1; + + replacer.Update (assm); + + res = TargetMethod (); + if (res != typeof (int)) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Type TargetMethod () { + return typeof (string); + } +} + diff --git a/src/mono/mono/tests/enc/TypeTokenSwap_v1.cs b/src/mono/mono/tests/enc/TypeTokenSwap_v1.cs new file mode 100644 index 0000000000000..969bb89ae0a9c --- /dev/null +++ b/src/mono/mono/tests/enc/TypeTokenSwap_v1.cs @@ -0,0 +1,29 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class TypeTokenSwap { + public static int Main (string []args) { + Assembly assm = typeof (TypeTokenSwap).Assembly; + var replacer = EncHelper.Make (); + + var res = TargetMethod (); + if (res != typeof (string)) + return 1; + + replacer.Update (assm); + + res = TargetMethod (); + if (res != typeof (int)) + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static Type TargetMethod () { + return typeof (int); + } +} + diff --git a/src/mono/mono/tests/enc/UserStringSwap.cs b/src/mono/mono/tests/enc/UserStringSwap.cs new file mode 100644 index 0000000000000..dcb29e31477a7 --- /dev/null +++ b/src/mono/mono/tests/enc/UserStringSwap.cs @@ -0,0 +1,31 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class UserStringSwap { + public static int Main (string []args) { + Assembly assm = typeof (UserStringSwap).Assembly; + var replacer = EncHelper.Make (); + + string res = TargetMethod (); + if (res != "OLD STRING") + return 1; + + replacer.Update (assm); + + res = TargetMethod (); + if (res != "NEW STRING") + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static string TargetMethod () { + string s = "OLD STRING"; + Console.WriteLine (s); + return s; + } +} + diff --git a/src/mono/mono/tests/enc/UserStringSwap_v1.cs b/src/mono/mono/tests/enc/UserStringSwap_v1.cs new file mode 100644 index 0000000000000..88d4a683354d4 --- /dev/null +++ b/src/mono/mono/tests/enc/UserStringSwap_v1.cs @@ -0,0 +1,31 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoEnc; + +public class UserStringSwap { + public static int Main (string []args) { + Assembly assm = typeof (UserStringSwap).Assembly; + var replacer = EncHelper.Make (); + + string res = TargetMethod (); + if (res != "OLD STRING") + return 1; + + replacer.Update (assm); + + res = TargetMethod (); + if (res != "NEW STRING") + return 2; + + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static string TargetMethod () { + string s = "NEW STRING"; + Console.WriteLine (s); + return s; + } +} + diff --git a/src/mono/mono/utils/mono-logger-internals.h b/src/mono/mono/utils/mono-logger-internals.h index ad8555fa527cb..9a2406adc0501 100644 --- a/src/mono/mono/utils/mono-logger-internals.h +++ b/src/mono/mono/utils/mono-logger-internals.h @@ -164,6 +164,6 @@ void mono_log_write_recorder (const char *log_domain, GLogLevelFlags level, mono void mono_log_close_recorder (void); void mono_log_dump_recorder (void); -void mono_dump_mem (gpointer d, int len); +void mono_dump_mem (gconstpointer d, int len); #endif /* __MONO_LOGGER_INTERNAL_H__ */ diff --git a/src/mono/mono/utils/mono-logger.c b/src/mono/mono/utils/mono-logger.c index 8d2d2f8ce13ce..6f5cae1e6dd17 100644 --- a/src/mono/mono/utils/mono-logger.c +++ b/src/mono/mono/utils/mono-logger.c @@ -552,7 +552,7 @@ conv_ascii_char (gchar s) /* No memfree because only called during crash */ void -mono_dump_mem (gpointer d, int len) +mono_dump_mem (gconstpointer d, int len) { guint8 *data = (guint8 *) d; From 631525a914deb9576f2c427afc6b1ac29283d90f Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 2 Nov 2020 16:01:02 -0500 Subject: [PATCH 02/31] Add metadata-update.{c,h} to CMakeLists.txt --- src/mono/mono/metadata/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mono/mono/metadata/CMakeLists.txt b/src/mono/mono/metadata/CMakeLists.txt index be616aafddf21..232729b8014c2 100644 --- a/src/mono/mono/metadata/CMakeLists.txt +++ b/src/mono/mono/metadata/CMakeLists.txt @@ -128,6 +128,8 @@ set(metadata_common_sources metadata.c metadata-verify.c metadata-internals.h + metadata-update.h + metadata-update.c method-builder.h method-builder-internals.h method-builder.c From d1dc08cfe96869c8305c38f6e5cd29c2096967b4 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 2 Nov 2020 16:01:18 -0500 Subject: [PATCH 03/31] fix icall-def-netcore typo --- src/mono/mono/metadata/icall-def-netcore.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 9e55858d05f04..7fe58b065e54b 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -327,7 +327,7 @@ HANDLES(MPROP_4, "get_property_info", ves_icall_RuntimePropertyInfo_get_property HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_RuntimePropertyInfo_internal_from_handle_type, MonoReflectionProperty, 2, (MonoProperty_ptr, MonoType_ptr)) ICALL_TYPE(RUNF, "System.Runtime.CompilerServices.RuntimeFeature", RUNF_1) -HANDLES(RUNT_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate, void, 5, (MonoReflectionAssembly, gconstpointer, gint32, gconstpointer, gint32)) +HANDLES(RUNF_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate, void, 5, (MonoReflectionAssembly, gconstpointer, gint32, gconstpointer, gint32)) ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1) HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject)) From 8b9992fdf45958159019776f490e4a1e8cd1b4de Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 2 Nov 2020 16:01:27 -0500 Subject: [PATCH 04/31] Add icall to corelib --- .../Runtime/CompilerServices/RuntimeFeature.Mono.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs index 958eb38f174a1..4296cea74b51f 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Reflection; namespace System.Runtime.CompilerServices { @@ -16,5 +17,17 @@ public static bool IsDynamicCodeCompiled [Intrinsic] // the JIT/AOT compiler will change this flag to false for FullAOT scenarios, otherwise true get => IsDynamicCodeCompiled; } + + [MethodImplAttribute (MethodImplOptions.InternalCall)] + private static unsafe extern void LoadMetadataUpdate_internal (Assembly base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length); + + public static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { + unsafe { + fixed (byte* dmeta_bytes = dmeta_data) + fixed (byte* dil_bytes = dil_data) { + LoadMetadataUpdate_internal (assm, dmeta_bytes, dmeta_data.Length, dil_bytes, dil_data.Length); + } + } + } } } From 971b97063fb6e3be02f118d60a28f94968fa6f62 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 2 Nov 2020 16:41:43 -0500 Subject: [PATCH 05/31] Add console and browser metadata update samples Both samples depend on the roslynildiff tool which should be specified with a RoslynILDiffFullPath property in the .csproj files for the projects. --- .../netcore/sample/mbr/DeltaHelper.targets | 96 +++++++++++++++++++ .../sample/mbr/DeltaHelper/DeltaHelper.cs | 64 +++++++++++++ .../sample/mbr/DeltaHelper/DeltaHelper.csproj | 6 ++ src/mono/netcore/sample/mbr/browser/Makefile | 21 ++++ .../netcore/sample/mbr/browser/Program.cs | 32 +++++++ .../netcore/sample/mbr/browser/Program_v1.cs | 32 +++++++ .../netcore/sample/mbr/browser/Program_v2.cs | 32 +++++++ .../sample/mbr/browser/WasmDelta.csproj | 76 +++++++++++++++ .../netcore/sample/mbr/browser/index.html | 39 ++++++++ .../netcore/sample/mbr/browser/runtime.js | 15 +++ src/mono/netcore/sample/mbr/browser/server.py | 37 +++++++ .../sample/mbr/console/ConsoleDelta.csproj | 38 ++++++++ src/mono/netcore/sample/mbr/console/Makefile | 26 +++++ .../netcore/sample/mbr/console/Program.cs | 47 +++++++++ .../netcore/sample/mbr/console/Program_v1.cs | 47 +++++++++ 15 files changed, 608 insertions(+) create mode 100644 src/mono/netcore/sample/mbr/DeltaHelper.targets create mode 100644 src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs create mode 100644 src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj create mode 100644 src/mono/netcore/sample/mbr/browser/Makefile create mode 100644 src/mono/netcore/sample/mbr/browser/Program.cs create mode 100644 src/mono/netcore/sample/mbr/browser/Program_v1.cs create mode 100644 src/mono/netcore/sample/mbr/browser/Program_v2.cs create mode 100644 src/mono/netcore/sample/mbr/browser/WasmDelta.csproj create mode 100644 src/mono/netcore/sample/mbr/browser/index.html create mode 100644 src/mono/netcore/sample/mbr/browser/runtime.js create mode 100644 src/mono/netcore/sample/mbr/browser/server.py create mode 100644 src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj create mode 100644 src/mono/netcore/sample/mbr/console/Makefile create mode 100644 src/mono/netcore/sample/mbr/console/Program.cs create mode 100644 src/mono/netcore/sample/mbr/console/Program_v1.cs diff --git a/src/mono/netcore/sample/mbr/DeltaHelper.targets b/src/mono/netcore/sample/mbr/DeltaHelper.targets new file mode 100644 index 0000000000000..1d400bdcace5b --- /dev/null +++ b/src/mono/netcore/sample/mbr/DeltaHelper.targets @@ -0,0 +1,96 @@ + + + + + + + <_JustDeltas Include="%(DeltaBase.Deltas)"/> + + + @(_JustDeltas->Count()) + + + + + + + + + + /Users/alklig/work/roslynildiff/roslynildiff + -msbuild:$(MSBuildProjectFullPath) + + $(RoslynILDiffArgs) -p:Configuration=$(Configuration) + $(RoslynILDiffArgs) -p:RuntimeIdentifier=$(RuntimeIdentifier) + $(RoslynILDiffArgs) @(DeltaFiles, ' ') + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .\%(Filename)%(Extension) + always + + + + + + diff --git a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs new file mode 100644 index 0000000000000..5d7d4aeebb64e --- /dev/null +++ b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs @@ -0,0 +1,64 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Collections.Generic; + +namespace MonoDelta { + public class DeltaHelper { +#if true + const string name = "System.Runtime.CompilerServices.RuntimeFeature"; +#else + const string name = "Mono.Runtime"; +#endif + private static MethodBase _updateMethod; + + private static MethodBase UpdateMethod => _updateMethod ?? InitUpdateMethod(); + + private static MethodBase InitUpdateMethod () + { + var monoType = Type.GetType (name, throwOnError: true); + if (monoType == null) + throw new Exception ($"Couldn't get the type {name}"); + _updateMethod = monoType.GetMethod ("LoadMetadataUpdate"); + if (_updateMethod == null) + throw new Exception ($"Couldn't get LoadMetadataUpdate from {name}"); + return _updateMethod; + } + + private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) + { + UpdateMethod.Invoke (null, new object [] { assm, dmeta_data, dil_data}); + } + + DeltaHelper () { } + + public static DeltaHelper Make () + { + return new DeltaHelper (); + } + + private Dictionary assembly_count = new Dictionary (); + + public void Update (Assembly assm) { + int count; + if (!assembly_count.TryGetValue (assm, out count)) + count = 1; + else + count++; + assembly_count [assm] = count; + + /* FIXME WASM: Location is empty on wasm. Make up a name based on Name */ + string basename = assm.Location; + if (basename == "") + basename = assm.GetName().Name + ".dll"; + Console.WriteLine ($"Apply Delta Update for {basename}, revision {count}"); + + string dmeta_name = $"{basename}.{count}.dmeta"; + string dil_name = $"{basename}.{count}.dil"; + byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name); + byte[] dil_data = System.IO.File.ReadAllBytes (dil_name); + + LoadMetadataUpdate (assm, dmeta_data, dil_data); + } + } +} diff --git a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj new file mode 100644 index 0000000000000..5e537f96fc906 --- /dev/null +++ b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj @@ -0,0 +1,6 @@ + + + $(NetCoreAppCurrent) + false + + diff --git a/src/mono/netcore/sample/mbr/browser/Makefile b/src/mono/netcore/sample/mbr/browser/Makefile new file mode 100644 index 0000000000000..8fae501ffec30 --- /dev/null +++ b/src/mono/netcore/sample/mbr/browser/Makefile @@ -0,0 +1,21 @@ +TOP=../../../../../.. +DOTNET=$(TOP)/dotnet.sh + +ifeq ($(V),) +DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary +else +DOTNET_Q_ARGS=--nologo +endif + +CONFIG?=Release + +all: build + +build: + $(DOTNET) build $(DOTNET_Q_ARGS) /p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG) WasmDelta.csproj + +clean: + rm -rf bin + +run: + cd bin/$(CONFIG)/publish && python3 server.py diff --git a/src/mono/netcore/sample/mbr/browser/Program.cs b/src/mono/netcore/sample/mbr/browser/Program.cs new file mode 100644 index 0000000000000..9174760b66f0b --- /dev/null +++ b/src/mono/netcore/sample/mbr/browser/Program.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoDelta; + +namespace Sample +{ + public class Test + { + static DeltaHelper replacer = DeltaHelper.Make (); + + public static void Main(string[] args) + { + Console.WriteLine ("Hello, World!"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TestMeaning() + { + return 42; + } + + public static void Update() + { + Assembly assm = typeof (Test).Assembly; + replacer.Update (assm); + } + } +} diff --git a/src/mono/netcore/sample/mbr/browser/Program_v1.cs b/src/mono/netcore/sample/mbr/browser/Program_v1.cs new file mode 100644 index 0000000000000..02974e4697bfc --- /dev/null +++ b/src/mono/netcore/sample/mbr/browser/Program_v1.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoDelta; + +namespace Sample +{ + public class Test + { + static DeltaHelper replacer = DeltaHelper.Make (); + + public static void Main(string[] args) + { + Console.WriteLine ("Hello, World!"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TestMeaning() + { + return 43; + } + + public static void Update() + { + Assembly assm = typeof (Test).Assembly; + replacer.Update (assm); + } + } +} diff --git a/src/mono/netcore/sample/mbr/browser/Program_v2.cs b/src/mono/netcore/sample/mbr/browser/Program_v2.cs new file mode 100644 index 0000000000000..98eec23781796 --- /dev/null +++ b/src/mono/netcore/sample/mbr/browser/Program_v2.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoDelta; + +namespace Sample +{ + public class Test + { + static DeltaHelper replacer = DeltaHelper.Make (); + + public static void Main(string[] args) + { + Console.WriteLine ("Hello, World!"); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int TestMeaning() + { + return 128; + } + + public static void Update() + { + Assembly assm = typeof (Test).Assembly; + replacer.Update (assm); + } + } +} diff --git a/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj b/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj new file mode 100644 index 0000000000000..b7b0180fc8da5 --- /dev/null +++ b/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj @@ -0,0 +1,76 @@ + + + Exe + bin + false + $(NetCoreAppCurrent) + wasm + Browser + $(ArtifactsBinDir)microsoft.netcore.app.runtime.browser-wasm\$(Configuration)\runtimes\browser-wasm\ + $(MSBuildThisFileDirectory)obj\$(Configuration)\wasm + $(MSBuildThisFileDirectory)bin\$(Configuration)\publish + + + + + + + + + + + + + + + + + + + + + + + + + \%(_DeltaFileForPublish.Filename)%(_DeltaFileForPublish.Extension) + + + + + + + + + + + + Always + + + Always + + + + + + Program_v1.cs;Program_v2.cs + + + + + + + diff --git a/src/mono/netcore/sample/mbr/browser/index.html b/src/mono/netcore/sample/mbr/browser/index.html new file mode 100644 index 0000000000000..472cf5f29ea05 --- /dev/null +++ b/src/mono/netcore/sample/mbr/browser/index.html @@ -0,0 +1,39 @@ + + + + + + TESTS + + + + + + Result from Sample.Test.TestMeaning: +
+ Click here (upto 2 times): +
+ + + + + + + + + diff --git a/src/mono/netcore/sample/mbr/browser/runtime.js b/src/mono/netcore/sample/mbr/browser/runtime.js new file mode 100644 index 0000000000000..7db3cb4fc70e7 --- /dev/null +++ b/src/mono/netcore/sample/mbr/browser/runtime.js @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +var Module = { + onRuntimeInitialized: function () { + config.loaded_cb = function () { + App.init (); + }; + config.fetch_file_cb = function (asset) { + return fetch (asset, { credentials: 'same-origin' }); + } + + MONO.mono_load_runtime_and_bcl_args (config); + }, +}; diff --git a/src/mono/netcore/sample/mbr/browser/server.py b/src/mono/netcore/sample/mbr/browser/server.py new file mode 100644 index 0000000000000..ab1e3edd81517 --- /dev/null +++ b/src/mono/netcore/sample/mbr/browser/server.py @@ -0,0 +1,37 @@ +# Licensed to the .NET Foundation under one or more agreements. +# The .NET Foundation licenses this file to you under the MIT license. + +import sys + +if sys.version_info[0] == 2: + + import SimpleHTTPServer + import SocketServer + + PORT = 8000 + + class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): + pass + + Handler.extensions_map['.wasm'] = 'application/wasm' + + httpd = SocketServer.TCPServer(("", PORT), Handler) + + print ("python 2 serving at port", PORT) + httpd.serve_forever() + + +if sys.version_info[0] == 3: + + import http.server + import socketserver + + PORT = 8000 + + Handler = http.server.SimpleHTTPRequestHandler + Handler.extensions_map['.wasm'] = 'application/wasm' + + with socketserver.TCPServer(("", PORT), Handler) as httpd: + print("python 3 serving at port", PORT) + httpd.serve_forever() + diff --git a/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj b/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj new file mode 100644 index 0000000000000..b02b01ebe317a --- /dev/null +++ b/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj @@ -0,0 +1,38 @@ + + + Exe + $(NetCoreAppCurrent) + false + false + + + + + + + + + + + + + + Program_v1.cs + + + + + + + + $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration) + + + + + + + + + + diff --git a/src/mono/netcore/sample/mbr/console/Makefile b/src/mono/netcore/sample/mbr/console/Makefile new file mode 100644 index 0000000000000..90a96b77eb96c --- /dev/null +++ b/src/mono/netcore/sample/mbr/console/Makefile @@ -0,0 +1,26 @@ +TOP=../../../../../../ +DOTNET:=$(TOP)./dotnet.sh +DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary + +MONO_CONFIG ?=Release +MONO_ARCH=x64 + +OS := $(shell uname -s) +ifeq ($(OS),Darwin) + TARGET_OS=osx +else + TARGET_OS=linux +endif + +MONO_ENV_OPTIONS ?=--interp=-inline + +publish: + $(DOTNET) publish -c $(MONO_CONFIG) -r $(TARGET_OS)-$(MONO_ARCH) + +run: publish + COMPlus_DebugWriteToStdErr=1 \ + MONO_ENV_OPTIONS="$(MONO_ENV_OPTIONS)" \ + $(TOP)artifacts/bin/HelloWorld/$(MONO_ARCH)/$(MONO_CONFIG)/$(TARGET_OS)-$(MONO_ARCH)/publish/HelloWorld + +clean: + rm -rf $(TOP)artifacts/bin/HelloWorld/ diff --git a/src/mono/netcore/sample/mbr/console/Program.cs b/src/mono/netcore/sample/mbr/console/Program.cs new file mode 100644 index 0000000000000..c49288c1e719c --- /dev/null +++ b/src/mono/netcore/sample/mbr/console/Program.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoDelta; + +namespace HelloWorld +{ + internal class Program + { + private static int Main(string[] args) + { + bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null; + Console.WriteLine($"Hello World {(isMono ? "from Mono!" : "from CoreCLR!")}"); + Console.WriteLine(typeof(object).Assembly.FullName); + Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly ()); + Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); + + Assembly assm = typeof (TestClass).Assembly; + var replacer = DeltaHelper.Make (); + + string res = TestClass.TargetMethod (); + if (res != "OLD STRING") + return 1; + + replacer.Update (assm); + + res = TestClass.TargetMethod (); + if (res != "NEW STRING") + return 2; + + return 0; + } + + } +} + +public class TestClass { + [MethodImpl(MethodImplOptions.NoInlining)] + public static string TargetMethod () { + string s = "OLD STRING"; + Console.WriteLine (s); + return s; + } +} diff --git a/src/mono/netcore/sample/mbr/console/Program_v1.cs b/src/mono/netcore/sample/mbr/console/Program_v1.cs new file mode 100644 index 0000000000000..bbe63745079c1 --- /dev/null +++ b/src/mono/netcore/sample/mbr/console/Program_v1.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using MonoDelta; + +namespace HelloWorld +{ + internal class Program + { + private static int Main(string[] args) + { + bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null; + Console.WriteLine($"Hello World {(isMono ? "from Mono!" : "from CoreCLR!")}"); + Console.WriteLine(typeof(object).Assembly.FullName); + Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly ()); + Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); + + Assembly assm = typeof (TestClass).Assembly; + var replacer = DeltaHelper.Make (); + + string res = TestClass.TargetMethod (); + if (res != "OLD STRING") + return 1; + + replacer.Update (assm); + + res = TestClass.TargetMethod (); + if (res != "NEW STRING") + return 2; + + return 0; + } + + } +} + +public class TestClass { + [MethodImpl(MethodImplOptions.NoInlining)] + public static string TargetMethod () { + string s = "NEW STRING"; + Console.WriteLine (s); + return s; + } +} From 3b9306967b7586e243f61b60fc17e47ea65556c0 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 18 Nov 2020 11:13:01 -0500 Subject: [PATCH 06/31] Add README for mbr samples --- src/mono/netcore/sample/mbr/README.md | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/mono/netcore/sample/mbr/README.md diff --git a/src/mono/netcore/sample/mbr/README.md b/src/mono/netcore/sample/mbr/README.md new file mode 100644 index 0000000000000..c4f860327be7e --- /dev/null +++ b/src/mono/netcore/sample/mbr/README.md @@ -0,0 +1,62 @@ + +# Mono Method Body Replacement Samples + +## Prerequisites + +To run the tests you will need to build a runtime with the Mono metadata update +property set, and you will need the `roslynildiff` commandline tool. + +At this time there is only support for Mac and Linux. No Windows. + +## Building + +Build the runtime with the `/p:MonoMetadataUpdate=true` option. + +Both Debug and Release configurations should work, although mixing a Release +configuration with a Debug runtime configuration (or vice versa) is not +supported. + +For desktop: + +```console +build.sh -s Mono+Libs /p:MonoMetadataUpdate=true +``` + + +For Wasm: + +Make sure `EMSDK_PATH` is set (see [workflow](../../../../../docs/workflow/building/libraries/webassembly-instructions.md)) +```console +build.sh --os browser /p:MonoMetadataUpdate=true +``` + +## Running + +Edit [browser/WasmDelta.csproj](./browser/WasmDelta.csproj) or [console/ConsoleDelta.csproj](./console/ConsoleDelta.csproj) and set the `RoslynILDiffFullPath` property to the path to the `roslynildiff` shell script: + +```xml + + + /home/user/tools/roslyn-ildiff/roslynildiff + +``` + +(Make sure the configuration is the same as the runtime that you built) + +For console: + +``` +make MONO_CONFIG=Debug publish && make MONO_CONFIG=Debug run +``` + +The output from `run` should print an old string, apply an update and then print a new string + +For wasm: + +``` +make CONFIG=Debug && make CONFIG=Debug run +``` + +Then go to http://localhost:8000/ and click the button once or twice (the example has 2 updates prebuilt) + + From f5b6a73cd7765ab1b2f7041a42f881efda2f4210 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 18 Nov 2020 11:59:42 -0500 Subject: [PATCH 07/31] [build] Add initial runtime support for MonoMetadataUpdate property In the runtime defines cmake ENABLE_METADATA_UPDATE option and sets a preprocessor flag. In System.Private.CoreLib, defines FEATURE_METADATA_UPDATE and uses it to throw a NotSupportedException from LoadMetadataUpdate --- src/mono/cmake/config.h.in | 4 ++++ src/mono/cmake/options.cmake | 1 + src/mono/mono.proj | 10 ++++++++++ src/mono/mono/metadata/icall.c | 2 ++ .../System.Private.CoreLib.csproj | 5 +++++ .../Runtime/CompilerServices/RuntimeFeature.Mono.cs | 6 ++++++ src/mono/wasm/Makefile | 3 +++ src/mono/wasm/wasm.proj | 3 ++- 8 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index 3e42b7617cda8..835ba07a8a43c 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -1768,8 +1768,12 @@ /* Enable runtime checks of mempool references between metadata images (must set env var MONO_CHECK_MODE=metadata) */ #cmakedefine ENABLE_CHECKED_BUILD_METADATA 1 +/* Enable runtime support for metadata updates */ +#cmakedefine ENABLE_METADATA_UPDATE 1 + #if defined(ENABLE_LLVM) && defined(HOST_WIN32) && defined(TARGET_WIN32) && (!defined(TARGET_AMD64) || !defined(_MSC_VER)) #error LLVM for host=Windows and target=Windows is only supported on x64 MSVC build. #endif #endif + diff --git a/src/mono/cmake/options.cmake b/src/mono/cmake/options.cmake index 5170d66b0bf46..35507870e876d 100644 --- a/src/mono/cmake/options.cmake +++ b/src/mono/cmake/options.cmake @@ -65,6 +65,7 @@ option (ENABLE_CHECKED_BUILD_PRIVATE_TYPES "Enable compile time checking that ge option (ENABLE_CHECKED_BUILD_GC "Enable runtime GC Safe / Unsafe mode assertion checks (must set env var MONO_CHECK_MODE=gc)") option (ENABLE_CHECKED_BUILD_THREAD "Enable runtime history of per-thread coop state transitions (must set env var MONO_CHECK_MODE=thread)") option (ENABLE_CHECKED_BUILD_METADATA "Enable runtime checks of mempool references between metadata images (must set env var MONO_CHECK_MODE=metadata)") +option (ENABLE_METADATA_UPDATE "Enable runtime support for metadata updates") set (GC_SUSPEND "default" CACHE STRING "GC suspend method (default, preemptive, coop, hybrid)") set (CHECKED_BUILD "" CACHE STRING "Set ENABLE_CHECKED_BUILD_ options at once. Comma-separated list of lowercase ENABLE_CHECKED_BUILD_ options ie. 'gc,threads,private_types' etc.") diff --git a/src/mono/mono.proj b/src/mono/mono.proj index 8bc7108838d69..c460c4515dafc 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -8,6 +8,7 @@ - MonoAOTEnableLLVM - enable LLVM for an AOT-only Mono - MonoAOTLLVMDir - [optional] the directory where LLVM is located, for an AOT-only Mono - MonoVerboseBuild - enable verbose build + - MonoMetadataUpdate - enable experimental method body replacement code --> @@ -302,6 +303,12 @@ <_MonoCXXFLAGS Include="-Wl,--build-id=sha1" /> + + + + <_MonoCMakeArgs Include="-DENABLE_METADATA_UPDATE=1" /> + + <_MonoCFLAGSOption>-DCMAKE_C_FLAGS="@(_MonoCPPFLAGS, ' ') @(_MonoCFLAGS, ' ')" <_MonoCXXFLAGSOption>-DCMAKE_CXX_FLAGS="@(_MonoCPPFLAGS, ' ') @(_MonoCXXFLAGS, ' ')" @@ -403,6 +410,9 @@ + + + diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index e1d8c1626dc2b..8a486304431af 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -6777,6 +6777,7 @@ ves_icall_Mono_Runtime_DumpStateTotal (guint64 *portable_hash, guint64 *unportab return result; } +#ifdef ENABLE_METADATA_UPDATE void ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoReflectionAssemblyHandle refassm, gconstpointer dmeta_bytes, int32_t dmeta_len, @@ -6792,6 +6793,7 @@ ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoReflectionAssemblyHandle refassm, MonoDomain *domain = mono_domain_get (); mono_image_load_enc_delta (domain, image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error); } +#endif MonoBoolean ves_icall_System_Reflection_AssemblyName_ParseAssemblyName (const char *name, MonoAssemblyName *aname, MonoBoolean *is_version_defined_arg, MonoBoolean *is_token_defined_arg) diff --git a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj index ebdf5f595b899..e72d1e788bfe7 100644 --- a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -118,6 +118,11 @@ $(DefineConstants);FEATURE_PERFTRACING + + + $(DefineConstants);FEATURE_METADATA_UPDATE + + true diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs index 4296cea74b51f..3fd90590dd233 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs @@ -18,6 +18,11 @@ public static bool IsDynamicCodeCompiled get => IsDynamicCodeCompiled; } +#if !FEATURE_METADATA_UPDATE + public static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { + throw new NotSupportedException ("Method body replacement not supported in this runtime"); + } +#else [MethodImplAttribute (MethodImplOptions.InternalCall)] private static unsafe extern void LoadMetadataUpdate_internal (Assembly base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length); @@ -29,5 +34,6 @@ public static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] } } } +#endif } } diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index a1f1752d1f1e1..0f9e11e20f185 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -63,6 +63,9 @@ EMCC_FLAGS=--profiling-funcs -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=1 -s US ifneq ($(ENABLE_ES6),false) EMCC_FLAGS+=-s MODULARIZE=1 -s EXPORT_ES6=1 endif +ifeq ($(ENABLE_METADATA_UPDATE),true) + EMCC_FLAGS+=-DENABLE_METADATA_UPDATE=1 +endif EMCC_DEBUG_FLAGS =-g -Os -s ASSERTIONS=1 -DENABLE_NETCORE=1 -DDEBUG=1 EMCC_RELEASE_FLAGS=-Oz --llvm-opts 2 -DENABLE_NETCORE=1 diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 1993f1d252f08..092c10cbf17ec 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -13,6 +13,7 @@ $([MSBuild]::NormalizeDirectory('$(MicrosoftNetCoreAppRuntimePackRidDir)', 'native')) false false + true @@ -71,7 +72,7 @@ <_IcuLibdir>$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)/runtimes/browser-wasm/native/lib - From 135efa506e811fc0c6f51d7cf6e9e7ddf6dea920 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 6 Nov 2020 16:35:27 -0500 Subject: [PATCH 08/31] [runtime] ifdef out metadata updates if not enabled Also move execution engine initialization into the main update function and use a MonoError to signal failures (such as if interp inlining is not turned off) instead of asserting at startup. --- src/mono/mono/metadata/domain.c | 8 +++-- src/mono/mono/metadata/icall-def-netcore.h | 2 ++ src/mono/mono/metadata/icall-def.h | 2 ++ src/mono/mono/metadata/image.c | 33 ++++++++++++++++----- src/mono/mono/metadata/loader.c | 4 +-- src/mono/mono/metadata/metadata-internals.h | 18 +++++++++++ src/mono/mono/metadata/metadata-update.c | 31 +++++++++++++++++-- src/mono/mono/metadata/metadata-update.h | 4 +++ src/mono/mono/metadata/metadata.c | 6 +++- src/mono/mono/metadata/object-internals.h | 4 ++- src/mono/mono/mini/ee.h | 2 +- src/mono/mono/mini/interp-stubs.c | 2 +- src/mono/mono/mini/interp/interp.c | 13 ++++++-- src/mono/mono/mini/mini-runtime.c | 15 ++++++---- src/mono/wasm/runtime/driver.c | 3 ++ 15 files changed, 119 insertions(+), 28 deletions(-) diff --git a/src/mono/mono/metadata/domain.c b/src/mono/mono/metadata/domain.c index 1b62a5213c0a3..e8b59755b5bd6 100644 --- a/src/mono/mono/metadata/domain.c +++ b/src/mono/mono/metadata/domain.c @@ -919,8 +919,12 @@ mono_cleanup (void) void mono_close_exe_image (void) { - /* EnC: shutdown hack. We mess something up and try to double-close/free it. */ - if (exe_image && !exe_image->delta_image) + gboolean do_close = exe_image != NULL; +#ifdef ENABLE_METADATA_UPDATE + /* FIXME: shutdown hack. We mess something up and try to double-close/free it. */ + do_close = do_close && !exe_image->delta_image; +#endif + if (do_close) mono_image_close (exe_image); } diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 7fe58b065e54b..37e75aebbc017 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -326,8 +326,10 @@ HANDLES_REUSE_WRAPPER(MPROP_3, "get_metadata_token", ves_icall_reflection_get_to HANDLES(MPROP_4, "get_property_info", ves_icall_RuntimePropertyInfo_get_property_info, void, 3, (MonoReflectionProperty, MonoPropertyInfo_ref, PInfo)) HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_RuntimePropertyInfo_internal_from_handle_type, MonoReflectionProperty, 2, (MonoProperty_ptr, MonoType_ptr)) +#ifdef ENABLE_METADATA_UPDATE ICALL_TYPE(RUNF, "System.Runtime.CompilerServices.RuntimeFeature", RUNF_1) HANDLES(RUNF_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate, void, 5, (MonoReflectionAssembly, gconstpointer, gint32, gconstpointer, gint32)) +#endif ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1) HANDLES(RUNH_1, "GetObjectValue", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_GetObjectValue, MonoObject, 1, (MonoObject)) diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index a77e69c75982c..31a79575994e5 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -152,7 +152,9 @@ HANDLES(RUNTIME_2, "EnableMicrosoftTelemetry_internal", ves_icall_Mono_Runtime_E HANDLES(RUNTIME_3, "ExceptionToState_internal", ves_icall_Mono_Runtime_ExceptionToState, MonoString, 3, (MonoException, guint64_ref, guint64_ref)) HANDLES(RUNTIME_4, "GetDisplayName", ves_icall_Mono_Runtime_GetDisplayName, MonoString, 0, ()) HANDLES(RUNTIME_12, "GetNativeStackTrace", ves_icall_Mono_Runtime_GetNativeStackTrace, MonoString, 1, (MonoException)) +#ifdef ENABLE_METADATA_UPDATE HANDLES(RUNTIME_22, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate, void, 5, (MonoReflectionAssembly, gconstpointer, gint32, gconstpointer, gint32)) +#endif NOHANDLES(ICALL(RUNTIME_21, "RegisterReportingForAllNativeLibs_internal", ves_icall_Mono_Runtime_RegisterReportingForAllNativeLibs)) NOHANDLES(ICALL(RUNTIME_17, "RegisterReportingForNativeLib_internal", ves_icall_Mono_Runtime_RegisterReportingForNativeLib)) HANDLES(RUNTIME_13, "SendMicrosoftTelemetry_internal", ves_icall_Mono_Runtime_SendMicrosoftTelemetry, void, 3, (const_char_ptr, guint64, guint64)) diff --git a/src/mono/mono/metadata/image.c b/src/mono/mono/metadata/image.c index 9c1d068adc145..31adfc268ac47 100644 --- a/src/mono/mono/metadata/image.c +++ b/src/mono/mono/metadata/image.c @@ -616,12 +616,14 @@ load_tables (MonoImage *image) image->idx_guid_wide = ((heap_sizes & 0x02) == 2); image->idx_blob_wide = ((heap_sizes & 0x04) == 4); - if (image->minimal_delta) { +#ifdef ENABLE_METADATA_UPDATE + if (G_UNLIKELY (image->minimal_delta)) { /* sanity check */ g_assert (image->idx_string_wide); g_assert (image->idx_guid_wide); g_assert (image->idx_blob_wide); } +#endif valid_mask = read64 (heap_tables + 8); rows = (const guint32 *) (heap_tables + 24); @@ -637,7 +639,6 @@ load_tables (MonoImage *image) g_warning("bits in valid must be zero above 0x37 (II - 23.1.6)"); } else { image->tables [table].rows = read32 (rows); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "found %s in image %s with rows = %d", mono_meta_table_name (table), image->assembly_name, image->tables [table].rows); } rows++; valid++; @@ -1472,6 +1473,7 @@ mono_is_problematic_image (MonoImage *image) return FALSE; } +#ifdef ENABLE_METADATA_UPDATE static void dump_encmap (MonoImage *image) { @@ -1479,14 +1481,17 @@ dump_encmap (MonoImage *image) if (!encmap || !encmap->rows) return; - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "ENCMAP for %s", image->filename); - for (int i = 0; i < encmap->rows; ++i) { - guint32 cols [MONO_ENCMAP_SIZE]; - mono_metadata_decode_row (encmap, i, cols, MONO_ENCMAP_SIZE); - int token = cols [MONO_ENCMAP_TOKEN]; - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "\t0x%08x: 0x%08x table: %s", i+1, token, mono_meta_table_name (mono_metadata_token_table (token))); + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "ENCMAP for %s", image->filename); + for (int i = 0; i < encmap->rows; ++i) { + guint32 cols [MONO_ENCMAP_SIZE]; + mono_metadata_decode_row (encmap, i, cols, MONO_ENCMAP_SIZE); + int token = cols [MONO_ENCMAP_TOKEN]; + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "\t0x%08x: 0x%08x table: %s", i+1, token, mono_meta_table_name (mono_metadata_token_table (token))); + } } } +#endif static MonoImage * do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status, @@ -1552,7 +1557,9 @@ do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status, if (image->loader == &pe_loader && !image->metadata_only && !mono_verifier_verify_table_data (image, error)) goto invalid_image; +#ifdef ENABLE_METADATA_UPDATE dump_encmap (image); +#endif mono_image_load_names (image); @@ -2541,6 +2548,7 @@ mono_image_close_except_pools_all (MonoImage**images, int image_count) } } +#ifdef ENABLE_METADATA_UPDATE static void mono_image_close_except_pools_all_list (GSList *images) { @@ -2552,6 +2560,7 @@ mono_image_close_except_pools_all_list (GSList *images) } } } +#endif /* * Returns whether mono_image_close_finish() must be called as well. @@ -2592,7 +2601,9 @@ mono_image_close_except_pools (MonoImage *image) mono_metadata_clean_for_image (image); +#ifdef ENABLE_METADATA_UPDATE mono_metadata_update_cleanup_on_close (image); +#endif /* * The caches inside a MonoImage might refer to metadata which is stored in referenced @@ -2721,8 +2732,10 @@ mono_image_close_except_pools (MonoImage *image) mono_image_close_except_pools_all (image->modules, image->module_count); g_free (image->modules_loaded); +#ifdef ENABLE_METADATA_UPDATE if (image->delta_image) mono_image_close_except_pools_all_list (image->delta_image); +#endif mono_os_mutex_destroy (&image->szarray_cache_lock); mono_os_mutex_destroy (&image->lock); @@ -2750,6 +2763,7 @@ mono_image_close_all (MonoImage**images, int image_count) g_free (images); } +#ifdef ENABLE_METADATA_UPDATE static void mono_image_close_all_list (GSList *images) { @@ -2761,6 +2775,7 @@ mono_image_close_all_list (GSList *images) g_slist_free (images); } +#endif void mono_image_close_finish (MonoImage *image) @@ -2780,7 +2795,9 @@ mono_image_close_finish (MonoImage *image) mono_image_close_all (image->files, image->file_count); mono_image_close_all (image->modules, image->module_count); +#ifdef ENABLE_METADATA_UPDATE mono_image_close_all_list (image->delta_image); +#endif #ifndef DISABLE_PERFCOUNTERS /* FIXME: use an explicit subtraction method as soon as it's available */ diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index e97c578a22b6a..b90b65886164f 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -2024,7 +2024,6 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error) { int idx; guint32 rva; - gboolean from_dmeta_image = FALSE; MonoImage* img; gpointer loc = NULL; MonoGenericContainer *container; @@ -2072,12 +2071,13 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error) g_assert (mono_metadata_token_table (method->token) == MONO_TABLE_METHOD); idx = mono_metadata_token_index (method->token); +#ifdef ENABLE_METADATA_UPDATE /* EnC case */ if (G_UNLIKELY (img->method_table_delta_index)) { /* pre-computed rva pointer into delta IL image */ loc = g_hash_table_lookup (img->method_table_delta_index, GUINT_TO_POINTER (idx)); - from_dmeta_image = !!loc; } +#endif if (!loc) { rva = mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_RVA); diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 76095c436b255..8c15209126824 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -587,6 +587,7 @@ struct _MonoImage { /* Contains 1 based indexes */ GHashTable *weak_field_indexes; +#ifdef ENABLE_METADATA_UPDATE GHashTable *method_table_delta_index; /* EnC index for method updates */ /* List of MonoImages of deltas. Parent image owns 1 refcount ref of the delta image */ @@ -596,6 +597,7 @@ struct _MonoImage { /* Metadata delta images only */ uint32_t generation; +#endif /* * No other runtime locks must be taken while holding this lock. @@ -897,6 +899,12 @@ mono_install_image_loader (const MonoImageLoader *loader); void mono_image_append_class_to_reflection_info_set (MonoClass *klass); +#ifndef ENABLE_METADATA_UPDATE +static inline void +mono_image_effective_table (const MonoTableInfo **t, int *idx) +{ +} +#else /* ENABLE_METADATA_UPDATE */ void mono_image_effective_table (const MonoTableInfo **t, int *idx); @@ -905,6 +913,7 @@ mono_image_relative_delta_index (MonoImage *image_dmeta, int token); void mono_image_load_enc_delta (MonoDomain *domain, MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); +#endif /* ENABLE_METADATA_UPDATE */ gpointer mono_image_set_alloc (MonoImageSet *set, guint size); @@ -962,8 +971,17 @@ mono_metadata_clean_generic_classes_for_image (MonoImage *image); MONO_API void mono_metadata_cleanup (void); +#ifndef ENABLE_METADATA_UPDATE +static inline gboolean +mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index) +{ + /* token_index is 1-based. TRUE means the token is out of bounds */ + return token_index > image->tables [table_index].rows; +} +#else gboolean mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index); +#endif const char * mono_meta_table_name (int table); void mono_metadata_compute_table_bases (MonoImage *meta); diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index a5fabe45c6265..fa73238272b72 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -8,6 +8,10 @@ */ #include +#include "mono/utils/mono-compiler.h" + +#ifdef ENABLE_METADATA_UPDATE + #include #include "mono/metadata/metadata-internals.h" #include "mono/metadata/metadata-update.h" @@ -31,6 +35,9 @@ typedef struct _EncRecs { } EncRecs; +static void +mono_metadata_update_ee_init (MonoError *error); + /* Maps each MonoTableInfo* to the MonoImage that it belongs to. This is * mapping the base image MonoTableInfos to the base MonoImage. We don't need * this for deltas. @@ -111,10 +118,20 @@ void mono_metadata_update_init (void) { table_to_image_init (); - if (mono_get_runtime_callbacks ()->metadata_update_init) - mono_get_runtime_callbacks ()->metadata_update_init (); } +/* Inform the execution engine that updates are coming */ +static void +mono_metadata_update_ee_init (MonoError *error) +{ + static gboolean inited = FALSE; + + if (inited) + return; + if (mono_get_runtime_callbacks ()->metadata_update_init) + mono_get_runtime_callbacks ()->metadata_update_init (error); + inited = TRUE; +} static void @@ -307,7 +324,7 @@ dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) void mono_image_effective_table (const MonoTableInfo **t, int *idx) { - if (*idx < (*t)->rows) + if (G_LIKELY (*idx < (*t)->rows)) return; MonoImage *base = mono_table_info_get_base_image (*t); @@ -628,6 +645,10 @@ append_heap (MonoStreamHeader *base, MonoStreamHeader *appendix) void mono_image_load_enc_delta (MonoDomain *domain, MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes, uint32_t dil_length, MonoError *error) { + mono_metadata_update_ee_init (error); + if (!is_ok (error)) + return; + const char *basename = image_base->filename; /* FIXME: * (1) do we need to memcpy dmeta_bytes ? (maybe) @@ -729,3 +750,7 @@ mono_image_load_enc_delta (MonoDomain *domain, MonoImage *image_base, gconstpoin mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, ">>> EnC delta for base=%s (generation %d) applied", basename, generation); } + +#else /* ENABLE_METADATA_UPDATE */ +MONO_EMPTY_SOURCE_FILE (metadata_update); +#endif /* ENABLE_METADATA_UPDATE */ diff --git a/src/mono/mono/metadata/metadata-update.h b/src/mono/mono/metadata/metadata-update.h index 6bab202798329..6bfef02c216ca 100644 --- a/src/mono/mono/metadata/metadata-update.h +++ b/src/mono/mono/metadata/metadata-update.h @@ -9,6 +9,8 @@ #include "mono/metadata/loader-internals.h" #include "mono/metadata/metadata-internals.h" +#ifdef ENABLE_METADATA_UPDATE + void mono_metadata_update_init (void); @@ -33,4 +35,6 @@ mono_metadata_update_cleanup_on_close (MonoImage *base_image); MonoImage * mono_table_info_get_base_image (const MonoTableInfo *t); +#endif /* ENABLE_METADATA_UPDATE */ + #endif /*__MONO_METADATA_UPDATE_H__*/ diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index 869dded8e17f3..8f95197ed0b6c 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -990,11 +990,12 @@ mono_metadata_compute_size (MonoImage *meta, int tableindex, guint32 *result_bit return size; } +#ifdef ENABLE_METADATA_UPDATE /* returns true if given index is not in bounds with provided table/index pair */ gboolean mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index) { - if (token_index <= image->tables [table_index].rows) + if (G_LIKELY (token_index <= image->tables [table_index].rows)) return FALSE; GSList *list = image->delta_image; @@ -1014,6 +1015,7 @@ mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_i return FALSE; } +#endif /** * mono_metadata_compute_table_bases: @@ -1879,7 +1881,9 @@ mono_metadata_init (void) mono_counters_register ("ImgSet Cache Miss", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_cache_miss); mono_counters_register ("ImgSet Count", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &img_set_count); +#ifdef ENABLE_METADATA_UPDATE mono_metadata_update_init (); +#endif } /** diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index 8e17b48befacb..027f5ddb68f48 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -841,8 +841,10 @@ typedef struct { GHashTable *(*get_weak_field_indexes) (MonoImage *image); void (*install_state_summarizer) (void); gboolean (*is_interpreter_enabled) (void); - void (*metadata_update_init) (void); +#ifdef ENABLE_METADATA_UPDATE + void (*metadata_update_init) (MonoError *error); void (*metadata_update_published) (MonoDomain *domain, MonoAssemblyLoadContext *alc, uint32_t generation); +#endif } MonoRuntimeCallbacks; typedef gboolean (*MonoInternalStackWalk) (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer data); diff --git a/src/mono/mono/mini/ee.h b/src/mono/mono/mini/ee.h index e4c2ee7b7cd0b..110e9be694738 100644 --- a/src/mono/mono/mini/ee.h +++ b/src/mono/mono/mini/ee.h @@ -57,7 +57,7 @@ typedef gpointer MonoInterpFrameHandle; MONO_EE_CALLBACK (void, stop_single_stepping, (void)) \ MONO_EE_CALLBACK (void, free_context, (gpointer)) \ MONO_EE_CALLBACK (void, set_optimizations, (guint32)) \ - MONO_EE_CALLBACK (void, metadata_update_init, (void)) \ + MONO_EE_CALLBACK (void, metadata_update_init, (MonoError *error)) \ MONO_EE_CALLBACK (void, invalidate_transformed, (MonoDomain *domain)) \ MONO_EE_CALLBACK (void, cleanup, (void)) \ MONO_EE_CALLBACK (void, mark_stack, (gpointer thread_info, GcScanFunc func, gpointer gc_data, gboolean precise)) \ diff --git a/src/mono/mono/mini/interp-stubs.c b/src/mono/mono/mini/interp-stubs.c index 7adba1f63fd72..1179bd1dd3e14 100644 --- a/src/mono/mono/mini/interp-stubs.c +++ b/src/mono/mono/mini/interp-stubs.c @@ -80,7 +80,7 @@ stub_set_optimizations (guint32 i) } static void -stub_metadata_update_init (void) +stub_metadata_update_init (MonoError *error) { } diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index d71fbb6e4128d..e9a262c3ce1df 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -7568,14 +7568,19 @@ copy_imethod_for_frame (MonoDomain *domain, InterpFrame *frame) } static void -interp_metadata_update_init (void) +interp_metadata_update_init (MonoError *error) { - g_assertf ((mono_interp_opt & INTERP_OPT_INLINE) == 0, "Interpreter inlining must be turned off for metadata updates"); + if ((mono_interp_opt & INTERP_OPT_INLINE) != 0) + mono_error_set_execution_engine (error, "Interpreter inlining must be turned off for metadata updates"); } static void interp_invalidate_transformed (MonoDomain *domain) { + gboolean need_stw_restart = FALSE; +#ifdef ENABLE_METADATA_UPDATE + need_stw_restart = TRUE; + mono_gc_stop_world (); /* (1) make a copy of imethod for every interpframe that is on the stack, * so we do not invalidate currently running methods */ @@ -7602,12 +7607,14 @@ interp_invalidate_transformed (MonoDomain *domain) } FOREACH_THREAD_END /* (2) invalidate all the registered imethods */ +#endif MonoJitDomainInfo *info = domain_jit_info (domain); mono_domain_jit_code_hash_lock (domain); mono_internal_hash_table_apply (&info->interp_code_hash, invalidate_transform); mono_domain_jit_code_hash_unlock (domain); - mono_gc_restart_world (); + if (need_stw_restart) + mono_gc_restart_world (); } static void diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index e8ab558966377..f8c1fd0ccf957 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -169,9 +169,10 @@ GSList *mono_interp_only_classes; static void register_icalls (void); static void runtime_cleanup (MonoDomain *domain, gpointer user_data); -static void mini_metadata_update_init (void); +#ifdef ENABLE_METADATA_UPDATE +static void mini_metadata_update_init (MonoError *error); static void mini_invalidate_transformed_interp_methods (MonoDomain *domain, MonoAssemblyLoadContext *alc, uint32_t generation); - +#endif gboolean @@ -4433,8 +4434,10 @@ mini_init (const char *filename, const char *runtime_version) #ifndef DISABLE_CRASH_REPORTING callbacks.install_state_summarizer = mini_register_sigterm_handler; #endif +#ifdef ENABLE_METADATA_UPDATE callbacks.metadata_update_init = mini_metadata_update_init; callbacks.metadata_update_published = mini_invalidate_transformed_interp_methods; +#endif mono_install_callbacks (&callbacks); @@ -5329,10 +5332,11 @@ mono_runtime_install_custom_handlers_usage (void) } #endif /* HOST_WIN32 */ +#ifdef ENABLE_METADATA_UPDATE void -mini_metadata_update_init (void) +mini_metadata_update_init (MonoError *error) { - mini_get_interp_callbacks ()->metadata_update_init (); + mini_get_interp_callbacks ()->metadata_update_init (error); } void @@ -5340,5 +5344,4 @@ mini_invalidate_transformed_interp_methods (MonoDomain *domain, MonoAssemblyLoad { mini_get_interp_callbacks ()->invalidate_transformed (domain); } - - +#endif diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index 1e66fcd913aa3..ab7895f0eb3be 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -466,6 +466,9 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_runtime (const char *unused, int debug_level) { const char *interp_opts = ""; +#ifdef ENABLE_METADATA_UPDATE + interp_opts = "-inline"; /* FIXME: EnC hack - make this configurable */ +#endif #ifdef DEBUG monoeg_g_setenv ("MONO_LOG_LEVEL", "debug", 0); From 486b5725cffc2fd9e2c25c3df2fcde3ca012c841 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 3 Dec 2020 14:28:21 -0500 Subject: [PATCH 09/31] [wasm] set log mask to metadata-update --- src/mono/wasm/runtime/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index ab7895f0eb3be..d2f7fa0f0f439 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -472,7 +472,7 @@ mono_wasm_load_runtime (const char *unused, int debug_level) #ifdef DEBUG monoeg_g_setenv ("MONO_LOG_LEVEL", "debug", 0); - monoeg_g_setenv ("MONO_LOG_MASK", "gc", 0); + monoeg_g_setenv ("MONO_LOG_MASK", "metadata-update", 0); // Setting this env var allows Diagnostic.Debug to write to stderr. In a browser environment this // output will be sent to the console. Right now this is the only way to emit debug logging from // corlib assemblies. From 0e3789138035c9a3e9bebb058990f6d1cc02d4b4 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 3 Dec 2020 14:29:05 -0500 Subject: [PATCH 10/31] [mbr] Add InjectUpdate fn to sample --- .../sample/mbr/DeltaHelper/DeltaHelper.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs index 5d7d4aeebb64e..2c77960cd7a89 100644 --- a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs +++ b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs @@ -1,6 +1,7 @@ using System; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.Loader; using System.Collections.Generic; namespace MonoDelta { @@ -37,6 +38,23 @@ public static DeltaHelper Make () return new DeltaHelper (); } + public static void InjectUpdate (string assemblyName, string dmeta_base64, string dil_base64) { + var an = new AssemblyName (assemblyName); + Assembly assm = null; + /* TODO: non-default ALCs */ + foreach (var candidate in AssemblyLoadContext.Default.Assemblies) { + if (candidate.GetName().Name == an.Name) { + assm = candidate; + break; + } + } + if (assm == null) + throw new ArgumentException ("assemblyName"); + var dmeta_data = Convert.FromBase64String (dmeta_base64); + var dil_data = Convert.FromBase64String (dil_base64); + LoadMetadataUpdate (assm, dmeta_data, dil_data); + } + private Dictionary assembly_count = new Dictionary (); public void Update (Assembly assm) { From d76c153c562d2497249cb7642980ef5cdfeb97b7 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 3 Dec 2020 15:44:27 -0500 Subject: [PATCH 11/31] [metadata-update] don't merge heaps --- src/mono/mono/metadata/metadata-update.c | 23 +---- src/mono/mono/metadata/metadata.c | 119 ++++++++++++++++++++++- 2 files changed, 118 insertions(+), 24 deletions(-) diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index fa73238272b72..e3e38dc0bd317 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -681,13 +681,8 @@ mono_image_load_enc_delta (MonoDomain *domain, MonoImage *image_base, gconstpoin if (image_dmeta->minimal_delta) { guint32 idx = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_MODULE], 0, MONO_MODULE_NAME); - /* TODO: hmm. */ const char *module_name = NULL; - if (idx >= image_base->heap_strings.size) { - module_name = mono_metadata_string_heap (image_dmeta, idx - image_base->heap_strings.size); - } else { - module_name = mono_metadata_string_heap (image_base, idx); - } + module_name = mono_metadata_string_heap (image_base, idx); /* Set the module name now that we know the base String heap size */ g_assert (!image_dmeta->module_name); @@ -719,22 +714,6 @@ mono_image_load_enc_delta (MonoDomain *domain, MonoImage *image_base, gconstpoin mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base guid: %s", image_base->guid); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "dmeta guid: %s", image_dmeta->guid); - if (image_dmeta->minimal_delta) { - append_heap (&image_base->heap_strings, &image_dmeta->heap_strings); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image strings heap addr (merged): %p", image_base->heap_strings.data); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image strings heap size (merged): 0x%08x", image_base->heap_strings.size); - - append_heap (&image_base->heap_us, &image_dmeta->heap_us); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image user string heap addr (merged): %p", image_base->heap_us.data); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image user string heap size (merged): 0x%08x", image_base->heap_us.size); - - append_heap (&image_base->heap_blob, &image_dmeta->heap_blob); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image blob heap addr (merged): %p", image_base->heap_blob.data); - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base image blob heap size (merged): 0x%08x", image_base->heap_blob.size); - } else { - g_error ("implement me for regular delta images"); - } - if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) dump_update_summary (image_base, image_dmeta); diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index 8f95197ed0b6c..4a040b135a627 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -1073,6 +1073,58 @@ mono_metadata_locate_token (MonoImage *meta, guint32 token) return mono_metadata_locate (meta, token >> 24, token & 0xffffff); } + + +typedef MonoStreamHeader* (*MetadataHeapGetterFunc) (MonoImage*); + +#ifdef ENABLE_METADATA_UPDATE +static MonoStreamHeader * +get_string_heap (MonoImage *image) +{ + return &image->heap_strings; +} + +static MonoStreamHeader * +get_user_string_heap (MonoImage *image) +{ + return &image->heap_us; +} + +static MonoStreamHeader * +get_blob_heap (MonoImage *image) +{ + return &image->heap_blob; +} + +static gboolean +mono_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, guint32 orig_index, MonoImage **image_out, guint32 *index_out) +{ + g_assert (image_out); + g_assert (index_out); + MonoStreamHeader *heap = get_heap (base_image); + g_assert (orig_index >= heap->size && base_image->delta_image); + + *image_out = base_image; + *index_out = orig_index; + + guint32 prev_size = heap->size; + + GSList *cur; + for (cur = base_image->delta_image; cur; cur = cur->next) { + *image_out = (MonoImage*)cur->data; + heap = get_heap (*image_out); + + /* FIXME: for non-minimal deltas we should just look in the last published image. */ + if (G_LIKELY ((*image_out)->minimal_delta)) + *index_out -= prev_size; + if (*index_out < heap->size) + break; + prev_size = heap->size; + } + return (cur != NULL); +} +#endif + /** * mono_metadata_string_heap: * \param meta metadata context @@ -1082,7 +1134,18 @@ mono_metadata_locate_token (MonoImage *meta, guint32 token) const char * mono_metadata_string_heap (MonoImage *meta, guint32 index) { - g_assert (index < meta->heap_strings.size); +#ifdef ENABLE_METADATA_UPDATE + if (G_UNLIKELY (index >= meta->heap_strings.size && meta->delta_image)) { + MonoImage *dmeta; + guint32 dindex; + gboolean ok = mono_delta_heap_lookup (meta, &get_string_heap, index, &dmeta, &dindex); + g_assertf (ok, "Could not find token=0x%08x in string heap of assembly=%s and its delta images", index, meta && meta->name ? meta->name : "unknown image"); + meta = dmeta; + index = dindex; + } +#endif + + g_assertf (index < meta->heap_strings.size, " index = 0x%08x size = 0x%08x meta=%s ", index, meta->heap_strings.size, meta && meta->name ? meta->name : "unknown image" ); g_return_val_if_fail (index < meta->heap_strings.size, ""); return meta->heap_strings.data + index; } @@ -1108,7 +1171,24 @@ mono_metadata_string_heap_checked (MonoImage *meta, guint32 index, MonoError *er } return img->sheap.data + index; } - else if (G_UNLIKELY (!(index < meta->heap_strings.size))) { + +#ifdef ENABLE_METADATA_UPDATE + if (G_UNLIKELY (index >= meta->heap_strings.size && meta->delta_image)) { + MonoImage *dmeta; + guint32 dindex; + gboolean ok = mono_delta_heap_lookup (meta, &get_string_heap, index, &dmeta, &dindex); + if (G_UNLIKELY (!ok)) { + const char *image_name = meta && meta->name ? meta->name : "unknown image"; + mono_error_set_bad_image_by_name (error, image_name, "string heap index %ud out bounds %u: %s, also checked delta images", index, meta->heap_strings.size, image_name); + + return NULL; + } + meta = dmeta; + index = dindex; + } +#endif + + if (G_UNLIKELY (!(index < meta->heap_strings.size))) { const char *image_name = meta && meta->name ? meta->name : "unknown image"; mono_error_set_bad_image_by_name (error, image_name, "string heap index %ud out bounds %u: %s", index, meta->heap_strings.size, image_name); return NULL; @@ -1125,6 +1205,16 @@ mono_metadata_string_heap_checked (MonoImage *meta, guint32 index, MonoError *er const char * mono_metadata_user_string (MonoImage *meta, guint32 index) { +#ifdef ENABLE_METADATA_UPDATE + if (G_UNLIKELY (index >= meta->heap_us.size && meta->delta_image)) { + MonoImage *dmeta; + guint32 dindex; + gboolean ok = mono_delta_heap_lookup (meta, &get_user_string_heap, index, &dmeta, &dindex); + g_assertf (ok, "Could not find token=0x%08x in user string heap of assembly=%s and its delta images", index, meta && meta->name ? meta->name : "unknown image"); + meta = dmeta; + index = dindex; + } +#endif g_assert (index < meta->heap_us.size); g_return_val_if_fail (index < meta->heap_us.size, ""); return meta->heap_us.data + index; @@ -1144,6 +1234,16 @@ mono_metadata_blob_heap (MonoImage *meta, guint32 index) * assertion is hit, consider updating caller to use * mono_metadata_blob_heap_null_ok and handling a null return value. */ g_assert (!(index == 0 && meta->heap_blob.size == 0)); +#ifdef ENABLE_METADATA_UPDATE + if (G_UNLIKELY (index >= meta->heap_blob.size && meta->delta_image)) { + MonoImage *dmeta; + guint32 dindex; + gboolean ok = mono_delta_heap_lookup (meta, &get_blob_heap, index, &dmeta, &dindex); + g_assertf (ok, "Could not find token=0x%08x in blob heap of assembly=%s and its delta images", index, meta && meta->name ? meta->name : "unknown image"); + meta = dmeta; + index = dindex; + } +#endif g_assert (index < meta->heap_blob.size); return meta->heap_blob.data + index; } @@ -1189,6 +1289,20 @@ mono_metadata_blob_heap_checked (MonoImage *meta, guint32 index, MonoError *erro } if (G_UNLIKELY (index == 0 && meta->heap_blob.size == 0)) return NULL; +#ifdef ENABLE_METADATA_UPDATE + if (G_UNLIKELY (index >= meta->heap_blob.size && meta->delta_image)) { + MonoImage *dmeta; + guint32 dindex; + gboolean ok = mono_delta_heap_lookup (meta, &get_blob_heap, index, &dmeta, &dindex); + if (G_UNLIKELY(!ok)) { + const char *image_name = meta && meta->name ? meta->name : "unknown image"; + mono_error_set_bad_image_by_name (error, image_name, "Could not find token=0x%08x in blob heap of assembly=%s and its delta images", index, image_name); + return NULL; + } + meta = dmeta; + index = dindex; + } +#endif if (G_UNLIKELY (!(index < meta->heap_blob.size))) { const char *image_name = meta && meta->name ? meta->name : "unknown image"; mono_error_set_bad_image_by_name (error, image_name, "blob heap index %u out of bounds %u: %s", index, meta->heap_blob.size, image_name); @@ -1206,6 +1320,7 @@ mono_metadata_blob_heap_checked (MonoImage *meta, guint32 index, MonoError *erro const char * mono_metadata_guid_heap (MonoImage *meta, guint32 index) { + /* EnC TODO: lookup in MonoImage:delta_image_last. Unlike the other heaps, the GUID heaps are always full in every delta, even in minimal delta images. */ --index; index *= 16; /* adjust for guid size and 1-based index */ g_return_val_if_fail (index < meta->heap_guid.size, ""); From 0de44c589996b9b22a3f733931ec51e6842de78b Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 6 Nov 2020 14:13:04 -0500 Subject: [PATCH 12/31] Don't make entrypoint public yet --- .../System/Runtime/CompilerServices/RuntimeFeature.Mono.cs | 4 ++-- src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs index 3fd90590dd233..7aba9f770d4bf 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs @@ -19,14 +19,14 @@ public static bool IsDynamicCodeCompiled } #if !FEATURE_METADATA_UPDATE - public static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { + internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { throw new NotSupportedException ("Method body replacement not supported in this runtime"); } #else [MethodImplAttribute (MethodImplOptions.InternalCall)] private static unsafe extern void LoadMetadataUpdate_internal (Assembly base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length); - public static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { + internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { unsafe { fixed (byte* dmeta_bytes = dmeta_data) fixed (byte* dil_bytes = dil_data) { diff --git a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs index 2c77960cd7a89..7188782df8b5e 100644 --- a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs +++ b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.cs @@ -6,11 +6,8 @@ namespace MonoDelta { public class DeltaHelper { -#if true const string name = "System.Runtime.CompilerServices.RuntimeFeature"; -#else - const string name = "Mono.Runtime"; -#endif + private static MethodBase _updateMethod; private static MethodBase UpdateMethod => _updateMethod ?? InitUpdateMethod(); @@ -20,7 +17,7 @@ private static MethodBase InitUpdateMethod () var monoType = Type.GetType (name, throwOnError: true); if (monoType == null) throw new Exception ($"Couldn't get the type {name}"); - _updateMethod = monoType.GetMethod ("LoadMetadataUpdate"); + _updateMethod = monoType.GetMethod ("LoadMetadataUpdate", BindingFlags.NonPublic | BindingFlags.Static); if (_updateMethod == null) throw new Exception ($"Couldn't get LoadMetadataUpdate from {name}"); return _updateMethod; From 05e8c36830e6b9503b38e7bb8d5c5f9aa8c86727 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Sat, 5 Dec 2020 19:30:34 -0500 Subject: [PATCH 13/31] Add LoadMetadataUpdate to linker descriptor --- .../System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mono/netcore/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml b/src/mono/netcore/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml index 39ab94fb1193b..a7123b5c756b1 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml +++ b/src/mono/netcore/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml @@ -551,6 +551,11 @@ + + + + + From 91080db762468dcc451abeada1c47e67222189b5 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Sat, 5 Dec 2020 20:39:36 -0500 Subject: [PATCH 14/31] [wasm] add default Makefile variable value --- src/mono/wasm/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 0f9e11e20f185..f07aedab6d27f 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -21,6 +21,7 @@ ICU_LIBDIR?= SYSTEM_NATIVE_LIBDIR?=$(TOP)/src/libraries/Native/Unix/System.Native ENABLE_ES6?=false _MSBUILD_WASM_BUILD_ARGS=/p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=$(CONFIG) +ENABLE_METADATA_UPDATE?=false all: build-native icu-files source-files header-files From 2f69e35176b0e117cfdd81df219b897500eb2e0b Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Sun, 6 Dec 2020 15:29:37 -0500 Subject: [PATCH 15/31] fix mono/mono CI don't try to run enc tests yet since they depend on roslynildiff --- src/mono/m4/mono-output.m4 | 1 - src/mono/mono/tests/Makefile.am | 2 +- src/mono/mono/tests/enc/Makefile.am | 7 +++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/mono/m4/mono-output.m4 b/src/mono/m4/mono-output.m4 index 1456be63e2d20..40d72ea7d6d14 100644 --- a/src/mono/m4/mono-output.m4 +++ b/src/mono/m4/mono-output.m4 @@ -28,6 +28,5 @@ AC_DEFUN([AC_MONO_OUTPUT], [ mono/eglib/Makefile mono/eglib/eglib-config.h mono/eglib/test/Makefile - mono/tests/enc/Makefile ]) ]) diff --git a/src/mono/mono/tests/Makefile.am b/src/mono/mono/tests/Makefile.am index 9771c11dc0dc8..52f0c42e08601 100755 --- a/src/mono/mono/tests/Makefile.am +++ b/src/mono/mono/tests/Makefile.am @@ -1,6 +1,6 @@ MAKEFLAGS := $(MAKEFLAGS) --no-builtin-rules -SUBDIRS = gc-descriptors . testing_gac assembly-load-reference llvmonly-mixed fullaot-mixed enc +SUBDIRS = gc-descriptors . testing_gac assembly-load-reference llvmonly-mixed fullaot-mixed check-local: ok=:; \ diff --git a/src/mono/mono/tests/enc/Makefile.am b/src/mono/mono/tests/enc/Makefile.am index 835e1dd53f966..9d146fad0cf39 100644 --- a/src/mono/mono/tests/enc/Makefile.am +++ b/src/mono/mono/tests/enc/Makefile.am @@ -80,3 +80,10 @@ EncHelper.dll: EncHelper.cs bash -x -c "$(ROSLYNILDIFF_RUN) $(ROSLYNILDIFF_ARGS) -l:EncHelper.dll $< $(basename $<)_v?.cs" CLEANFILES += *.dll.*.dil *.dll.*.dmeta *.dll.*.dpdb + +all-local: + true + +# TODO: run the tests on CI +check-local: + true From e847fd89a25e88df335d2aee2031080d4d6ff7c4 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Sun, 6 Dec 2020 20:25:03 -0500 Subject: [PATCH 16/31] remove mono/mono/tests/enc Will add as runtime tests in a future PR --- src/mono/mono/tests/enc/.gitignore | 4 - src/mono/mono/tests/enc/AddClass.cs | 36 -------- src/mono/mono/tests/enc/AddClass_v1.cs | 38 -------- src/mono/mono/tests/enc/AddStaticField.cs | 29 ------ src/mono/mono/tests/enc/AddStaticField_v1.cs | 30 ------- src/mono/mono/tests/enc/AddStaticMethod.cs | 31 ------- src/mono/mono/tests/enc/AddStaticMethod_v1.cs | 36 -------- src/mono/mono/tests/enc/CallExisting.cs | 36 -------- src/mono/mono/tests/enc/CallExisting_v1.cs | 36 -------- src/mono/mono/tests/enc/EncHelper.cs | 59 ------------ src/mono/mono/tests/enc/LambdaFunc.cs | 38 -------- src/mono/mono/tests/enc/LambdaFunc_v1.cs | 38 -------- src/mono/mono/tests/enc/Makefile.am | 89 ------------------- src/mono/mono/tests/enc/OnStackMethod.cs | 43 --------- .../mono/tests/enc/OnStackMethodThread.cs | 55 ------------ .../tests/enc/OnStackMethodThreadThread.cs | 82 ----------------- .../tests/enc/OnStackMethodThreadThread_v1.cs | 77 ---------------- .../mono/tests/enc/OnStackMethodThread_v1.cs | 52 ----------- src/mono/mono/tests/enc/OnStackMethod_v1.cs | 40 --------- src/mono/mono/tests/enc/ReplaceMethod.cs | 36 -------- src/mono/mono/tests/enc/ReplaceMethodOften.cs | 54 ----------- .../mono/tests/enc/ReplaceMethodOften_v1.cs | 54 ----------- .../mono/tests/enc/ReplaceMethodOften_v2.cs | 54 ----------- .../mono/tests/enc/ReplaceMethodOften_v3.cs | 54 ----------- .../mono/tests/enc/ReplaceMethodOften_v4.cs | 54 ----------- .../mono/tests/enc/ReplaceMethodOften_v5.cs | 54 ----------- .../mono/tests/enc/ReplaceMethodOften_v6.cs | 54 ----------- src/mono/mono/tests/enc/ReplaceMethod_v1.cs | 34 ------- .../tests/enc/ReplacePrivateVirtualMethod.cs | 54 ----------- .../enc/ReplacePrivateVirtualMethod_v1.cs | 54 ----------- src/mono/mono/tests/enc/TypeTokenSwap.cs | 29 ------ src/mono/mono/tests/enc/TypeTokenSwap_v1.cs | 29 ------ src/mono/mono/tests/enc/UserStringSwap.cs | 31 ------- src/mono/mono/tests/enc/UserStringSwap_v1.cs | 31 ------- 34 files changed, 1525 deletions(-) delete mode 100644 src/mono/mono/tests/enc/.gitignore delete mode 100644 src/mono/mono/tests/enc/AddClass.cs delete mode 100644 src/mono/mono/tests/enc/AddClass_v1.cs delete mode 100644 src/mono/mono/tests/enc/AddStaticField.cs delete mode 100644 src/mono/mono/tests/enc/AddStaticField_v1.cs delete mode 100644 src/mono/mono/tests/enc/AddStaticMethod.cs delete mode 100644 src/mono/mono/tests/enc/AddStaticMethod_v1.cs delete mode 100644 src/mono/mono/tests/enc/CallExisting.cs delete mode 100644 src/mono/mono/tests/enc/CallExisting_v1.cs delete mode 100644 src/mono/mono/tests/enc/EncHelper.cs delete mode 100644 src/mono/mono/tests/enc/LambdaFunc.cs delete mode 100644 src/mono/mono/tests/enc/LambdaFunc_v1.cs delete mode 100644 src/mono/mono/tests/enc/Makefile.am delete mode 100644 src/mono/mono/tests/enc/OnStackMethod.cs delete mode 100644 src/mono/mono/tests/enc/OnStackMethodThread.cs delete mode 100644 src/mono/mono/tests/enc/OnStackMethodThreadThread.cs delete mode 100644 src/mono/mono/tests/enc/OnStackMethodThreadThread_v1.cs delete mode 100644 src/mono/mono/tests/enc/OnStackMethodThread_v1.cs delete mode 100644 src/mono/mono/tests/enc/OnStackMethod_v1.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethod.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v1.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v2.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v3.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v4.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v5.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethodOften_v6.cs delete mode 100644 src/mono/mono/tests/enc/ReplaceMethod_v1.cs delete mode 100644 src/mono/mono/tests/enc/ReplacePrivateVirtualMethod.cs delete mode 100644 src/mono/mono/tests/enc/ReplacePrivateVirtualMethod_v1.cs delete mode 100644 src/mono/mono/tests/enc/TypeTokenSwap.cs delete mode 100644 src/mono/mono/tests/enc/TypeTokenSwap_v1.cs delete mode 100644 src/mono/mono/tests/enc/UserStringSwap.cs delete mode 100644 src/mono/mono/tests/enc/UserStringSwap_v1.cs diff --git a/src/mono/mono/tests/enc/.gitignore b/src/mono/mono/tests/enc/.gitignore deleted file mode 100644 index 3fe5e125b6c99..0000000000000 --- a/src/mono/mono/tests/enc/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.dil -*.dmeta -*.dpdb -Makefile diff --git a/src/mono/mono/tests/enc/AddClass.cs b/src/mono/mono/tests/enc/AddClass.cs deleted file mode 100644 index 24e362dc18326..0000000000000 --- a/src/mono/mono/tests/enc/AddClass.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class AddClass { - public static int Main (string []args) { - Assembly assm = typeof (AddClass).Assembly; - var replacer = EncHelper.Make (); - - var secondClassType = ReplaceMe (); - if (secondClassType != typeof (AddClass)) - return 1; - - try { - replacer.Update (assm); - /* doesn't work yet, thus should throw exception */ - return 2; - } catch (TargetInvocationException e) { - Console.WriteLine ("e: " + e); - if (e.InnerException.Message.Contains ("cannot add new class")) - return 0; - } - /* we should not get here, yet */ - secondClassType = ReplaceMe (); - if (secondClassType != typeof (AddClass)) { - /* would be expected if it would work */ - return 4; - } - return 3; - } - - public static System.Type ReplaceMe () { - return typeof (AddClass); - } -} diff --git a/src/mono/mono/tests/enc/AddClass_v1.cs b/src/mono/mono/tests/enc/AddClass_v1.cs deleted file mode 100644 index ada9324b39411..0000000000000 --- a/src/mono/mono/tests/enc/AddClass_v1.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class AddClass { - public static int Main (string []args) { - Assembly assm = typeof (AddClass).Assembly; - var replacer = EncHelper.Make (); - - var secondClassType = ReplaceMe (); - if (secondClassType != typeof (AddClass)) - return 1; - - try { - replacer.Update (assm); - /* doesn't work yet, thus should throw exception */ - return 2; - } catch (TargetInvocationException e) { - Console.WriteLine ("e: " + e); - if (e.InnerException.Message.Contains ("cannot add new class")) - return 0; - } - /* we should not get here, yet */ - secondClassType = ReplaceMe (); - if (secondClassType != typeof (AddClass)) { - /* would be expected if it would work */ - return 4; - } - return 3; - } - - public static System.Type ReplaceMe () { - return typeof (SecondClass); - } -} - -class SecondClass { } diff --git a/src/mono/mono/tests/enc/AddStaticField.cs b/src/mono/mono/tests/enc/AddStaticField.cs deleted file mode 100644 index 9ffba39a0b4b8..0000000000000 --- a/src/mono/mono/tests/enc/AddStaticField.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class AddStaticField { - public static int field1 = 1; - - public static int Main (string []args) { - Assembly assm = typeof (AddStaticField).Assembly; - var replacer = EncHelper.Make (); - - if (AddStaticField.field1 != 1) - return 1; - - try { - replacer.Update (assm); - /* doesn't work yet, thus should throw exception */ - return 2; - } catch (TargetInvocationException e) { - Console.WriteLine ("e: " + e); -#if false - if (e.InnerException.Message.Contains ("cannot add new class")) - return 0; -#endif - } - return 3; - } -} diff --git a/src/mono/mono/tests/enc/AddStaticField_v1.cs b/src/mono/mono/tests/enc/AddStaticField_v1.cs deleted file mode 100644 index c082ecc065d53..0000000000000 --- a/src/mono/mono/tests/enc/AddStaticField_v1.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class AddStaticField { - public static int field1 = 1; - public static int field2 = 2; - - public static int Main (string []args) { - Assembly assm = typeof (AddStaticField).Assembly; - var replacer = EncHelper.Make (); - - if (AddStaticField.field1 != 1) - return 1; - - try { - replacer.Update (assm); - /* doesn't work yet, thus should throw exception */ - return 2; - } catch (TargetInvocationException e) { - Console.WriteLine ("e: " + e); -#if false - if (e.InnerException.Message.Contains ("cannot add new class")) - return 0; -#endif - } - return 3; - } -} diff --git a/src/mono/mono/tests/enc/AddStaticMethod.cs b/src/mono/mono/tests/enc/AddStaticMethod.cs deleted file mode 100644 index 04528fccccb70..0000000000000 --- a/src/mono/mono/tests/enc/AddStaticMethod.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class AddStaticMethod { - public static int Main (string []args) { - Assembly assm = typeof (AddStaticMethod).Assembly; - var replacer = EncHelper.Make (); - - int res = TargetMethod (); - if (res != 1) - return 1; - - try { - replacer.Update (assm); - /* doesn't work yet, thus should throw exception */ - } catch (TargetInvocationException e) { - if (e.InnerException.Message.Contains ("cannot add new method")) - return 0; - } - return 3; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int TargetMethod () { - Console.WriteLine ("Hello old World"); - return 1; - } -} - diff --git a/src/mono/mono/tests/enc/AddStaticMethod_v1.cs b/src/mono/mono/tests/enc/AddStaticMethod_v1.cs deleted file mode 100644 index cd386b5d311d7..0000000000000 --- a/src/mono/mono/tests/enc/AddStaticMethod_v1.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class AddStaticMethod { - public static int Main (string []args) { - Assembly assm = typeof (AddStaticMethod).Assembly; - var replacer = EncHelper.Make (); - - int res = TargetMethod (); - if (res != 1) - return 1; - - try { - replacer.Update (assm); - /* doesn't work yet, thus should throw exception */ - } catch (TargetInvocationException e) { - if (e.InnerException.Message.Contains ("cannot add new method")) - return 0; - } - return 3; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int TargetMethod () { - return NewMethod (); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int NewMethod () { - Console.WriteLine ("Hello NEW World"); - return 2; - } -} - diff --git a/src/mono/mono/tests/enc/CallExisting.cs b/src/mono/mono/tests/enc/CallExisting.cs deleted file mode 100644 index 51b1d6d7a705e..0000000000000 --- a/src/mono/mono/tests/enc/CallExisting.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class CallExisting { - public static int Main (string []args) { - Assembly assm = typeof (CallExisting).Assembly; - var replacer = EncHelper.Make (); - - int res = TargetMethod (); - if (res != 2) - return 1; - - replacer.Update (assm); - - res = TargetMethod (); - if (res != 4) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int TargetMethod () { - Console.WriteLine ("Hello old World"); - return 2; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int SecondMethod () { - Console.WriteLine ("SecondMethod"); - return 1; - } -} - diff --git a/src/mono/mono/tests/enc/CallExisting_v1.cs b/src/mono/mono/tests/enc/CallExisting_v1.cs deleted file mode 100644 index 60d2faeef8fe6..0000000000000 --- a/src/mono/mono/tests/enc/CallExisting_v1.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class CallExisting { - public static int Main (string []args) { - Assembly assm = typeof (CallExisting).Assembly; - var replacer = EncHelper.Make (); - - int res = TargetMethod (); - if (res != 2) - return 1; - - replacer.Update (assm); - - res = TargetMethod (); - if (res != 4) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int TargetMethod () { - Console.WriteLine ("Hello new World"); - return 3 + SecondMethod (); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int SecondMethod () { - Console.WriteLine ("SecondMethod"); - return 1; - } -} - diff --git a/src/mono/mono/tests/enc/EncHelper.cs b/src/mono/mono/tests/enc/EncHelper.cs deleted file mode 100644 index af7eb2d2fd55a..0000000000000 --- a/src/mono/mono/tests/enc/EncHelper.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Collections.Generic; - -namespace MonoEnc { - public class EncHelper { -#if false - const string name = "System.Runtime.CompilerServices.RuntimeFeature"; -#else - const string name = "Mono.Runtime"; -#endif - private static MethodBase _updateMethod; - - private static MethodBase UpdateMethod => _updateMethod ?? InitUpdateMethod(); - - private static MethodBase InitUpdateMethod () - { - var monoType = Type.GetType (name, false); - _updateMethod = monoType.GetMethod ("LoadMetadataUpdate"); - if (_updateMethod == null) - throw new Exception ($"Couldn't get LoadMetadataUpdate from {name}"); - return _updateMethod; - } - - private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) - { - UpdateMethod.Invoke (null, new object [] { assm, dmeta_data, dil_data}); - } - - EncHelper () { } - - public static EncHelper Make () - { - return new EncHelper (); - } - - private Dictionary assembly_count = new Dictionary (); - - public void Update (Assembly assm) { - int count; - if (!assembly_count.TryGetValue (assm, out count)) - count = 1; - else - count++; - assembly_count [assm] = count; - - string basename = assm.Location; - Console.WriteLine ($"Apply Delta Update for {basename}, revision {count}"); - - string dmeta_name = $"{basename}.{count}.dmeta"; - string dil_name = $"{basename}.{count}.dil"; - byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name); - byte[] dil_data = System.IO.File.ReadAllBytes (dil_name); - - LoadMetadataUpdate (assm, dmeta_data, dil_data); - } - } -} diff --git a/src/mono/mono/tests/enc/LambdaFunc.cs b/src/mono/mono/tests/enc/LambdaFunc.cs deleted file mode 100644 index dd8845e31e500..0000000000000 --- a/src/mono/mono/tests/enc/LambdaFunc.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class LambdaFunc { - public delegate int TestDelegate (int a); - - public static int Main (string []args) { - Assembly assm = typeof (LambdaFunc).Assembly; - var replacer = EncHelper.Make (); - - TestDelegate del = CreateDelegate (); - - int res = del (1); - if (res != 2) { - Console.WriteLine ("#1: " + res); - return 1; - } - - replacer.Update (assm); - - res = del (1); - if (res != 3) { - Console.WriteLine ("#2: " + res); - return 2; - } - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static TestDelegate CreateDelegate () { - TestDelegate ret = delegate (int a) { return a + 1; }; - return ret; - } -} - diff --git a/src/mono/mono/tests/enc/LambdaFunc_v1.cs b/src/mono/mono/tests/enc/LambdaFunc_v1.cs deleted file mode 100644 index 5f290c24c3882..0000000000000 --- a/src/mono/mono/tests/enc/LambdaFunc_v1.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class LambdaFunc { - public delegate int TestDelegate (int a); - - public static int Main (string []args) { - Assembly assm = typeof (LambdaFunc).Assembly; - var replacer = EncHelper.Make (); - - TestDelegate del = CreateDelegate (); - - int res = del (1); - if (res != 2) { - Console.WriteLine ("#1: " + res); - return 1; - } - - replacer.Update (assm); - - res = del (1); - if (res != 3) { - Console.WriteLine ("#2: " + res); - return 2; - } - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static TestDelegate CreateDelegate () { - TestDelegate ret = delegate (int a) { return a + 2; }; - return ret; - } -} - diff --git a/src/mono/mono/tests/enc/Makefile.am b/src/mono/mono/tests/enc/Makefile.am deleted file mode 100644 index 9d146fad0cf39..0000000000000 --- a/src/mono/mono/tests/enc/Makefile.am +++ /dev/null @@ -1,89 +0,0 @@ -include ../common_mixed.mk - -#ROSLYNILDIFF_KIND=Mono -ROSLYNILDIFF_KIND=Net5 - -ROSLYNILDIFF ?=$(HOME)/work/roslynildiff/bin/Debug/roslynildiff.$(if $(findstring Net5,$(ROSLYNILDIFF_KIND)),dll,exe) - -ROSLYNILDIFF_CMD =$(if $(findstring Mono,$(ROSLYNILDIFF_KIND)),mono --debug,dotnet) -ROSLYNILDIFF_RUN =$(ROSLYNILDIFF_CMD) $(ROSLYNILDIFF) - -ROSLYNILDIFF_ARGS =$(if $(findstring Mono,$(ROSLYNILDIFF_KIND)),,-mono -bcl:$(CLASS)) - -DISABLED_TESTS := \ - AddClass.dll \ - AddStaticField.dll \ - LambdaFunc.dll - -TESTS_REGULAR := \ - AddClass.dll \ - AddStaticField.dll \ - AddStaticMethod.dll \ - CallExisting.dll \ - LambdaFunc.dll \ - OnStackMethod.dll \ - OnStackMethodThread.dll \ - OnStackMethodThreadThread.dll \ - ReplaceMethod.dll \ - ReplaceMethodOften.dll \ - ReplacePrivateVirtualMethod.dll \ - TypeTokenSwap.dll \ - UserStringSwap.dll - -TESTS_ACTUAL=$(filter-out $(DISABLED_TESTS),$(TESTS_REGULAR)) - -# DEBUG_FLAGS=MONO_LOG_MASK=metadata-update MONO_LOG_LEVEL=debug MONO_VERBOSE_METHOD=DiffTestMethod1 - -TESTS_BASE_SRCS:= $(TESTS_REGULAR:.dll=.cs) - -TESTS_UPDATE_SRCS:= $(foreach b,$(basename $(TESTS_REGULAR)),$(wildcard $(b)_v?.cs)) - -TESTS_SRCS:= $(TEST_BASE_SRCS) $(TEST_UPDATE_SRCS) - -EXTRA_DIST= $(TESTS_SRCS) - -.PHONY: names - -check-local: $(TESTS_REGULAR) - @failed=0; \ - passed=0; \ - failed_tests=""; \ - for i in $(TESTS_ACTUAL); do \ - echo "======== $$i start ======== "; \ - if $(DEBUG_FLAGS) $(RUNTIME) $(TEST_RUNTIME_ARGS) --interp=-inline $$i ; \ - then \ - passed=`expr $${passed} + 1`; \ - echo "======== $$i end (passed) ======== "; \ - else \ - failed=`expr $${failed} + 1`; \ - failed_tests="$${failed_tests} $$i"; \ - echo "======== $$i end (failed) ======== "; \ - fi; \ - done; \ - echo "$${passed} test(s) passed. $${failed} test(s) did not pass."; \ - exit $${failed} - -EXTRA_DIST+= EncHelper.cs - -.PHONY: run-demo1 run-demo2 - -run-demo1: UserStringSwap.dll - $(DEBUG_FLAGS) $(RUNTIME) $(TEST_RUNTIME_ARGS) --interp=-inline $< - -run-demo2: ReplaceMethodOften.dll - $(DEBUG_FLAGS) $(RUNTIME) $(TEST_RUNTIME_ARGS) --interp=-inline $< - -EncHelper.dll: EncHelper.cs - $(MCS) -target:library -out:$@ $< - -%.dll: %.cs EncHelper.dll - bash -x -c "$(ROSLYNILDIFF_RUN) $(ROSLYNILDIFF_ARGS) -l:EncHelper.dll $< $(basename $<)_v?.cs" - -CLEANFILES += *.dll.*.dil *.dll.*.dmeta *.dll.*.dpdb - -all-local: - true - -# TODO: run the tests on CI -check-local: - true diff --git a/src/mono/mono/tests/enc/OnStackMethod.cs b/src/mono/mono/tests/enc/OnStackMethod.cs deleted file mode 100644 index 5a84b4cb173d1..0000000000000 --- a/src/mono/mono/tests/enc/OnStackMethod.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class OnStackMethod { - public static EncHelper replacer = null; - public static Assembly assm = null; - public static int state = 0; - - public static int Main (string []args) { - assm = typeof (OnStackMethod).Assembly; - replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (); - if (res != 1) - return 1; - - res = DiffTestMethod1 (); - if (res != 2) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 () { - Console.WriteLine ("Hello old World"); - DoTheUpdate (); - Console.WriteLine ("Hello old World #2"); - Console.WriteLine ("Hello old World #3"); - return 1; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void DoTheUpdate () { - if (state == 0) { - replacer.Update (assm); - state++; - } - } -} - diff --git a/src/mono/mono/tests/enc/OnStackMethodThread.cs b/src/mono/mono/tests/enc/OnStackMethodThread.cs deleted file mode 100644 index ea6bd45cfc451..0000000000000 --- a/src/mono/mono/tests/enc/OnStackMethodThread.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading; -using MonoEnc; - -public class OnStackMethod { - public static EncHelper replacer = null; - public static Assembly assm = null; - public static int state = 0; - - public static int Main (string []args) { - assm = typeof (OnStackMethod).Assembly; - replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (); - if (res != 1) - return 1; - - res = DiffTestMethod1 (); - if (res != 2) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 () { - Console.WriteLine ("Hello old World"); - DoTheUpdate (); - Console.WriteLine ("Hello old World #2"); - Console.WriteLine ("Hello old World #3"); - return 1; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Wrapper () { - int ret = DiffTestMethod1 (); - if (ret != 2) - Environment.Exit (5); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void DoTheUpdate () { - if (state == 0) { - replacer.Update (assm); - state++; - - Thread thread = new Thread(new ThreadStart(Wrapper)); - thread.Start(); - thread.Join (); - } - } -} - diff --git a/src/mono/mono/tests/enc/OnStackMethodThreadThread.cs b/src/mono/mono/tests/enc/OnStackMethodThreadThread.cs deleted file mode 100644 index 30c23d490a2f5..0000000000000 --- a/src/mono/mono/tests/enc/OnStackMethodThreadThread.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading; -using MonoEnc; - -public class OnStackMethodThreadThread { - public static EncHelper replacer = null; - public static Assembly assm = null; - public static int state = 0; - public static Semaphore sem = null; - - public static int Main (string []args) { - assm = typeof (OnStackMethodThreadThread).Assembly; - replacer = EncHelper.Make (); - sem = new Semaphore (0, 1); - - int res = DiffTestMethod1 (0); - if (res != 1) - return 1; - - res = DiffTestMethod1 (0); - if (res != 2) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int worker) { - if (worker == 1) { - Console.WriteLine ("Hello from Wrapper1"); - sem.WaitOne (); - Console.WriteLine ("Hello from Wrapper1: Done waiting"); - return 0x10001; - } else if (worker == 2) { - throw new Exception ("should not happen"); - } else { - Console.WriteLine ("Hello old World"); - DoTheUpdate (); - Console.WriteLine ("Hello old World #2"); - Console.WriteLine ("Hello old World #3"); - return 1; - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Wrapper1 () { - int ret = DiffTestMethod1 (1); - if (ret != 0x10001) - Environment.Exit (5); - ret = DiffTestMethod1 (1); - if (ret != 0x10002) - Environment.Exit (6); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Wrapper2 () { - int ret = DiffTestMethod1 (2); - if (ret != 0x20002) - Environment.Exit (7); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void DoTheUpdate () { - if (state == 0) { - Thread thread1 = new Thread(new ThreadStart(Wrapper1)); - thread1.Start(); - - replacer.Update (assm); - state++; - - Thread thread2 = new Thread(new ThreadStart(Wrapper2)); - thread2.Start(); - thread2.Join (); - - sem.Release (1); - thread1.Join (); - } - } -} - diff --git a/src/mono/mono/tests/enc/OnStackMethodThreadThread_v1.cs b/src/mono/mono/tests/enc/OnStackMethodThreadThread_v1.cs deleted file mode 100644 index c3e74872a7b6a..0000000000000 --- a/src/mono/mono/tests/enc/OnStackMethodThreadThread_v1.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading; -using MonoEnc; - -public class OnStackMethodThreadThread { - public static EncHelper replacer = null; - public static Assembly assm = null; - public static int state = 0; - public static Semaphore sem = null; - - public static int Main (string []args) { - assm = typeof (OnStackMethodThreadThread).Assembly; - replacer = EncHelper.Make (); - sem = new Semaphore (0, 1); - - int res = DiffTestMethod1 (0); - if (res != 1) - return 1; - - res = DiffTestMethod1 (0); - if (res != 2) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int worker) { - if (worker == 1) { - Console.WriteLine ("Hello from Wrapper1: NEW"); - return 0x10002; - } else if (worker == 2) { - return 0x20002; - } else { - Console.WriteLine ("Hello NEW World"); - return 2; - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Wrapper1 () { - int ret = DiffTestMethod1 (1); - if (ret != 0x10001) - Environment.Exit (5); - ret = DiffTestMethod1 (1); - if (ret != 0x10002) - Environment.Exit (6); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Wrapper2 () { - int ret = DiffTestMethod1 (2); - if (ret != 0x20002) - Environment.Exit (7); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void DoTheUpdate () { - if (state == 0) { - Thread thread1 = new Thread(new ThreadStart(Wrapper1)); - thread1.Start(); - - replacer.Update (assm); - state++; - - Thread thread2 = new Thread(new ThreadStart(Wrapper2)); - thread2.Start(); - thread2.Join (); - - sem.Release (1); - thread1.Join (); - } - } -} - diff --git a/src/mono/mono/tests/enc/OnStackMethodThread_v1.cs b/src/mono/mono/tests/enc/OnStackMethodThread_v1.cs deleted file mode 100644 index 6db05b2576275..0000000000000 --- a/src/mono/mono/tests/enc/OnStackMethodThread_v1.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading; -using MonoEnc; - -public class OnStackMethod { - public static EncHelper replacer = null; - public static Assembly assm = null; - public static int state = 0; - - public static int Main (string []args) { - assm = typeof (OnStackMethod).Assembly; - replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (); - if (res != 1) - return 1; - - res = DiffTestMethod1 (); - if (res != 2) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 () { - Console.WriteLine ("Hello NEW World"); - return 2; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void Wrapper () { - int ret = DiffTestMethod1 (); - if (ret != 2) - Environment.Exit (5); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void DoTheUpdate () { - if (state == 0) { - replacer.Update (assm); - state++; - - Thread thread = new Thread(new ThreadStart(Wrapper)); - thread.Start(); - thread.Join (); - } - } -} - diff --git a/src/mono/mono/tests/enc/OnStackMethod_v1.cs b/src/mono/mono/tests/enc/OnStackMethod_v1.cs deleted file mode 100644 index 1314745f18da3..0000000000000 --- a/src/mono/mono/tests/enc/OnStackMethod_v1.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class OnStackMethod { - public static EncHelper replacer = null; - public static Assembly assm = null; - public static int state = 0; - - public static int Main (string []args) { - assm = typeof (OnStackMethod).Assembly; - replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (); - if (res != 1) - return 1; - - res = DiffTestMethod1 (); - if (res != 2) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 () { - Console.WriteLine ("Hello new World"); - return 2; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static void DoTheUpdate () { - if (state == 0) { - replacer.Update (assm); - state++; - } - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethod.cs b/src/mono/mono/tests/enc/ReplaceMethod.cs deleted file mode 100644 index 8855af301bdd2..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethod.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != (3 + 1)) - return 1; - - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != (8 + 2)) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - Console.WriteLine ("Hello old World"); - return x + SecondMethod (); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int SecondMethod () { - Console.WriteLine ("Print something old-worldish"); - return 1; - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften.cs b/src/mono/mono/tests/enc/ReplaceMethodOften.cs deleted file mode 100644 index 0cbc382b447d2..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethodOften.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != 0) - return 0; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 1) - return 1; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 2) - return 2; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 3) - return 3; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 4) - return 4; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 5) - return 5; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 6) - return 6; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - // Console.WriteLine ("Version 0"); - return 0; - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v1.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v1.cs deleted file mode 100644 index 806ff3cc052c8..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethodOften_v1.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != 0) - return 0; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 1) - return 1; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 2) - return 2; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 3) - return 3; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 4) - return 4; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 5) - return 5; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 6) - return 6; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - // Console.WriteLine ("Version 1"); - return 1; - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v2.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v2.cs deleted file mode 100644 index 9dca89f345b77..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethodOften_v2.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != 0) - return 0; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 1) - return 1; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 2) - return 2; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 3) - return 3; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 4) - return 4; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 5) - return 5; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 6) - return 6; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - // Console.WriteLine ("Version 2"); - return 2; - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v3.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v3.cs deleted file mode 100644 index 0fb7525aaf585..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethodOften_v3.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != 0) - return 0; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 1) - return 1; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 2) - return 2; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 3) - return 3; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 4) - return 4; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 5) - return 5; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 6) - return 6; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - // Console.WriteLine ("Version 3"); - return 3; - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v4.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v4.cs deleted file mode 100644 index 3f38bc6bfd478..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethodOften_v4.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != 0) - return 0; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 1) - return 1; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 2) - return 2; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 3) - return 3; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 4) - return 4; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 5) - return 5; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 6) - return 6; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - // Console.WriteLine ("Version 4"); - return 4; - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v5.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v5.cs deleted file mode 100644 index 4223f73d9dc7d..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethodOften_v5.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != 0) - return 0; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 1) - return 1; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 2) - return 2; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 3) - return 3; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 4) - return 4; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 5) - return 5; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 6) - return 6; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - // Console.WriteLine ("Version 5"); - return 5; - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethodOften_v6.cs b/src/mono/mono/tests/enc/ReplaceMethodOften_v6.cs deleted file mode 100644 index 3ebc4bc7c7139..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethodOften_v6.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != 0) - return 0; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 1) - return 1; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 2) - return 2; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 3) - return 3; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 4) - return 4; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 5) - return 5; - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != 6) - return 6; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - // Console.WriteLine ("Version 6"); - return 6; - } -} - diff --git a/src/mono/mono/tests/enc/ReplaceMethod_v1.cs b/src/mono/mono/tests/enc/ReplaceMethod_v1.cs deleted file mode 100644 index 7f3ab94ad5aa4..0000000000000 --- a/src/mono/mono/tests/enc/ReplaceMethod_v1.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class Sample3 { - public static int Main (string []args) { - Assembly assm = typeof (Sample3).Assembly; - var replacer = EncHelper.Make (); - - int res = DiffTestMethod1 (3, 8); - if (res != (3 + 1)) - return 1; - - replacer.Update (assm); - - res = DiffTestMethod1 (3, 8); - if (res != (8 + 2)) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int DiffTestMethod1 (int x, int y) { - return y + SecondMethod ();; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int SecondMethod () { - Console.WriteLine ("HELLO NEW WORLD"); - return 2; - } -} diff --git a/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod.cs b/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod.cs deleted file mode 100644 index 6c8946bbc1605..0000000000000 --- a/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -class Container { - private Thickness _margin; - - public Thickness Margin - { - get { return _margin; } - set { _margin = value; } - } -} - -class Thickness { - public int val; - - public Thickness (int val) - { - this.val = val; - } -} - -public class Sample { - private Container listView; - - public static int Main (string []args) { - Assembly assm = typeof (Sample).Assembly; - var replacer = EncHelper.Make (); - - Sample s = new Sample (); - s.listView = new Container (); - - s.OnItemSelected (null, null); - if (s.listView.Margin.val != 30) - return 1; - - replacer.Update (assm); - - s.OnItemSelected (null, null); - if (s.listView.Margin.val != 40) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void OnItemSelected (object sender, object s) - { - listView.Margin = new Thickness (30); - } -} - diff --git a/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod_v1.cs b/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod_v1.cs deleted file mode 100644 index ed40ab374c1e7..0000000000000 --- a/src/mono/mono/tests/enc/ReplacePrivateVirtualMethod_v1.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -class Container { - private Thickness _margin; - - public Thickness Margin - { - get { return _margin; } - set { _margin = value; } - } -} - -class Thickness { - public int val; - - public Thickness (int val) - { - this.val = val; - } -} - -public class Sample { - private Container listView; - - public static int Main (string []args) { - Assembly assm = typeof (Sample).Assembly; - var replacer = EncHelper.Make (); - - Sample s = new Sample (); - s.listView = new Container (); - - s.OnItemSelected (null, null); - if (s.listView.Margin.val != 30) - return 1; - - replacer.Update (assm); - - s.OnItemSelected (null, null); - if (s.listView.Margin.val != 40) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void OnItemSelected (object sender, object s) - { - listView.Margin = new Thickness (40); - } -} - diff --git a/src/mono/mono/tests/enc/TypeTokenSwap.cs b/src/mono/mono/tests/enc/TypeTokenSwap.cs deleted file mode 100644 index 8e6d75555536e..0000000000000 --- a/src/mono/mono/tests/enc/TypeTokenSwap.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class TypeTokenSwap { - public static int Main (string []args) { - Assembly assm = typeof (TypeTokenSwap).Assembly; - var replacer = EncHelper.Make (); - - var res = TargetMethod (); - if (res != typeof (string)) - return 1; - - replacer.Update (assm); - - res = TargetMethod (); - if (res != typeof (int)) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Type TargetMethod () { - return typeof (string); - } -} - diff --git a/src/mono/mono/tests/enc/TypeTokenSwap_v1.cs b/src/mono/mono/tests/enc/TypeTokenSwap_v1.cs deleted file mode 100644 index 969bb89ae0a9c..0000000000000 --- a/src/mono/mono/tests/enc/TypeTokenSwap_v1.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class TypeTokenSwap { - public static int Main (string []args) { - Assembly assm = typeof (TypeTokenSwap).Assembly; - var replacer = EncHelper.Make (); - - var res = TargetMethod (); - if (res != typeof (string)) - return 1; - - replacer.Update (assm); - - res = TargetMethod (); - if (res != typeof (int)) - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static Type TargetMethod () { - return typeof (int); - } -} - diff --git a/src/mono/mono/tests/enc/UserStringSwap.cs b/src/mono/mono/tests/enc/UserStringSwap.cs deleted file mode 100644 index dcb29e31477a7..0000000000000 --- a/src/mono/mono/tests/enc/UserStringSwap.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class UserStringSwap { - public static int Main (string []args) { - Assembly assm = typeof (UserStringSwap).Assembly; - var replacer = EncHelper.Make (); - - string res = TargetMethod (); - if (res != "OLD STRING") - return 1; - - replacer.Update (assm); - - res = TargetMethod (); - if (res != "NEW STRING") - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static string TargetMethod () { - string s = "OLD STRING"; - Console.WriteLine (s); - return s; - } -} - diff --git a/src/mono/mono/tests/enc/UserStringSwap_v1.cs b/src/mono/mono/tests/enc/UserStringSwap_v1.cs deleted file mode 100644 index 88d4a683354d4..0000000000000 --- a/src/mono/mono/tests/enc/UserStringSwap_v1.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoEnc; - -public class UserStringSwap { - public static int Main (string []args) { - Assembly assm = typeof (UserStringSwap).Assembly; - var replacer = EncHelper.Make (); - - string res = TargetMethod (); - if (res != "OLD STRING") - return 1; - - replacer.Update (assm); - - res = TargetMethod (); - if (res != "NEW STRING") - return 2; - - return 0; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static string TargetMethod () { - string s = "NEW STRING"; - Console.WriteLine (s); - return s; - } -} - From 122e8bdb1215515648acfb3e7fa43c8b4bc5b001 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 9 Dec 2020 14:28:36 -0500 Subject: [PATCH 17/31] [metadata-update] Add per-thread exposed generation A thread has to voluntarily roll up to the latest published generation in order to see updates. - Roll up to the latest published generation when attaching a thread - The updater thread sees the allocated unpublished generation --- src/mono/mono/metadata/metadata-update.c | 53 +++++++++++++++++++++++- src/mono/mono/metadata/metadata-update.h | 10 +++++ src/mono/mono/metadata/metadata.c | 11 ++++- src/mono/mono/metadata/threads.c | 5 +++ 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index e3e38dc0bd317..61dcd146a9b33 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -23,6 +23,9 @@ #include "mono/utils/mono-logger-internals.h" #include "mono/utils/mono-path.h" +/* TLS value is a uint32_t of the latest published generation that the thread can see */ +static MonoNativeTlsKey exposed_generation_id; + #if 1 #define UPDATE_DEBUG(stmt) do { stmt; } while (0) #else @@ -118,6 +121,13 @@ void mono_metadata_update_init (void) { table_to_image_init (); + mono_native_tls_alloc (&exposed_generation_id, NULL); +} + +void +mono_metadata_update_cleanup (void) +{ + mono_native_tls_free (exposed_generation_id); } /* Inform the execution engine that updates are coming */ @@ -164,6 +174,12 @@ initialize (void) mono_coop_mutex_init (&publish_mutex); } +static void +thread_set_exposed_generation (uint32_t value) +{ + mono_native_tls_set_value (exposed_generation_id, GUINT_TO_POINTER((guint)value)); +} + uint32_t mono_metadata_update_prepare (MonoDomain *domain) { mono_lazy_initialize (&metadata_update_lazy_init, initialize); @@ -171,7 +187,10 @@ mono_metadata_update_prepare (MonoDomain *domain) { * TODO: assert that the updater isn't depending on current metadata, else publishing might block. */ publish_lock (); - return ++update_alloc_frontier; + uint32_t alloc_gen = ++update_alloc_frontier; + /* Expose the alloc frontier to the updater thread */ + thread_set_exposed_generation (alloc_gen); + return alloc_gen; } gboolean @@ -179,6 +198,35 @@ mono_metadata_update_available (void) { return update_published < update_alloc_frontier; } +/** + * mono_metadata_update_thread_expose_published: + * + * Allow the current thread to see the latest published deltas. + * + * Returns the current published generation that the thread will see. + */ +uint32_t +mono_metadata_update_thread_expose_published (void) +{ + mono_memory_read_barrier (); + uint32_t thread_current_gen = update_published; + thread_set_exposed_generation (thread_current_gen); + return thread_current_gen; +} + +/** + * mono_metadata_update_get_thread_generation: + * + * Return the published generation that the current thread is allowed to see. + * May be behind the latest published generation if the thread hasn't called + * \c mono_metadata_update_thread_expose_published in a while. + */ +uint32_t +mono_metadata_update_get_thread_generation (void) +{ + return (uint32_t)GPOINTER_TO_UINT(mono_native_tls_get_value(exposed_generation_id)); +} + gboolean mono_metadata_wait_for_update (uint32_t timeout_ms) { @@ -192,6 +240,7 @@ mono_metadata_update_publish (MonoDomain *domain, MonoAssemblyLoadContext *alc, /* TODO: wait for all threads that are using old metadata to update. */ mono_metadata_update_invoke_hook (domain, alc, generation); update_published = update_alloc_frontier; + mono_memory_write_barrier (); publish_unlock (); } @@ -202,6 +251,8 @@ mono_metadata_update_cancel (uint32_t generation) g_assert (update_alloc_frontier > 0); g_assert (update_alloc_frontier - 1 >= update_published); --update_alloc_frontier; + /* Roll back exposed generation to the last published one */ + thread_set_exposed_generation (update_published); publish_unlock (); } diff --git a/src/mono/mono/metadata/metadata-update.h b/src/mono/mono/metadata/metadata-update.h index 6bfef02c216ca..035d322ed1454 100644 --- a/src/mono/mono/metadata/metadata-update.h +++ b/src/mono/mono/metadata/metadata-update.h @@ -14,9 +14,18 @@ void mono_metadata_update_init (void); +void +mono_metadata_update_cleanup (void); + gboolean mono_metadata_update_available (void); +uint32_t +mono_metadata_update_thread_expose_published (void); + +uint32_t +mono_metadata_update_get_thread_generation (void); + gboolean mono_metadata_wait_for_update (uint32_t timeout_ms); @@ -35,6 +44,7 @@ mono_metadata_update_cleanup_on_close (MonoImage *base_image); MonoImage * mono_table_info_get_base_image (const MonoTableInfo *t); + #endif /* ENABLE_METADATA_UPDATE */ #endif /*__MONO_METADATA_UPDATE_H__*/ diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index 4a040b135a627..bb91b8ead4e93 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -1003,11 +1003,13 @@ mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_i MonoTableInfo *table; int ridx; - /* FIXME: Only lookup upto the latest published image, not all images. */ + uint32_t exposed_gen = mono_metadata_update_get_thread_generation (); do { if (!list) return TRUE; dmeta = list->data; + if (dmeta->generation > exposed_gen) + return TRUE; list = list->next; table = &dmeta->tables [table_index]; ridx = mono_image_relative_delta_index (dmeta, mono_metadata_make_token (table_index, token_index + 1)) - 1; @@ -1109,11 +1111,15 @@ mono_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, guint32 prev_size = heap->size; + uint32_t current_gen = mono_metadata_update_get_thread_generation (); GSList *cur; for (cur = base_image->delta_image; cur; cur = cur->next) { *image_out = (MonoImage*)cur->data; heap = get_heap (*image_out); + if ((*image_out)->generation > current_gen) + return FALSE; + /* FIXME: for non-minimal deltas we should just look in the last published image. */ if (G_LIKELY ((*image_out)->minimal_delta)) *index_out -= prev_size; @@ -2010,6 +2016,9 @@ mono_metadata_init (void) void mono_metadata_cleanup (void) { +#ifdef ENABLE_METADATA_UPDATE + mono_metadata_update_cleanup (); +#endif g_hash_table_destroy (type_cache); type_cache = NULL; g_ptr_array_free (image_sets, TRUE); diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c index ad2e3485fa676..63a99cdff83d0 100644 --- a/src/mono/mono/metadata/threads.c +++ b/src/mono/mono/metadata/threads.c @@ -938,6 +938,11 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean set_current_thread_for_domain (domain, internal, thread); +#ifdef MONO_METADATA_UPDATE + /* Roll up to the latest published metadata generation */ + mono_metadata_update_thread_expose_published (); +#endif + THREAD_DEBUG (g_message ("%s: Attached thread ID %" G_GSIZE_FORMAT " (handle %p)", __func__, internal->tid, internal->handle)); return TRUE; From d98a6fac9a7c39f7dd321400daa56e4e24ad3351 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 15 Dec 2020 13:52:07 -0500 Subject: [PATCH 18/31] [mbr] Fixup console sample Use a single changing testfile --- .../sample/mbr/console/ConsoleDelta.csproj | 5 +- src/mono/netcore/sample/mbr/console/Makefile | 6 +-- .../netcore/sample/mbr/console/Program.cs | 8 ---- .../netcore/sample/mbr/console/Program_v1.cs | 47 ------------------- .../netcore/sample/mbr/console/TestClass.cs | 11 +++++ .../sample/mbr/console/TestClass_v1.cs | 11 +++++ 6 files changed, 28 insertions(+), 60 deletions(-) delete mode 100644 src/mono/netcore/sample/mbr/console/Program_v1.cs create mode 100644 src/mono/netcore/sample/mbr/console/TestClass.cs create mode 100644 src/mono/netcore/sample/mbr/console/TestClass_v1.cs diff --git a/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj b/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj index b02b01ebe317a..0eac4c73c27c8 100644 --- a/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj +++ b/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj @@ -12,12 +12,13 @@ + - - Program_v1.cs + + TestClass_v1.cs diff --git a/src/mono/netcore/sample/mbr/console/Makefile b/src/mono/netcore/sample/mbr/console/Makefile index 90a96b77eb96c..359522a516392 100644 --- a/src/mono/netcore/sample/mbr/console/Makefile +++ b/src/mono/netcore/sample/mbr/console/Makefile @@ -2,7 +2,7 @@ TOP=../../../../../../ DOTNET:=$(TOP)./dotnet.sh DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary -MONO_CONFIG ?=Release +CONFIG ?=Release MONO_ARCH=x64 OS := $(shell uname -s) @@ -15,12 +15,12 @@ endif MONO_ENV_OPTIONS ?=--interp=-inline publish: - $(DOTNET) publish -c $(MONO_CONFIG) -r $(TARGET_OS)-$(MONO_ARCH) + $(DOTNET) publish -c $(CONFIG) -r $(TARGET_OS)-$(MONO_ARCH) run: publish COMPlus_DebugWriteToStdErr=1 \ MONO_ENV_OPTIONS="$(MONO_ENV_OPTIONS)" \ - $(TOP)artifacts/bin/HelloWorld/$(MONO_ARCH)/$(MONO_CONFIG)/$(TARGET_OS)-$(MONO_ARCH)/publish/HelloWorld + $(TOP)artifacts/bin/ConsoleDelta/$(MONO_ARCH)/$(CONFIG)/$(TARGET_OS)-$(MONO_ARCH)/publish/ConsoleDelta clean: rm -rf $(TOP)artifacts/bin/HelloWorld/ diff --git a/src/mono/netcore/sample/mbr/console/Program.cs b/src/mono/netcore/sample/mbr/console/Program.cs index c49288c1e719c..1e71f677ec4b3 100644 --- a/src/mono/netcore/sample/mbr/console/Program.cs +++ b/src/mono/netcore/sample/mbr/console/Program.cs @@ -37,11 +37,3 @@ private static int Main(string[] args) } } -public class TestClass { - [MethodImpl(MethodImplOptions.NoInlining)] - public static string TargetMethod () { - string s = "OLD STRING"; - Console.WriteLine (s); - return s; - } -} diff --git a/src/mono/netcore/sample/mbr/console/Program_v1.cs b/src/mono/netcore/sample/mbr/console/Program_v1.cs deleted file mode 100644 index bbe63745079c1..0000000000000 --- a/src/mono/netcore/sample/mbr/console/Program_v1.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using MonoDelta; - -namespace HelloWorld -{ - internal class Program - { - private static int Main(string[] args) - { - bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null; - Console.WriteLine($"Hello World {(isMono ? "from Mono!" : "from CoreCLR!")}"); - Console.WriteLine(typeof(object).Assembly.FullName); - Console.WriteLine(System.Reflection.Assembly.GetEntryAssembly ()); - Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); - - Assembly assm = typeof (TestClass).Assembly; - var replacer = DeltaHelper.Make (); - - string res = TestClass.TargetMethod (); - if (res != "OLD STRING") - return 1; - - replacer.Update (assm); - - res = TestClass.TargetMethod (); - if (res != "NEW STRING") - return 2; - - return 0; - } - - } -} - -public class TestClass { - [MethodImpl(MethodImplOptions.NoInlining)] - public static string TargetMethod () { - string s = "NEW STRING"; - Console.WriteLine (s); - return s; - } -} diff --git a/src/mono/netcore/sample/mbr/console/TestClass.cs b/src/mono/netcore/sample/mbr/console/TestClass.cs new file mode 100644 index 0000000000000..c18db33b47257 --- /dev/null +++ b/src/mono/netcore/sample/mbr/console/TestClass.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.CompilerServices; + +public class TestClass { + [MethodImpl(MethodImplOptions.NoInlining)] + public static string TargetMethod () { + string s = "OLD STRING"; + Console.WriteLine (s); + return s; + } +} diff --git a/src/mono/netcore/sample/mbr/console/TestClass_v1.cs b/src/mono/netcore/sample/mbr/console/TestClass_v1.cs new file mode 100644 index 0000000000000..a41793c3f7dcc --- /dev/null +++ b/src/mono/netcore/sample/mbr/console/TestClass_v1.cs @@ -0,0 +1,11 @@ +using System; +using System.Runtime.CompilerServices; + +public class TestClass { + [MethodImpl(MethodImplOptions.NoInlining)] + public static string TargetMethod () { + string s = "NEW STRING"; + Console.WriteLine (s); + return s; + } +} From a2a252e7304d040841421e038754e6822c5c13a7 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 15 Dec 2020 15:38:44 -0500 Subject: [PATCH 19/31] [metadata-update] delete unused method --- src/mono/mono/metadata/metadata-update.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 61dcd146a9b33..1e751cc4739b6 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -678,17 +678,6 @@ apply_enclog_pass2 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer return TRUE; } -static void -append_heap (MonoStreamHeader *base, MonoStreamHeader *appendix) -{ - int size = base->size + appendix->size; - char *data = (char *) g_malloc (size * sizeof (char)); - memcpy (data, base->data, base->size); - memcpy (data + base->size, appendix->data, appendix->size); - base->data = data; - base->size = size; -} - /** * * LOCKING: Takes the publish_lock From 44e165f3a573318ccd1abce96eed43221032b388 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 15 Dec 2020 15:39:18 -0500 Subject: [PATCH 20/31] [mbr] Use 2 threads in console sample --- .../netcore/sample/mbr/console/Program.cs | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/mono/netcore/sample/mbr/console/Program.cs b/src/mono/netcore/sample/mbr/console/Program.cs index 1e71f677ec4b3..70bc11bb8c8f6 100644 --- a/src/mono/netcore/sample/mbr/console/Program.cs +++ b/src/mono/netcore/sample/mbr/console/Program.cs @@ -4,12 +4,38 @@ using System; using System.Reflection; using System.Runtime.CompilerServices; +using System.Threading; using MonoDelta; namespace HelloWorld { internal class Program { + class State { + public readonly ManualResetEventSlim mreIn; + public readonly ManualResetEventSlim mreOut; + public string res; + public State() { + mreIn = new ManualResetEventSlim (); + mreOut = new ManualResetEventSlim (); + res = ""; + } + + public string ConsumerStep () { + mreIn.Set (); + mreOut.Wait (); + mreOut.Reset (); + return res; + } + + public void ProducerStep (Func step) { + mreIn.Wait (); + mreIn.Reset (); + res = step (); + mreOut.Set (); + } + } + private static int Main(string[] args) { bool isMono = typeof(object).Assembly.GetType("Mono.RuntimeStructs") != null; @@ -21,19 +47,31 @@ private static int Main(string[] args) Assembly assm = typeof (TestClass).Assembly; var replacer = DeltaHelper.Make (); - string res = TestClass.TargetMethod (); + var st = new State (); + var t = new Thread (MutatorThread); + t.Start (st); + + string res = st.ConsumerStep (); if (res != "OLD STRING") return 1; replacer.Update (assm); - res = TestClass.TargetMethod (); + res = st.ConsumerStep (); if (res != "NEW STRING") return 2; return 0; } + private static void MutatorThread (object o) + { + var st = (State)o; + static string Step () => TestClass.TargetMethod (); + st.ProducerStep (Step); + st.ProducerStep (Step); + } + } } From 687516f88cad88965767ea3c555bddaba6392bf9 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 15 Dec 2020 15:40:13 -0500 Subject: [PATCH 21/31] [metadata-update] Respect exposed generation in MethdDef RVA lookups --- src/mono/mono/metadata/loader.c | 28 ++++++++++++++++++-- src/mono/mono/metadata/metadata-internals.h | 9 +++++-- src/mono/mono/metadata/metadata-update.c | 29 +++++++++++++++++---- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index b90b65886164f..8684a2b04c2c1 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -2017,6 +2018,26 @@ mono_method_has_no_body (MonoMethod *method) (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)); } +#ifdef ENABLE_METADATA_UPDATE +static gpointer +get_method_update_rva (MonoImage *image_base, uint32_t idx) +{ + gpointer loc = NULL; + uint32_t cur = mono_metadata_update_get_thread_generation (); + GSList *ptr = image_base->delta_image; + /* Go through all the updates that the current thread can see and see + * if they updated the method. Keep the latest visible update */ + for (; ptr != NULL; ptr = ptr->next) { + MonoImage *image_delta = (MonoImage*) ptr->data; + if (image_delta->generation > cur) + break; + if (image_delta->method_table_update) + loc = g_hash_table_lookup (image_delta->method_table_update, GUINT_TO_POINTER (idx)); + } + return loc; +} +#endif + // FIXME Replace all internal callers of mono_method_get_header_checked with // mono_method_get_header_internal; the difference is in error initialization. MonoMethodHeader* @@ -2073,9 +2094,12 @@ mono_method_get_header_internal (MonoMethod *method, MonoError *error) #ifdef ENABLE_METADATA_UPDATE /* EnC case */ - if (G_UNLIKELY (img->method_table_delta_index)) { + if (G_UNLIKELY (img->method_table_update)) { /* pre-computed rva pointer into delta IL image */ - loc = g_hash_table_lookup (img->method_table_delta_index, GUINT_TO_POINTER (idx)); + uint32_t gen = GPOINTER_TO_UINT (g_hash_table_lookup (img->method_table_update, GUINT_TO_POINTER (idx))); + if (G_UNLIKELY (gen > 0)) { + loc = get_method_update_rva (img, idx); + } } #endif diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 8c15209126824..eeb6b0351ea57 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -588,8 +588,6 @@ struct _MonoImage { GHashTable *weak_field_indexes; #ifdef ENABLE_METADATA_UPDATE - GHashTable *method_table_delta_index; /* EnC index for method updates */ - /* List of MonoImages of deltas. Parent image owns 1 refcount ref of the delta image */ GSList *delta_image; /* Tail of delta_image for fast appends */ @@ -597,6 +595,13 @@ struct _MonoImage { /* Metadata delta images only */ uint32_t generation; + + /* Maps MethodDef token indices to something. In base images a boolean + * flag that there's an update for the method; in delta images a + * pointer into the RVA of the delta IL */ + GHashTable *method_table_update; + + #endif /* diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 1e751cc4739b6..58ccb8550e1fa 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -589,9 +589,17 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer return TRUE; } +static void +set_update_method (MonoImage *image_base, uint32_t generation, MonoImage *image_dmeta, uint32_t token_index, const char* il_address) +{ + /* FIXME: this is a race if other threads are doing a lookup. */ + g_hash_table_insert (image_base->method_table_update, GUINT_TO_POINTER (token_index), GUINT_TO_POINTER (generation)); + g_hash_table_insert (image_dmeta->method_table_update, GUINT_TO_POINTER (token_index), (gpointer) il_address); +} + /* do actuall enclog application */ static gboolean -apply_enclog_pass2 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer dil_data, uint32_t dil_length, MonoError *error) +apply_enclog_pass2 (MonoImage *image_base, uint32_t generation, MonoImage *image_dmeta, gconstpointer dil_data, uint32_t dil_length, MonoError *error) { MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG]; int rows = table_enclog->rows; @@ -651,14 +659,25 @@ apply_enclog_pass2 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer g_error ("EnC: new method added, should be caught by pass1"); } - if (!image_base->method_table_delta_index) - image_base->method_table_delta_index = g_hash_table_new (g_direct_hash, g_direct_equal); + if (!image_base->method_table_update) + image_base->method_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); + if (!image_dmeta->method_table_update) + image_dmeta->method_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); int mapped_token = mono_image_relative_delta_index (image_dmeta, mono_metadata_make_token (token_table, token_index)); int rva = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_METHOD], mapped_token - 1, MONO_METHOD_RVA); if (rva < dil_length) { + /* + * FIXME: this mutates all threads' views.x Need + * a pair of a delta image and a rva. + * + * base: token->generation + * delta: token->rva ? + * + * if multiple updates, go through all deltas in order + */ char *il_address = ((char *) dil_data) + rva; - g_hash_table_insert (image_base->method_table_delta_index, GUINT_TO_POINTER (token_index), (gpointer) il_address); + set_update_method (image_base, generation, image_dmeta, token_index, il_address); } else { /* rva points probably into image_base IL stream. can this ever happen? */ g_print ("TODO: this case is still a bit WTF. token=0x%08x with rva=0x%04x\n", log_token, rva); @@ -757,7 +776,7 @@ mono_image_load_enc_delta (MonoDomain *domain, MonoImage *image_base, gconstpoin if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) dump_update_summary (image_base, image_dmeta); - if (!apply_enclog_pass2 (image_base, image_dmeta, dil_bytes, dil_length, error)) { + if (!apply_enclog_pass2 (image_base, generation, image_dmeta, dil_bytes, dil_length, error)) { mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error applying delta image to base=%s, due to: %s", basename, mono_error_get_message (error)); mono_metadata_update_cancel (generation); return; From a7c10c228c84a2d2ab7ce6f25bf593173672dd49 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 15 Dec 2020 15:42:58 -0500 Subject: [PATCH 22/31] [interp] Expose latest metadata update before transforming methods --- src/mono/mono/mini/interp/transform.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index ae59aff6bbb47..123b8b71d36d5 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -8251,6 +8252,10 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon error_init (error); +#ifdef ENABLE_METADATA_UPDATE + mono_metadata_update_thread_expose_published (); +#endif + if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) { mono_error_set_invalid_operation (error, "%s", "Could not execute the method because the containing type is not fully instantiated."); return; From e629fb6937b9ba3904df6de9650419f5d7ac248f Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 16 Dec 2020 13:29:35 -0500 Subject: [PATCH 23/31] [mbr] Update samples after rebase Use the WasmApp.targets --- .../sample/mbr/DeltaHelper/DeltaHelper.csproj | 2 +- src/mono/netcore/sample/mbr/browser/Makefile | 8 ++- .../sample/mbr/browser/WasmDelta.csproj | 57 ++++++++----------- src/mono/netcore/sample/mbr/browser/server.py | 37 ------------ .../sample/mbr/console/ConsoleDelta.csproj | 2 +- 5 files changed, 32 insertions(+), 74 deletions(-) delete mode 100644 src/mono/netcore/sample/mbr/browser/server.py diff --git a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj index 5e537f96fc906..47bb08e4c66e9 100644 --- a/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj +++ b/src/mono/netcore/sample/mbr/DeltaHelper/DeltaHelper.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppCurrent) + $(NetCoreAppToolCurrent) false diff --git a/src/mono/netcore/sample/mbr/browser/Makefile b/src/mono/netcore/sample/mbr/browser/Makefile index 8fae501ffec30..e4c115d70a786 100644 --- a/src/mono/netcore/sample/mbr/browser/Makefile +++ b/src/mono/netcore/sample/mbr/browser/Makefile @@ -18,4 +18,10 @@ clean: rm -rf bin run: - cd bin/$(CONFIG)/publish && python3 server.py + if ! $(DOTNET) tool list --global | grep dotnet-serve; then \ + echo "The tool dotnet-serve could not be found. Install with: $(DOTNET) tool install --global dotnet-serve"; \ + exit 1; \ + else \ + $(DOTNET) serve -d bin/$(CONFIG)/publish -p 8000; \ + fi + diff --git a/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj b/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj index b7b0180fc8da5..7440308f883ed 100644 --- a/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj +++ b/src/mono/netcore/sample/mbr/browser/WasmDelta.csproj @@ -1,12 +1,12 @@ - + Exe bin false - $(NetCoreAppCurrent) + $(NetCoreAppToolCurrent) wasm Browser - $(ArtifactsBinDir)microsoft.netcore.app.runtime.browser-wasm\$(Configuration)\runtimes\browser-wasm\ + $(ArtifactsBinDir)microsoft.netcore.app.runtime.browser-wasm\$(Configuration)\runtimes\browser-wasm\ $(MSBuildThisFileDirectory)obj\$(Configuration)\wasm $(MSBuildThisFileDirectory)bin\$(Configuration)\publish @@ -15,43 +15,30 @@ - - - - - + + + $(AppDir) + bin\WasmDelta.dll + runtime.js - - + true + + + - + + + + + - + - - - \%(_DeltaFileForPublish.Filename)%(_DeltaFileForPublish.Extension) - - - - @@ -59,9 +46,6 @@ Always - - Always - @@ -70,6 +54,11 @@ + + + + + diff --git a/src/mono/netcore/sample/mbr/browser/server.py b/src/mono/netcore/sample/mbr/browser/server.py deleted file mode 100644 index ab1e3edd81517..0000000000000 --- a/src/mono/netcore/sample/mbr/browser/server.py +++ /dev/null @@ -1,37 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -import sys - -if sys.version_info[0] == 2: - - import SimpleHTTPServer - import SocketServer - - PORT = 8000 - - class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): - pass - - Handler.extensions_map['.wasm'] = 'application/wasm' - - httpd = SocketServer.TCPServer(("", PORT), Handler) - - print ("python 2 serving at port", PORT) - httpd.serve_forever() - - -if sys.version_info[0] == 3: - - import http.server - import socketserver - - PORT = 8000 - - Handler = http.server.SimpleHTTPRequestHandler - Handler.extensions_map['.wasm'] = 'application/wasm' - - with socketserver.TCPServer(("", PORT), Handler) as httpd: - print("python 3 serving at port", PORT) - httpd.serve_forever() - diff --git a/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj b/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj index 0eac4c73c27c8..d807eff508bdc 100644 --- a/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj +++ b/src/mono/netcore/sample/mbr/console/ConsoleDelta.csproj @@ -1,7 +1,7 @@ Exe - $(NetCoreAppCurrent) + $(NetCoreAppToolCurrent) false false From a0ab40e272ca1ca0be299e265b9caf6b0f7194c4 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 16 Dec 2020 14:44:48 -0500 Subject: [PATCH 24/31] [metadata-update] Don't fail after the first unsupported edit Log all the unsupported edits, then cancel the update --- src/mono/mono/metadata/metadata-update.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 58ccb8550e1fa..b765e94ee2de1 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -532,6 +532,8 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG]; int rows = table_enclog->rows; + gboolean unsupported_edits = FALSE; + /* hack: make a pass over it, looking only for table method updates, in * order to give more meaningful error messages first */ @@ -545,12 +547,16 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer int token_table = mono_metadata_token_table (log_token); int token_index = mono_metadata_token_index (log_token); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x (%s idx=0x%02x) (base table has 0x%04x rows)\tfunc=0x%02x\n", i, log_token, mono_meta_table_name (token_table), token_index, image_base->tables [token_table].rows, func_code); + + if (token_table != MONO_TABLE_METHOD) continue; if (token_index > image_base->tables [token_table].rows) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "\tcannot add new method with token 0x%08x", log_token); mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: cannot add new method with token 0x%08x", log_token); - return FALSE; + unsupported_edits = TRUE; } g_assert (func_code == 0); /* anything else doesn't make sense here */ @@ -572,21 +578,30 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer /* handled above */ } else { if (token_index <= image_base->tables [token_table].rows) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x we do not support patching of existing table cols.", i, log_token); mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing table cols. token=0x%08x", log_token); - return FALSE; + unsupported_edits = TRUE; + continue; } } + /* + * So the way a non-default func_code works is that it's attached to the EnCLog + * record preceeding the new member defintion (so e.g. an addMethod code will be on + * the preceeding MONO_TABLE_TYPEDEF enc record that identifies the parent type). + */ switch (func_code) { case 0: /* default */ break; default: + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x FunCode %d (%s) not supported (token=0x%08x)", i, log_token, func_code, funccode_to_str (func_code), log_token); mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: FuncCode %d (%s) not supported (token=0x%08x)", func_code, funccode_to_str (func_code), log_token); - return FALSE; + unsupported_edits = TRUE; + continue; } } - return TRUE; + return !unsupported_edits; } static void From a402aa1656be74013c9d05f31ac23b2238c32782 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 2 Nov 2020 14:19:48 -0500 Subject: [PATCH 25/31] [metadata_update] Keep track of logical table sizes for deltas Keep track of inserted/modified rows for each table in each delta. This will help to use a simpler algorithm to locate effective table rows by keeping track of the logical number of rows in the appended tables --- src/mono/mono/metadata/metadata-internals.h | 2 +- src/mono/mono/metadata/metadata-update.c | 233 +++++++++++++++++--- 2 files changed, 201 insertions(+), 34 deletions(-) diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index eeb6b0351ea57..13cdb36cb769d 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -594,7 +594,7 @@ struct _MonoImage { GSList *delta_image_last; /* Metadata delta images only */ - uint32_t generation; + uint32_t generation; /* global update ID that added this delta image */ /* Maps MethodDef token indices to something. In base images a boolean * flag that there's an update for the method; in delta images a diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index b765e94ee2de1..01844dbf7d30b 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -32,10 +32,29 @@ static MonoNativeTlsKey exposed_generation_id; #define UPDATE_DEBUG(stmt) /*empty */ #endif -typedef struct _EncRecs { +/* For each delta image, for each table: + * - the total logical number of rows for the previous generation + * - the number of modified rows in the current generation + * - the number of inserted rows in the current generation + * + * In each delta, the physical tables contain the rows that modify existing rows of a prior generation, + * followed by inserted rows. + * https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataAggregator.cs#L324 + * + * The total logical number of rows in a table for a particular generation is + * prev_gen_rows + inserted_rows. + */ +typedef struct _delta_row_count { + guint32 prev_gen_rows; + guint32 modified_rows; + guint32 inserted_rows; +} delta_row_count; + +typedef struct _DeltaInfo { // for each table, the row in the EncMap table that has the first token for remapping it? - uint32_t enc_recs [MONO_TABLE_NUM + 1]; -} EncRecs; + uint32_t enc_recs [MONO_TABLE_NUM]; + delta_row_count count [MONO_TABLE_NUM]; +} DeltaInfo; static void @@ -45,7 +64,9 @@ mono_metadata_update_ee_init (MonoError *error); * mapping the base image MonoTableInfos to the base MonoImage. We don't need * this for deltas. */ -static GHashTable *table_to_image, *delta_image_to_encrecs; +static GHashTable *table_to_image, *delta_image_to_info; +/* Low-level lock to protects table_to_image and delta_image_to_info */ +/* FIXME: use concurrent hash tables so that readers don't have to lock. */ static MonoCoopMutex table_to_image_mutex; static void @@ -60,12 +81,35 @@ table_to_image_unlock (void) mono_coop_mutex_unlock (&table_to_image_mutex); } + +static void +delta_info_destroy (DeltaInfo *dinfo) +{ + g_free (dinfo); +} + +static DeltaInfo * +delta_info_lookup_locked (MonoImage *delta_image) +{ + return (DeltaInfo*)g_hash_table_lookup (delta_image_to_info, delta_image); +} + +static DeltaInfo * +delta_info_lookup (MonoImage *delta_image) +{ + DeltaInfo *result; + table_to_image_lock (); + result = delta_info_lookup_locked (delta_image); + table_to_image_unlock (); + return result; +} + static void table_to_image_init (void) { mono_coop_mutex_init (&table_to_image_mutex); table_to_image = g_hash_table_new (NULL, NULL); - delta_image_to_encrecs = g_hash_table_new (NULL, NULL); + delta_image_to_info = g_hash_table_new (NULL, NULL); } static gboolean @@ -77,11 +121,17 @@ remove_base_image (gpointer key, gpointer value, gpointer user_data) } void -mono_metadata_update_cleanup_on_close (MonoImage *base_image) +mono_metadata_update_cleanup_on_close (MonoImage *image) { - /* remove all keys that map to the given image */ table_to_image_lock (); - g_hash_table_foreach_remove (table_to_image, remove_base_image, (gpointer)base_image); + /* remove all keys (delta images) that map to the given image (base image) */ + g_hash_table_foreach_remove (table_to_image, remove_base_image, (gpointer)image); + /* remove delta image info */ + DeltaInfo *delta_info = delta_info_lookup_locked (image); + if (delta_info) { + g_hash_table_remove (delta_image_to_info, image); + delta_info_destroy (delta_info); + } table_to_image_unlock (); } @@ -116,6 +166,8 @@ mono_image_open_dmeta_from_data (MonoImage *base_image, uint32_t generation, gco static void mono_image_append_delta (MonoImage *base, MonoImage *delta); +static int +metadata_update_local_generation (MonoImage *base, MonoImage *delta); void mono_metadata_update_init (void) @@ -287,6 +339,29 @@ mono_image_open_dmeta_from_data (MonoImage *base_image, uint32_t generation, gco return dmeta_image; } +static const char * +scope_to_string (uint32_t tok) +{ + const char *scope; + switch (tok & MONO_RESOLUTION_SCOPE_MASK) { + case MONO_RESOLUTION_SCOPE_MODULE: + scope = "."; + break; + case MONO_RESOLUTION_SCOPE_MODULEREF: + scope = "M"; + break; + case MONO_RESOLUTION_SCOPE_TYPEREF: + scope = "T"; + break; + case MONO_RESOLUTION_SCOPE_ASSEMBLYREF: + scope = "A"; + break; + default: + g_assert_not_reached (); + } + return scope; +} + static void dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) { @@ -303,12 +378,10 @@ dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) for (int i = 1; i <= rows; ++i) { guint32 cols [MONO_TYPEREF_SIZE]; mono_metadata_decode_row (&image_base->tables [MONO_TABLE_TYPEREF], i - 1, cols, MONO_TYPEREF_SIZE); - const char *scope = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_SCOPE]); + const char *scope = scope_to_string (cols [MONO_TYPEREF_SCOPE]); const char *name = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_NAME]); const char *nspace = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_NAMESPACE]); - if (!scope) - scope = ""; if (!name) name = ""; if (!nspace) @@ -323,7 +396,7 @@ dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) for (int i = 1; i <= rows; ++i) { guint32 cols [MONO_TYPEREF_SIZE]; mono_metadata_decode_row (&image_dmeta->tables [MONO_TABLE_TYPEREF], i - 1, cols, MONO_TYPEREF_SIZE); - const char *scope = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_SCOPE]); + const char *scope = scope_to_string (cols [MONO_TYPEREF_SCOPE]); const char *name = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_NAME]); const char *nspace = mono_metadata_string_heap (image_base, cols [MONO_TYPEREF_NAMESPACE]); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "dmeta typeref i=%d (token=0x%08x) -> scope=%s, nspace=%s, name=%s", i, MONO_TOKEN_TYPE_REF | i, scope, nspace, name); @@ -378,6 +451,10 @@ mono_image_effective_table (const MonoTableInfo **t, int *idx) if (G_LIKELY (*idx < (*t)->rows)) return; + /* FIXME: don't let any thread other than the updater thread see values from a delta image + * with a generation past update_published + */ + MonoImage *base = mono_table_info_get_base_image (*t); if (!base || !base->delta_image) return; @@ -393,9 +470,31 @@ mono_image_effective_table (const MonoTableInfo **t, int *idx) size_t s = ALIGN_TO (sizeof (MonoTableInfo), sizeof (gpointer)); int tbl_index = ((intptr_t) *t - (intptr_t) base->tables) / s; + /* FIXME: I don't understand how ReplaceMethodOften works - it always has a + * EnCMap entry 2: 0x06000002 (MethodDef) for every revision. Shouldn't the number of methodDef rows be going up? + + * Apparently not - because conceptually the EnC log is saying to overwrite the existing rows. + */ + + /* FIXME: so if the tables are conceptually mutated by each delta, we can't just stop at the + * first lookup that gets a relative index in the right range, can we? that will always be + * the oldest delta. + */ + + /* FIXME: the other problem is that the EnClog is a sequence of actions to MUTATE rows. So when looking up an existing row we have to be able to make it so that naive callers decoding that row see the updated data. + * + * That's the main thing that PAss1 should eb doing for us. + * + * I think we can't get away from mutating. The format is just too geared toward it. + * + * We should make the mutations atomic, though. (And I guess the heap extension is probably unavoidable) + * + * 1. Keep a table of inv + */ + do { - g_assertf (list, "couldn't find idx=0x%08x in assembly=%s", idx, dmeta && dmeta->name ? dmeta->name : "unknown image"); - dmeta = list->data; + g_assertf (list, "couldn't find idx=0x%08x in assembly=%s", *idx, dmeta && dmeta->name ? dmeta->name : "unknown image"); + dmeta = (MonoImage*)list->data; list = list->next; table = &dmeta->tables [tbl_index]; ridx = mono_image_relative_delta_index (dmeta, mono_metadata_make_token (tbl_index, *idx + 1)) - 1; @@ -436,10 +535,10 @@ mono_image_relative_delta_index (MonoImage *image_dmeta, int token) if (!encmap->rows || !image_dmeta->minimal_delta) return mono_metadata_token_index (token); - EncRecs *enc_recs = g_hash_table_lookup (delta_image_to_encrecs, image_dmeta); - g_assert (enc_recs); + DeltaInfo *delta_info = delta_info_lookup (image_dmeta); + g_assert (delta_info); - int index_map = enc_recs->enc_recs [table]; + int index_map = delta_info->enc_recs [table]; guint32 cols[MONO_ENCMAP_SIZE]; mono_metadata_decode_row (encmap, index_map - 1, cols, MONO_ENCMAP_SIZE); int map_entry = cols [MONO_ENCMAP_TOKEN]; @@ -458,54 +557,94 @@ mono_image_relative_delta_index (MonoImage *image_dmeta, int token) g_print ("warning: map_entry=0x%08x != index=0x%08x. is this a problem?\n", map_entry, index); } - int return_val = index_map - enc_recs->enc_recs [table] + 1; + int return_val = index_map - delta_info->enc_recs [table] + 1; g_assert (return_val > 0); return return_val; } -static void -start_encmap (MonoImage *image_dmeta) +static DeltaInfo* +delta_info_init (MonoImage *image_dmeta, MonoImage *image_base) { MonoTableInfo *encmap = &image_dmeta->tables [MONO_TABLE_ENCMAP]; int table, prev_table = -1, idx; - g_assert (!g_hash_table_lookup (delta_image_to_encrecs, image_dmeta)); + g_assert (!delta_info_lookup (image_dmeta)); if (!encmap->rows) - return; + return NULL; - EncRecs *enc_recs = g_malloc0 (sizeof (EncRecs)); + DeltaInfo *delta_info = g_malloc0 (sizeof (DeltaInfo)); + + /*** Compute logical table sizes ***/ + if (image_base->delta_image == image_base->delta_image_last) { + /* this is the first update. */ + for (int i = 0; i < MONO_TABLE_NUM; ++i) { + delta_info->count[i].prev_gen_rows = image_base->tables[i].rows; + } + } else { + /* Current image_dmeta is image_base->delta_image_last->data, + * find its predecessor + */ + MonoImage *prev_delta = NULL; + for (GSList *cur = image_base->delta_image; cur != NULL; cur = cur->next) { + if (cur->next == NULL) { + prev_delta = (MonoImage*)cur->data; + break; + } + } + g_assert (prev_delta); + DeltaInfo *prev_gen_info = delta_info_lookup (prev_delta); + for (int i = 0; i < MONO_TABLE_NUM; ++i) { + delta_info->count[i].prev_gen_rows = prev_gen_info->count[i].prev_gen_rows + prev_gen_info->count[i].inserted_rows; + } + } + + + /* TODO: while going through the tables, update delta_info->count[tbl].{modified,inserted}_rows */ for (idx = 1; idx <= encmap->rows; ++idx) { guint32 cols[MONO_ENCMAP_SIZE]; mono_metadata_decode_row (encmap, idx - 1, cols, MONO_ENCMAP_SIZE); uint32_t tok = cols [MONO_ENCMAP_TOKEN]; table = mono_metadata_token_table (tok); + uint32_t rid = mono_metadata_token_index (tok); g_assert (table >= 0 && table < MONO_TABLE_NUM); g_assert (table != MONO_TABLE_ENCLOG); g_assert (table != MONO_TABLE_ENCMAP); g_assert (table >= prev_table); + /* FIXME: check bounds - is it < or <=. */ + if (rid < delta_info->count[table].prev_gen_rows) + delta_info->count[table].modified_rows++; + else + delta_info->count[table].inserted_rows++; if (table == prev_table) continue; while (prev_table < table) { prev_table++; - enc_recs->enc_recs [prev_table] = idx; + delta_info->enc_recs [prev_table] = idx; } } - g_assert (prev_table < MONO_TABLE_NUM); - while (prev_table < MONO_TABLE_NUM) { + g_assert (prev_table < MONO_TABLE_NUM - 1); + while (prev_table < MONO_TABLE_NUM - 1) { prev_table++; - enc_recs->enc_recs [prev_table] = idx; + delta_info->enc_recs [prev_table] = idx; } - for (int i = 0 ; i < MONO_TABLE_NUM; ++i) { - if (!image_dmeta->tables [i].base) - continue; + if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE)) { + for (int i = 0 ; i < MONO_TABLE_NUM; ++i) { + if (!image_dmeta->tables [i].base) + continue; - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "enc_recs [%02x] / %s = 0x%02x", i, mono_meta_table_name (i), enc_recs->enc_recs[i]); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "enc_recs [%02x] / %s = 0x%02x\t(inserted: %03d, modified: %03d)", i, mono_meta_table_name (i), delta_info->enc_recs[i], delta_info->count[i].inserted_rows, delta_info->count[i].modified_rows); + } } - g_hash_table_insert (delta_image_to_encrecs, image_dmeta, enc_recs); + table_to_image_lock (); + g_hash_table_insert (delta_image_to_info, image_dmeta, delta_info); + table_to_image_unlock (); + + + return delta_info; } static const char* @@ -783,7 +922,7 @@ mono_image_load_enc_delta (MonoDomain *domain, MonoImage *image_base, gconstpoin if (table_enclog->rows) table_to_image_add (image_base); - start_encmap (image_dmeta); + delta_info_init (image_dmeta, image_base); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "base guid: %s", image_base->guid); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "dmeta guid: %s", image_dmeta->guid); @@ -804,6 +943,34 @@ mono_image_load_enc_delta (MonoDomain *domain, MonoImage *image_base, gconstpoin mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, ">>> EnC delta for base=%s (generation %d) applied", basename, generation); } +/* + * Returns how many times the base image was updated upto and including the given delta. + */ +static int +metadata_update_local_generation (MonoImage *base, MonoImage *delta) +{ + if (delta == base) + return 0; + int index = g_slist_index (base->delta_image, delta); + g_assert (index != -1); + return 1 + index; +} + +/* + * Returns how many times the given base image has been updated so far. + * + * NOTE: doesn't look at update_published or update_alloc_frontier, and therefore only usable by the + * update originator. + */ +static int +metadata_update_count_updates (MonoImage *base) +{ + if (!base->delta_image_last) + return 0; + else + return metadata_update_local_generation (base, (MonoImage*)base->delta_image_last->data); +} #else /* ENABLE_METADATA_UPDATE */ MONO_EMPTY_SOURCE_FILE (metadata_update); #endif /* ENABLE_METADATA_UPDATE */ + From 64293722af967d10335723b2c922f81b267e2b0e Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 18 Dec 2020 16:37:19 -0500 Subject: [PATCH 26/31] [metadata-update] Use a GList for MonoImage:delta_image We're going to need to walk backwards from the latest published delta --- src/mono/mono/metadata/image.c | 10 +++--- src/mono/mono/metadata/loader.c | 2 +- src/mono/mono/metadata/metadata-internals.h | 4 +-- src/mono/mono/metadata/metadata-update.c | 38 +++++++++------------ src/mono/mono/metadata/metadata.c | 4 +-- 5 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/mono/mono/metadata/image.c b/src/mono/mono/metadata/image.c index 31adfc268ac47..757324eabd441 100644 --- a/src/mono/mono/metadata/image.c +++ b/src/mono/mono/metadata/image.c @@ -2550,9 +2550,9 @@ mono_image_close_except_pools_all (MonoImage**images, int image_count) #ifdef ENABLE_METADATA_UPDATE static void -mono_image_close_except_pools_all_list (GSList *images) +mono_image_close_except_pools_all_list (GList *images) { - for (GSList *ptr = images; ptr; ptr = ptr->next) { + for (GList *ptr = images; ptr; ptr = ptr->next) { MonoImage *image = (MonoImage *)ptr->data; if (image) { if (!mono_image_close_except_pools (image)) @@ -2765,15 +2765,15 @@ mono_image_close_all (MonoImage**images, int image_count) #ifdef ENABLE_METADATA_UPDATE static void -mono_image_close_all_list (GSList *images) +mono_image_close_all_list (GList *images) { - for (GSList *ptr = images; ptr; ptr = ptr->next) { + for (GList *ptr = images; ptr; ptr = ptr->next) { MonoImage *image = (MonoImage *)ptr->data; if (image) mono_image_close_finish (image); } - g_slist_free (images); + g_list_free (images); } #endif diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index 8684a2b04c2c1..6d45ea9a751ed 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -2024,7 +2024,7 @@ get_method_update_rva (MonoImage *image_base, uint32_t idx) { gpointer loc = NULL; uint32_t cur = mono_metadata_update_get_thread_generation (); - GSList *ptr = image_base->delta_image; + GList *ptr = image_base->delta_image; /* Go through all the updates that the current thread can see and see * if they updated the method. Keep the latest visible update */ for (; ptr != NULL; ptr = ptr->next) { diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 13cdb36cb769d..0e620ef6fdbfb 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -589,9 +589,9 @@ struct _MonoImage { #ifdef ENABLE_METADATA_UPDATE /* List of MonoImages of deltas. Parent image owns 1 refcount ref of the delta image */ - GSList *delta_image; + GList *delta_image; /* Tail of delta_image for fast appends */ - GSList *delta_image_last; + GList *delta_image_last; /* Metadata delta images only */ uint32_t generation; /* global update ID that added this delta image */ diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 01844dbf7d30b..1f3082b08cc69 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -315,11 +315,12 @@ void mono_image_append_delta (MonoImage *base, MonoImage *delta) { if (!base->delta_image) { - base->delta_image = base->delta_image_last = g_slist_prepend (NULL, delta); + base->delta_image = base->delta_image_last = g_list_alloc (); + base->delta_image->data = (gpointer)delta; return; } g_assert (((MonoImage*)base->delta_image_last->data)->generation < delta->generation); - base->delta_image_last = g_slist_append (base->delta_image_last, delta); + base->delta_image_last = g_list_append (base->delta_image_last, delta); } /** @@ -459,7 +460,7 @@ mono_image_effective_table (const MonoTableInfo **t, int *idx) if (!base || !base->delta_image) return; - GSList *list = base->delta_image; + GList *list = base->delta_image; MonoImage *dmeta; int ridx; MonoTableInfo *table; @@ -586,13 +587,8 @@ delta_info_init (MonoImage *image_dmeta, MonoImage *image_base) * find its predecessor */ MonoImage *prev_delta = NULL; - for (GSList *cur = image_base->delta_image; cur != NULL; cur = cur->next) { - if (cur->next == NULL) { - prev_delta = (MonoImage*)cur->data; - break; - } - } - g_assert (prev_delta); + g_assert (image_base->delta_image_last->prev != NULL); + prev_delta = (MonoImage*)image_base->delta_image_last->prev->data; DeltaInfo *prev_gen_info = delta_info_lookup (prev_delta); for (int i = 0; i < MONO_TABLE_NUM; ++i) { delta_info->count[i].prev_gen_rows = prev_gen_info->count[i].prev_gen_rows + prev_gen_info->count[i].inserted_rows; @@ -680,6 +676,11 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer guint32 cols [MONO_ENCLOG_SIZE]; mono_metadata_decode_row (table_enclog, i, cols, MONO_ENCLOG_SIZE); + // FIXME: the top bit 0x8000000 of log_token is some kind of + // indicator see IsRecId in metamodelrw.cpp and + // MDInternalRW::EnumDeltaTokensInit which skips over those + // records when EditAndContinueModule::ApplyEditAndContinue is + // iterating. int log_token = cols [MONO_ENCLOG_TOKEN]; int func_code = cols [MONO_ENCLOG_FUNC_CODE]; @@ -770,6 +771,8 @@ apply_enclog_pass2 (MonoImage *image_base, uint32_t generation, MonoImage *image int token_index = mono_metadata_token_index (log_token); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "enclog i=%d: token=0x%08x (table=%s): %d", i, log_token, mono_meta_table_name (token_table), func_code); + /* TODO: See CMiniMdRW::ApplyDelta for how to drive this. + */ switch (func_code) { case 0: /* default */ break; @@ -786,8 +789,10 @@ apply_enclog_pass2 (MonoImage *image_base, uint32_t generation, MonoImage *image assemblyref_updated = TRUE; + /* FIXME: use DeltaInfo:prev_gen_rows instead of looping */ + /* TODO: do we know that there will never be modified rows in ASSEMBLYREF? */ int old_rows = image_base->tables [MONO_TABLE_ASSEMBLYREF].rows; - for (GSList *l = image_base->delta_image; l; l = l->next) { + for (GList *l = image_base->delta_image; l; l = l->next) { MonoImage *delta_child = l->data; old_rows += delta_child->tables [MONO_TABLE_ASSEMBLYREF].rows; } @@ -821,15 +826,6 @@ apply_enclog_pass2 (MonoImage *image_base, uint32_t generation, MonoImage *image int mapped_token = mono_image_relative_delta_index (image_dmeta, mono_metadata_make_token (token_table, token_index)); int rva = mono_metadata_decode_row_col (&image_dmeta->tables [MONO_TABLE_METHOD], mapped_token - 1, MONO_METHOD_RVA); if (rva < dil_length) { - /* - * FIXME: this mutates all threads' views.x Need - * a pair of a delta image and a rva. - * - * base: token->generation - * delta: token->rva ? - * - * if multiple updates, go through all deltas in order - */ char *il_address = ((char *) dil_data) + rva; set_update_method (image_base, generation, image_dmeta, token_index, il_address); } else { @@ -951,7 +947,7 @@ metadata_update_local_generation (MonoImage *base, MonoImage *delta) { if (delta == base) return 0; - int index = g_slist_index (base->delta_image, delta); + int index = g_list_index (base->delta_image, delta); g_assert (index != -1); return 1 + index; } diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index bb91b8ead4e93..b1c1ec83cafb5 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -998,7 +998,7 @@ mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_i if (G_LIKELY (token_index <= image->tables [table_index].rows)) return FALSE; - GSList *list = image->delta_image; + GList *list = image->delta_image; MonoImage *dmeta; MonoTableInfo *table; int ridx; @@ -1112,7 +1112,7 @@ mono_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, guint32 prev_size = heap->size; uint32_t current_gen = mono_metadata_update_get_thread_generation (); - GSList *cur; + GList *cur; for (cur = base_image->delta_image; cur; cur = cur->next) { *image_out = (MonoImage*)cur->data; heap = get_heap (*image_out); From d3fc78e0c2f122c2947bfe80b4ca490e02273859 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 18 Dec 2020 16:47:55 -0500 Subject: [PATCH 27/31] [metadata-update] add effective table lookup debug output --- src/mono/mono/metadata/metadata-update.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 1f3082b08cc69..88775d0f9e903 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -501,6 +501,8 @@ mono_image_effective_table (const MonoTableInfo **t, int *idx) ridx = mono_image_relative_delta_index (dmeta, mono_metadata_make_token (tbl_index, *idx + 1)) - 1; } while (ridx < 0 || ridx >= table->rows); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "effective table for %s: 0x%08x -> 0x%08x (gen %d)", mono_meta_table_name (tbl_index), *idx, ridx, metadata_update_local_generation (base, dmeta)); + *t = table; *idx = ridx; } From dce3d2c1bb4c4ceebe500c58970e00e1b31f9f57 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 7 Jan 2021 10:28:44 -0500 Subject: [PATCH 28/31] Address review feedback --- src/mono/mono/metadata/icall-decl.h | 4 ++++ src/mono/mono/metadata/icall-def-netcore.h | 2 +- src/mono/mono/metadata/icall-def.h | 3 --- src/mono/mono/metadata/icall.c | 12 +++++------ src/mono/mono/metadata/metadata-internals.h | 21 +++++++++++++++++-- src/mono/mono/metadata/metadata-update.c | 2 +- src/mono/mono/metadata/metadata.c | 2 +- .../CompilerServices/RuntimeFeature.Mono.cs | 5 +++-- src/mono/wasm/runtime/driver.c | 2 +- 9 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/mono/mono/metadata/icall-decl.h b/src/mono/mono/metadata/icall-decl.h index 909621cf44dc6..16b5278bb42e7 100644 --- a/src/mono/mono/metadata/icall-decl.h +++ b/src/mono/mono/metadata/icall-decl.h @@ -308,4 +308,8 @@ ICALL_EXPORT void ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseIn ICALL_EXPORT void ves_icall_System_Runtime_Intrinsics_X86_X86Base___cpuidex (int abcd[4], int function_id, int subfunction_id); #endif +#if defined(ENABLE_NETCORE) && defined(ENABLE_METADATA_UPDATE) +ICALL_EXPORT void ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dil_bytes, int32_t dil_len); +#endif + #endif // __MONO_METADATA_ICALL_DECL_H__ diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index 37e75aebbc017..2939685c312bf 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -328,7 +328,7 @@ HANDLES(MPROP_5, "internal_from_handle_type", ves_icall_System_Reflection_Runtim #ifdef ENABLE_METADATA_UPDATE ICALL_TYPE(RUNF, "System.Runtime.CompilerServices.RuntimeFeature", RUNF_1) -HANDLES(RUNF_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate, void, 5, (MonoReflectionAssembly, gconstpointer, gint32, gconstpointer, gint32)) +NOHANDLES(ICALL(RUNF_1, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate)) #endif ICALL_TYPE(RUNH, "System.Runtime.CompilerServices.RuntimeHelpers", RUNH_1) diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 31a79575994e5..56dd99bccb57e 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -152,9 +152,6 @@ HANDLES(RUNTIME_2, "EnableMicrosoftTelemetry_internal", ves_icall_Mono_Runtime_E HANDLES(RUNTIME_3, "ExceptionToState_internal", ves_icall_Mono_Runtime_ExceptionToState, MonoString, 3, (MonoException, guint64_ref, guint64_ref)) HANDLES(RUNTIME_4, "GetDisplayName", ves_icall_Mono_Runtime_GetDisplayName, MonoString, 0, ()) HANDLES(RUNTIME_12, "GetNativeStackTrace", ves_icall_Mono_Runtime_GetNativeStackTrace, MonoString, 1, (MonoException)) -#ifdef ENABLE_METADATA_UPDATE -HANDLES(RUNTIME_22, "LoadMetadataUpdate_internal", ves_icall_Mono_Runtime_LoadMetadataUpdate, void, 5, (MonoReflectionAssembly, gconstpointer, gint32, gconstpointer, gint32)) -#endif NOHANDLES(ICALL(RUNTIME_21, "RegisterReportingForAllNativeLibs_internal", ves_icall_Mono_Runtime_RegisterReportingForAllNativeLibs)) NOHANDLES(ICALL(RUNTIME_17, "RegisterReportingForNativeLib_internal", ves_icall_Mono_Runtime_RegisterReportingForNativeLib)) HANDLES(RUNTIME_13, "SendMicrosoftTelemetry_internal", ves_icall_Mono_Runtime_SendMicrosoftTelemetry, void, 3, (const_char_ptr, guint64, guint64)) diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 8a486304431af..c9293be7aade6 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -6777,21 +6777,21 @@ ves_icall_Mono_Runtime_DumpStateTotal (guint64 *portable_hash, guint64 *unportab return result; } -#ifdef ENABLE_METADATA_UPDATE +#if defined (ENABLE_NETCORE) && defined (ENABLE_METADATA_UPDATE) void -ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoReflectionAssemblyHandle refassm, +ves_icall_Mono_Runtime_LoadMetadataUpdate (MonoAssembly *assm, gconstpointer dmeta_bytes, int32_t dmeta_len, - gconstpointer dil_bytes, int32_t dil_len, - MonoError *error) + gconstpointer dil_bytes, int32_t dil_len) { - g_assert (MONO_HANDLE_BOOL (refassm)); + ERROR_DECL (error); + g_assert (assm); g_assert (dmeta_len >= 0); - MonoAssembly *assm = MONO_HANDLE_GETVAL (refassm, assembly); MonoImage *image_base = assm->image; g_assert (image_base); MonoDomain *domain = mono_domain_get (); mono_image_load_enc_delta (domain, image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error); + mono_error_set_pending_exception (error); } #endif diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 0e620ef6fdbfb..ce785758b9799 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -911,7 +911,15 @@ mono_image_effective_table (const MonoTableInfo **t, int *idx) } #else /* ENABLE_METADATA_UPDATE */ void -mono_image_effective_table (const MonoTableInfo **t, int *idx); +mono_image_effective_table_slow (const MonoTableInfo **t, int *idx); + +static inline void +mono_image_effective_table (const MonoTableInfo **t, int *idx) +{ + if (G_LIKELY (*idx < (*t)->rows)) + return; + mono_image_effective_table_slow (t, idx); +} int mono_image_relative_delta_index (MonoImage *image_dmeta, int token); @@ -985,7 +993,16 @@ mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_i } #else gboolean -mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index); +mono_metadata_table_bounds_check_slow (MonoImage *image, int table_index, int token_index); + +static inline gboolean +mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index) +{ + /* returns true if given index is not in bounds with provided table/index pair */ + if (G_LIKELY (token_index <= image->tables [table_index].rows)) + return FALSE; + return mono_metadata_table_bounds_check_slow (image, table_index, token_index); +} #endif const char * mono_meta_table_name (int table); diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 88775d0f9e903..e6cb5f47a0c60 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -447,7 +447,7 @@ dump_update_summary (MonoImage *image_base, MonoImage *image_dmeta) } void -mono_image_effective_table (const MonoTableInfo **t, int *idx) +mono_image_effective_table_slow (const MonoTableInfo **t, int *idx) { if (G_LIKELY (*idx < (*t)->rows)) return; diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index b1c1ec83cafb5..bf5d119541bf2 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -993,7 +993,7 @@ mono_metadata_compute_size (MonoImage *meta, int tableindex, guint32 *result_bit #ifdef ENABLE_METADATA_UPDATE /* returns true if given index is not in bounds with provided table/index pair */ gboolean -mono_metadata_table_bounds_check (MonoImage *image, int table_index, int token_index) +mono_metadata_table_bounds_check_slow (MonoImage *image, int table_index, int token_index) { if (G_LIKELY (token_index <= image->tables [table_index].rows)) return FALSE; diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs index 7aba9f770d4bf..58c0efb6c90d0 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeFeature.Mono.cs @@ -24,13 +24,14 @@ internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[ } #else [MethodImplAttribute (MethodImplOptions.InternalCall)] - private static unsafe extern void LoadMetadataUpdate_internal (Assembly base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length); + private static unsafe extern void LoadMetadataUpdate_internal (IntPtr base_assm, byte* dmeta_bytes, int dmeta_length, byte *dil_bytes, int dil_length); internal static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data) { unsafe { fixed (byte* dmeta_bytes = dmeta_data) fixed (byte* dil_bytes = dil_data) { - LoadMetadataUpdate_internal (assm, dmeta_bytes, dmeta_data.Length, dil_bytes, dil_data.Length); + IntPtr mono_assembly = ((RuntimeAssembly)assm).GetUnderlyingNativeHandle (); + LoadMetadataUpdate_internal (mono_assembly, dmeta_bytes, dmeta_data.Length, dil_bytes, dil_data.Length); } } } diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index d2f7fa0f0f439..ab7895f0eb3be 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -472,7 +472,7 @@ mono_wasm_load_runtime (const char *unused, int debug_level) #ifdef DEBUG monoeg_g_setenv ("MONO_LOG_LEVEL", "debug", 0); - monoeg_g_setenv ("MONO_LOG_MASK", "metadata-update", 0); + monoeg_g_setenv ("MONO_LOG_MASK", "gc", 0); // Setting this env var allows Diagnostic.Debug to write to stderr. In a browser environment this // output will be sent to the console. Right now this is the only way to emit debug logging from // corlib assemblies. From 945b0602257a077245edc1042c1d1d3416c4b5e6 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 8 Jan 2021 20:12:30 -0500 Subject: [PATCH 29/31] [interp] Save top interp frame at MINT_SAFEPOINT to ThreadContext Give metadata updates a peek at the interp frames since the LMF so that it can copy the InterpMethods that are currently executing This only works with hybrid and full coop suspend. Preemptive suspend will need another mechanism. --- src/mono/mono/mini/interp/interp-internals.h | 5 ++ src/mono/mono/mini/interp/interp.c | 65 ++++++++++++++++---- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/mono/mono/mini/interp/interp-internals.h b/src/mono/mono/mini/interp/interp-internals.h index 69fcc403fb340..a7bbfb77feb7a 100644 --- a/src/mono/mono/mini/interp/interp-internals.h +++ b/src/mono/mono/mini/interp/interp-internals.h @@ -225,6 +225,11 @@ typedef struct { guchar *stack_pointer; /* Used for allocation of localloc regions */ FrameDataAllocator data_stack; + /* Used when a thread self-suspends at a safepoint in the interpreter, points to the + * currently executing frame. (If a thread self-suspends somewhere else in the runtime, this + * is NULL - the LMF will point to the InterpFrame before the thread exited the interpreter) + */ + InterpFrame *safepoint_frame; } ThreadContext; typedef struct { diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index e9a262c3ce1df..7aaf8b1d53d60 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -362,6 +362,7 @@ clear_resume_state (ThreadContext *context) g_assert (context->exc_gchandle); mono_gchandle_free_internal (context->exc_gchandle); context->exc_gchandle = 0; + context->safepoint_frame = NULL; } /* @@ -420,6 +421,8 @@ interp_free_context (gpointer ctx) set_context (NULL); } + context->safepoint_frame = NULL; + mono_vfree (context->stack_start, INTERP_STACK_SIZE, MONO_MEM_ACCOUNT_INTERP_STACK); /* Prevent interp_mark_stack from trying to scan the data_stack, before freeing it */ context->stack_start = NULL; @@ -428,6 +431,19 @@ interp_free_context (gpointer ctx) g_free (context); } +static void +context_set_safepoint_frame (ThreadContext *context, InterpFrame *frame) +{ + g_assert (!context->has_resume_state); + context->safepoint_frame = frame; +} + +static void +context_clear_safepoint_frame (ThreadContext *context) +{ + context->safepoint_frame = NULL; +} + void mono_interp_error_cleanup (MonoError* error) { @@ -2014,6 +2030,7 @@ interp_entry (InterpEntryData *data) context->stack_pointer = (guchar*)sp; g_assert (!context->has_resume_state); + g_assert (!context->safepoint_frame); if (rmethod->needs_thread_attach) mono_threads_detach_coop (orig_domain, &attach_cookie); @@ -5218,9 +5235,11 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_CASE(MINT_SAFEPOINT) /* Do synchronous checking of abort requests */ EXCEPTION_CHECKPOINT; + context_set_safepoint_frame (context, frame); /* Poll safepoint */ mono_threads_safepoint (); ++ip; + context_clear_safepoint_frame (context); MINT_IN_BREAK; MINT_IN_CASE(MINT_LDFLDA_UNSAFE) { sp[-1].data.p = (char*)sp [-1].data.o + ip [1]; @@ -7574,32 +7593,44 @@ interp_metadata_update_init (MonoError *error) mono_error_set_execution_engine (error, "Interpreter inlining must be turned off for metadata updates"); } +#ifdef ENABLE_METADATA_UPDATE static void -interp_invalidate_transformed (MonoDomain *domain) +metadata_update_backup_frames (MonoDomain *domain, MonoThreadInfo *info, InterpFrame *frame) { - gboolean need_stw_restart = FALSE; -#ifdef ENABLE_METADATA_UPDATE - need_stw_restart = TRUE; + while (frame) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "threadinfo=%p, copy imethod for method=%s", info, mono_method_full_name (frame->imethod->method, 1)); + copy_imethod_for_frame (domain, frame); + frame = frame->parent; + } +} - mono_gc_stop_world (); +static void +metadata_update_prepare_to_invalidate (MonoDomain *domain) +{ /* (1) make a copy of imethod for every interpframe that is on the stack, * so we do not invalidate currently running methods */ FOREACH_THREAD_EXCLUDE (info, MONO_THREAD_INFO_FLAGS_NO_GC) { - if (!info) + if (!info || !info->jit_data) continue; + ThreadContext *context = (ThreadContext*)info->jit_data->interp_context; + + /* If the thread was in the interpreter and hit a safepoint + * opcode and suspended, backup the frames since the last lmf. + */ + if (context && context->safepoint_frame) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "threadinfo=%p, has safepoint frame %p", info, context->safepoint_frame); + metadata_update_backup_frames (domain, info, context->safepoint_frame); + } + MonoLMF *lmf = info->jit_data->lmf; while (lmf) { if (((gsize) lmf->previous_lmf) & 2) { MonoLMFExt *ext = (MonoLMFExt *) lmf; if (ext->kind == MONO_LMFEXT_INTERP_EXIT || ext->kind == MONO_LMFEXT_INTERP_EXIT_WITH_CTX) { InterpFrame *frame = ext->interp_exit_data; - while (frame) { - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "threadinfo=%p, copy imethod for method=%s", info, mono_method_full_name (frame->imethod->method, 1)); - copy_imethod_for_frame (domain, frame); - frame = frame->parent; - } + metadata_update_backup_frames (domain, info, frame); } } lmf = (MonoLMF *)(((gsize) lmf->previous_lmf) & ~3); @@ -7607,6 +7638,18 @@ interp_invalidate_transformed (MonoDomain *domain) } FOREACH_THREAD_END /* (2) invalidate all the registered imethods */ +} +#endif + + +static void +interp_invalidate_transformed (MonoDomain *domain) +{ + gboolean need_stw_restart = FALSE; +#ifdef ENABLE_METADATA_UPDATE + need_stw_restart = TRUE; + mono_gc_stop_world (); + metadata_update_prepare_to_invalidate (domain); #endif MonoJitDomainInfo *info = domain_jit_info (domain); mono_domain_jit_code_hash_lock (domain); From d40eb92721b766b91163b8af99514e92904c32af Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 8 Jan 2021 20:14:27 -0500 Subject: [PATCH 30/31] [mbr] Extend console sample Add a busy thread to demonstrate that interp frames since the last managed frame are visible to the metadata update mechanism and the active method bodies are copied before being invalidated. --- .../netcore/sample/mbr/console/Program.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/mono/netcore/sample/mbr/console/Program.cs b/src/mono/netcore/sample/mbr/console/Program.cs index 70bc11bb8c8f6..01fba0d8e4ee1 100644 --- a/src/mono/netcore/sample/mbr/console/Program.cs +++ b/src/mono/netcore/sample/mbr/console/Program.cs @@ -14,11 +14,24 @@ internal class Program class State { public readonly ManualResetEventSlim mreIn; public readonly ManualResetEventSlim mreOut; + public readonly ManualResetEventSlim mreBusy; public string res; + private volatile bool busyChanged; + public State() { mreIn = new ManualResetEventSlim (); mreOut = new ManualResetEventSlim (); + mreBusy = new ManualResetEventSlim (); res = ""; + busyChanged = false; + } + + + public bool BusyChanged {get => busyChanged ; set { busyChanged = value; mreBusy.Set ();} } + + public void WaitForBusy () { + mreBusy.Wait (); + mreBusy.Reset (); } public string ConsumerStep () { @@ -50,6 +63,8 @@ private static int Main(string[] args) var st = new State (); var t = new Thread (MutatorThread); t.Start (st); + var t2 = new Thread (BusyThread) { IsBackground = true }; + t2.Start (st); string res = st.ConsumerStep (); if (res != "OLD STRING") @@ -61,6 +76,9 @@ private static int Main(string[] args) if (res != "NEW STRING") return 2; + st.WaitForBusy (); + Console.WriteLine ("BusyChanged: {0}", st.BusyChanged); + return 0; } @@ -70,6 +88,28 @@ private static void MutatorThread (object o) static string Step () => TestClass.TargetMethod (); st.ProducerStep (Step); st.ProducerStep (Step); + } + + // This method is not affected by the update, but it calls the target + // method which is. Still we expect to see "BusyThread" and its + // callees show up in the trace output when it safepoints during an + // update. + private static void BusyThread (object o) + { + State st = (State)o; + string prev = TestClass.TargetMethod (); + while (true) { + Thread.Sleep (0); + for (int i = 0; i < 5000; ++i) { + if (i % 1000 == 0) { + string cur = TestClass.TargetMethod (); + if (cur != prev) { + st.BusyChanged = true; + prev = cur; + } + } + } + } } } From 04e153c12c1821fbeb35ff59ea5782e5c301a84d Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Sat, 9 Jan 2021 09:36:52 -0500 Subject: [PATCH 31/31] [interp] Check mono_polling_required at safepoint --- src/mono/mono/mini/interp/interp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 7aaf8b1d53d60..9d5b99cecba85 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -362,7 +362,6 @@ clear_resume_state (ThreadContext *context) g_assert (context->exc_gchandle); mono_gchandle_free_internal (context->exc_gchandle); context->exc_gchandle = 0; - context->safepoint_frame = NULL; } /* @@ -5235,11 +5234,13 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs MINT_IN_CASE(MINT_SAFEPOINT) /* Do synchronous checking of abort requests */ EXCEPTION_CHECKPOINT; - context_set_safepoint_frame (context, frame); - /* Poll safepoint */ - mono_threads_safepoint (); + if (G_UNLIKELY (mono_polling_required)) { + context_set_safepoint_frame (context, frame); + /* Poll safepoint */ + mono_threads_safepoint (); + context_clear_safepoint_frame (context); + } ++ip; - context_clear_safepoint_frame (context); MINT_IN_BREAK; MINT_IN_CASE(MINT_LDFLDA_UNSAFE) { sp[-1].data.p = (char*)sp [-1].data.o + ip [1];