Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -262,7 +262,6 @@ jobs:
isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
runOnlyOnWasmOnlyPipelines: true
shouldContinueOnError: true

# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput)

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData("GetTestData")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName)
{
await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2);
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -217,7 +217,6 @@ await EvaluateAndCheck(
[MemberData(nameof(FalseConditions))]
[MemberData(nameof(TrueConditions))]
[MemberData(nameof(InvalidConditions))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
{
Result [] bps = new Result[2];
Expand DownExpand Up@@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE
[InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task StepThroughOrNonUserCodeAttributeWithUserBp(
bool justMyCodeEnabled, string debuggingFunction, string evalFunName,
string functionNameCheck1, int line1, int col1,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

Expand Down
16 changes: 13 additions & 3 deletions src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs
);
}

[ConditionalFact(nameof(WasmMultiThreaded))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task TestDebugUsingMultiThreadedRuntime()
public static TheoryData<int> CountToTen()
{
var data = new TheoryData<int>();
for(int i=0;i<10;i++)
{
data.Add(i);
}
return data;
}

[ConditionalTheory(nameof(WasmMultiThreaded))]
[MemberData(nameof(CountToTen))]
public async Task TestDebugUsingMultiThreadedRuntime(int _attempt)
{
var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350,
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode)
{
await SetJustMyCode(justMyCode);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,8 @@
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- is 40 threads too many for debugger ? -->
<_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10</_WasmPThreadPoolSize>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
Expand Down
17 changes: 15 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
Expand DownExpand Up@@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) {
}

export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString());

Expand All@@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n
}

export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length);

Expand All@@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult {
}

export function mono_wasm_debugger_resume(): void {
//nothing
forceThreadMemoryViewRefresh();
}

export function mono_wasm_detach_debugger(): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(false);
}

export function mono_wasm_change_debugger_log_level(level: number): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_change_debugger_log_level(level);
}

Expand DownExpand Up@@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise<void> {
export function mono_wasm_debugger_attached(): void {
if (runtimeHelpers.waitForDebugger == -1)
runtimeHelpers.waitForDebugger = 1;
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(true);
}

Expand All@@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr
console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`);
// eslint-disable-next-line no-debugger
debugger;

forceThreadMemoryViewRefresh();
}

function _create_proxy_from_object_id(objectId: string, details: any) {
Expand DownExpand Up@@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) {
}

export function mono_wasm_call_function_on(request: CallRequest): CFOResponse {
forceThreadMemoryViewRefresh();

if (request.arguments != undefined && !Array.isArray(request.arguments))
throw new Error(`"arguments" should be an array, but was ${request.arguments}`);

Expand DownExpand Up@@ -329,6 +340,7 @@ type ValueAsJsonString = {
}

export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString {
forceThreadMemoryViewRefresh();
return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args);
}

Expand All@@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void {
}

export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void {
forceThreadMemoryViewRefresh();
const message = utf8ToString(message_ptr);

if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") {
Expand Down
24 changes: 24 additions & 0 deletions src/mono/wasm/runtime/memory.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer {
// See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer";
}

/*
Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime.
After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view.
This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime.
*/
export function forceThreadMemoryViewRefresh() {
// this condition should be eliminated by rollup on non-threading builds and it would become empty method.
if (!MonoWasmThreads) return;

const wasmMemory = Module.getMemory();

/*
Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked.
It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location),
which means the size change has not taken effect in this worker.
wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer,
and will update the current worker's knowledge of the buffer's size.
After that we should be able to safely updateMemoryViews and get a correctly sized view.
This only works because their implementation does not skip doing work even when you ask to grow by 0 pages.
*/
wasmMemory.grow(0);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
runtimeHelpers.updateMemoryViews();
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -262,7 +262,6 @@ jobs:
isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
runOnlyOnWasmOnlyPipelines: true
shouldContinueOnError: true

# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput)

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData("GetTestData")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName)
{
await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2);
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -217,7 +217,6 @@ await EvaluateAndCheck(
[MemberData(nameof(FalseConditions))]
[MemberData(nameof(TrueConditions))]
[MemberData(nameof(InvalidConditions))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
{
Result [] bps = new Result[2];
Expand DownExpand Up@@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE
[InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task StepThroughOrNonUserCodeAttributeWithUserBp(
bool justMyCodeEnabled, string debuggingFunction, string evalFunName,
string functionNameCheck1, int line1, int col1,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

Expand Down
16 changes: 13 additions & 3 deletions src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs
);
}

[ConditionalFact(nameof(WasmMultiThreaded))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task TestDebugUsingMultiThreadedRuntime()
public static TheoryData<int> CountToTen()
{
var data = new TheoryData<int>();
for(int i=0;i<10;i++)
{
data.Add(i);
}
return data;
}

[ConditionalTheory(nameof(WasmMultiThreaded))]
[MemberData(nameof(CountToTen))]
public async Task TestDebugUsingMultiThreadedRuntime(int _attempt)
{
var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350,
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode)
{
await SetJustMyCode(justMyCode);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,8 @@
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- is 40 threads too many for debugger ? -->
<_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10</_WasmPThreadPoolSize>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
Expand Down
17 changes: 15 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
Expand DownExpand Up@@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) {
}

export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString());

Expand All@@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n
}

export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length);

Expand All@@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult {
}

export function mono_wasm_debugger_resume(): void {
//nothing
forceThreadMemoryViewRefresh();
}

export function mono_wasm_detach_debugger(): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(false);
}

export function mono_wasm_change_debugger_log_level(level: number): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_change_debugger_log_level(level);
}

Expand DownExpand Up@@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise<void> {
export function mono_wasm_debugger_attached(): void {
if (runtimeHelpers.waitForDebugger == -1)
runtimeHelpers.waitForDebugger = 1;
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(true);
}

Expand All@@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr
console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`);
// eslint-disable-next-line no-debugger
debugger;

