dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.96k stars 4.65k forks source link

.NET exception call stack not present when running as WASM #105664

Open ghosttie opened 1 month ago

ghosttie commented 1 month ago

Description

I am writing an application based on the "WebAssembly Browser App" template (not Blazor). The documentation says that exceptions should be caught natively, but when my code throws an exception I get a stack trace like this

Uncaught ManagedError Error: Nullable object must have a value.
    at Jn (c:\Dropbox (Personal)\personal\projects\C#\Tactics\Tactics\wwwroot\_framework\https:\raw.githubusercontent.com\dotnet\runtime\2aade6beb02ea367fd97c4070a4198802fe61c03\src\mono\wasm\runtime\marshal-to-js.ts:349:18)
    at kr (c:\Dropbox (Personal)\personal\projects\C#\Tactics\Tactics\wwwroot\_framework\https:\raw.githubusercontent.com\dotnet\runtime\2aade6beb02ea367fd97c4070a4198802fe61c03\src\mono\wasm\runtime\invoke-cs.ts:277:19)
    at <anonymous> (c:\Dropbox (Personal)\personal\projects\C#\Tactics\Tactics\wwwroot\_framework\https:\raw.githubusercontent.com\dotnet\runtime\2aade6beb02ea367fd97c4070a4198802fe61c03\src\mono\wasm\runtime\invoke-cs.ts:247:13)
    at paint (C:\Dropbox (Personal)\personal\projects\C#\Tactics\Tactics\wwwroot\main.js:102:26)

where it only tells me where it was in the JS WASM runtime, not in the .NET code, which makes exceptions very hard to debug.

Reproduction Steps

My code does a window.requestAnimationFrame in JS which sends an interop to the .NET code which throws an exception. I don't know if the requestAnimationFrame part is relevant.

Expected behavior

Ideally the exception breaks in Visual Studio, but even just showing the .NET stack track would be helpful.

Actual behavior

It shows the stack trace in the JS WASM runtime code.

Regression?

No response

Known Workarounds

No response

Configuration

.NET 8.0.7 Windows 10 v10.0.19045 x64 Chrome v127

Other information

No response

tommcdon commented 1 month ago

@thaystg

thaystg commented 1 month ago

Can you please provide a sample app to reproduce the problem?

ghosttie commented 1 month ago

It was easier to reproduce than I thought it was going to be repro.zip

maraf commented 1 month ago

cc @pavelsavara

pavelsavara commented 1 month ago

I think this is possibly because the runtime already exited, before trying to format the stack trace.

https://github.com/dotnet/runtime/blob/2aade6beb02ea367fd97c4070a4198802fe61c03/src/mono/wasm/runtime/marshal.ts#L346

In the latest version of the code we are more explicit about that https://github.com/dotnet/runtime/blob/74c608d67d64675ff840c5888368669777c8aa2c/src/mono/browser/runtime/marshal.ts#L418-L420

somehow you still use the old template with await dotnet.run();

we now have better template with runMain which will kee the runtime running https://github.com/dotnet/runtime/blob/74c608d67d64675ff840c5888368669777c8aa2c/src/mono/wasm/templates/templates/browser/wwwroot/main.js#L31-L32

ghosttie commented 1 month ago

I set it up with

dotnet workload install wasm-tools
dotnet workload install wasm-experimental
dotnet new wasmbrowser

following the instructions from here - I couldn't find newer instructions, are there any?

maraf commented 1 month ago

No, .NET 8 template contains dotnet.run() that exits the runtime after finished. You can replace it with runMain() that won't exit the runtime (which is used in the .NET 9 template by defualt)

ghosttie commented 1 month ago

I changed

await dotnet.run();

to

await dotnet.runMain();

in main.js but it doesn't seem to have made any difference

I also tried just

await runMain();

and it's the same

ilonatommy commented 1 month ago

Testing the repro app in net9, I am getting a nice stack (no edition to runMain() / dotnet.run(), @maraf):

marshal-to-js.ts:349 Uncaught    at MyClass.Greeting() in C:\Users\user\Downloads\repro\Program.cs:line 9
   at MyClass.__Wrapper_Greeting_1428811289(JSMarshalerArgument* __arguments_buffer) in C:\Users\user\Downloads\repro\obj\Debug\net8.0\Microsoft.Interop.JavaScript.JSImportGenerator\Microsoft.Interop.JavaScript.JSExportGenerator\JSExports.g.cs:line 38
Error: Error in the application.
    at Jn (http://localhost:5250/_framework/dotnet.runtime.8.0.7.pga4svny9i.js:3:31614)
    at kr (http://localhost:5250/_framework/dotnet.runtime.8.0.7.pga4svny9i.js:3:35529)
    at Object.<anonymous> (http://localhost:5250/_framework/dotnet.runtime.8.0.7.pga4svny9i.js:3:180960)
    at http://localhost:5250/main.js:21:30
maraf commented 1 month ago

@ghosttie I can't repro the issue as you see it. Running the repro app that you produces this on my machine.

Uncaught    at MyClass.Greeting() in /Users/marekfisera/Development/samples/Browser8JSExportCallstack/repro/Program.cs:line 9
   at MyClass.__Wrapper_Greeting_1196689670(JSMarshalerArgument* __arguments_buffer) in /Users/marekfisera/Development/samples/Browser8JSExportCallstack/repro/Microsoft.Interop.JavaScript.JSImportGenerator/Microsoft.Interop.JavaScript.JSExportGenerator/JSExports.g.cs:line 38
Error: Error in the application.

Is the managed stack trace missing even for the repro on your machine?

About the replace of dotnet.run with runMain, you can see it here (in the .NET 9 version of the template) https://github.com/dotnet/runtime/blob/main/src/mono/wasm/templates/templates/browser/wwwroot/main.js#L32. It's just runMain(), not dotnet.runMain, and you get function on line 6. Does this change help on your app?

ghosttie commented 1 month ago

I was able to get the repro to work by getting the runMain function from line 6 of the .NET 9 version of the template, thanks.

It only shows the managed stack trace in the browser console, not in Visual Studio, is that how it's supposed to work?

I also haven' been able to get it to work in my actual application - I made the same runMain changes but I'm getting

TypeError: Cannot read properties of undefined (reading 'length')
    at startup.ts:632:43
    at Object.Oc [as runMain] (run.ts:38:5)
    at main.js:54:12
maraf commented 1 month ago

I also haven' been able to get it to work in my actual application - I made the same runMain changes but I'm gettin

In .NET 8 it expects an array with ARGS, so runMain([]) should work

It only shows the managed stack trace in the browser console, not in Visual Studio, is that how it's supposed to work?

I'll check that..

ghosttie commented 1 month ago

I got it working with

runMain(mainAssemblyName, [])
pavelsavara commented 1 month ago

Please re-open if necessary.

maraf commented 1 month ago

Validated that VS omits details from the from ManagedError

image

It doesn't event print message from ManagedError

EDIT: If I try-catch the error and log it is displayed correctly

try {
    exports.JSInterop.Greet();
} catch (e) {
    console.log(e);
}

=> image

It looks like VS doesn't process the ManagedError wrapper

ghosttie commented 1 month ago

So should the issue be reopened?

maraf commented 1 month ago

After brief investigation it seems it will need a change on the VS/debugger side. Moving to Future as there won't be needed a change in the runtime. I'll futher investigate what needs to be changed.

The repo responsible for object transformation is https://github.com/microsoft/vscode-js-debug/