Skip to content

Commit d39ad7a

Browse files
authored
[mono][mbr] Implement DOTNET_MODIFIABLE_ASSEMBLIES support (#49507)
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
1 parent 1d9c402 commit d39ad7a

File tree

9 files changed

+122
-13
lines changed

9 files changed

+122
-13
lines changed

src/mono/mono/metadata/metadata-update.c

+71
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifdef ENABLE_METADATA_UPDATE
1414

1515
#include <glib.h>
16+
#include "mono/metadata/assembly-internals.h"
1617
#include "mono/metadata/metadata-internals.h"
1718
#include "mono/metadata/metadata-update.h"
1819
#include "mono/metadata/object-internals.h"
@@ -62,6 +63,71 @@ typedef struct _DeltaInfo {
6263
} DeltaInfo;
6364

6465

66+
#define DOTNET_MODIFIABLE_ASSEMBLIES "DOTNET_MODIFIABLE_ASSEMBLIES"
67+
68+
/**
69+
* mono_metadata_update_enable:
70+
* \param modifiable_assemblies_out: set to MonoModifiableAssemblies value
71+
*
72+
* Returns \c TRUE if metadata updates are enabled at runtime. False otherwise.
73+
*
74+
* If \p modifiable_assemblies_out is not \c NULL, it's set on return.
75+
*
76+
* The result depends on the value of the DOTNET_MODIFIABLE_ASSEMBLIES
77+
* environment variable. "debug" means debuggable assemblies are modifiable,
78+
* all other values are ignored and metadata updates are disabled.
79+
*/
80+
gboolean
81+
mono_metadata_update_enabled (int *modifiable_assemblies_out)
82+
{
83+
static gboolean inited = FALSE;
84+
static int modifiable = MONO_MODIFIABLE_ASSM_NONE;
85+
86+
if (!inited) {
87+
char *val = g_getenv (DOTNET_MODIFIABLE_ASSEMBLIES);
88+
if (!g_strcasecmp (val, "debug"))
89+
modifiable = MONO_MODIFIABLE_ASSM_DEBUG;
90+
g_free (val);
91+
inited = TRUE;
92+
}
93+
if (modifiable_assemblies_out)
94+
*modifiable_assemblies_out = modifiable;
95+
return modifiable != MONO_MODIFIABLE_ASSM_NONE;
96+
}
97+
98+
static gboolean
99+
assembly_update_supported (MonoAssembly *assm)
100+
{
101+
int modifiable = 0;
102+
if (!mono_metadata_update_enabled (&modifiable))
103+
return FALSE;
104+
if (modifiable == MONO_MODIFIABLE_ASSM_DEBUG &&
105+
mono_assembly_is_jit_optimizer_disabled (assm))
106+
return TRUE;
107+
return FALSE;
108+
}
109+
110+
/**
111+
* mono_metadata_update_no_inline:
112+
* \param caller: the calling method
113+
* \param callee: the method being called
114+
*
115+
* Returns \c TRUE if \p callee should not be inlined into \p caller.
116+
*
117+
* If metadata updates are enabled either for the caller or callee's module,
118+
* the callee should not be inlined.
119+
*
120+
*/
121+
gboolean
122+
mono_metadata_update_no_inline (MonoMethod *caller, MonoMethod *callee)
123+
{
124+
if (!mono_metadata_update_enabled (NULL))
125+
return FALSE;
126+
MonoAssembly *caller_assm = m_class_get_image(caller->klass)->assembly;
127+
MonoAssembly *callee_assm = m_class_get_image(callee->klass)->assembly;
128+
return mono_assembly_is_jit_optimizer_disabled (caller_assm) || mono_assembly_is_jit_optimizer_disabled (callee_assm);
129+
}
130+
65131
static void
66132
mono_metadata_update_ee_init (MonoError *error);
67133

@@ -881,6 +947,11 @@ mono_image_load_enc_delta (MonoImage *image_base, gconstpointer dmeta_bytes, uin
881947
if (!is_ok (error))
882948
return;
883949

950+
if (!assembly_update_supported (image_base->assembly)) {
951+
mono_error_set_invalid_operation (error, "The assembly can not be edited or changed.");
952+
return;
953+
}
954+
884955
const char *basename = image_base->filename;
885956
/* FIXME:
886957
* (1) do we need to memcpy dmeta_bytes ? (maybe)

src/mono/mono/metadata/metadata-update.h

+28
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111

1212
#ifdef ENABLE_METADATA_UPDATE
1313

14+
enum MonoModifiableAssemblies {
15+
/* modifiable assemblies are disabled */
16+
MONO_MODIFIABLE_ASSM_NONE = 0,
17+
/* assemblies with the Debug flag are modifiable */
18+
MONO_MODIFIABLE_ASSM_DEBUG = 1,
19+
};
20+
21+
gboolean
22+
mono_metadata_update_enabled (int *modifiable_assemblies_out);
23+
24+
gboolean
25+
mono_metadata_update_no_inline (MonoMethod *caller, MonoMethod *callee);
26+
1427
void
1528
mono_metadata_update_init (void);
1629

@@ -44,6 +57,21 @@ mono_metadata_update_cleanup_on_close (MonoImage *base_image);
4457
MonoImage *
4558
mono_table_info_get_base_image (const MonoTableInfo *t);
4659

60+
#else /* ENABLE_METADATA_UPDATE */
61+
62+
static inline gboolean
63+
mono_metadata_update_enabled (int *modifiable_assemblies_out)
64+
{
65+
if (modifiable_assemblies_out)
66+
*modifiable_assemblies_out = 0;
67+
return FALSE;
68+
}
69+
70+
static inline gboolean
71+
mono_metadata_update_no_inline (MonoMethod *caller, MonoMethod *callee)
72+
{
73+
return FALSE;
74+
}
4775

4876
#endif /* ENABLE_METADATA_UPDATE */
4977

src/mono/mono/mini/interp/interp.c

-2
Original file line numberDiff line numberDiff line change
@@ -7230,8 +7230,6 @@ copy_imethod_for_frame (InterpFrame *frame)
72307230
static void
72317231
interp_metadata_update_init (MonoError *error)
72327232
{
7233-
if ((mono_interp_opt & INTERP_OPT_INLINE) != 0)
7234-
mono_error_set_execution_engine (error, "Interpreter inlining must be turned off for metadata updates");
72357233
}
72367234

72377235
#ifdef ENABLE_METADATA_UPDATE

src/mono/mono/mini/interp/transform.c

+13
Original file line numberDiff line numberDiff line change
@@ -2632,6 +2632,16 @@ interp_icall_op_for_sig (MonoMethodSignature *sig)
26322632
#define INLINE_LENGTH_LIMIT 20
26332633
#define INLINE_DEPTH_LIMIT 10
26342634

2635+
static gboolean
2636+
is_metadata_update_disabled (void)
2637+
{
2638+
static gboolean disabled = FALSE;
2639+
if (disabled)
2640+
return disabled;
2641+
disabled = !mono_metadata_update_enabled (NULL);
2642+
return disabled;
2643+
}
2644+
26352645
static gboolean
26362646
interp_method_check_inlining (TransformData *td, MonoMethod *method, MonoMethodSignature *csignature)
26372647
{
@@ -2686,6 +2696,9 @@ interp_method_check_inlining (TransformData *td, MonoMethod *method, MonoMethodS
26862696
if (td->prof_coverage)
26872697
return FALSE;
26882698

2699+
if (!is_metadata_update_disabled () && mono_metadata_update_no_inline (td->method, method))
2700+
return FALSE;
2701+
26892702
if (g_list_find (td->dont_inline, method))
26902703
return FALSE;
26912704

src/mono/sample/mbr/DeltaHelper.targets

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
What other properties do I need to pass? Maybe hotreload-delta-gen should just expose an MSBuild task so we can pass everything -->
1313
<HotreloadDeltaGenArgs Condition="'$(Configuration)' != ''">$(HotreloadDeltaGenArgs) -p:Configuration=$(Configuration)</HotreloadDeltaGenArgs>
1414
<HotreloadDeltaGenArgs Condition="'$(RuntimeIdentifier)' != ''">$(HotreloadDeltaGenArgs) -p:RuntimeIdentifier=$(RuntimeIdentifier)</HotreloadDeltaGenArgs>
15+
<HotreloadDeltaGenArgs Condition="'$(BuiltRuntimeConfiguration)' != ''">$(HotreloadDeltaGenArgs) -p:BuiltRuntimeConfiguration=$(BuiltRuntimeConfiguration)</HotreloadDeltaGenArgs>
1516
<HotreloadDeltaGenArgs>$(HotreloadDeltaGenArgs) -script:$(DeltaScript)</HotreloadDeltaGenArgs>
1617
</PropertyGroup>
1718
<Exec Command="$(HotreloadDeltaGenFullPath) $(HotreloadDeltaGenArgs)"/>

src/mono/sample/mbr/browser/runtime.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ var Module = {
77
App.init ();
88
};
99
config.environment_variables = {
10-
"MONO_METADATA_UPDATE": "1"
10+
"DOTNET_MODIFIABLE_ASSEMBLIES": "debug"
1111
};
1212
config.fetch_file_cb = function (asset) {
1313
return fetch (asset, { credentials: 'same-origin' });

src/mono/sample/mbr/console/ConsoleDelta.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<Target Name="TrickRuntimePackLocation" AfterTargets="ProcessFrameworkReferences">
2626
<ItemGroup>
2727
<RuntimePack>
28-
<PackageDirectory>$(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration)</PackageDirectory>
28+
<PackageDirectory>$(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(BuiltRuntimeConfiguration)</PackageDirectory>
2929
</RuntimePack>
3030
</ItemGroup>
3131
<Message Text="Packaged ID: %(RuntimePack.PackageDirectory)" Importance="high" />

src/mono/sample/mbr/console/Makefile

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ TOP=../../../../../
22
DOTNET:=$(TOP)./dotnet.sh
33
DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary
44

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

811
OS := $(shell uname -s)
@@ -12,14 +15,15 @@ else
1215
TARGET_OS=linux
1316
endif
1417

15-
MONO_ENV_OPTIONS ?=--interp=-inline
18+
MONO_ENV_OPTIONS = --interp
1619

1720
publish:
18-
$(DOTNET) publish -c $(CONFIG) -r $(TARGET_OS)-$(MONO_ARCH)
21+
$(DOTNET) publish -c $(CONFIG) -r $(TARGET_OS)-$(MONO_ARCH) -p:BuiltRuntimeConfiguration=$(BUILT_RUNTIME_CONFIG)
1922

2023
run: publish
2124
COMPlus_DebugWriteToStdErr=1 \
2225
MONO_ENV_OPTIONS="$(MONO_ENV_OPTIONS)" \
26+
DOTNET_MODIFIABLE_ASSEMBLIES=debug \
2327
$(TOP)artifacts/bin/ConsoleDelta/$(MONO_ARCH)/$(CONFIG)/$(TARGET_OS)-$(MONO_ARCH)/publish/ConsoleDelta
2428

2529
clean:

src/mono/wasm/runtime/driver.c

-6
Original file line numberDiff line numberDiff line change
@@ -514,12 +514,6 @@ mono_wasm_load_runtime (const char *unused, int debug_level)
514514
#else
515515
mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP_ONLY);
516516

517-
#ifdef ENABLE_METADATA_UPDATE
518-
if (monoeg_g_hasenv ("MONO_METADATA_UPDATE")) {
519-
interp_opts = "-inline";
520-
}
521-
#endif
522-
523517
/*
524518
* debug_level > 0 enables debugging and sets the debug log level to debug_level
525519
* debug_level == 0 disables debugging and enables interpreter optimizations

0 commit comments

Comments
 (0)