forceThreadMemoryViewRefresh();
}

function _create_proxy_from_object_id(objectId: string, details: any) {
Expand DownExpand Up@@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) {
}

export function mono_wasm_call_function_on(request: CallRequest): CFOResponse {
forceThreadMemoryViewRefresh();

if (request.arguments != undefined && !Array.isArray(request.arguments))
throw new Error(`"arguments" should be an array, but was ${request.arguments}`);

Expand DownExpand Up@@ -329,6 +340,7 @@ type ValueAsJsonString = {
}

export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString {
forceThreadMemoryViewRefresh();
return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args);
}

Expand All@@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void {
}

export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void {
forceThreadMemoryViewRefresh();
const message = utf8ToString(message_ptr);

if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") {
Expand Down
24 changes: 24 additions & 0 deletions src/mono/wasm/runtime/memory.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer {
// See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer";
}

/*
Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime.
After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view.
This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime.
*/
export function forceThreadMemoryViewRefresh() {
// this condition should be eliminated by rollup on non-threading builds and it would become empty method.
if (!MonoWasmThreads) return;

const wasmMemory = Module.getMemory();

/*
Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked.
It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location),
which means the size change has not taken effect in this worker.
wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer,
and will update the current worker's knowledge of the buffer's size.
After that we should be able to safely updateMemoryViews and get a correctly sized view.
This only works because their implementation does not skip doing work even when you ask to grow by 0 pages.
*/
wasmMemory.grow(0);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
runtimeHelpers.updateMemoryViews();
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -262,7 +262,6 @@ jobs:
isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
runOnlyOnWasmOnlyPipelines: true
shouldContinueOnError: true

# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput)

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData("GetTestData")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName)
{
await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2);
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -217,7 +217,6 @@ await EvaluateAndCheck(
[MemberData(nameof(FalseConditions))]
[MemberData(nameof(TrueConditions))]
[MemberData(nameof(InvalidConditions))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
{
Result [] bps = new Result[2];
Expand DownExpand Up@@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE
[InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task StepThroughOrNonUserCodeAttributeWithUserBp(
bool justMyCodeEnabled, string debuggingFunction, string evalFunName,
string functionNameCheck1, int line1, int col1,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

Expand Down
16 changes: 13 additions & 3 deletions src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs
);
}

[ConditionalFact(nameof(WasmMultiThreaded))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task TestDebugUsingMultiThreadedRuntime()
public static TheoryData<int> CountToTen()
{
var data = new TheoryData<int>();
for(int i=0;i<10;i++)
{
data.Add(i);
}
return data;
}

[ConditionalTheory(nameof(WasmMultiThreaded))]
[MemberData(nameof(CountToTen))]
public async Task TestDebugUsingMultiThreadedRuntime(int _attempt)
{
var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350,
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode)
{
await SetJustMyCode(justMyCode);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,8 @@
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- is 40 threads too many for debugger ? -->
<_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10</_WasmPThreadPoolSize>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
Expand Down
17 changes: 15 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
Expand DownExpand Up@@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) {
}

export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString());

Expand All@@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n
}

export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length);

Expand All@@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult {
}

export function mono_wasm_debugger_resume(): void {
//nothing
forceThreadMemoryViewRefresh();
}

export function mono_wasm_detach_debugger(): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(false);
}

export function mono_wasm_change_debugger_log_level(level: number): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_change_debugger_log_level(level);
}

