Skip to content

Commit

Permalink
[mono][mbr] Implement DOTNET_MODIFIABLE_ASSEMBLIES support (#49507)
Browse files Browse the repository at this point in the history
Implements #47274 for Mono.

1. `DOTNET_MODIFIABLE_ASSEMBLIES` environment variable must be set to `debug` for hot reload to be enabled (in addition to the interpreter being enabled)
2. Assemblies must be compiled in Debug mode - we check `DebuggableAttribute` for the `DisableOptimizations` bit.  If an assembly doesn't have the bit set, it can't be reloaded.
3. In the interpreter - don't try to inline when hot reload is enabled and the caller or callee has the DisableOptimizations bit set.



* [mbr] Check DOTNET_MODIFIABLE_ASSEMBLIES for hot reload support

   If it's set to "debug" (case insensitive) then hot reload is enabled for assemblies compiled in debug mode.  Otherwise hot reload is disabled

* [mbr] Disable inlining for DebuggerAttribue assemblies

   with the optimizer disabled flag

* [mbr] Update samples

* [mbr] Throw InvalidOperationException is hot reload is not supported

   On a per-assembly basis

* [mbr] Stub implementations if !ENABLE_METADATA_UPDATE
  • Loading branch information
lambdageek authored Mar 13, 2021
1 parent 1d9c402 commit d39ad7a
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 13 deletions.
71 changes: 71 additions & 0 deletions src/mono/mono/metadata/metadata-update.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifdef ENABLE_METADATA_UPDATE

#include <glib.h>
#include "mono/metadata/assembly-internals.h"
#include "mono/metadata/metadata-internals.h"
#include "mono/metadata/metadata-update.h"
#include "mono/metadata/object-internals.h"
Expand Down Expand Up @@ -62,6 +63,71 @@ typedef struct _DeltaInfo {
} DeltaInfo;


#define DOTNET_MODIFIABLE_ASSEMBLIES "DOTNET_MODIFIABLE_ASSEMBLIES"

/**
* mono_metadata_update_enable:
* \param modifiable_assemblies_out: set to MonoModifiableAssemblies value
*
* Returns \c TRUE if metadata updates are enabled at runtime. False otherwise.
*
* If \p modifiable_assemblies_out is not \c NULL, it's set on return.
*
* The result depends on the value of the DOTNET_MODIFIABLE_ASSEMBLIES
* environment variable. "debug" means debuggable assemblies are modifiable,
* all other values are ignored and metadata updates are disabled.
*/
gboolean
mono_metadata_update_enabled (int *modifiable_assemblies_out)
{
static gboolean inited = FALSE;
static int modifiable = MONO_MODIFIABLE_ASSM_NONE;

if (!inited) {
char *val = g_getenv (DOTNET_MODIFIABLE_ASSEMBLIES);
if (!g_strcasecmp (val, "debug"))
modifiable = MONO_MODIFIABLE_ASSM_DEBUG;
g_free (val);
inited = TRUE;
}
if (modifiable_assemblies_out)
*modifiable_assemblies_out = modifiable;
return modifiable != MONO_MODIFIABLE_ASSM_NONE;
}

static gboolean
assembly_update_supported (MonoAssembly *assm)
{
int modifiable = 0;
if (!mono_metadata_update_enabled (&modifiable))
return FALSE;
if (modifiable == MONO_MODIFIABLE_ASSM_DEBUG &&
mono_assembly_is_jit_optimizer_disabled (assm))
return TRUE;
return FALSE;
}

/**
* mono_metadata_update_no_inline:
* \param caller: the calling method
* \param callee: the method being called
*
* Returns \c TRUE if \p callee should not be inlined into \p caller.
*
* If metadata updates are enabled either for the caller or callee's module,
* the callee should not be inlined.
*
*/
gboolean
mono_metadata_update_no_inline (MonoMethod *caller, MonoMethod *callee)
{
if (!mono_metadata_update_enabled (NULL))
return FALSE;
MonoAssembly *caller_assm = m_class_get_image(caller->klass)->assembly;
MonoAssembly *callee_assm = m_class_get_image(callee->klass)->assembly;
return mono_assembly_is_jit_optimizer_disabled (caller_assm) || mono_assembly_is_jit_optimizer_disabled (callee_assm);
}

static void
mono_metadata_update_ee_init (MonoError *error);

Expand Down Expand Up @@ -881,6 +947,11 @@ mono_image_load_enc_delta (MonoImage *image_base, gconstpointer dmeta_bytes, uin
if (!is_ok (error))
return;

if (!assembly_update_supported (image_base->assembly)) {
mono_error_set_invalid_operation (error, "The assembly can not be edited or changed.");
return;
}

const char *basename = image_base->filename;
/* FIXME:
* (1) do we need to memcpy dmeta_bytes ? (maybe)
Expand Down
28 changes: 28 additions & 0 deletions src/mono/mono/metadata/metadata-update.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@

#ifdef ENABLE_METADATA_UPDATE

enum MonoModifiableAssemblies {
/* modifiable assemblies are disabled */
MONO_MODIFIABLE_ASSM_NONE = 0,
/* assemblies with the Debug flag are modifiable */
MONO_MODIFIABLE_ASSM_DEBUG = 1,
};

gboolean
mono_metadata_update_enabled (int *modifiable_assemblies_out);

gboolean
mono_metadata_update_no_inline (MonoMethod *caller, MonoMethod *callee);

void
mono_metadata_update_init (void);

Expand Down Expand Up @@ -44,6 +57,21 @@ mono_metadata_update_cleanup_on_close (MonoImage *base_image);
MonoImage *
mono_table_info_get_base_image (const MonoTableInfo *t);

#else /* ENABLE_METADATA_UPDATE */

static inline gboolean
mono_metadata_update_enabled (int *modifiable_assemblies_out)
{
if (modifiable_assemblies_out)
*modifiable_assemblies_out = 0;
return FALSE;
}

static inline gboolean
mono_metadata_update_no_inline (MonoMethod *caller, MonoMethod *callee)
{
return FALSE;
}

#endif /* ENABLE_METADATA_UPDATE */

Expand Down
2 changes: 0 additions & 2 deletions src/mono/mono/mini/interp/interp.c
Original file line number Diff line number Diff line change
Expand Up @@ -7230,8 +7230,6 @@ copy_imethod_for_frame (InterpFrame *frame)
static void
interp_metadata_update_init (MonoError *error)
{
if ((mono_interp_opt & INTERP_OPT_INLINE) != 0)
mono_error_set_execution_engine (error, "Interpreter inlining must be turned off for metadata updates");
}

#ifdef ENABLE_METADATA_UPDATE
Expand Down
13 changes: 13 additions & 0 deletions src/mono/mono/mini/interp/transform.c
Original file line number Diff line number Diff line change
Expand Up @@ -2632,6 +2632,16 @@ interp_icall_op_for_sig (MonoMethodSignature *sig)
#define INLINE_LENGTH_LIMIT 20
#define INLINE_DEPTH_LIMIT 10

static gboolean
is_metadata_update_disabled (void)
{
static gboolean disabled = FALSE;
if (disabled)
return disabled;
disabled = !mono_metadata_update_enabled (NULL);
return disabled;
}

static gboolean
interp_method_check_inlining (TransformData *td, MonoMethod *method, MonoMethodSignature *csignature)
{
Expand Down Expand Up @@ -2686,6 +2696,9 @@ interp_method_check_inlining (TransformData *td, MonoMethod *method, MonoMethodS
if (td->prof_coverage)
return FALSE;

if (!is_metadata_update_disabled () && mono_metadata_update_no_inline (td->method, method))
return FALSE;

if (g_list_find (td->dont_inline, method))
return FALSE;

Expand Down
1 change: 1 addition & 0 deletions src/mono/sample/mbr/DeltaHelper.targets
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
What other properties do I need to pass? Maybe hotreload-delta-gen should just expose an MSBuild task so we can pass everything -->
<HotreloadDeltaGenArgs Condition="'$(Configuration)' != ''">$(HotreloadDeltaGenArgs) -p:Configuration=$(Configuration)</HotreloadDeltaGenArgs>
<HotreloadDeltaGenArgs Condition="'$(RuntimeIdentifier)' != ''">$(HotreloadDeltaGenArgs) -p:RuntimeIdentifier=$(RuntimeIdentifier)</HotreloadDeltaGenArgs>
<HotreloadDeltaGenArgs Condition="'$(BuiltRuntimeConfiguration)' != ''">$(HotreloadDeltaGenArgs) -p:BuiltRuntimeConfiguration=$(BuiltRuntimeConfiguration)</HotreloadDeltaGenArgs>
<HotreloadDeltaGenArgs>$(HotreloadDeltaGenArgs) -script:$(DeltaScript)</HotreloadDeltaGenArgs>
</PropertyGroup>
<Exec Command="$(HotreloadDeltaGenFullPath) $(HotreloadDeltaGenArgs)"/>
Expand Down
2 changes: 1 addition & 1 deletion src/mono/sample/mbr/browser/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var Module = {
App.init ();
};
config.environment_variables = {
"MONO_METADATA_UPDATE": "1"
"DOTNET_MODIFIABLE_ASSEMBLIES": "debug"
};
config.fetch_file_cb = function (asset) {
return fetch (asset, { credentials: 'same-origin' });
Expand Down
2 changes: 1 addition & 1 deletion src/mono/sample/mbr/console/ConsoleDelta.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<Target Name="TrickRuntimePackLocation" AfterTargets="ProcessFrameworkReferences">
<ItemGroup>
<RuntimePack>
<PackageDirectory>$(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)</PackageDirectory>
<PackageDirectory>$(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(BuiltRuntimeConfiguration)</PackageDirectory>
</RuntimePack>
</ItemGroup>
<Message Text="Packaged ID: %(RuntimePack.PackageDirectory)" Importance="high" />
Expand Down
10 changes: 7 additions & 3 deletions src/mono/sample/mbr/console/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ TOP=../../../../../
DOTNET:=$(TOP)./dotnet.sh
DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary

CONFIG ?=Release
# How to build the project. For hot reload this must be Debug
CONFIG ?=Debug
# How was dotnet/runtime built? should be the same as build.sh -c option
BUILT_RUNTIME_CONFIG ?= Release
MONO_ARCH=x64

OS := $(shell uname -s)
Expand All @@ -12,14 +15,15 @@ else
TARGET_OS=linux
endif

MONO_ENV_OPTIONS ?=--interp=-inline
MONO_ENV_OPTIONS = --interp

publish:
$(DOTNET) publish -c $(CONFIG) -r $(TARGET_OS)-$(MONO_ARCH)
$(DOTNET) publish -c $(CONFIG) -r $(TARGET_OS)-$(MONO_ARCH) -p:BuiltRuntimeConfiguration=$(BUILT_RUNTIME_CONFIG)

run: publish
COMPlus_DebugWriteToStdErr=1 \
MONO_ENV_OPTIONS="$(MONO_ENV_OPTIONS)" \
DOTNET_MODIFIABLE_ASSEMBLIES=debug \
$(TOP)artifacts/bin/ConsoleDelta/$(MONO_ARCH)/$(CONFIG)/$(TARGET_OS)-$(MONO_ARCH)/publish/ConsoleDelta

clean:
Expand Down
6 changes: 0 additions & 6 deletions src/mono/wasm/runtime/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,6 @@ mono_wasm_load_runtime (const char *unused, int debug_level)
#else
mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP_ONLY);

#ifdef ENABLE_METADATA_UPDATE
if (monoeg_g_hasenv ("MONO_METADATA_UPDATE")) {
interp_opts = "-inline";
}
#endif

/*
* debug_level > 0 enables debugging and sets the debug log level to debug_level
* debug_level == 0 disables debugging and enables interpreter optimizations
Expand Down

0 comments on commit d39ad7a

Please sign in to comment.