* First version of multithreaded debugging.
* Revert package-lock.json
* New line at package-lock.json
* Fix not used variable.
* Fix debugger on firefox.
* Rewrite code to avoid duplicated code.
* Fix where mono_init_debugger_agent_common is called.
* Remove whitespace.
* Update src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
Co-authored-by: Ankit Jain <radical@gmail.com>
* Update src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
Co-authored-by: Ankit Jain <radical@gmail.com>
* Update src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
Co-authored-by: Ankit Jain <radical@gmail.com>
* [wasm] Debugger tests: support running with multithreaded runtime
* Add runtime-wasm-dbgtests pipeline with debugger tests running on a multi-threaded runtime
* Add multi-threaded debugger tests to runtime-wasm
* fix yml
* Always run the new tests when the pipeline is invoked manually
* Pass through extra build args for wasm debugger tests
* Addressing @radical comments.
* Apply suggestions from code review
Co-authored-by: Ankit Jain <radical@gmail.com>
* addressing radical comments
* Fixing tests failures and adding a schema to run a test that will only run in a multithreaded environment.
* Adding support for run debugger-tests in a multithreaded runtime.
* Fix running debugger tests for multithreaded runtime, passing sessionId where it's necessary.
* Fix CI.
* Addressing @radical comments
Adding a test case.
* Update src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
Co-authored-by: Ankit Jain <radical@gmail.com>
* Update src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Co-authored-by: Ankit Jain <radical@gmail.com>
* Update src/mono/wasm/debugger/DebuggerTestSuite/InspectorClient.cs
Co-authored-by: Ankit Jain <radical@gmail.com>
* Dictionary with the scriptId also uses sessionId.
* Addressing @radical review.
* Apply suggestions from code review
Co-authored-by: Ankit Jain <radical@gmail.com>
* Avoiding getting this error: Cannot transition thread 0x2a15360 from STATE_BLOCKING with DO_BLOCKING.
In the transport_send we don't save the thread context, we save it before the send function.
* Addressing @radical comments.
* Using more threads in unit test.
* Apply suggestions from code review
Co-authored-by: Ankit Jain <radical@gmail.com>
* Addressing @radical comments, and trying to fix ci.
* Removing unnecessary changes.
* Export function used on mini-wasm-debugger.
* Fixing line number.
* Fix run tests on release.
* fix compilation for multithread runtime
* trying to fix multithread debugger tests on ci
* trying to fix debugger tests on ci
* disabling tests on multithreaded runtime
* Update eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml
Co-authored-by: Ankit Jain <radical@gmail.com>
* Throwing an exception if the "what" is not the one that is being get from the nextNotificationQueue.
---------
Co-authored-by: Ankit Jain <radical@gmail.com>
isWasmOnlyBuild: false
browser: 'chrome'
shouldContinueOnError: false
+ extraBuildArgs: ''
+ nameSuffix: ''
platforms: []
jobs:
jobParameters:
testGroup: innerloop
isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }}
- nameSuffix: Mono_DebuggerTests_${{ parameters.browser }}
- buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:TestWasmDebuggerTests=true /p:TestAssemblies=false /p:BrowserHost=$(_hostedOs) /p:DebuggerHost=${{ parameters.browser }}
+ ${{ if eq(parameters.nameSuffix, '') }}:
+ nameSuffix: Mono_DebuggerTests_${{ parameters.browser }}
+ ${{ else }}:
+ nameSuffix: ${{ parameters.nameSuffix }}
+ buildArgs: -s mono+libs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true /p:TestWasmDebuggerTests=true /p:TestAssemblies=false /p:BrowserHost=$(_hostedOs) /p:DebuggerHost=${{ parameters.browser }} ${{ parameters.extraBuildArgs }}
timeoutInMinutes: 180
# if !alwaysRun, then:
# if this is runtime-wasm (isWasmOnlyBuild):
- WasmTestOnBrowser
- WasmTestOnNodeJS
- # Library tests with full threading
+ # Library tests with full threading
- template: /eng/pipelines/common/templates/wasm-library-tests.yml
parameters:
platforms:
# ff tests are unstable currently
shouldContinueOnError: true
+ - template: /eng/pipelines/common/templates/wasm-debugger-tests.yml
+ parameters:
+ platforms:
+ - Browser_wasm
+ - Browser_wasm_win
+ extraBuildArgs: /p:MonoWasmBuildVariant=multithread /p:WasmEnableThreads=true
+ nameSuffix: DebuggerTests_MultiThreaded
+ alwaysRun: ${{ parameters.isWasmOnlyBuild }}
+ isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
+ isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
+
# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
#parameters:
--- /dev/null
+trigger: none
+
+variables:
+ - template: /eng/pipelines/common/variables.yml
+
+jobs:
+
+#
+# Evaluate paths
+#
+- template: /eng/pipelines/common/evaluate-default-paths.yml
+
+# Debugger tests
+- template: /eng/pipelines/common/templates/wasm-debugger-tests.yml
+ parameters:
+ platforms:
+ - Browser_wasm
+ - Browser_wasm_win
+ isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
+ isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
+
+- template: /eng/pipelines/common/templates/wasm-debugger-tests.yml
+ parameters:
+ platforms:
+ - Browser_wasm
+ - Browser_wasm_win
+ extraBuildArgs: /p:MonoWasmBuildVariant=multithread /p:WasmEnableThreads=true
+ nameSuffix: DebuggerTests_MultiThreaded
+ isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
+ isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
+
+- template: /eng/pipelines/common/templates/wasm-debugger-tests.yml
+ parameters:
+ platforms:
+ - Browser_wasm_firefox
+ browser: firefox
+ isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
+ isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
+ alwaysRun: ${{ parameters.isWasmOnlyBuild }}
+ # ff tests are unstable currently
+ shouldContinueOnError: true
/*
* Globals
*/
-#ifdef TARGET_WASM
-static DebuggerTlsData debugger_wasm_thread;
-#endif
static AgentConfig agent_config;
/*
/* Whenever to buffer reply messages and send them together */
static gboolean buffer_replies;
-#ifndef TARGET_WASM
+
#define GET_TLS_DATA_FROM_THREAD(thread) \
DebuggerTlsData *tls = NULL; \
mono_loader_lock(); \
#define GET_DEBUGGER_TLS() \
DebuggerTlsData *tls; \
tls = (DebuggerTlsData *)mono_native_tls_get_value (debugger_tls_id);
-#else
-/* the thread argument is omitted on wasm, to avoid compiler warning */
-#define GET_TLS_DATA_FROM_THREAD(...) \
- DebuggerTlsData *tls; \
- tls = &debugger_wasm_thread;
-#define GET_DEBUGGER_TLS() \
- DebuggerTlsData *tls; \
- tls = &debugger_wasm_thread;
-#endif
#define GET_EXTRA_SPACE_FOR_REF_FIELDS(klass) \
extra_space_size = 0; \
static void invalidate_frames (DebuggerTlsData *tls);
+static void mono_init_debugger_agent_common (MonoProfilerHandle *prof);
+
/* Callbacks used by debugger-engine */
static MonoContext* tls_get_restore_state (void *the_tls);
static gboolean try_process_suspend (void *tls, MonoContext *ctx, gboolean from_breakpoint);
mono_profiler_set_domain_loaded_callback (prof, appdomain_load);
mono_profiler_set_domain_unloading_callback (prof, appdomain_start_unload);
mono_profiler_set_domain_unloaded_callback (prof, appdomain_unload);
- mono_profiler_set_thread_started_callback (prof, thread_startup);
- mono_profiler_set_thread_stopped_callback (prof, thread_end);
mono_profiler_set_assembly_loaded_callback (prof, assembly_load);
mono_profiler_set_assembly_unloading_callback (prof, assembly_unload);
- mono_profiler_set_jit_done_callback (prof, jit_done);
mono_profiler_set_jit_failed_callback (prof, jit_failed);
mono_profiler_set_gc_finalizing_callback (prof, gc_finalizing);
mono_profiler_set_gc_finalized_callback (prof, gc_finalized);
- mono_native_tls_alloc (&debugger_tls_id, NULL);
-
- /* Needed by the hash_table_new_type () call below */
- mono_gc_base_init ();
-
- thread_to_tls = mono_g_hash_table_new_type_internal ((GHashFunc)mono_object_hash_internal, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger TLS Table");
-
- tid_to_thread = mono_g_hash_table_new_type_internal (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Thread Table");
-
- tid_to_thread_obj = mono_g_hash_table_new_type_internal (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Thread Object Table");
+ mono_init_debugger_agent_common (&prof);
pending_assembly_loads = g_ptr_array_new ();
}
mono_de_set_log_level (log_level, log_file);
- ids_init ();
objrefs_init ();
suspend_init ();
/* Protected by the dbg lock */
static MonoGHashTable *suspended_objs;
+static void
+mono_init_debugger_agent_common (MonoProfilerHandle *prof)
+{
+ ids_init ();
+
+ event_requests = g_ptr_array_new ();
+
+ pending_assembly_loads = g_ptr_array_new ();
+
+ mono_profiler_set_thread_started_callback (*prof, thread_startup);
+ mono_profiler_set_thread_stopped_callback (*prof, thread_end);
+ mono_profiler_set_jit_done_callback (*prof, jit_done);
+
+ mono_native_tls_alloc (&debugger_tls_id, NULL);
+
+ /* Needed by the hash_table_new_type () call below */
+ mono_gc_base_init ();
+
+ thread_to_tls = mono_g_hash_table_new_type_internal ((GHashFunc)mono_object_hash_internal, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger TLS Table");
+
+ tid_to_thread = mono_g_hash_table_new_type_internal (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Thread Table");
+
+ tid_to_thread_obj = mono_g_hash_table_new_type_internal (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, NULL, "Debugger Thread Object Table");
+}
+
#ifdef TARGET_WASM
void
mono_init_debugger_agent_for_wasm (int log_level_parm, MonoProfilerHandle *prof)
int ntransports = 0;
DebuggerTransport *transports = mono_debugger_agent_get_transports (&ntransports);
- 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 ();
+
vm_start_event_sent = TRUE;
transport = &transports [0];
- memset(&debugger_wasm_thread, 0, sizeof(DebuggerTlsData));
- mono_native_tls_alloc (&debugger_tls_id, NULL);
- mono_native_tls_set_value (debugger_tls_id, &debugger_wasm_thread);
-
agent_config.enabled = TRUE;
- mono_profiler_set_jit_done_callback (*prof, jit_done);
+ mono_init_debugger_agent_common (prof);
}
void
void
mono_wasm_save_thread_context (void)
{
- debugger_wasm_thread.really_suspended = TRUE;
- mono_thread_state_init_from_current (&debugger_wasm_thread.context);
+ DebuggerTlsData* tls = mono_wasm_get_tls ();
+ tls->really_suspended = TRUE;
+ mono_thread_state_init_from_current (&tls->context);
}
DebuggerTlsData*
mono_wasm_get_tls (void)
{
- return &debugger_wasm_thread;
+ MonoThread *thread = mono_thread_current ();
+ GET_TLS_DATA_FROM_THREAD (thread);
+ return tls;
}
#endif
static gboolean
is_suspended (void)
{
+#ifdef HOST_WASM
+ return true;
+#else
return count_threads_to_wait_for () == 0;
+#endif
}
static void
#ifdef TARGET_WASM
PRINT_DEBUG_MSG (1, "[%p] Sent %d events %s(%d), suspend=%d.\n", (gpointer) (gsize) mono_native_thread_id_get (), nevents, event_to_string (event), ecount, suspend_policy);
+ mono_wasm_save_thread_context();
#endif
send_success = send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf);
/*
* suspend_vm () could have missed this thread, so wait for a resume.
*/
-
+#ifndef HOST_WASM
suspend_current_func ();
+#endif
}
static void
// Wait for suspending if it already started
// FIXME: Races with suspend_count
-#ifndef HOST_WASI
+#if !defined(HOST_WASI) && !defined(HOST_WASM)
while (!is_suspended ()) {
if (suspend_count)
wait_for_suspend ();
int objid;
ErrorCode err;
MonoThread *thread_obj;
-#ifndef TARGET_WASM
MonoInternalThread *thread;
-#endif
int pos, i, len, frame_idx;
StackFrame *frame;
MonoDebugMethodJitInfo *jit;
if (err != ERR_NONE)
return err;
-#ifndef TARGET_WASM
thread = THREAD_TO_INTERNAL (thread_obj);
-#endif
id = decode_id (p, &p, end);
-#ifndef TARGET_WASM
GET_TLS_DATA_FROM_THREAD (thread);
-#else
- GET_TLS_DATA_FROM_THREAD ();
-#endif
g_assert (tls);
for (i = 0; i < tls->frame_count; ++i) {
//JS functions imported that we use
-extern void mono_wasm_fire_debugger_agent_message (void);
+extern void mono_wasm_fire_debugger_agent_message_with_data (const char *data, int len);
extern void mono_wasm_asm_loaded (const char *asm_name, const char *assembly_data, guint32 assembly_len, const char *pdb_data, guint32 pdb_len);
G_END_DECLS
goto done;
}
MdbgProtBuffer bufWithParms;
- buffer_init (&bufWithParms, 128);
+ m_dbgprot_buffer_init (&bufWithParms, 128);
m_dbgprot_buffer_add_data (&bufWithParms, data, size);
if (!write_value_to_buffer(&bufWithParms, valtype, newvalue)) {
mono_wasm_add_dbg_command_received(0, id, 0, 0);
}
ss_calculate_framecount (NULL, NULL, TRUE, NULL, NULL);
MdbgProtBuffer buf;
- buffer_init (&buf, 128);
gboolean no_reply;
MdbgProtErrorCode error = 0;
if (command_set == MDBGPROT_CMD_SET_VM && command == MDBGPROT_CMD_VM_INVOKE_METHOD )
{
+ m_dbgprot_buffer_init (&buf, 128);
DebuggerTlsData* tls = mono_wasm_get_tls ();
InvokeData invoke_data;
memset (&invoke_data, 0, sizeof (InvokeData));
char* assembly_name = m_dbgprot_decode_string (data, &data, data + size);
if (assembly_name == NULL)
{
+ m_dbgprot_buffer_init (&buf, 128);
m_dbgprot_buffer_add_int (&buf, 0);
m_dbgprot_buffer_add_int (&buf, 0);
}
int symfile_size = 0;
const unsigned char* assembly_bytes = mono_wasm_get_assembly_bytes (assembly_name, &assembly_size);
const unsigned char* pdb_bytes = mono_get_symfile_bytes_from_bundle (assembly_name, &symfile_size);
+ m_dbgprot_buffer_init (&buf, assembly_size + symfile_size);
m_dbgprot_buffer_add_byte_array (&buf, (uint8_t *) assembly_bytes, assembly_size);
m_dbgprot_buffer_add_byte_array (&buf, (uint8_t *) pdb_bytes, symfile_size);
}
}
else
+ {
+ m_dbgprot_buffer_init (&buf, 128);
error = mono_process_dbg_packet (id, command_set, command, &no_reply, data, data + size, &buf);
+ }
mono_wasm_add_dbg_command_received (error == MDBGPROT_ERR_NONE, id, buf.buf, buf.p-buf.buf);
- buffer_free (&buf);
+ m_dbgprot_buffer_free (&buf);
result = TRUE;
done:
MONO_EXIT_GC_UNSAFE;
static gboolean
receive_debugger_agent_message (void *data, int len)
{
- mono_wasm_add_dbg_command_received(1, 0, data, len);
- mono_wasm_save_thread_context();
- mono_wasm_fire_debugger_agent_message ();
+ mono_wasm_fire_debugger_agent_message_with_data ((const char*)data, len);
return FALSE;
}
SdbAgent = sdbAgent;
PauseOnExceptions = pauseOnExceptions;
}
-
+ public ExecutionContext CreateChildAsyncExecutionContext(SessionId sessionId)
+ => new ExecutionContext(null, Id, AuxData, PauseOnExceptions)
+ {
+ ParentContext = this,
+ SessionId = sessionId
+ };
+ public bool CopyDataFromParentContext()
+ {
+ if (SdbAgent != null)
+ return false;
+ ready = ParentContext.ready;
+ store = ParentContext.store;
+ Source = ParentContext.Source;
+ SdbAgent = ParentContext.SdbAgent.Clone(SessionId);
+ return true;
+ }
public string DebugId { get; set; }
public Dictionary<string, BreakpointRequest> BreakpointRequests { get; } = new Dictionary<string, BreakpointRequest>();
public int breakpointId;
public bool IsResumedAfterBp { get; set; }
public int ThreadId { get; set; }
public int Id { get; set; }
+ public ExecutionContext ParentContext { get; private set; }
+ public SessionId SessionId { get; private set; }
public bool PausedOnWasm { get; set; }
public string[] LoadedFiles { get; set; }
internal DebugStore store;
- internal MonoSDBHelper SdbAgent { get; init; }
- public TaskCompletionSource<DebugStore> Source { get; } = new TaskCompletionSource<DebugStore>();
+ internal MonoSDBHelper SdbAgent { get; private set; }
+ public TaskCompletionSource<DebugStore> Source { get; private set; } = new TaskCompletionSource<DebugStore>();
private Dictionary<int, PerScopeCache> perScopeCaches { get; } = new Dictionary<int, PerScopeCache>();
var topFunc = args["frame"]["displayName"].Value<string>();
switch (topFunc)
{
- case "mono_wasm_fire_debugger_agent_message":
- case "_mono_wasm_fire_debugger_agent_message":
+ case "mono_wasm_fire_debugger_agent_message_with_data_to_pause":
+ case "_mono_wasm_fire_debugger_agent_message_with_data_to_pause":
{
ctx.PausedOnWasm = true;
- return await OnReceiveDebuggerAgentEvent(sessionId, args, token);
+ return await OnReceiveDebuggerAgentEvent(sessionId, args, GetLastDebuggerAgentBuffer(args), token);
}
default:
ctx.PausedOnWasm = false;
context.LastDebuggerAgentBufferReceived = debuggerAgentBufferTask;
}
+ internal static Result GetLastDebuggerAgentBuffer(JObject args)
+ {
+ var result = new JArray();
+ result.Add(JObject.FromObject(new { value = new {value = args?["frame"]?["arguments"]?[0].Value<string>()}}));
+ Result res = Result.OkFromObject(new
+ {
+ result
+ });
+ return res;
+ }
private async Task<bool> SendPauseToBrowser(SessionId sessionId, JObject args, CancellationToken token)
{
var context = GetContextFixefox(sessionId);
Result res = await context.LastDebuggerAgentBufferReceived;
- if (!res.IsOk)
+ if (!res.IsOk || res.Value?["result"].Value<JArray>().Count == 0)
+ {
+ logger.LogTrace($"Unexpected DebuggerAgentBufferReceived {res}");
return false;
+ }
context.LastDebuggerAgentBufferReceived = null;
- byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value<string>());
+ byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?[0]?["value"]?["value"]?.Value<string>());
using var retDebuggerCmdReader = new MonoBinaryReader(newBytes);
retDebuggerCmdReader.ReadBytes(11);
retDebuggerCmdReader.ReadByte();
return SendCommand(id, "evaluateJSAsync", o, token);
}
- internal override async Task OnSourceFileAdded(SessionId sessionId, SourceFile source, ExecutionContext context, CancellationToken token)
+ internal override async Task OnSourceFileAdded(SessionId sessionId, SourceFile source, ExecutionContext context, CancellationToken token, bool resolveBreakpoints = true)
{
//different behavior when debugging from VSCode and from Firefox
var ctx = context as FirefoxExecutionContext;
});
}
await SendEvent(sessionId, "", sourcesJObj, token);
-
+ if (!resolveBreakpoints)
+ return;
foreach (var req in context.BreakpointRequests.Values)
{
if (req.TryResolve(source))
string function_name = frame["displayName"]?.Value<string>();
if (function_name != null && !(function_name.StartsWith("Module._mono_wasm", StringComparison.Ordinal) ||
function_name.StartsWith("Module.mono_wasm", StringComparison.Ordinal) ||
- function_name == "mono_wasm_fire_debugger_agent_message" ||
- function_name == "_mono_wasm_fire_debugger_agent_message" ||
+ function_name == "mono_wasm_fire_debugger_agent_message_with_data" ||
+ function_name == "_mono_wasm_fire_debugger_agent_message_with_data" ||
function_name == "(wasmcall)"))
{
callFrames.Add(frame);
using Newtonsoft.Json.Linq;
using System.Net.Http;
using BrowserDebugProxy;
+using static System.Formats.Asn1.AsnWriter;
+using System.Reflection;
namespace Microsoft.WebAssembly.Diagnostics
{
case "Debugger.paused":
{
- // Don't process events from sessions we aren't tracking
- if (!contexts.ContainsKey(sessionId))
- return false;
+ if (args["asyncStackTraceId"] != null)
+ {
+ if (!contexts.TryGetValue(sessionId, out ExecutionContext context))
+ return false;
+ if (context.CopyDataFromParentContext())
+ {
+ var store = await LoadStore(sessionId, true, token);
+ foreach (var source in store.AllSources())
+ {
+ await OnSourceFileAdded(sessionId, source, context, token, false);
+ }
+ }
+ }
//TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack
string top_func = args?["callFrames"]?[0]?["functionName"]?.Value<string>();
await ReloadSymbolsFromSymbolServer(sessionId, GetContext(sessionId), token);
return true;
}
- case "mono_wasm_fire_debugger_agent_message":
- case "_mono_wasm_fire_debugger_agent_message":
+ case "mono_wasm_fire_debugger_agent_message_with_data_to_pause":
+ case "_mono_wasm_fire_debugger_agent_message_with_data_to_pause":
+ try
{
- try {
- return await OnReceiveDebuggerAgentEvent(sessionId, args, token);
- }
- catch (Exception) //if the page is refreshed maybe it stops here.
- {
- await SendResume(sessionId, token);
- return true;
- }
+ return await OnReceiveDebuggerAgentEvent(sessionId, args, await GetLastDebuggerAgentBuffer(sessionId, args, token), token);
+ }
+ catch (Exception) //if the page is refreshed maybe it stops here.
+ {
+ await SendResume(sessionId, token);
+ return true;
}
}
break;
case "Target.attachedToTarget":
{
- if (args["targetInfo"]["type"]?.ToString() == "page")
+ var targetType = args["targetInfo"]["type"]?.ToString();
+ if (targetType == "page")
await AttachToTarget(new SessionId(args["sessionId"]?.ToString()), token);
+ else if (targetType == "worker")
+ CreateWorkerExecutionContext(new SessionId(args["sessionId"]?.ToString()), new SessionId(parms["sessionId"]?.ToString()));
break;
}
return false;
}
+
+ protected void CreateWorkerExecutionContext(SessionId workerSessionId, SessionId originSessionId)
+ {
+ if (!contexts.TryGetValue(originSessionId, out ExecutionContext context))
+ {
+ logger.LogDebug($"Origin sessionId does not exist - {originSessionId}");
+ return;
+ }
+ if (contexts.ContainsKey(workerSessionId))
+ {
+ logger.LogDebug($"Worker sessionId already exists - {originSessionId}");
+ return;
+ }
+ contexts[workerSessionId] = context.CreateChildAsyncExecutionContext(workerSessionId);
+ }
+
protected virtual async Task SendResume(SessionId id, CancellationToken token)
{
await SendCommand(id, "Debugger.resume", new JObject(), token);
data,
hitBreakpoints = bp_list,
});
+ if (args["asyncStackTraceId"] != null)
+ o["asyncStackTraceId"] = args["asyncStackTraceId"];
if (!await EvaluateCondition(sessionId, context, context.CallStack.First(), bp, token))
{
context.ClearState();
{
}
- internal async Task<bool> OnReceiveDebuggerAgentEvent(SessionId sessionId, JObject args, CancellationToken token)
+ internal async Task<Result> GetLastDebuggerAgentBuffer(SessionId sessionId, JObject args, CancellationToken token)
+ {
+ if (args?["callFrames"].Value<JArray>().Count == 0 || args["callFrames"][0]["scopeChain"].Value<JArray>().Count == 0)
+ return Result.Err($"Unexpected callFrames {args}");
+ var argsNew = JObject.FromObject(new
+ {
+ objectId = args["callFrames"][0]["scopeChain"][0]["object"]["objectId"].Value<string>(),
+ });
+ Result res = await SendCommand(sessionId, "Runtime.getProperties", argsNew, token);
+ return res;
+ }
+
+ internal async Task<bool> OnReceiveDebuggerAgentEvent(SessionId sessionId, JObject args, Result debuggerAgentBuffer, CancellationToken token)
{
var debuggerAgentBufferTask = SendMonoCommand(sessionId, MonoCommands.GetDebuggerAgentBufferReceived(RuntimeId), token);
SaveLastDebuggerAgentBufferReceivedToContext(sessionId, debuggerAgentBufferTask);
- var res = await debuggerAgentBufferTask;
- if (!res.IsOk)
+ if (!debuggerAgentBuffer.IsOk || debuggerAgentBuffer.Value?["result"].Value<JArray>().Count == 0)
+ {
+ logger.LogTrace($"Unexpected DebuggerAgentBufferReceived {debuggerAgentBuffer}");
return false;
-
+ }
ExecutionContext context = GetContext(sessionId);
- byte[] newBytes = Convert.FromBase64String(res.Value?["result"]?["value"]?["value"]?.Value<string>());
+ byte[] newBytes = Convert.FromBase64String(debuggerAgentBuffer.Value?["result"]?[0]?["value"]?["value"]?.Value<string>());
using var retDebuggerCmdReader = new MonoBinaryReader(newBytes);
retDebuggerCmdReader.ReadBytes(11); //skip HEADER_LEN
retDebuggerCmdReader.ReadByte(); //suspend_policy
else if (event_kind == EventKind.Breakpoint)
context.PauseKind = "breakpoint";
Breakpoint bp = context.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == request_id);
+ if (bp == null && context.ParentContext != null)
+ {
+ bp = context.ParentContext.BreakpointRequests.Values.SelectMany(v => v.Locations).FirstOrDefault(b => b.RemoteId == request_id);
+ }
if (request_id == context.TempBreakpointForSetNextIP)
{
context.TempBreakpointForSetNextIP = -1;
return bp;
}
- internal virtual async Task OnSourceFileAdded(SessionId sessionId, SourceFile source, ExecutionContext context, CancellationToken token)
+ internal virtual async Task OnSourceFileAdded(SessionId sessionId, SourceFile source, ExecutionContext context, CancellationToken token, bool resolveBreakpoints = true)
{
JObject scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData));
// Log("debug", $"sending {source.Url} {context.Id} {sessionId.sessionId}");
await SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);
-
+ if (!resolveBreakpoints)
+ return;
foreach (var req in context.BreakpointRequests.Values)
{
if (req.TryResolve(source))
ResetStore(null);
}
+ public MonoSDBHelper Clone(SessionId sessionId)
+ => new MonoSDBHelper(proxy, logger, sessionId)
+ {
+ VmMajorVersion = VmMajorVersion,
+ VmMinorVersion = VmMinorVersion,
+ store = store,
+ };
+
public void ResetStore(DebugStore store)
{
this.store = store;
// FIXME: check object properties..
//FIXME: function name
- [ConditionalTheory(nameof(RunningOnChrome))]
+ [ConditionalTheory(nameof(WasmSingleThreaded), nameof(RunningOnChrome))]
[InlineData("ContinueWithStaticAsync", "DebuggerTests.AsyncTests.ContinueWithTests.ContinueWithStaticAsync.AnonymousMethod__3_0")]
[InlineData("ContinueWithInstanceAsync", "DebuggerTests.AsyncTests.ContinueWithTests.ContinueWithInstanceAsync.AnonymousMethod__5_0")]
public async Task AsyncLocalsInContinueWith(string method_name, string expected_method_name) => await CheckInspectLocalsAtBreakpointSite(
}, "locals");
});
- [Theory]
+ [ConditionalTheory(nameof(WasmSingleThreaded), nameof(RunningOnChrome))]
[InlineData("Run", 246, 16, 252, 16, "RunCSharpScope")]
[InlineData("RunContinueWith", 277, 20, 283, 20, "RunContinueWithSameVariableName")]
[InlineData("RunNestedContinueWith", 309, 24, 315, 24, "RunNestedContinueWithSameVariableName.AnonymousMethod__1")]
await SetBreakpoint("/debugger-driver.html", line_bp, column_bp, condition: condition);
await SetBreakpoint("/debugger-driver.html", 80, 11);
- await EvaluateAndCheck(
+ var pause_location = await EvaluateAndCheck(
"window.setTimeout(function() { conditional_breakpoint_test(5, 10, null); }, 1);",
"debugger-driver.html", line_expected, column_expected, "conditional_breakpoint_test");
}
#else
=> WasmHost.Firefox;
#endif
+
+ public static bool WasmMultiThreaded => EnvironmentVariables.WasmTestsUsingVariant == "multithreaded";
+
+ public static bool WasmSingleThreaded => !WasmMultiThreaded;
+
public static bool RunningOnChrome => RunningOn == WasmHost.Chrome;
public static bool RunningOnChromeAndLinux => RunningOn == WasmHost.Chrome && PlatformDetection.IsLinux;
{
Func<InspectorClient, CancellationToken, List<(string, Task<Result>)>> fn = (client, token) =>
{
- Func<string, (string, Task<Result>)> getInitCmdFn = (cmd) => (cmd, client.SendCommand(cmd, null, token));
+ Func<string, JObject, (string, Task<Result>)> getInitCmdFn = (cmd, args) => (cmd, client.SendCommand(cmd, args, token));
var init_cmds = new List<(string, Task<Result>)>
{
- getInitCmdFn("Profiler.enable"),
- getInitCmdFn("Runtime.enable"),
- getInitCmdFn("Debugger.enable"),
- getInitCmdFn("Runtime.runIfWaitingForDebugger")
+ getInitCmdFn("Profiler.enable", null),
+ getInitCmdFn("Runtime.enable", null),
+ getInitCmdFn("Debugger.enable", null),
+ getInitCmdFn("Runtime.runIfWaitingForDebugger", null),
+ getInitCmdFn("Debugger.setAsyncCallStackDepth", JObject.FromObject(new { maxDepth = 32 })),
+ getInitCmdFn("Target.setAutoAttach", JObject.FromObject(new { autoAttach = true, waitForDebuggerOnStart = true, flatten = true }))
+ //getInitCmdFn("ServiceWorker.enable", null)
};
-
return init_cmds;
};
{
var script_id = args?["scriptId"]?.Value<string>();
var url = args["url"]?.Value<string>();
+ script_id += args["sessionId"]?.Value<string>();
if (script_id.StartsWith("dotnet://"))
{
var dbgUrl = args["dotNetUrl"]?.Value<string>();
internal virtual void CheckLocation(string script_loc, int line, int column, Dictionary<string, string> scripts, JToken location)
{
- var loc_str = $"{ scripts[location["scriptId"].Value<string>()] }" +
+ var loc_str = $"{ scripts[location["scriptId"].Value<string>()+cli.CurrentSessionId.sessionId] }" +
$"#{ location["lineNumber"].Value<int>() }" +
$"#{ location["columnNumber"].Value<int>() }";
public static readonly string? TestLogPath = Environment.GetEnvironmentVariable("TEST_LOG_PATH");
public static readonly bool SkipCleanup = Environment.GetEnvironmentVariable("SKIP_CLEANUP") == "1" ||
Environment.GetEnvironmentVariable("SKIP_CLEANUP") == "true";
+ public static readonly string? WasmTestsUsingVariant = Environment.GetEnvironmentVariable("WASM_TESTS_USING_VARIANT");
}
AssertEqual("Failed to resolve member access for DebuggerTests.InvalidEvaluateStaticClass.StaticProperty2", res.Error["result"]?["description"]?.Value<string>(), "wrong error message");
});
- [ConditionalFact(nameof(RunningOnChrome))]
+ [ConditionalFact(nameof(WasmSingleThreaded), nameof(RunningOnChrome))]
public async Task AsyncLocalsInContinueWithBlock() => await CheckInspectLocalsAtBreakpointSite(
"DebuggerTests.AsyncTests.ContinueWithTests", "ContinueWithStaticAsync", 4, "DebuggerTests.AsyncTests.ContinueWithTests.ContinueWithStaticAsync.AnonymousMethod__3_0",
"window.setTimeout(function() { invoke_static_method('[debugger-test] DebuggerTests.AsyncTests.ContinueWithTests:RunAsync'); })",
if (res["type"]?.Value<string>() == "newSource")
{
var method = res["type"]?.Value<string>();
- return onEvent(method, res, token);
+ return onEvent("", method, res, token);
}
if (res["type"]?.Value<string>() == "target-available-form" && res["target"] is JObject target)
break;
}
}
- return onEvent(method, res, token);
+ return onEvent("", method, res, token);
}
return null;
}
ConcurrentDictionary<string, TaskCompletionSource<JObject>> notifications = new ();
ConcurrentDictionary<string, Func<JObject, CancellationToken, Task<ProtocolEventHandlerReturn>>> eventListeners = new ();
-
+ ConcurrentQueue<(string, JObject)> nextNotifications = new (); //in a multithreaded runtime we can receive more than one pause at same time
public const string PAUSE = "pause";
public const string APP_READY = "app-ready";
public CancellationToken Token { get; }
{
if (tcs.Task.IsCompleted)
{
+ Client.CurrentSessionId = new SessionId(tcs.Task.Result["sessionId"]?.Value<string>());
notifications.Remove(what, out _);
return tcs.Task;
}
throw new Exception($"Invalid internal state, waiting for {what} while another wait is already setup");
}
+ else if (nextNotifications.TryDequeue(out (string what, JObject args) notification))
+ {
+ var n = new TaskCompletionSource<JObject>();
+ Client.CurrentSessionId = new SessionId(notification.args["sessionId"]?.Value<string>());
+ if (what != notification.what)
+ throw new Exception($"Unexpected different notification type");
+ n.SetResult(notification.args);
+ return n.Task;
+ }
else
{
var n = new TaskCompletionSource<JObject>();
if (notifications.TryGetValue(what, out TaskCompletionSource<JObject>? tcs))
{
if (tcs.Task.IsCompleted)
- throw new Exception($"Invalid internal state. Notifying for {what} again, but the previous one hasn't been read.");
-
+ {
+ nextNotifications.Enqueue((what, args));
+ return;
+ //throw new Exception($"Invalid internal state. Notifying for {what} again, but the previous one hasn't been read.");
+ }
+ Client.CurrentSessionId = new SessionId(args["sessionId"]?.Value<string>());
notifications[what].SetResult(args);
notifications.Remove(what, out _);
}
return ($"console.{type}: {output}", type);
}
- async Task OnMessage(string method, JObject args, CancellationToken token)
+ async Task OnMessage(string sessionId, string method, JObject args, CancellationToken token)
{
bool fail = false;
switch (method)
{
+ case "Target.attachedToTarget":
+ {
+ var sessionIdNewTarget = new SessionId(args["sessionId"]?.Value<string>());
+ await Client.SendCommand(sessionIdNewTarget, "Profiler.enable", null, token);
+ await Client.SendCommand(sessionIdNewTarget, "Runtime.enable", null, token);
+ await Client.SendCommand(sessionIdNewTarget, "Debugger.enable", null, token);
+ await Client.SendCommand(sessionIdNewTarget, "Runtime.runIfWaitingForDebugger", null, token);
+ await Client.SendCommand(sessionIdNewTarget, "Debugger.setAsyncCallStackDepth", JObject.FromObject(new { maxDepth = 32}), token);
+ break;
+ }
case "Debugger.paused":
+ {
+ if (sessionId != "")
+ args.Add("sessionId", sessionId);
NotifyOf(PAUSE, args);
break;
+ }
case "Mono.runtimeReady":
{
_gotRuntimeReady = true;
if (eventListeners.TryGetValue(method, out Func<JObject, CancellationToken, Task<ProtocolEventHandlerReturn>>? listener)
&& listener != null)
{
+ if (sessionId != "")
+ args.Add("sessionId", sessionId);
ProtocolEventHandlerReturn result = await listener(args, token).ConfigureAwait(false);
if (result is ProtocolEventHandlerReturn.RemoveHandler)
eventListeners.Remove(method, out _);
internal class InspectorClient : DevToolsClient
{
protected Dictionary<MessageId, TaskCompletionSource<Result>> pending_cmds = new Dictionary<MessageId, TaskCompletionSource<Result>>();
- protected Func<string, JObject, CancellationToken, Task> onEvent;
+ protected Func<string, string, JObject, CancellationToken, Task> onEvent;
protected int next_cmd_id;
+ public SessionId CurrentSessionId { get; set; } = SessionId.Null;
+
public InspectorClient(ILogger logger) : base(logger) { }
protected override async Task<WasmDebuggerConnection> SetupConnection(Uri webserverUri, CancellationToken token)
var res = JObject.Parse(msg);
if (res["id"] == null)
- return onEvent(res["method"].Value<string>(), res["params"] as JObject, token);
+ return onEvent(res["sessionId"]?.Value<string>(), res["method"].Value<string>(), res["params"] as JObject, token);
var id = res.ToObject<MessageId>();
if (!pending_cmds.Remove(id, out var item))
public virtual async Task Connect(
Uri uri,
- Func<string, JObject, CancellationToken, Task> onEvent,
+ Func<string, string, JObject, CancellationToken, Task> onEvent,
CancellationTokenSource cts)
{
this.onEvent = onEvent;
}
public Task<Result> SendCommand(string method, JObject args, CancellationToken token)
- => SendCommand(new SessionId(null), method, args, token);
+ => SendCommand(CurrentSessionId, method, args, token);
public virtual Task<Result> SendCommand(SessionId sessionId, string method, JObject args, CancellationToken token)
{
@params = args
});
+ if (sessionId != SessionId.Null)
+ o.Add("sessionId", sessionId.sessionId);
var tcs = new TaskCompletionSource<Result>();
pending_cmds[new MessageId(sessionId.sessionId, id)] = tcs;
Assert.False(source.Value["scriptSource"].Value<string>().Contains("// Unable to read document"));
}
- [ConditionalFact(nameof(RunningOnChrome))]
+ [ConditionalFact(nameof(WasmSingleThreaded), nameof(RunningOnChrome))]
public async Task InspectTaskAtLocals() => await CheckInspectLocalsAtBreakpointSite(
"InspectTask",
"RunInspectTask",
}
);
}
+
+ [ConditionalFact(nameof(WasmMultiThreaded))]
+ public async Task TestDebugUsingMultiThreadedRuntime()
+ {
+ var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
+ var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
+
+ var pause_location = await EvaluateAndCheck(
+ "window.setTimeout(function() {" + expression + "; }, 1);",
+ "dotnet://debugger-test.dll/debugger-test.cs", 1598, 8,
+ "MultiThreadedTest.Write");
+
+ var locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value<string>());
+ Assert.Equal(locals[1]["value"]["type"], "number");
+ Assert.Equal(locals[1]["name"], "currentThread");
+
+ pause_location = await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs", 1598, 8, "MultiThreadedTest.Write");
+ locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value<string>());
+ Assert.Equal(locals[1]["value"]["type"], "number");
+ Assert.Equal(locals[1]["name"], "currentThread");
+
+ pause_location = await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs", 1598, 8, "MultiThreadedTest.Write");
+ locals = await GetProperties(pause_location["callFrames"][0]["callFrameId"].Value<string>());
+ Assert.Equal(locals[1]["value"]["type"], "number");
+ Assert.Equal(locals[1]["name"], "currentThread");
+ }
}
}
await StepAndCheck(StepKind.Out, source_file, 15, 4, "TestAsyncStepOut");
}
- [Fact]
+ [ConditionalFact(nameof(WasmSingleThreaded))]
public async Task ResumeOutOfAsyncMethodToAsyncCallerWithBreakpoint()
{
string source_file = "dotnet://debugger-test.dll/debugger-async-step.cs";
public string PagePath { get; set; }
public string NodeApp { get; set; }
public string BrowserParms { get; set; }
+ public bool WebServerUseCors { get; set; }
+ public bool WebServerUseCrossOriginPolicy { get; set; }
public Func<string, ILogger<TestHarnessProxy>, Task<string>> ExtractConnUrl { get; set; }
}
}
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Microsoft.WebAssembly.Diagnostics;
using Xunit.Abstractions;
public static Task Start(string appPath, string pagePath, string url, ITestOutputHelper testOutput)
{
+ TestHarnessOptions options = new()
+ {
+ AppPath = appPath,
+ PagePath = pagePath,
+ DevToolsUrl = new Uri(url),
+ WebServerUseCors = false,
+ WebServerUseCrossOriginPolicy = true
+ };
+
lock (proxyLock)
{
if (hostTask != null)
})
.ConfigureServices((ctx, services) =>
{
- services.Configure<TestHarnessOptions>(ctx.Configuration);
- services.Configure<TestHarnessOptions>(options =>
+ if (options.WebServerUseCors)
{
- options.AppPath = appPath;
- options.PagePath = pagePath;
- options.DevToolsUrl = new Uri(url);
- });
+ services.AddCors(o => o.AddPolicy("AnyCors", builder =>
+ {
+ builder.AllowAnyOrigin()
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .WithExposedHeaders("*");
+ }));
+ }
+ services.AddSingleton(Options.Create(options));
})
.UseStartup<TestHarnessStartup>()
.UseUrls(Endpoint.ToString())
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IOptionsMonitor<TestHarnessOptions> optionsAccessor, IWebHostEnvironment env, ILogger<TestHarnessProxy> logger, ILoggerFactory loggerFactory)
+ public void Configure(IApplicationBuilder app, IOptions<TestHarnessOptions> optionsContainer, IWebHostEnvironment env, ILogger<TestHarnessProxy> logger, ILoggerFactory loggerFactory)
{
this.Logger = logger;
this._loggerFactory = loggerFactory;
+ TestHarnessOptions options = optionsContainer.Value;
app.UseWebSockets();
- TestHarnessOptions options = optionsAccessor.CurrentValue;
-
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".wasm"] = "application/wasm";
FileProvider = new PhysicalFileProvider(options.AppPath),
ServeUnknownFileTypes = true, //Cuz .wasm is not a known file type :cry:
RequestPath = "",
- ContentTypeProvider = provider
+ ContentTypeProvider = provider,
+ OnPrepareResponse = (context) => {
+ if (options.WebServerUseCrossOriginPolicy)
+ {
+ context.Context.Response.Headers.Add("Cross-Origin-Embedder-Policy", "require-corp");
+ context.Context.Response.Headers.Add("Cross-Origin-Opener-Policy", "same-origin");
+ }
+ }
});
+ if (options.WebServerUseCors)
+ {
+ app.UseCors("AnyCors");
+ }
+
app.UseRouter(router =>
{
router.MapGet("launch-host-and-connect", async context =>
<_DotnetCommand Condition="'$(OS)' == 'Windows_NT'">dotnet.exe</_DotnetCommand>
<RunScriptCommand>$(_DotnetCommand) test DebuggerTestSuite/DebuggerTestSuite.dll</RunScriptCommand>
+ <RunScriptCommand Condition="'$(MonoWasmBuildVariant)' == 'multithread'">$(RunScriptCommand) /e:WASM_TESTS_USING_VARIANT=multithreaded</RunScriptCommand>
<RunScriptCommand>$(RunScriptCommand) "-l:trx%3BLogFileName=testResults.trx"</RunScriptCommand>
<RunScriptCommand Condition="'$(ContinuousIntegrationBuild)' == 'true'">$(RunScriptCommand) "-l:console%3BVerbosity=normal"</RunScriptCommand>
public void Modify(double newDouble) {
r1.d1 = newDouble;
}
-
+
public double Run() {
return r1.d1;
}
myR1.s2 = new S1();
myR1.s2.d1 = 30;
myR1.s2.d2 = 40;
- double xyz = 123.0;
+ double xyz = 123.0;
R2Sample2 r2 = new R2Sample2(ref xyz);
xyz = 456.0;
System.Diagnostics.Debugger.Break();
array.Add(date);
}
}
+
+public class MultiThreadedTest
+{
+ public static void Run()
+ {
+ System.Collections.Generic.List<System.Threading.Thread> myThreads = new();
+ for (int i = 0 ; i < 3; i++)
+ {
+ var t = new System.Threading.Thread (() => Write("y"));
+ myThreads.Add(t);
+ t.Start();
+ }
+ foreach (System.Threading.Thread curThread in myThreads)
+ {
+ curThread.Join();
+ }
+ }
+ static void Write(string input)
+ {
+ var currentThread = System.Threading.Thread.CurrentThread.ManagedThreadId;
+ Console.WriteLine($"Thread:{currentThread} - {input}");
+ }
+}
+
<WasmGenerateAppBundle>true</WasmGenerateAppBundle>
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
+ <_WasmPThreadPoolSize Condition="'$(WasmEnableThreads)'=='true'">10</_WasmPThreadPoolSize>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
});
});
-
applicationLifetime.ApplicationStarted.Register(() =>
{
TaskCompletionSource<ServerURLs> tcs = realUrlsAvailableTcs;
debugger;
}
-export function mono_wasm_fire_debugger_agent_message(): void {
+export function mono_wasm_fire_debugger_agent_message_with_data_to_pause(base64String: string): void {
+ //keep this console.assert, otherwise optimization will remove the assignments
+ console.assert(true, `mono_wasm_fire_debugger_agent_message_with_data ${base64String}`);
// eslint-disable-next-line no-debugger
debugger;
}
+export function mono_wasm_fire_debugger_agent_message_with_data(data: number, len: number): void {
+ const base64String = toBase64StringImpl(new Uint8Array(Module.HEAPU8.buffer, data, len));
+ mono_wasm_fire_debugger_agent_message_with_data_to_pause(base64String);
+}
+
export function mono_wasm_add_dbg_command_received(res_ok: boolean, id: number, buffer: number, buffer_len: number): void {
- const assembly_data = new Uint8Array(Module.HEAPU8.buffer, buffer, buffer_len);
- const base64String = toBase64StringImpl(assembly_data);
+ const dbg_command = new Uint8Array(Module.HEAPU8.buffer, buffer, buffer_len);
+ const base64String = toBase64StringImpl(dbg_command);
const buffer_obj = {
res_ok,
res: {
interface MonoObjectRef extends ManagedPointer {
__brandMonoObjectRef: "MonoObjectRef";
}
-declare type MemOffset = number | VoidPtr | NativePointer | ManagedPointer;
-declare type NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer;
+type MemOffset = number | VoidPtr | NativePointer | ManagedPointer;
+type NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer;
interface WasmRoot<T extends MonoObject> {
get_address(): MonoObjectRef;
get_address_32(): number;
/**
* @deprecated Please use methods in top level API object instead
*/
-declare type BINDINGType = {
+type BINDINGType = {
/**
* @deprecated Please use [JSExportAttribute] instead
*/
/**
* @deprecated Please use methods in top level API object instead
*/
-declare type MONOType = {
+type MONOType = {
/**
* @deprecated Please use setEnvironmentVariable() instead
*/
// mini-wasm-debugger.c
"mono_wasm_asm_loaded",
- "mono_wasm_fire_debugger_agent_message",
+ "mono_wasm_fire_debugger_agent_message_with_data",
"mono_wasm_debugger_log",
"mono_wasm_add_dbg_command_received",
"mono_wasm_set_entrypoint_breakpoint",
// The .NET Foundation licenses this file to you under the MIT license.
import MonoWasmThreads from "consts:monoWasmThreads";
-import { mono_wasm_fire_debugger_agent_message, mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint } from "./debug";
+import { mono_wasm_fire_debugger_agent_message_with_data, mono_wasm_fire_debugger_agent_message_with_data_to_pause, mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint } from "./debug";
+
import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_load_icu_data, mono_wasm_get_icudt_name } from "./icu";
import { mono_wasm_bind_cs_function } from "./invoke-cs";
// mini-wasm-debugger.c
mono_wasm_asm_loaded,
- mono_wasm_fire_debugger_agent_message,
mono_wasm_debugger_log,
mono_wasm_add_dbg_command_received,
-
+ mono_wasm_fire_debugger_agent_message_with_data,
+ mono_wasm_fire_debugger_agent_message_with_data_to_pause,
// mono-threads-wasm.c
schedule_background_exec,
mangle: {
// because of stack walk at src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
// and unit test at src\libraries\System.Runtime.InteropServices.JavaScript\tests\System.Runtime.InteropServices.JavaScript.Legacy.UnitTests\timers.mjs
- keep_fnames: /(mono_wasm_runtime_ready|mono_wasm_fire_debugger_agent_message|mono_wasm_set_timeout_exec)/,
+ keep_fnames: /(mono_wasm_runtime_ready|mono_wasm_fire_debugger_agent_message_with_data_to_pause|mono_wasm_set_timeout_exec)/,
keep_classnames: /(ManagedObject|ManagedError|Span|ArraySegment|WasmRootBuffer|SessionOptionsBuilder)/,
},
format: {