Expand DownExpand Up@@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise<void> {
export function mono_wasm_debugger_attached(): void {
if (runtimeHelpers.waitForDebugger == -1)
runtimeHelpers.waitForDebugger = 1;
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(true);
}

Expand All@@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr
console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`);
// eslint-disable-next-line no-debugger
debugger;

forceThreadMemoryViewRefresh();
}

function _create_proxy_from_object_id(objectId: string, details: any) {
Expand DownExpand Up@@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) {
}

export function mono_wasm_call_function_on(request: CallRequest): CFOResponse {
forceThreadMemoryViewRefresh();

if (request.arguments != undefined && !Array.isArray(request.arguments))
throw new Error(`"arguments" should be an array, but was ${request.arguments}`);

Expand DownExpand Up@@ -329,6 +340,7 @@ type ValueAsJsonString = {
}

export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString {
forceThreadMemoryViewRefresh();
return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args);
}

Expand All@@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void {
}

export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void {
forceThreadMemoryViewRefresh();
const message = utf8ToString(message_ptr);

if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") {
Expand Down
24 changes: 24 additions & 0 deletions src/mono/wasm/runtime/memory.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer {
// See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer";
}

/*
Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime.
After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view.
This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime.
*/
export function forceThreadMemoryViewRefresh() {
// this condition should be eliminated by rollup on non-threading builds and it would become empty method.
if (!MonoWasmThreads) return;

const wasmMemory = Module.getMemory();

/*
Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked.
It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location),
which means the size change has not taken effect in this worker.
wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer,
and will update the current worker's knowledge of the buffer's size.
After that we should be able to safely updateMemoryViews and get a correctly sized view.
This only works because their implementation does not skip doing work even when you ask to grow by 0 pages.
*/
wasmMemory.grow(0);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
runtimeHelpers.updateMemoryViews();
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -262,7 +262,6 @@ jobs:
isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
runOnlyOnWasmOnlyPipelines: true
shouldContinueOnError: true

# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput)

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData("GetTestData")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName)
{
await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2);
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -217,7 +217,6 @@ await EvaluateAndCheck(
[MemberData(nameof(FalseConditions))]
[MemberData(nameof(TrueConditions))]
[MemberData(nameof(InvalidConditions))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
{
Result [] bps = new Result[2];
Expand DownExpand Up@@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE
[InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task StepThroughOrNonUserCodeAttributeWithUserBp(
bool justMyCodeEnabled, string debuggingFunction, string evalFunName,
string functionNameCheck1, int line1, int col1,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

Expand Down
16 changes: 13 additions & 3 deletions src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs
);
}

[ConditionalFact(nameof(WasmMultiThreaded))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task TestDebugUsingMultiThreadedRuntime()
public static TheoryData<int> CountToTen()
{
var data = new TheoryData<int>();
for(int i=0;i<10;i++)
{
data.Add(i);
}
return data;
}

[ConditionalTheory(nameof(WasmMultiThreaded))]
[MemberData(nameof(CountToTen))]
public async Task TestDebugUsingMultiThreadedRuntime(int _attempt)
{
var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350,
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode)
{
await SetJustMyCode(justMyCode);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,8 @@
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- is 40 threads too many for debugger ? -->
<_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10</_WasmPThreadPoolSize>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
Expand Down
17 changes: 15 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
Expand DownExpand Up@@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) {
}

export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString());

Expand All@@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n
}

export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length);

Expand All@@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult {
}

export function mono_wasm_debugger_resume(): void {
//nothing
forceThreadMemoryViewRefresh();
}

export function mono_wasm_detach_debugger(): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(false);
}

export function mono_wasm_change_debugger_log_level(level: number): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_change_debugger_log_level(level);
}

Expand DownExpand Up@@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise<void> {
export function mono_wasm_debugger_attached(): void {
if (runtimeHelpers.waitForDebugger == -1)
runtimeHelpers.waitForDebugger = 1;
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(true);
}

Expand All@@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr
console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`);
// eslint-disable-next-line no-debugger
debugger;

