From c786e4f4f982b79410f8f6937c8069e2829d220a Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Mon, 12 Jul 2021 21:27:30 -0300 Subject: [PATCH] [mono][debugger] Support debug after hot-reload (#55220) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add test case for debug after hot reload. * Implementing debug after hot reload, on runtime side, and on client side for wasm. * Reuse some code. * Remove unrelated comment. * Reuse code. * Refactor new code. * Fix log. * Apply suggestions from code review Co-authored-by: Aleksey Kliger (λgeek) * Fixing hot reload without debug information. * replacing spaces with tabs. * Fixing CI. Co-authored-by: Aleksey Kliger (λgeek) --- src/mono/mono/component/debugger-agent.c | 58 ++++++- src/mono/mono/component/debugger-agent.h | 2 +- src/mono/mono/component/debugger-protocol.h | 4 +- src/mono/mono/component/debugger-stub.c | 13 +- src/mono/mono/component/debugger.h | 3 + src/mono/mono/component/hot_reload-stub.c | 17 +- src/mono/mono/component/hot_reload.c | 99 +++++++++-- src/mono/mono/component/hot_reload.h | 4 +- src/mono/mono/component/mini-wasm-debugger.c | 11 +- src/mono/mono/metadata/debug-mono-ppdb.c | 162 ++++++++++-------- src/mono/mono/metadata/debug-mono-ppdb.h | 3 + src/mono/mono/metadata/icall.c | 4 +- src/mono/mono/metadata/metadata-internals.h | 2 +- src/mono/mono/metadata/metadata-update.c | 13 +- src/mono/mono/metadata/metadata-update.h | 3 + src/mono/mono/metadata/mono-debug.c | 8 +- src/mono/mono/mini/interp/transform.c | 3 +- .../debugger/BrowserDebugProxy/DebugStore.cs | 103 +++++++++-- .../debugger/BrowserDebugProxy/MonoProxy.cs | 95 +++++++++- .../BrowserDebugProxy/MonoSDBHelper.cs | 21 ++- .../DebuggerTestSuite/BreakpointTests.cs | 58 +++++++ .../DebuggerTestSuite/DebuggerTestBase.cs | 49 ++++++ .../ApplyUpdateReferencedAssembly.csproj | 31 ++++ .../MethodBody1.cs | 34 ++++ .../MethodBody1_v1.cs | 34 ++++ .../MethodBody1_v2.cs | 34 ++++ .../deltascript.json | 7 + .../tests/debugger-test/debugger-test.cs | 75 ++++++++ .../tests/debugger-test/debugger-test.csproj | 28 ++- .../tests/debugger-test/runtime-debugger.js | 6 +- 30 files changed, 852 insertions(+), 132 deletions(-) create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1.cs create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs create mode 100644 src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/deltascript.json diff --git a/src/mono/mono/component/debugger-agent.c b/src/mono/mono/component/debugger-agent.c index f72c95392e974..bec98edb3c2f3 100644 --- a/src/mono/mono/component/debugger-agent.c +++ b/src/mono/mono/component/debugger-agent.c @@ -307,6 +307,14 @@ typedef struct { MonoStackHash *hashes; } EventInfo; +typedef struct { + MonoImage *image; + gconstpointer meta_bytes; + int meta_len; + gconstpointer pdb_bytes; + int pdb_len; +} EnCInfo; + #ifdef HOST_WIN32 #define get_last_sock_error() WSAGetLastError() #define MONO_EWOULDBLOCK WSAEWOULDBLOCK @@ -1604,7 +1612,7 @@ static MonoGHashTable *suspended_objs; #ifdef TARGET_WASM void -mono_init_debugger_agent_for_wasm (int log_level_parm) +mono_init_debugger_agent_for_wasm (int log_level_parm, MonoProfilerHandle *prof) { if (mono_atomic_cas_i32 (&agent_inited, 1, 0) == 1) return; @@ -1615,6 +1623,7 @@ mono_init_debugger_agent_for_wasm (int log_level_parm) ids_init(); objrefs = g_hash_table_new_full (NULL, NULL, NULL, mono_debugger_free_objref); obj_to_objref = g_hash_table_new (NULL, NULL); + pending_assembly_loads = g_ptr_array_new (); log_level = log_level_parm; event_requests = g_ptr_array_new (); @@ -1622,6 +1631,8 @@ mono_init_debugger_agent_for_wasm (int log_level_parm) transport = &transports [0]; memset(&debugger_wasm_thread, 0, sizeof(DebuggerTlsData)); agent_config.enabled = TRUE; + + mono_profiler_set_jit_done_callback (*prof, jit_done); } #endif @@ -3600,6 +3611,9 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case EVENT_KIND_TYPE_LOAD: buffer_add_typeid (&buf, domain, (MonoClass *)arg); break; + case MDBGPROT_EVENT_KIND_METHOD_UPDATE: + buffer_add_methodid (&buf, domain, (MonoMethod *)arg); + break; case EVENT_KIND_BREAKPOINT: case EVENT_KIND_STEP: { GET_DEBUGGER_TLS(); @@ -3655,6 +3669,15 @@ process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx case EVENT_KIND_KEEPALIVE: suspend_policy = SUSPEND_POLICY_NONE; break; + + case MDBGPROT_EVENT_KIND_ENC_UPDATE: { + EnCInfo *ei = (EnCInfo *)arg; + buffer_add_moduleid (&buf, mono_domain_get (), ei->image); + m_dbgprot_buffer_add_byte_array (&buf, (uint8_t *) ei->meta_bytes, ei->meta_len); + m_dbgprot_buffer_add_byte_array (&buf, (uint8_t *) ei->pdb_bytes, ei->pdb_len); + break; + } + default: g_assert_not_reached (); } @@ -4060,6 +4083,9 @@ jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) send_type_load (method->klass); + if (m_class_get_image(method->klass)->has_updates) { + process_profiler_event (MDBGPROT_EVENT_KIND_METHOD_UPDATE, method); + } if (jinfo) mono_de_add_pending_breakpoints (method, jinfo); } @@ -6517,6 +6543,28 @@ get_types_for_source_file (gpointer key, gpointer value, gpointer user_data) } } +static void +send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len) +{ + //TODO: if it came from debugger we don't need to pass the parameters back, they are already on debugger client side. + if (agent_config.enabled) { + int suspend_policy; + GSList *events; + mono_loader_lock (); + events = create_event_list (MDBGPROT_EVENT_KIND_ENC_UPDATE, NULL, NULL, NULL, &suspend_policy); + mono_loader_unlock (); + + EnCInfo info; + info.image = image; + info.meta_bytes = dpdb_bytes; + info.meta_len = dpdb_len; + info.pdb_bytes = dpdb_bytes; + info.pdb_len = dpdb_len; + + process_event (MDBGPROT_EVENT_KIND_ENC_UPDATE, &info, 0, NULL, events, suspend_policy); + } +} + static gboolean module_apply_changes (MonoImage *image, MonoArray *dmeta, MonoArray *dil, MonoArray *dpdb, MonoError *error) { @@ -6525,9 +6573,9 @@ module_apply_changes (MonoImage *image, MonoArray *dmeta, MonoArray *dil, MonoAr int32_t dmeta_len = mono_array_length_internal (dmeta); gpointer dil_bytes = (gpointer)mono_array_addr_internal (dil, char, 0); int32_t dil_len = mono_array_length_internal (dil); - gpointer dpdb_bytes G_GNUC_UNUSED = !dpdb ? NULL : (gpointer)mono_array_addr_internal (dpdb, char, 0); - int32_t dpdb_len G_GNUC_UNUSED = !dpdb ? 0 : mono_array_length_internal (dpdb); - mono_image_load_enc_delta (image, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error); + gpointer dpdb_bytes = !dpdb ? NULL : (gpointer)mono_array_addr_internal (dpdb, char, 0); + int32_t dpdb_len = !dpdb ? 0 : mono_array_length_internal (dpdb); + mono_image_load_enc_delta (image, dmeta_bytes, dmeta_len, dil_bytes, dil_len, dpdb_bytes, dpdb_len, error); return is_ok (error); } @@ -7239,6 +7287,7 @@ event_commands (int command, guint8 *p, guint8 *end, Buffer *buf) req->info = mono_de_set_breakpoint (NULL, METHOD_EXIT_IL_OFFSET, req, NULL); } else if (req->event_kind == EVENT_KIND_EXCEPTION) { } else if (req->event_kind == EVENT_KIND_TYPE_LOAD) { + } else if (req->event_kind == MDBGPROT_EVENT_KIND_METHOD_UPDATE) { } else { if (req->nmodifiers) { g_free (req); @@ -10278,6 +10327,7 @@ debugger_agent_add_function_pointers(MonoComponentDebugger* fn_table) fn_table->debug_log_is_enabled = debugger_agent_debug_log_is_enabled; fn_table->send_crash = mono_debugger_agent_send_crash; fn_table->transport_handshake = debugger_agent_transport_handshake; + fn_table->send_enc_delta = send_enc_delta; } diff --git a/src/mono/mono/component/debugger-agent.h b/src/mono/mono/component/debugger-agent.h index 60f4244820247..ad5a0cb1d0b1d 100644 --- a/src/mono/mono/component/debugger-agent.h +++ b/src/mono/mono/component/debugger-agent.h @@ -23,7 +23,7 @@ DebuggerTlsData* mono_wasm_get_tls (void); void -mono_init_debugger_agent_for_wasm (int log_level); +mono_init_debugger_agent_for_wasm (int log_level, MonoProfilerHandle *prof); void mono_wasm_save_thread_context (void); diff --git a/src/mono/mono/component/debugger-protocol.h b/src/mono/mono/component/debugger-protocol.h index 24e8a2e42c27e..292b723cc351b 100644 --- a/src/mono/mono/component/debugger-protocol.h +++ b/src/mono/mono/component/debugger-protocol.h @@ -294,7 +294,9 @@ typedef enum { MDBGPROT_EVENT_KIND_KEEPALIVE = 14, MDBGPROT_EVENT_KIND_USER_BREAK = 15, MDBGPROT_EVENT_KIND_USER_LOG = 16, - MDBGPROT_EVENT_KIND_CRASH = 17 + MDBGPROT_EVENT_KIND_CRASH = 17, + MDBGPROT_EVENT_KIND_ENC_UPDATE = 18, + MDBGPROT_EVENT_KIND_METHOD_UPDATE = 19, } MdbgProtEventKind; typedef enum { diff --git a/src/mono/mono/component/debugger-stub.c b/src/mono/mono/component/debugger-stub.c index 1c5467adadddd..3628c0fbc532a 100644 --- a/src/mono/mono/component/debugger-stub.c +++ b/src/mono/mono/component/debugger-stub.c @@ -66,6 +66,9 @@ stub_mono_wasm_breakpoint_hit (void); static void stub_mono_wasm_single_step_hit (void); +static void +stub_send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len); + static MonoComponentDebugger fn_table = { { MONO_COMPONENT_ITF_VERSION, &debugger_avaliable }, &stub_debugger_init, @@ -87,7 +90,10 @@ static MonoComponentDebugger fn_table = { //wasm &stub_mono_wasm_breakpoint_hit, - &stub_mono_wasm_single_step_hit + &stub_mono_wasm_single_step_hit, + + //HotReload + &stub_send_enc_delta, }; static bool @@ -201,3 +207,8 @@ static void stub_mono_wasm_single_step_hit (void) { } + +static void +stub_send_enc_delta (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len) +{ +} diff --git a/src/mono/mono/component/debugger.h b/src/mono/mono/component/debugger.h index de50c5641137a..81ef259f3ebe1 100644 --- a/src/mono/mono/component/debugger.h +++ b/src/mono/mono/component/debugger.h @@ -194,6 +194,9 @@ typedef struct MonoComponentDebugger { void (*mono_wasm_breakpoint_hit) (void); void (*mono_wasm_single_step_hit) (void); + //HotReload + void (*send_enc_delta) (MonoImage *image, gconstpointer dmeta_bytes, int32_t dmeta_len, gconstpointer dpdb_bytes, int32_t dpdb_len); + } MonoComponentDebugger; diff --git a/src/mono/mono/component/hot_reload-stub.c b/src/mono/mono/component/hot_reload-stub.c index 48033c1c7a0cf..0ebf79a50c8f6 100644 --- a/src/mono/mono/component/hot_reload-stub.c +++ b/src/mono/mono/component/hot_reload-stub.c @@ -15,7 +15,7 @@ static bool hot_reload_stub_available (void); static void -hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); +hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); static MonoComponentHotReload * component_hot_reload_stub_init (void); @@ -59,6 +59,9 @@ hot_reload_stub_table_bounds_check (MonoImage *base_image, int table_index, int static gboolean hot_reload_stub_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); +static gpointer +hot_reload_stub_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx); + static gboolean hot_reload_stub_has_modified_rows (const MonoTableInfo *table); @@ -78,6 +81,7 @@ static MonoComponentHotReload fn_table = { &hot_reload_stub_get_updated_method_rva, &hot_reload_stub_table_bounds_check, &hot_reload_stub_delta_heap_lookup, + &hot_reload_stub_get_updated_method_ppdb, &hot_reload_stub_has_modified_rows, }; @@ -143,7 +147,7 @@ hot_reload_stub_relative_delta_index (MonoImage *image_dmeta, int token) } void -hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error) +hot_reload_stub_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error) { mono_error_set_not_supported (error, "Hot reload not supported in this runtime."); } @@ -176,8 +180,15 @@ hot_reload_stub_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc g_assert_not_reached (); } +static gpointer +hot_reload_stub_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx) +{ + g_assert_not_reached (); +} + static gboolean -hot_reload_stub_has_modified_rows (const MonoTableInfo *table){ +hot_reload_stub_has_modified_rows (const MonoTableInfo *table) +{ return FALSE; } diff --git a/src/mono/mono/component/hot_reload.c b/src/mono/mono/component/hot_reload.c index 76861056eda15..32820888b2638 100644 --- a/src/mono/mono/component/hot_reload.c +++ b/src/mono/mono/component/hot_reload.c @@ -53,7 +53,7 @@ static void hot_reload_effective_table_slow (const MonoTableInfo **t, int *idx); static void -hot_reload_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); +hot_reload_apply_changes (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); static int hot_reload_relative_delta_index (MonoImage *image_dmeta, int token); @@ -73,6 +73,9 @@ hot_reload_table_bounds_check (MonoImage *base_image, int table_index, int token static gboolean hot_reload_delta_heap_lookup (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); +static gpointer +hot_reload_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx); + static gboolean hot_reload_has_modified_rows (const MonoTableInfo *table); @@ -92,6 +95,7 @@ static MonoComponentHotReload fn_table = { &hot_reload_get_updated_method_rva, &hot_reload_table_bounds_check, &hot_reload_delta_heap_lookup, + &hot_reload_get_updated_method_ppdb, &hot_reload_has_modified_rows, }; @@ -150,6 +154,9 @@ typedef struct _DeltaInfo { /* Maps MethodDef token indices to a pointer into the RVA of the delta IL */ GHashTable *method_table_update; + /* Maps MethodDef token indices to a pointer into the RVA of the delta PPDB */ + GHashTable *method_ppdb_table_update; + // for each table, the row in the EncMap table that has the first token for remapping it? uint32_t enc_recs [MONO_TABLE_NUM]; delta_row_count count [MONO_TABLE_NUM]; @@ -319,6 +326,8 @@ delta_info_destroy (DeltaInfo *dinfo) { if (dinfo->method_table_update) g_hash_table_destroy (dinfo->method_table_update); + if (dinfo->method_ppdb_table_update) + g_hash_table_destroy (dinfo->method_ppdb_table_update); g_free (dinfo); } @@ -1173,17 +1182,50 @@ apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, gconstpointer } static void -set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, uint32_t token_index, const char* il_address) +set_update_method (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, uint32_t token_index, const char* il_address, const char* pdb_address) { mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "setting method 0x%08x in g=%d IL=%p", token_index, generation, (void*)il_address); /* FIXME: this is a race if other threads are doing a lookup. */ g_hash_table_insert (base_info->method_table_update, GUINT_TO_POINTER (token_index), GUINT_TO_POINTER (generation)); g_hash_table_insert (delta_info->method_table_update, GUINT_TO_POINTER (token_index), (gpointer) il_address); + g_hash_table_insert (delta_info->method_ppdb_table_update, GUINT_TO_POINTER (token_index), (gpointer) pdb_address); +} + +static const char * +hot_reload_get_method_debug_information (MonoImage *image_dppdb, int idx) +{ + if (!image_dppdb) + return NULL; + + MonoTableInfo *table_encmap = &image_dppdb->tables [MONO_TABLE_ENCMAP]; + int rows = table_info_get_rows (table_encmap); + for (int i = 0; i < rows ; ++i) { + guint32 cols [MONO_ENCMAP_SIZE]; + mono_metadata_decode_row (table_encmap, i, cols, MONO_ENCMAP_SIZE); + int map_token = cols [MONO_ENCMAP_TOKEN]; + int token_table = mono_metadata_token_table (map_token); + if (token_table != MONO_TABLE_METHODBODY) + continue; + int token_index = mono_metadata_token_index (map_token); + if (token_index == idx) + { + guint32 cols [MONO_METHODBODY_SIZE]; + MonoTableInfo *methodbody_table = &image_dppdb->tables [MONO_TABLE_METHODBODY]; + mono_metadata_decode_row (methodbody_table, i, cols, MONO_METHODBODY_SIZE); + if (!cols [MONO_METHODBODY_SEQ_POINTS]) + return NULL; + + const char *ptr = mono_metadata_blob_heap (image_dppdb, cols [MONO_METHODBODY_SEQ_POINTS]); + return ptr; + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb encmap i=%d: token=0x%08x (table=%s)", i, map_token, mono_meta_table_name (token_table)); + } + return NULL; } /* do actuall enclog application */ static gboolean -apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, MonoError *error) +apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, MonoImage *image_dppdb, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, MonoError *error) { MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG]; int rows = table_info_get_rows (table_enclog); @@ -1254,12 +1296,16 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen base_info->method_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); if (!delta_info->method_table_update) delta_info->method_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); + if (!delta_info->method_ppdb_table_update) + + delta_info->method_ppdb_table_update = g_hash_table_new (g_direct_hash, g_direct_equal); int mapped_token = hot_reload_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; - set_update_method (image_base, base_info, generation, image_dmeta, delta_info, token_index, il_address); + const char *method_debug_information = hot_reload_get_method_debug_information (image_dppdb, token_index); + set_update_method (image_base, base_info, generation, image_dmeta, delta_info, token_index, il_address, method_debug_information); } else { /* rva points probably into image_base IL stream. can this ever happen? */ g_print ("TODO: this case is still a bit contrived. token=0x%08x with rva=0x%04x\n", log_token, rva); @@ -1300,7 +1346,7 @@ apply_enclog_pass2 (MonoImage *image_base, BaselineInfo *base_info, uint32_t gen * LOCKING: Takes the publish_lock */ void -hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes_orig, uint32_t dil_length, MonoError *error) +hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint32_t dmeta_length, gconstpointer dil_bytes_orig, uint32_t dil_length, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error) { if (!assembly_update_supported (image_base->assembly)) { mono_error_set_invalid_operation (error, "The assembly can not be edited or changed."); @@ -1335,7 +1381,16 @@ hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint /* makes a copy of dil_bytes_orig */ gpointer dil_bytes = open_dil_data (image_base, dil_bytes_orig, dil_length); - /* TODO: make a copy of the dpdb bytes, once we consume them */ + + MonoImage *image_dpdb = NULL; + if (dpdb_length > 0) + { + MonoImage *image_dpdb = image_open_dmeta_from_data (image_base, generation, dpdb_bytes_orig, dpdb_length); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image string size: 0x%08x", image_dpdb->heap_strings.size); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image user string size: 0x%08x", image_dpdb->heap_us.size); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image blob heap addr: %p", image_dpdb->heap_blob.data); + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "pdb image blob heap size: 0x%08x", image_dpdb->heap_blob.size); + } BaselineInfo *base_info = baseline_info_lookup_or_add (image_base); @@ -1391,7 +1446,7 @@ hot_reload_apply_changes (MonoImage *image_base, gconstpointer dmeta_bytes, uint 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, base_info, generation, image_dmeta, delta_info, dil_bytes, dil_length, error)) { + if (!apply_enclog_pass2 (image_base, base_info, generation, image_dmeta, image_dpdb, delta_info, 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)); hot_reload_update_cancel (generation); return; @@ -1434,7 +1489,7 @@ metadata_update_count_updates (MonoImage *base) } static gpointer -get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t idx) +get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t idx, gboolean is_pdb) { gpointer loc = NULL; uint32_t cur = hot_reload_get_thread_generation (); @@ -1448,8 +1503,13 @@ get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t g_assert (delta_info); if (delta_info->generation > cur) break; - if (delta_info->method_table_update) { - gpointer result = g_hash_table_lookup (delta_info->method_table_update, GUINT_TO_POINTER (idx)); + GHashTable *table = NULL; + if (is_pdb) + table = delta_info->method_ppdb_table_update; + else + table = delta_info->method_table_update; + if (table) { + gpointer result = g_hash_table_lookup (table, GUINT_TO_POINTER (idx)); /* if it's not in the table of a later generation, the * later generation didn't modify the method */ @@ -1463,6 +1523,23 @@ get_method_update_rva (MonoImage *image_base, BaselineInfo *base_info, uint32_t return loc; } +gpointer +hot_reload_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx) +{ + BaselineInfo *info = baseline_info_lookup (base_image); + if (!info) + return NULL; + gpointer loc = NULL; + /* EnC case */ + if (G_UNLIKELY (info->method_table_update)) { + uint32_t gen = GPOINTER_TO_UINT (g_hash_table_lookup (info->method_table_update, GUINT_TO_POINTER (idx))); + if (G_UNLIKELY (gen > 0)) { + loc = get_method_update_rva (base_image, info, idx, TRUE); + } + } + return loc; +} + gpointer hot_reload_get_updated_method_rva (MonoImage *base_image, uint32_t idx) { @@ -1474,7 +1551,7 @@ hot_reload_get_updated_method_rva (MonoImage *base_image, uint32_t idx) if (G_UNLIKELY (info->method_table_update)) { uint32_t gen = GPOINTER_TO_UINT (g_hash_table_lookup (info->method_table_update, GUINT_TO_POINTER (idx))); if (G_UNLIKELY (gen > 0)) { - loc = get_method_update_rva (base_image, info, idx); + loc = get_method_update_rva (base_image, info, idx, FALSE); } } return loc; diff --git a/src/mono/mono/component/hot_reload.h b/src/mono/mono/component/hot_reload.h index 0bdb63d121d01..26ca0089a69ef 100644 --- a/src/mono/mono/component/hot_reload.h +++ b/src/mono/mono/component/hot_reload.h @@ -23,14 +23,14 @@ typedef struct _MonoComponentHotReload { void (*cleanup_on_close) (MonoImage *image); void (*effective_table_slow) (const MonoTableInfo **t, int *idx); int (*relative_delta_index) (MonoImage *image_dmeta, int token); - void (*apply_changes) (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); + void (*apply_changes) (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb_bytes_orig, uint32_t dpdb_length, MonoError *error); void (*image_close_except_pools_all) (MonoImage *base_image); void (*image_close_all) (MonoImage *base_image); gpointer (*get_updated_method_rva) (MonoImage *base_image, uint32_t idx); gboolean (*table_bounds_check) (MonoImage *base_image, int table_index, int token_index); gboolean (*delta_heap_lookup) (MonoImage *base_image, MetadataHeapGetterFunc get_heap, uint32_t orig_index, MonoImage **image_out, uint32_t *index_out); + gpointer (*get_updated_method_ppdb) (MonoImage *base_image, uint32_t idx); gboolean (*has_modified_rows) (const MonoTableInfo *table); - } MonoComponentHotReload; MONO_COMPONENT_EXPORT_ENTRYPOINT diff --git a/src/mono/mono/component/mini-wasm-debugger.c b/src/mono/mono/component/mini-wasm-debugger.c index 4df2172841efe..5dd9d8ce1d402 100644 --- a/src/mono/mono/component/mini-wasm-debugger.c +++ b/src/mono/mono/component/mini-wasm-debugger.c @@ -78,12 +78,6 @@ void wasm_debugger_log (int level, const gchar *format, ...) g_free (mesg); } -static void -jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo) -{ - mono_de_add_pending_breakpoints (method, jinfo); -} - static void appdomain_load (MonoProfiler *prof, MonoDomain *domain) { @@ -149,9 +143,9 @@ handle_multiple_ss_requests (void) { static void mono_wasm_enable_debugging_internal (int debug_level) { + log_level = debug_level; PRINT_DEBUG_MSG (1, "DEBUGGING ENABLED\n"); debugger_enabled = TRUE; - log_level = debug_level; } static void @@ -186,7 +180,6 @@ mono_wasm_debugger_init (MonoDefaults *mono_defaults) get_mini_debug_options ()->load_aot_jit_info_eagerly = TRUE; MonoProfilerHandle prof = mono_profiler_create (NULL); - mono_profiler_set_jit_done_callback (prof, jit_done); //FIXME support multiple appdomains mono_profiler_set_domain_loaded_callback (prof, appdomain_load); mono_profiler_set_assembly_loaded_callback (prof, assembly_loaded); @@ -197,7 +190,7 @@ mono_wasm_debugger_init (MonoDefaults *mono_defaults) trans.send = receive_debugger_agent_message; mono_debugger_agent_register_transport (&trans); - mono_init_debugger_agent_for_wasm (log_level); + mono_init_debugger_agent_for_wasm (log_level, &prof); } static void diff --git a/src/mono/mono/metadata/debug-mono-ppdb.c b/src/mono/mono/metadata/debug-mono-ppdb.c index 070f9b8b0cf28..67bd4715d33bd 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.c +++ b/src/mono/mono/metadata/debug-mono-ppdb.c @@ -456,95 +456,57 @@ mono_ppdb_is_embedded (MonoPPDBFile *ppdb) return ppdb->is_embedded; } -void -mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) +static int +mono_ppdb_get_seq_points_internal (const char* ptr, MonoSymSeqPoint **seq_points, int *n_seq_points, int docidx, MonoImage *image, MonoPPDBFile *ppdb, GPtrArray **sfiles, char **source_file, int **source_files, GPtrArray **sindexes, gboolean read_doc_value) { - MonoPPDBFile *ppdb = minfo->handle->ppdb; - MonoImage *image = ppdb->image; - MonoMethod *method = minfo->method; - MonoTableInfo *tables = image->tables; - guint32 cols [MONO_METHODBODY_SIZE]; - const char *ptr; - const char *end; - MonoDebugSourceInfo *docinfo; - int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col; - gboolean first = TRUE, first_non_hidden = TRUE; GArray *sps; MonoSymSeqPoint sp; - GPtrArray *sfiles = NULL; - GPtrArray *sindexes = NULL; - - if (source_file) - *source_file = NULL; - if (source_file_list) - *source_file_list = NULL; - if (source_files) - *source_files = NULL; - if (seq_points) - *seq_points = NULL; - if (n_seq_points) - *n_seq_points = 0; - - if (source_file_list) - *source_file_list = sfiles = g_ptr_array_new (); - if (source_files) - sindexes = g_ptr_array_new (); - - if (!method->token || table_info_get_rows (&tables [MONO_TABLE_METHODBODY]) == 0) - return; - - method_idx = mono_metadata_token_index (method->token); - - MonoTableInfo *methodbody_table = &tables [MONO_TABLE_METHODBODY]; - if (G_UNLIKELY (method_idx - 1 >= table_info_get_rows (methodbody_table))) { - char *method_name = mono_method_full_name (method, FALSE); - g_error ("Method idx %d is greater than number of rows (%d) in PPDB MethodDebugInformation table, for method %s in '%s'. Likely a malformed PDB file.", - method_idx - 1, table_info_get_rows (methodbody_table), method_name, image->name); - g_free (method_name); - } - mono_metadata_decode_row (methodbody_table, method_idx - 1, cols, MONO_METHODBODY_SIZE); - - docidx = cols [MONO_METHODBODY_DOCUMENT]; - - if (!cols [MONO_METHODBODY_SEQ_POINTS]) - return; - - ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]); - size = mono_metadata_decode_blob_size (ptr, &ptr); - end = ptr + size; + int iloffset = 0; + int start_line = 0; + int start_col = 0; + int delta_cols = 0; + gboolean first_non_hidden = TRUE; + int adv_line, adv_col; + int size = mono_metadata_decode_blob_size (ptr, &ptr); + const char* end = ptr + size; + MonoDebugSourceInfo *docinfo; + gboolean first = TRUE; sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint)); /* Header */ /* LocalSignature */ mono_metadata_decode_value (ptr, &ptr); - if (docidx == 0) + if (docidx == 0 && read_doc_value) docidx = mono_metadata_decode_value (ptr, &ptr); - docinfo = get_docinfo (ppdb, image, docidx); - - if (sfiles) - g_ptr_array_add (sfiles, docinfo); + if (sfiles && *sfiles) + { + docinfo = get_docinfo (ppdb, image, docidx); + g_ptr_array_add (*sfiles, docinfo); + } - if (source_file) + if (source_file && *source_file) *source_file = g_strdup (docinfo->source_file); iloffset = 0; start_line = 0; start_col = 0; while (ptr < end) { - delta_il = mono_metadata_decode_value (ptr, &ptr); - if (!first && delta_il == 0) { + int delta_il = mono_metadata_decode_value (ptr, &ptr); + if (!first && delta_il == 0 && read_doc_value) { /* subsequent-document-record */ docidx = mono_metadata_decode_value (ptr, &ptr); docinfo = get_docinfo (ppdb, image, docidx); - if (sfiles) - g_ptr_array_add (sfiles, docinfo); + if (sfiles && *sfiles) + { + g_ptr_array_add (*sfiles, docinfo); + } continue; } iloffset += delta_il; first = FALSE; - delta_lines = mono_metadata_decode_value (ptr, &ptr); + int delta_lines = mono_metadata_decode_value (ptr, &ptr); if (delta_lines == 0) delta_cols = mono_metadata_decode_value (ptr, &ptr); else @@ -574,8 +536,9 @@ mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrAr sp.end_column = start_col + delta_cols; g_array_append_val (sps, sp); - if (source_files) - g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1)); + if (sindexes && *sindexes) { + g_ptr_array_add (*sindexes, GUINT_TO_POINTER ((*sfiles)->len - 1)); + } } if (n_seq_points) { @@ -584,15 +547,76 @@ mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrAr *seq_points = g_new (MonoSymSeqPoint, sps->len); memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint)); } + int sps_len = sps->len; + g_array_free (sps, TRUE); + return sps_len; +} + +void +mono_ppdb_get_seq_points_enc (const char* ptr, MonoSymSeqPoint **seq_points, int *n_seq_points) +{ + mono_ppdb_get_seq_points_internal (ptr, seq_points, n_seq_points, 0, NULL, NULL, NULL, NULL, NULL, NULL, FALSE); +} + +void +mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) +{ + MonoPPDBFile *ppdb = minfo->handle->ppdb; + MonoImage *image = ppdb->image; + MonoMethod *method = minfo->method; + MonoTableInfo *tables = image->tables; + guint32 cols [MONO_METHODBODY_SIZE]; + const char *ptr; + int i, method_idx, docidx; + GPtrArray *sfiles = NULL; + GPtrArray *sindexes = NULL; + + if (source_file) + *source_file = NULL; + if (source_file_list) + *source_file_list = NULL; + if (source_files) + *source_files = NULL; + if (seq_points) + *seq_points = NULL; + if (n_seq_points) + *n_seq_points = 0; + + if (source_file_list) + *source_file_list = sfiles = g_ptr_array_new (); + if (source_files) + sindexes = g_ptr_array_new (); + + if (!method->token || table_info_get_rows (&tables [MONO_TABLE_METHODBODY]) == 0) + return; + + method_idx = mono_metadata_token_index (method->token); + + MonoTableInfo *methodbody_table = &tables [MONO_TABLE_METHODBODY]; + if (G_UNLIKELY (method_idx - 1 >= table_info_get_rows (methodbody_table))) { + char *method_name = mono_method_full_name (method, FALSE); + g_error ("Method idx %d is greater than number of rows (%d) in PPDB MethodDebugInformation table, for method %s in '%s'. Likely a malformed PDB file.", + method_idx - 1, table_info_get_rows (methodbody_table), method_name, image->name); + g_free (method_name); + } + mono_metadata_decode_row (methodbody_table, method_idx - 1, cols, MONO_METHODBODY_SIZE); + + docidx = cols [MONO_METHODBODY_DOCUMENT]; + + if (!cols [MONO_METHODBODY_SEQ_POINTS]) + return; + + ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]); + + int sps_len = mono_ppdb_get_seq_points_internal (ptr, seq_points, n_seq_points, docidx, image, ppdb, &sfiles, source_file, source_files, &sindexes, TRUE); if (source_files) { - *source_files = g_new (int, sps->len); - for (i = 0; i < sps->len; ++i) + *source_files = g_new (int, sps_len); + for (i = 0; i < sps_len; ++i) (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i)); g_ptr_array_free (sindexes, TRUE); } - g_array_free (sps, TRUE); } MonoDebugLocalsInfo* diff --git a/src/mono/mono/metadata/debug-mono-ppdb.h b/src/mono/mono/metadata/debug-mono-ppdb.h index c27a97069c97e..e16317f9fc728 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.h +++ b/src/mono/mono/metadata/debug-mono-ppdb.h @@ -32,6 +32,9 @@ mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset); void mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points); +void +mono_ppdb_get_seq_points_enc (const char* ptr, MonoSymSeqPoint **seq_points, int *n_seq_points); + MonoDebugLocalsInfo* mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo); diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 7c3f33fc79219..240479fb90a56 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -5782,9 +5782,9 @@ ves_icall_AssemblyExtensions_ApplyUpdate (MonoAssembly *assm, g_assert (dmeta_len >= 0); MonoImage *image_base = assm->image; g_assert (image_base); - // TODO: use dpdb_bytes - mono_image_load_enc_delta (image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, error); + mono_image_load_enc_delta (image_base, dmeta_bytes, dmeta_len, dil_bytes, dil_len, dpdb_bytes, dpdb_len, error); + mono_error_set_pending_exception (error); } diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 20f36c60e8f5d..9a0d030a9f171 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -822,7 +822,7 @@ int mono_image_relative_delta_index (MonoImage *image_dmeta, int token); MONO_COMPONENT_API void -mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error); +mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error); gboolean mono_image_load_cli_header (MonoImage *image, MonoCLIImageInfo *iinfo); diff --git a/src/mono/mono/metadata/metadata-update.c b/src/mono/mono/metadata/metadata-update.c index 8787b28bca658..71520b8aa1e67 100644 --- a/src/mono/mono/metadata/metadata-update.c +++ b/src/mono/mono/metadata/metadata-update.c @@ -73,9 +73,12 @@ mono_image_relative_delta_index (MonoImage *image_dmeta, int token) } void -mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, MonoError *error) +mono_image_load_enc_delta (MonoImage *base_image, gconstpointer dmeta, uint32_t dmeta_len, gconstpointer dil, uint32_t dil_len, gconstpointer dpdb, uint32_t dpdb_len, MonoError *error) { - mono_component_hot_reload ()->apply_changes (base_image, dmeta, dmeta_len, dil, dil_len, error); + mono_component_hot_reload ()->apply_changes (base_image, dmeta, dmeta_len, dil, dil_len, dpdb, dpdb_len, error); + if (is_ok (error)) { + mono_component_debugger ()->send_enc_delta (base_image, dmeta, dmeta_len, dpdb, dpdb_len); + } } static void @@ -108,6 +111,12 @@ mono_metadata_update_get_updated_method_rva (MonoImage *base_image, uint32_t idx return mono_component_hot_reload ()->get_updated_method_rva (base_image, idx); } +gpointer +mono_metadata_update_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx) +{ + return mono_component_hot_reload ()->get_updated_method_ppdb (base_image, idx); +} + gboolean mono_metadata_update_table_bounds_check (MonoImage *base_image, int table_index, int token_index) { diff --git a/src/mono/mono/metadata/metadata-update.h b/src/mono/mono/metadata/metadata-update.h index 52bfa701e57f8..15d0d51e10f4a 100644 --- a/src/mono/mono/metadata/metadata-update.h +++ b/src/mono/mono/metadata/metadata-update.h @@ -48,6 +48,9 @@ mono_metadata_update_image_close_all (MonoImage *base_image); gpointer mono_metadata_update_get_updated_method_rva (MonoImage *base_image, uint32_t idx); +gpointer +mono_metadata_update_get_updated_method_ppdb (MonoImage *base_image, uint32_t idx); + gboolean mono_metadata_update_table_bounds_check (MonoImage *base_image, int table_index, int token_index); diff --git a/src/mono/mono/metadata/mono-debug.c b/src/mono/mono/metadata/mono-debug.c index bdef02c84f020..58ecee24d4590 100644 --- a/src/mono/mono/metadata/mono-debug.c +++ b/src/mono/mono/metadata/mono-debug.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #if NO_UNALIGNED_ACCESS @@ -1115,7 +1116,12 @@ mono_debug_enabled (void) void mono_debug_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points) { - if (minfo->handle->ppdb) + MonoImage* img = m_class_get_image (minfo->method->klass); + if (img->has_updates) { + int idx = mono_metadata_token_index (minfo->method->token); + gpointer ptr = mono_metadata_update_get_updated_method_ppdb (img, idx); + mono_ppdb_get_seq_points_enc (ptr, seq_points, n_seq_points); + } else if (minfo->handle->ppdb) mono_ppdb_get_seq_points (minfo, source_file, source_file_list, source_files, seq_points, n_seq_points); else mono_debug_symfile_get_seq_points (minfo, source_file, source_file_list, source_files, seq_points, n_seq_points); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 0eed6c7146e01..e75fefe393c7a 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -9758,8 +9758,7 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Mon MonoJitMemoryManager *jit_mm = get_default_jit_mm (); jit_mm_lock (jit_mm); - if (!g_hash_table_lookup (jit_mm->seq_points, imethod->method)) - g_hash_table_insert (jit_mm->seq_points, imethod->method, imethod->jinfo->seq_points); + g_hash_table_replace (jit_mm->seq_points, imethod->method, imethod->jinfo->seq_points); jit_mm_unlock (jit_mm); // FIXME: Add a different callback ? diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs index 81c419e4c5850..c1e8ce60bda6c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs @@ -62,12 +62,12 @@ internal class BreakpointRequest public int Line { get; private set; } public int Column { get; private set; } public string Condition { get; private set; } - public MethodInfo Method { get; private set; } + public MethodInfo Method { get; set; } private JObject request; public bool IsResolved => Assembly != null; - public List Locations { get; } = new List(); + public List Locations { get; set; } = new List(); public override string ToString() => $"BreakpointRequest Assembly: {Assembly} File: {File} Line: {Line} Column: {Column}"; @@ -321,21 +321,26 @@ internal class MethodInfo public string Name { get; } public MethodDebugInformation DebugInformation; public MethodDefinitionHandle methodDefHandle; + private MetadataReader pdbMetadataReader; - public SourceLocation StartLocation { get; } - public SourceLocation EndLocation { get; } + public SourceLocation StartLocation { get; set;} + public SourceLocation EndLocation { get; set;} public AssemblyInfo Assembly { get; } public int Token { get; } + internal bool IsEnCMethod; + internal LocalScopeHandleCollection localScopes; public bool IsStatic() => (methodDef.Attributes & MethodAttributes.Static) != 0; - public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type) + public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, int token, SourceFile source, TypeInfo type, MetadataReader asmMetadataReader, MetadataReader pdbMetadataReader) { this.Assembly = assembly; - this.methodDef = Assembly.asmMetadataReader.GetMethodDefinition(methodDefHandle); - this.DebugInformation = Assembly.pdbMetadataReader.GetMethodDebugInformation(methodDefHandle.ToDebugInformationHandle()); + this.methodDef = asmMetadataReader.GetMethodDefinition(methodDefHandle); + this.DebugInformation = pdbMetadataReader.GetMethodDebugInformation(methodDefHandle.ToDebugInformationHandle()); this.source = source; this.Token = token; this.methodDefHandle = methodDefHandle; - this.Name = Assembly.asmMetadataReader.GetString(methodDef.Name); + this.Name = asmMetadataReader.GetString(methodDef.Name); + this.pdbMetadataReader = pdbMetadataReader; + this.IsEnCMethod = false; if (!DebugInformation.SequencePointsBlob.IsNil) { var sps = DebugInformation.GetSequencePoints(); @@ -358,6 +363,37 @@ public MethodInfo(AssemblyInfo assembly, MethodDefinitionHandle methodDefHandle, StartLocation = new SourceLocation(this, start); EndLocation = new SourceLocation(this, end); } + localScopes = pdbMetadataReader.GetLocalScopes(methodDefHandle); + } + + public void UpdateEnC(MetadataReader asmMetadataReader, MetadataReader pdbMetadataReaderParm, int method_idx) + { + this.DebugInformation = pdbMetadataReaderParm.GetMethodDebugInformation(MetadataTokens.MethodDebugInformationHandle(method_idx)); + this.pdbMetadataReader = pdbMetadataReaderParm; + this.IsEnCMethod = true; + if (!DebugInformation.SequencePointsBlob.IsNil) + { + var sps = DebugInformation.GetSequencePoints(); + SequencePoint start = sps.First(); + SequencePoint end = sps.First(); + + foreach (SequencePoint sp in sps) + { + if (sp.StartLine < start.StartLine) + start = sp; + else if (sp.StartLine == start.StartLine && sp.StartColumn < start.StartColumn) + start = sp; + + if (sp.EndLine > end.EndLine) + end = sp; + else if (sp.EndLine == end.EndLine && sp.EndColumn > end.EndColumn) + end = sp; + } + + StartLocation = new SourceLocation(this, start); + EndLocation = new SourceLocation(this, end); + } + localScopes = pdbMetadataReader.GetLocalScopes(MetadataTokens.MethodDefinitionHandle(method_idx)); } public SourceLocation GetLocationByIl(int pos) @@ -394,18 +430,18 @@ public VarInfo[] GetLiveVarsAt(int offset) res.Add(new VarInfo(parameter, Assembly.asmMetadataReader)); } - var localScopes = Assembly.pdbMetadataReader.GetLocalScopes(methodDefHandle); + foreach (var localScopeHandle in localScopes) { - var localScope = Assembly.pdbMetadataReader.GetLocalScope(localScopeHandle); + var localScope = pdbMetadataReader.GetLocalScope(localScopeHandle); if (localScope.StartOffset <= offset && localScope.EndOffset > offset) { var localVariables = localScope.GetLocalVariables(); foreach (var localVariableHandle in localVariables) { - var localVariable = Assembly.pdbMetadataReader.GetLocalVariable(localVariableHandle); + var localVariable = pdbMetadataReader.GetLocalVariable(localVariableHandle); if (localVariable.Attributes != LocalVariableAttributes.DebuggerHidden) - res.Add(new VarInfo(localVariable, Assembly.pdbMetadataReader)); + res.Add(new VarInfo(localVariable, pdbMetadataReader)); } } } @@ -465,6 +501,8 @@ internal class AssemblyInfo internal string Url { get; } internal MetadataReader asmMetadataReader { get; } internal MetadataReader pdbMetadataReader { get; set; } + internal List enCMemoryStream = new List(); + internal List enCMetadataReader = new List(); internal PEReader peReader; internal MemoryStream asmStream; internal MemoryStream pdbStream; @@ -496,11 +534,39 @@ public unsafe AssemblyInfo(string url, byte[] assembly, byte[] pdb) Populate(); } + public bool EnC(byte[] meta, byte[] pdb) + { + var asmStream = new MemoryStream(meta); + MetadataReader asmMetadataReader = MetadataReaderProvider.FromMetadataStream(asmStream).GetMetadataReader(); + var pdbStream = new MemoryStream(pdb); + MetadataReader pdbMetadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); + enCMemoryStream.Add(asmStream); + enCMemoryStream.Add(pdbStream); + enCMetadataReader.Add(asmMetadataReader); + enCMetadataReader.Add(pdbMetadataReader); + PopulateEnC(asmMetadataReader, pdbMetadataReader); + return true; + } + public AssemblyInfo(ILogger logger) { this.logger = logger; } + private void PopulateEnC(MetadataReader asmMetadataReaderParm, MetadataReader pdbMetadataReaderParm) + { + int i = 1; + foreach (EntityHandle encMapHandle in asmMetadataReaderParm.GetEditAndContinueMapEntries()) + { + if (encMapHandle.Kind == HandleKind.MethodDebugInformation) + { + var method = methods[asmMetadataReader.GetRowNumber(encMapHandle)]; + method.UpdateEnC(asmMetadataReaderParm, pdbMetadataReaderParm, i); + i++; + } + } + } + private void Populate() { var d2s = new Dictionary(); @@ -543,7 +609,7 @@ SourceFile FindSource(DocumentHandle doc, int rowid, string documentName) var document = pdbMetadataReader.GetDocument(methodDebugInformation.Document); var documentName = pdbMetadataReader.GetString(document.Name); SourceFile source = FindSource(methodDebugInformation.Document, asmMetadataReader.GetRowNumber(methodDebugInformation.Document), documentName); - var methodInfo = new MethodInfo(this, method, asmMetadataReader.GetRowNumber(method), source, typeInfo); + var methodInfo = new MethodInfo(this, method, asmMetadataReader.GetRowNumber(method), source, typeInfo, asmMetadataReader, pdbMetadataReader); methods[asmMetadataReader.GetRowNumber(method)] = methodInfo; if (source != null) @@ -605,6 +671,7 @@ private Uri GetSourceLinkUrl(string document) } public IEnumerable Sources => this.sources; + public Dictionary Methods => this.methods; public Dictionary TypesByName => this.typesByName; public int Id => id; @@ -828,6 +895,16 @@ private class DebugItem public Task Data { get; set; } } + public IEnumerable EnC(SessionId sessionId, AssemblyInfo asm, byte[] meta_data, byte[] pdb_data) + { + asm.EnC(meta_data, pdb_data); + foreach (var method in asm.Methods) + { + if (method.Value.IsEnCMethod) + yield return method.Value; + } + } + public IEnumerable Add(SessionId sessionId, byte[] assembly_data, byte[] pdb_data) { AssemblyInfo assembly = null; diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs index f45d31ca1c874..f14bc1e16a99b 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs @@ -349,7 +349,7 @@ protected override async Task AcceptCommand(MessageId id, string method, J case "Debugger.removeBreakpoint": { - await RemoveBreakpoint(id, args, token); + await RemoveBreakpoint(id, args, false, token); break; } @@ -689,6 +689,56 @@ private async Task EvaluateCondition(SessionId sessionId, ExecutionContext } return false; } + + private async Task ProcessEnC(SessionId sessionId, ExecutionContext context, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + int moduleId = ret_debugger_cmd_reader.ReadInt32(); + int meta_size = ret_debugger_cmd_reader.ReadInt32(); + byte[] meta_buf = ret_debugger_cmd_reader.ReadBytes(meta_size); + int pdb_size = ret_debugger_cmd_reader.ReadInt32(); + byte[] pdb_buf = ret_debugger_cmd_reader.ReadBytes(pdb_size); + + var assembly_name = await sdbHelper.GetAssemblyNameFromModule(sessionId, moduleId, token); + DebugStore store = await LoadStore(sessionId, token); + AssemblyInfo asm = store.GetAssemblyByName(assembly_name); + foreach (var method in store.EnC(sessionId, asm, meta_buf, pdb_buf)) + await ResetBreakpoint(sessionId, method, token); + return true; + } + + private async Task SendBreakpointsOfMethodUpdated(SessionId sessionId, ExecutionContext context, MonoBinaryReader ret_debugger_cmd_reader, CancellationToken token) + { + var method_id = ret_debugger_cmd_reader.ReadInt32(); + var method_token = await sdbHelper.GetMethodToken(sessionId, method_id, token); + var assembly_id = await sdbHelper.GetAssemblyIdFromMethod(sessionId, method_id, token); + var assembly_name = await sdbHelper.GetAssemblyName(sessionId, assembly_id, token); + var method_name = await sdbHelper.GetMethodName(sessionId, method_id, token); + DebugStore store = await LoadStore(sessionId, token); + AssemblyInfo asm = store.GetAssemblyByName(assembly_name); + if (asm == null) + { + assembly_name = await sdbHelper.GetAssemblyNameFull(sessionId, assembly_id, token); + asm = store.GetAssemblyByName(assembly_name); + if (asm == null) + { + return true; + } + } + MethodInfo method = asm.GetMethodByToken(method_token); + if (method == null) + { + return true; + } + foreach (var req in context.BreakpointRequests.Values) + { + if (req.Method != null && req.Method.Assembly.Id == method.Assembly.Id && req.Method.Token == method.Token) + { + await SetBreakpoint(sessionId, context.store, req, true, token); + } + } + return true; + } + private async Task SendCallStack(SessionId sessionId, ExecutionContext context, string reason, int thread_id, Breakpoint bp, JObject data, IEnumerable orig_callframes, CancellationToken token) { var callFrames = new List(); @@ -844,6 +894,18 @@ private async Task OnReceiveDebuggerAgentEvent(SessionId sessionId, JObjec int thread_id = ret_debugger_cmd_reader.ReadInt32(); switch (event_kind) { + case EventKind.MethodUpdate: + { + var ret = await SendBreakpointsOfMethodUpdated(sessionId, context, ret_debugger_cmd_reader, token); + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + return ret; + } + case EventKind.EnC: + { + var ret = await ProcessEnC(sessionId, context, ret_debugger_cmd_reader, token); + await SendCommand(sessionId, "Debugger.resume", new JObject(), token); + return ret; + } case EventKind.Exception: { string reason = "exception"; @@ -1209,7 +1271,9 @@ private async Task RuntimeReady(SessionId sessionId, CancellationTok await sdbHelper.EnableExceptions(sessionId, "uncaught", token); await sdbHelper.SetProtocolVersion(sessionId, token); - await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token); + await sdbHelper.EnableReceiveRequests(sessionId, EventKind.UserBreak, token); + await sdbHelper.EnableReceiveRequests(sessionId, EventKind.EnC, token); + await sdbHelper.EnableReceiveRequests(sessionId, EventKind.MethodUpdate, token); DebugStore store = await LoadStore(sessionId, token); @@ -1218,7 +1282,21 @@ private async Task RuntimeReady(SessionId sessionId, CancellationTok return store; } - private async Task RemoveBreakpoint(MessageId msg_id, JObject args, CancellationToken token) + private async Task ResetBreakpoint(SessionId msg_id, MethodInfo method, CancellationToken token) + { + ExecutionContext context = GetContext(msg_id); + foreach (var req in context.BreakpointRequests.Values) + { + if (req.Method != null) + { + if (req.Method.Assembly.Id == method.Assembly.Id && req.Method.Token == method.Token) { + await RemoveBreakpoint(msg_id, JObject.FromObject(new {breakpointId = req.Id}), true, token); + } + } + } + } + + private async Task RemoveBreakpoint(SessionId msg_id, JObject args, bool isEnCReset, CancellationToken token) { string bpid = args?["breakpointId"]?.Value(); @@ -1232,10 +1310,16 @@ private async Task RemoveBreakpoint(MessageId msg_id, JObject args, Cancellation if (breakpoint_removed) { bp.RemoteId = -1; - bp.State = BreakpointState.Disabled; + if (isEnCReset) + bp.State = BreakpointState.Pending; + else + bp.State = BreakpointState.Disabled; } } - context.BreakpointRequests.Remove(bpid); + if (!isEnCReset) + context.BreakpointRequests.Remove(bpid); + else + breakpointRequest.Locations = new List(); } private async Task SetBreakpoint(SessionId sessionId, DebugStore store, BreakpointRequest req, bool sendResolvedEvent, CancellationToken token) @@ -1263,6 +1347,7 @@ private async Task SetBreakpoint(SessionId sessionId, DebugStore store, Breakpoi foreach (IGrouping sourceId in locations) { SourceLocation loc = sourceId.First(); + req.Method = loc.CliLocation.Method; Breakpoint bp = await SetMonoBreakpoint(sessionId, req.Id, loc, req.Condition, token); // If we didn't successfully enable the breakpoint diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 48ed3a9992bb9..42ad54e6e2c61 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -83,7 +83,9 @@ internal enum EventKind { KeepAlive = 14, UserBreak = 15, UserLog = 16, - Crash = 17 + Crash = 17, + EnC = 18, + MethodUpdate = 19 } internal enum ModifierKind { @@ -552,16 +554,18 @@ public async Task SetProtocolVersion(SessionId sessionId, CancellationToke var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdVM.SetProtocolVersion, command_params, token); return true; } - public async Task EnableReceiveUserBreakRequest(SessionId sessionId, CancellationToken token) + + public async Task EnableReceiveRequests(SessionId sessionId, EventKind event_kind, CancellationToken token) { var command_params = new MemoryStream(); var command_params_writer = new MonoBinaryWriter(command_params); - command_params_writer.Write((byte)EventKind.UserBreak); + command_params_writer.Write((byte)event_kind); command_params_writer.Write((byte)SuspendPolicy.None); command_params_writer.Write((byte)0); var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdEventRequest.Set, command_params, token); return true; } + internal async Task SendDebuggerAgentCommandInternal(SessionId sessionId, int command_set, int command, MemoryStream parms, CancellationToken token) { Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.SendDebuggerAgentCommand(GetId(), command_set, command, Convert.ToBase64String(parms.ToArray())), token); @@ -652,6 +656,17 @@ public async Task GetAssemblyId(SessionId sessionId, string asm_name, Cance return ret_debugger_cmd_reader.ReadInt32(); } + public async Task GetAssemblyNameFromModule(SessionId sessionId, int moduleId, CancellationToken token) + { + var command_params = new MemoryStream(); + var command_params_writer = new MonoBinaryWriter(command_params); + command_params_writer.Write(moduleId); + + var ret_debugger_cmd_reader = await SendDebuggerAgentCommand(sessionId, CmdModule.GetInfo, command_params, token); + ret_debugger_cmd_reader.ReadString(); + return ret_debugger_cmd_reader.ReadString(); + } + public async Task GetAssemblyName(SessionId sessionId, int assembly_id, CancellationToken token) { var command_params = new MemoryStream(); diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs index a4f53aac02b31..77a8cb6c12e16 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs @@ -287,5 +287,63 @@ await LoadAssemblyDynamically( CheckNumber(locals, "b", 10); } + [Fact] + public async Task DebugHotReloadMethodChangedUserBreak() + { + var pause_location = await LoadAssemblyAndTestHotReload( + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.dll"), + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.pdb"), + Path.Combine(DebuggerTestAppPath, "../wasm/ApplyUpdateReferencedAssembly.dll"), + "MethodBody1", "StaticMethod1"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 14, 8, "StaticMethod1"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "b", 15); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 14, 8, "StaticMethod1"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckBool(locals, "c", true); + } + + [Fact] + public async Task DebugHotReloadMethodUnchanged() + { + var pause_location = await LoadAssemblyAndTestHotReload( + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.dll"), + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.pdb"), + Path.Combine(DebuggerTestAppPath, "../wasm/ApplyUpdateReferencedAssembly.dll"), + "MethodBody2", "StaticMethod1"); + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 23, 8, "StaticMethod1"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 23, 8, "StaticMethod1"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + } + + [Fact] + public async Task DebugHotReloadMethodAddBreakpoint() + { + int line = 30; + await SetBreakpoint(".*/MethodBody1.cs$", line, 12, use_regex: true); + var pause_location = await LoadAssemblyAndTestHotReload( + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.dll"), + Path.Combine(DebuggerTestAppPath, "ApplyUpdateReferencedAssembly.pdb"), + Path.Combine(DebuggerTestAppPath, "../wasm/ApplyUpdateReferencedAssembly.dll"), + "MethodBody3", "StaticMethod3"); + + var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "a", 10); + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 29, 12, "StaticMethod3"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckNumber(locals, "b", 15); + + pause_location = await SendCommandAndCheck(JObject.FromObject(new { }), "Debugger.resume", "dotnet://ApplyUpdateReferencedAssembly.dll/MethodBody1.cs", 29, 12, "StaticMethod3"); + locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value()); + CheckBool(locals, "c", true); + } + } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs index a6af28d8e4592..b26624807150f 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs @@ -1040,6 +1040,55 @@ internal async Task LoadAssemblyDynamicallyALCAndRunMethod(string asm_f await cli.SendCommand("Runtime.evaluate", run_method, token); return await insp.WaitFor(Inspector.PAUSE); } + + internal async Task LoadAssemblyAndTestHotReload(string asm_file, string pdb_file, string asm_file_hot_reload, string class_name, string method_name) + { + // Simulate loading an assembly into the framework + byte[] bytes = File.ReadAllBytes(asm_file); + string asm_base64 = Convert.ToBase64String(bytes); + bytes = File.ReadAllBytes(pdb_file); + string pdb_base64 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.1.dmeta"); + string dmeta1 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.1.dil"); + string dil1 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.1.dpdb"); + string dpdb1 = Convert.ToBase64String(bytes); + + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.2.dmeta"); + string dmeta2 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.2.dil"); + string dil2 = Convert.ToBase64String(bytes); + + bytes = File.ReadAllBytes($"{asm_file_hot_reload}.2.dpdb"); + string dpdb2 = Convert.ToBase64String(bytes); + + string expression = $"let asm_b64 = '{asm_base64}'; let pdb_b64 = '{pdb_base64}';"; + expression = $"{expression} let dmeta1 = '{dmeta1}'; let dil1 = '{dil1}'; let dpdb1 = '{dpdb1}';"; + expression = $"{expression} let dmeta2 = '{dmeta2}'; let dil2 = '{dil2}'; let dpdb2 = '{dpdb2}';"; + expression = $"{{ {expression} invoke_static_method('[debugger-test] TestHotReload:LoadLazyHotReload', asm_b64, pdb_b64, dmeta1, dil1, dpdb1, dmeta2, dil2, dpdb2); }}"; + var load_assemblies = JObject.FromObject(new + { + expression + }); + + Result load_assemblies_res = await cli.SendCommand("Runtime.evaluate", load_assemblies, token); + + Assert.True(load_assemblies_res.IsOk); + Thread.Sleep(1000); + var run_method = JObject.FromObject(new + { + expression = "window.setTimeout(function() { invoke_static_method('[debugger-test] TestHotReload:RunMethod', '" + class_name + "', '" + method_name + "'); }, 1);" + }); + + await cli.SendCommand("Runtime.evaluate", run_method, token); + return await insp.WaitFor(Inspector.PAUSE); + } } class DotnetObjectId diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj new file mode 100644 index 0000000000000..550971c8b6f31 --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/ApplyUpdateReferencedAssembly.csproj @@ -0,0 +1,31 @@ + + + true + deltascript.json + library + false + true + + false + true + + false + false + false + + + + + + + + + + + diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1.cs b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1.cs new file mode 100644 index 0000000000000..b2b8f4d364c08 --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System; + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + Console.WriteLine("original"); + int a = 10; + Debugger.Break(); + return "OLD STRING"; + } + } + + public class MethodBody2 { + public static string StaticMethod1 () { + Console.WriteLine("original"); + int a = 10; + Debugger.Break(); + return "OLD STRING"; + } + } + + public class MethodBody3 { + public static string StaticMethod3 () { + int a = 10; + Console.WriteLine("original"); + return "OLD STRING"; + } + } +} diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs new file mode 100644 index 0000000000000..03a4d33b0646b --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v1.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System; + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () { + Console.WriteLine("v1"); + double b = 15; + Debugger.Break(); + return "NEW STRING"; + } + } + + public class MethodBody2 { + public static string StaticMethod1 () { + Console.WriteLine("original"); + int a = 10; + Debugger.Break(); + return "OLD STRING"; + } + } + + public class MethodBody3 { + public static string StaticMethod3 () { + float b = 15; + Console.WriteLine("v1"); + return "NEW STRING"; + } + } +} diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs new file mode 100644 index 0000000000000..4cd88d660a3f1 --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/MethodBody1_v2.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System; + +namespace ApplyUpdateReferencedAssembly +{ + public class MethodBody1 { + public static string StaticMethod1 () + { + Console.WriteLine("v2"); + bool c = true; + Debugger.Break(); + return "NEWEST STRING"; + } + } + + public class MethodBody2 { + public static string StaticMethod1 () { + Console.WriteLine("original"); + int a = 10; + Debugger.Break(); + return "OLD STRING"; + } + } + + public class MethodBody3 { + public static string StaticMethod3 () { + bool c = true; + Console.WriteLine("v2"); + return "NEWEST STRING"; + } + } +} diff --git a/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/deltascript.json b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/deltascript.json new file mode 100644 index 0000000000000..8e738364bc747 --- /dev/null +++ b/src/mono/wasm/debugger/tests/ApplyUpdateReferencedAssembly/deltascript.json @@ -0,0 +1,7 @@ +{ + "changes": [ + {"document": "MethodBody1.cs", "update": "MethodBody1_v1.cs"}, + {"document": "MethodBody1.cs", "update": "MethodBody1_v2.cs"}, + ] +} + diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index 32177bbc0c5b0..228d9d1ff62e5 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs @@ -578,3 +578,78 @@ public static void RunMethodInALC(string type_name, string method_name) } } + public class TestHotReload { + static System.Reflection.Assembly loadedAssembly; + static byte[] dmeta_data1_bytes; + static byte[] dil_data1_bytes; + static byte[] dpdb_data1_bytes; + static byte[] dmeta_data2_bytes; + static byte[] dil_data2_bytes; + static byte[] dpdb_data2_bytes; + public static void LoadLazyHotReload(string asm_base64, string pdb_base64, string dmeta_data1, string dil_data1, string dpdb_data1, string dmeta_data2, string dil_data2, string dpdb_data2) + { + byte[] asm_bytes = Convert.FromBase64String(asm_base64); + byte[] pdb_bytes = Convert.FromBase64String(pdb_base64); + + dmeta_data1_bytes = Convert.FromBase64String(dmeta_data1); + dil_data1_bytes = Convert.FromBase64String(dil_data1); + dpdb_data1_bytes = Convert.FromBase64String(dpdb_data1); + + dmeta_data2_bytes = Convert.FromBase64String(dmeta_data2); + dil_data2_bytes = Convert.FromBase64String(dil_data2); + dpdb_data2_bytes = Convert.FromBase64String(dpdb_data2); + + + loadedAssembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(new System.IO.MemoryStream(asm_bytes), new System.IO.MemoryStream(pdb_bytes)); + Console.WriteLine($"Loaded - {loadedAssembly}"); + + } + public static void RunMethod(string className, string methodName) + { + var ty = typeof(System.Reflection.Metadata.AssemblyExtensions); + var mi = ty.GetMethod("GetApplyUpdateCapabilities", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static, Array.Empty()); + + if (mi == null) + return; + + var caps = mi.Invoke(null, null) as string; + + if (String.IsNullOrEmpty(caps)) + return; + + var myType = loadedAssembly.GetType($"ApplyUpdateReferencedAssembly.{className}"); + var myMethod = myType.GetMethod(methodName); + myMethod.Invoke(null, null); + + ApplyUpdate(loadedAssembly, 1); + + myType = loadedAssembly.GetType($"ApplyUpdateReferencedAssembly.{className}"); + myMethod = myType.GetMethod(methodName); + myMethod.Invoke(null, null); + + ApplyUpdate(loadedAssembly, 2); + + myType = loadedAssembly.GetType($"ApplyUpdateReferencedAssembly.{className}"); + myMethod = myType.GetMethod(methodName); + myMethod.Invoke(null, null); + } + + internal static void ApplyUpdate (System.Reflection.Assembly assm, int version) + { + string basename = assm.Location; + if (basename == "") + basename = assm.GetName().Name + ".dll"; + Console.Error.WriteLine($"Apply Delta Update for {basename}, revision {version}"); + + if (version == 1) + { + System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assm, dmeta_data1_bytes, dil_data1_bytes, dpdb_data1_bytes); + } + else if (version == 2) + { + System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assm, dmeta_data2_bytes, dil_data2_bytes, dpdb_data2_bytes); + } + + } + } + diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj index 648dbcc64fc6d..797fa16751648 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj @@ -18,13 +18,15 @@ + $(AppDir) $(MonoProjectRoot)wasm\runtime-test.js - 1 + + -1 true @@ -38,6 +40,30 @@ + + + + + + + + + + + + + + + diff --git a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js index 8fb1e86211afe..2c24917f62033 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js +++ b/src/mono/wasm/debugger/tests/debugger-test/runtime-debugger.js @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -var Module = { +var Module = { config: null, preInit: async function() { @@ -27,6 +27,10 @@ var Module = { MONO.mono_wasm_setenv ("MONO_LOG_LEVEL", "debug"); MONO.mono_wasm_setenv ("MONO_LOG_MASK", "all"); */ + + Module.config.environment_variables = { + "DOTNET_MODIFIABLE_ASSEMBLIES": "debug" + }; MONO.mono_load_runtime_and_bcl_args (Module.config) }, };