forceThreadMemoryViewRefresh();
}

function _create_proxy_from_object_id(objectId: string, details: any) {
Expand DownExpand Up@@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) {
}

export function mono_wasm_call_function_on(request: CallRequest): CFOResponse {
forceThreadMemoryViewRefresh();

if (request.arguments != undefined && !Array.isArray(request.arguments))
throw new Error(`"arguments" should be an array, but was ${request.arguments}`);

Expand DownExpand Up@@ -329,6 +340,7 @@ type ValueAsJsonString = {
}

export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString {
forceThreadMemoryViewRefresh();
return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args);
}

Expand All@@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void {
}

export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void {
forceThreadMemoryViewRefresh();
const message = utf8ToString(message_ptr);

if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") {
Expand Down
24 changes: 24 additions & 0 deletions src/mono/wasm/runtime/memory.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer {
// See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer";
}

/*
Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime.
After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view.
This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime.
*/
export function forceThreadMemoryViewRefresh() {
// this condition should be eliminated by rollup on non-threading builds and it would become empty method.
if (!MonoWasmThreads) return;

const wasmMemory = Module.getMemory();

/*
Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked.
It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location),
which means the size change has not taken effect in this worker.
wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer,
and will update the current worker's knowledge of the buffer's size.
After that we should be able to safely updateMemoryViews and get a correctly sized view.
This only works because their implementation does not skip doing work even when you ask to grow by 0 pages.
*/
wasmMemory.grow(0);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
runtimeHelpers.updateMemoryViews();
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -262,7 +262,6 @@ jobs:
isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
runOnlyOnWasmOnlyPipelines: true
shouldContinueOnError: true

# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput)

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData("GetTestData")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName)
{
await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2);
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -217,7 +217,6 @@ await EvaluateAndCheck(
[MemberData(nameof(FalseConditions))]
[MemberData(nameof(TrueConditions))]
[MemberData(nameof(InvalidConditions))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
{
Result [] bps = new Result[2];
Expand DownExpand Up@@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE
[InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task StepThroughOrNonUserCodeAttributeWithUserBp(
bool justMyCodeEnabled, string debuggingFunction, string evalFunName,
string functionNameCheck1, int line1, int col1,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

Expand Down
16 changes: 13 additions & 3 deletions src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs
);
}

[ConditionalFact(nameof(WasmMultiThreaded))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task TestDebugUsingMultiThreadedRuntime()
public static TheoryData<int> CountToTen()
{
var data = new TheoryData<int>();
for(int i=0;i<10;i++)
{
data.Add(i);
}
return data;
}

[ConditionalTheory(nameof(WasmMultiThreaded))]
[MemberData(nameof(CountToTen))]
public async Task TestDebugUsingMultiThreadedRuntime(int _attempt)
{
var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350,
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode)
{
await SetJustMyCode(justMyCode);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,8 @@
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- is 40 threads too many for debugger ? -->
<_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10</_WasmPThreadPoolSize>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
Expand Down
17 changes: 15 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
Expand DownExpand Up@@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) {
}

export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString());

Expand All@@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n
}

export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length);

Expand All@@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult {
}

export function mono_wasm_debugger_resume(): void {
//nothing
forceThreadMemoryViewRefresh();
}

export function mono_wasm_detach_debugger(): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(false);
}

export function mono_wasm_change_debugger_log_level(level: number): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_change_debugger_log_level(level);
}

Expand DownExpand Up@@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise<void> {
export function mono_wasm_debugger_attached(): void {
if (runtimeHelpers.waitForDebugger == -1)
runtimeHelpers.waitForDebugger = 1;
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(true);
}

Expand All@@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr
console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`);
// eslint-disable-next-line no-debugger
debugger;

forceThreadMemoryViewRefresh();
}

function _create_proxy_from_object_id(objectId: string, details: any) {
Expand DownExpand Up@@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) {
}

export function mono_wasm_call_function_on(request: CallRequest): CFOResponse {
forceThreadMemoryViewRefresh();

if (request.arguments != undefined && !Array.isArray(request.arguments))
throw new Error(`"arguments" should be an array, but was ${request.arguments}`);

Expand DownExpand Up@@ -329,6 +340,7 @@ type ValueAsJsonString = {
}

export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString {
forceThreadMemoryViewRefresh();
return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args);
}

Expand All@@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void {
}

export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void {
forceThreadMemoryViewRefresh();
const message = utf8ToString(message_ptr);

if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") {
Expand Down
24 changes: 24 additions & 0 deletions src/mono/wasm/runtime/memory.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer {
// See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer";
}

/*
Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime.
After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view.
This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime.
*/
export function forceThreadMemoryViewRefresh() {
// this condition should be eliminated by rollup on non-threading builds and it would become empty method.
if (!MonoWasmThreads) return;

const wasmMemory = Module.getMemory();

/*
Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked.
It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location),
which means the size change has not taken effect in this worker.
wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer,
and will update the current worker's knowledge of the buffer's size.
After that we should be able to safely updateMemoryViews and get a correctly sized view.
This only works because their implementation does not skip doing work even when you ask to grow by 0 pages.
*/
wasmMemory.grow(0);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
runtimeHelpers.updateMemoryViews();
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -262,7 +262,6 @@ jobs:
isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
runOnlyOnWasmOnlyPipelines: true
shouldContinueOnError: true

# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput)

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData("GetTestData")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName)
{
await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2);
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -217,7 +217,6 @@ await EvaluateAndCheck(
[MemberData(nameof(FalseConditions))]
[MemberData(nameof(TrueConditions))]
[MemberData(nameof(InvalidConditions))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
{
Result [] bps = new Result[2];
Expand DownExpand Up@@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE
[InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task StepThroughOrNonUserCodeAttributeWithUserBp(
bool justMyCodeEnabled, string debuggingFunction, string evalFunName,
string functionNameCheck1, int line1, int col1,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

Expand Down
16 changes: 13 additions & 3 deletions src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs
);
}

[ConditionalFact(nameof(WasmMultiThreaded))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task TestDebugUsingMultiThreadedRuntime()
public static TheoryData<int> CountToTen()
{
var data = new TheoryData<int>();
for(int i=0;i<10;i++)
{
data.Add(i);
}
return data;
}

[ConditionalTheory(nameof(WasmMultiThreaded))]
[MemberData(nameof(CountToTen))]
public async Task TestDebugUsingMultiThreadedRuntime(int _attempt)
{
var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350,
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode)
{
await SetJustMyCode(justMyCode);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,8 @@
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- is 40 threads too many for debugger ? -->
<_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10</_WasmPThreadPoolSize>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
Expand Down
17 changes: 15 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
Expand DownExpand Up@@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) {
}

export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString());

Expand All@@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n
}

export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length);

Expand All@@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult {
}

export function mono_wasm_debugger_resume(): void {
//nothing
forceThreadMemoryViewRefresh();
}

export function mono_wasm_detach_debugger(): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(false);
}

export function mono_wasm_change_debugger_log_level(level: number): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_change_debugger_log_level(level);
}

Expand DownExpand Up@@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise<void> {
export function mono_wasm_debugger_attached(): void {
if (runtimeHelpers.waitForDebugger == -1)
runtimeHelpers.waitForDebugger = 1;
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(true);
}

Expand All@@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr
console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`);
// eslint-disable-next-line no-debugger
debugger;

forceThreadMemoryViewRefresh();
}

function _create_proxy_from_object_id(objectId: string, details: any) {
Expand DownExpand Up@@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) {
}

export function mono_wasm_call_function_on(request: CallRequest): CFOResponse {
forceThreadMemoryViewRefresh();

if (request.arguments != undefined && !Array.isArray(request.arguments))
throw new Error(`"arguments" should be an array, but was ${request.arguments}`);

Expand DownExpand Up@@ -329,6 +340,7 @@ type ValueAsJsonString = {
}

export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString {
forceThreadMemoryViewRefresh();
return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args);
}

Expand All@@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void {
}

export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void {
forceThreadMemoryViewRefresh();
const message = utf8ToString(message_ptr);

if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") {
Expand Down
24 changes: 24 additions & 0 deletions src/mono/wasm/runtime/memory.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer {
// See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer";
}

/*
Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime.
After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view.
This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime.
*/
export function forceThreadMemoryViewRefresh() {
// this condition should be eliminated by rollup on non-threading builds and it would become empty method.
if (!MonoWasmThreads) return;

const wasmMemory = Module.getMemory();

/*
Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked.
It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location),
which means the size change has not taken effect in this worker.
wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer,
and will update the current worker's knowledge of the buffer's size.
After that we should be able to safely updateMemoryViews and get a correctly sized view.
This only works because their implementation does not skip doing work even when you ask to grow by 0 pages.
*/
wasmMemory.grow(0);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
runtimeHelpers.updateMemoryViews();
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -262,7 +262,6 @@ jobs:
isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
runOnlyOnWasmOnlyPipelines: true
shouldContinueOnError: true

# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput)

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData("GetTestData")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName)
{
await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2);
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -217,7 +217,6 @@ await EvaluateAndCheck(
[MemberData(nameof(FalseConditions))]
[MemberData(nameof(TrueConditions))]
[MemberData(nameof(InvalidConditions))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
{
Result [] bps = new Result[2];
Expand DownExpand Up@@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE
[InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task StepThroughOrNonUserCodeAttributeWithUserBp(
bool justMyCodeEnabled, string debuggingFunction, string evalFunName,
string functionNameCheck1, int line1, int col1,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

Expand Down
16 changes: 13 additions & 3 deletions src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs
);
}

[ConditionalFact(nameof(WasmMultiThreaded))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task TestDebugUsingMultiThreadedRuntime()
public static TheoryData<int> CountToTen()
{
var data = new TheoryData<int>();
for(int i=0;i<10;i++)
{
data.Add(i);
}
return data;
}

[ConditionalTheory(nameof(WasmMultiThreaded))]
[MemberData(nameof(CountToTen))]
public async Task TestDebugUsingMultiThreadedRuntime(int _attempt)
{
var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350,
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode)
{
await SetJustMyCode(justMyCode);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,8 @@
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- is 40 threads too many for debugger ? -->
<_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10</_WasmPThreadPoolSize>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
Expand Down
17 changes: 15 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
Expand DownExpand Up@@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) {
}

export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString());

Expand All@@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n
}

export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length);

Expand All@@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult {
}

export function mono_wasm_debugger_resume(): void {
//nothing
forceThreadMemoryViewRefresh();
}

export function mono_wasm_detach_debugger(): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(false);
}

export function mono_wasm_change_debugger_log_level(level: number): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_change_debugger_log_level(level);
}

Expand DownExpand Up@@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise<void> {
export function mono_wasm_debugger_attached(): void {
if (runtimeHelpers.waitForDebugger == -1)
runtimeHelpers.waitForDebugger = 1;
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(true);
}

Expand All@@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr
console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`);
// eslint-disable-next-line no-debugger
debugger;

forceThreadMemoryViewRefresh();
}

function _create_proxy_from_object_id(objectId: string, details: any) {
Expand DownExpand Up@@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) {
}

export function mono_wasm_call_function_on(request: CallRequest): CFOResponse {
forceThreadMemoryViewRefresh();

if (request.arguments != undefined && !Array.isArray(request.arguments))
throw new Error(`"arguments" should be an array, but was ${request.arguments}`);

Expand DownExpand Up@@ -329,6 +340,7 @@ type ValueAsJsonString = {
}

export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString {
forceThreadMemoryViewRefresh();
return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args);
}

Expand All@@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void {
}

export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void {
forceThreadMemoryViewRefresh();
const message = utf8ToString(message_ptr);

if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") {
Expand Down
24 changes: 24 additions & 0 deletions src/mono/wasm/runtime/memory.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer {
// See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer";
}

/*
Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime.
After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view.
This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime.
*/
export function forceThreadMemoryViewRefresh() {
// this condition should be eliminated by rollup on non-threading builds and it would become empty method.
if (!MonoWasmThreads) return;

const wasmMemory = Module.getMemory();

/*
Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked.
It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location),
which means the size change has not taken effect in this worker.
wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer,
and will update the current worker's knowledge of the buffer's size.
After that we should be able to safely updateMemoryViews and get a correctly sized view.
This only works because their implementation does not skip doing work even when you ask to grow by 0 pages.
*/
wasmMemory.grow(0);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
runtimeHelpers.updateMemoryViews();
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -262,7 +262,6 @@ jobs:
isExtraPlatformsBuild: ${{ parameters.isExtraPlatformsBuild }}
isWasmOnlyBuild: ${{ parameters.isWasmOnlyBuild }}
runOnlyOnWasmOnlyPipelines: true
shouldContinueOnError: true

# Disable for now
#- template: /eng/pipelines/coreclr/perf-wasm-jobs.yml
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,7 +50,6 @@ public AssignmentTests(ITestOutputHelper testOutput)

[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData("GetTestData")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectVariableBeforeAndAfterAssignment(string clazz, JObject checkDefault, JObject checkValue, string methodName)
{
await SetBreakpointInMethod("debugger-test", "DebuggerTests." + clazz, "Prepare", 2);
Expand Down
2 changes: 0 additions & 2 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -217,7 +217,6 @@ await EvaluateAndCheck(
[MemberData(nameof(FalseConditions))]
[MemberData(nameof(TrueConditions))]
[MemberData(nameof(InvalidConditions))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task ConditionalBreakpoint2(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
{
Result [] bps = new Result[2];
Expand DownExpand Up@@ -700,7 +699,6 @@ public async Task StepThroughOrNonUserCodeAttributeResumeWithBp(bool justMyCodeE
[InlineData(true, "Debugger.stepInto", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(false, "Debugger.resume", "RunStepThroughWithNonUserCode", "StepThroughWithNonUserCodeUserBp", 927, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[InlineData(true, "Debugger.resume", "RunStepThroughWithNonUserCode", "RunStepThroughWithNonUserCode", -1, 8, "RunStepThroughWithNonUserCode", -1, 4)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task StepThroughOrNonUserCodeAttributeWithUserBp(
bool justMyCodeEnabled, string debuggingFunction, string evalFunName,
string functionNameCheck1, int line1, int col1,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -260,7 +260,6 @@ public GetPropertiesTests(ITestOutputHelper testOutput) : base(testOutput)
[MemberData(nameof(ClassGetPropertiesTestData), parameters: false)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: true)]
[MemberData(nameof(StructGetPropertiesTestData), parameters: false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task InspectTypeInheritedMembers(string type_name, bool? own_properties, bool? accessors_only, string[] expected_names, Dictionary<string, (JObject, bool)> all_props, bool is_async) =>
await InspectTypeInheritedMembersInternal(type_name, own_properties, accessors_only, expected_names, all_props, is_async, AutoEvaluate.Unset);

Expand Down
16 changes: 13 additions & 3 deletions src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1163,9 +1163,19 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs
);
}

[ConditionalFact(nameof(WasmMultiThreaded))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task TestDebugUsingMultiThreadedRuntime()
public static TheoryData<int> CountToTen()
{
var data = new TheoryData<int>();
for(int i=0;i<10;i++)
{
data.Add(i);
}
return data;
}

[ConditionalTheory(nameof(WasmMultiThreaded))]
[MemberData(nameof(CountToTen))]
public async Task TestDebugUsingMultiThreadedRuntime(int _attempt)
{
var bp = await SetBreakpointInMethod("debugger-test.dll", "MultiThreadedTest", "Write", 2);
var expression = $"{{ invoke_static_method('[debugger-test] MultiThreadedTest:Run'); }}";
Expand Down
1 change: 0 additions & 1 deletion src/mono/wasm/debugger/DebuggerTestSuite/SteppingTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1222,7 +1222,6 @@ await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350,
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/86496", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
public async Task SkipWasmFunctionsAccordinglyJustMyCode(bool justMyCode)
{
await SetJustMyCode(justMyCode);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,8 @@
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- is 40 threads too many for debugger ? -->
<_WasmPThreadPoolSize Condition="'$(MonoWasmBuildVariant)' == 'multithread'">10</_WasmPThreadPoolSize>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
Expand Down
17 changes: 15 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@ import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr, CharPtr } from "./types/emscripten";
import { mono_log_warn } from "./logging";
import { localHeapViewU8 } from "./memory";
import { forceThreadMemoryViewRefresh, localHeapViewU8 } from "./memory";
import { utf8ToString } from "./strings";
const commands_received: any = new Map<number, CommandResponse>();
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
Expand DownExpand Up@@ -75,6 +75,8 @@ function mono_wasm_malloc_and_set_debug_buffer(command_parameters: string) {
}

export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: string, length: number, valtype: number, newvalue: number): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, _debugger_buffer, length, valtype, newvalue.toString());

Expand All@@ -85,6 +87,8 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n
}

export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: string): CommandResponseResult {
forceThreadMemoryViewRefresh();

mono_wasm_malloc_and_set_debug_buffer(command_parameters);
cwraps.mono_wasm_send_dbg_command(id, command_set, command, _debugger_buffer, command_parameters.length);

Expand All@@ -105,14 +109,16 @@ export function mono_wasm_get_dbg_command_info(): CommandResponseResult {
}

export function mono_wasm_debugger_resume(): void {
//nothing
forceThreadMemoryViewRefresh();
}

export function mono_wasm_detach_debugger(): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(false);
}

export function mono_wasm_change_debugger_log_level(level: number): void {
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_change_debugger_log_level(level);
}

Expand DownExpand Up@@ -148,6 +154,7 @@ export function mono_wasm_wait_for_debugger(): Promise<void> {
export function mono_wasm_debugger_attached(): void {
if (runtimeHelpers.waitForDebugger == -1)
runtimeHelpers.waitForDebugger = 1;
forceThreadMemoryViewRefresh();
cwraps.mono_wasm_set_is_debugger_attached(true);
}

Expand All@@ -160,6 +167,8 @@ export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entr
console.assert(true, `Adding an entrypoint breakpoint ${_assembly_name_str} at method token ${_entrypoint_method_token}`);
// eslint-disable-next-line no-debugger
debugger;

forceThreadMemoryViewRefresh();
}

function _create_proxy_from_object_id(objectId: string, details: any) {
Expand DownExpand Up@@ -210,6 +219,8 @@ function _create_proxy_from_object_id(objectId: string, details: any) {
}

export function mono_wasm_call_function_on(request: CallRequest): CFOResponse {
forceThreadMemoryViewRefresh();

if (request.arguments != undefined && !Array.isArray(request.arguments))
throw new Error(`"arguments" should be an array, but was ${request.arguments}`);

Expand DownExpand Up@@ -329,6 +340,7 @@ type ValueAsJsonString = {
}

export function mono_wasm_get_details(objectId: string, args = {}): ValueAsJsonString {
forceThreadMemoryViewRefresh();
return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args);
}

Expand All@@ -344,6 +356,7 @@ export function mono_wasm_release_object(objectId: string): void {
}

export function mono_wasm_debugger_log(level: number, message_ptr: CharPtr): void {
forceThreadMemoryViewRefresh();
const message = utf8ToString(message_ptr);

if (INTERNAL["logging"] && typeof INTERNAL.logging["debugger"] === "function") {
Expand Down
24 changes: 24 additions & 0 deletions src/mono/wasm/runtime/memory.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -408,3 +408,27 @@ export function isSharedArrayBuffer(buffer: any): buffer is SharedArrayBuffer {
// See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
return sharedArrayBufferDefined && buffer[Symbol.toStringTag] === "SharedArrayBuffer";
}

/*
Problem: When WebWorker is suspended in the browser, the other running threads could `grow` the linear memory in the meantime.
After the thread is un-suspended C code may to try to de-reference pointer which is beyond it's known view.
This is likely V8 bug. We don't have direct evidence, just failed debugger unit tests with MT runtime.
*/
export function forceThreadMemoryViewRefresh() {
// this condition should be eliminated by rollup on non-threading builds and it would become empty method.
if (!MonoWasmThreads) return;

const wasmMemory = Module.getMemory();

/*
Normally when wasm memory grows in v8, this size change is broadcast to other web workers via an 'interrupt', which works by setting a thread-local flag that needs to be checked.
It's possible that at this point in execution the flag has not been checked yet (because this worker was suspended by the debugger in an unknown location),
which means the size change has not taken effect in this worker.
wasmMemory.grow's implementation in v8 checks to see whether other workers have already grown the buffer,
and will update the current worker's knowledge of the buffer's size.
After that we should be able to safely updateMemoryViews and get a correctly sized view.
This only works because their implementation does not skip doing work even when you ask to grow by 0 pages.
*/
wasmMemory.grow(0);
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
runtimeHelpers.updateMemoryViews();
Comment thread
pavelsavara marked this conversation as resolved.
Outdated
}