sentry-unity icon indicating copy to clipboard operation
sentry-unity copied to clipboard

Improve stack trace when targeting `WebGL`

Open dryyy27 opened this issue 7 months ago • 21 comments
trafficstars

Does it support the WebGL platform for WeChat mini-game development?

dryyy27 avatar Apr 01 '25 02:04 dryyy27

It does support webgl. but wechat specifically I'm not sure. could you try and let us know?

bruno-garcia avatar Apr 02 '25 11:04 bruno-garcia

But when I try to build a webgl package and display it on a web page, the stack cannot be displayed. Is there any step that is not executed? And Does it support crash log capture for webgl? When I enter the crash command of unity, there is no log capture.

dryyy27 avatar Apr 07 '25 02:04 dryyy27

Can you provide a sample for us to look at? When running the SDK's sample game, I do see the stack trace getting reported.

bitsandfoxes avatar Apr 08 '25 10:04 bitsandfoxes

Demo: https://github.com/dryyy27/SentryDemo.git. I deleted the dsn, auth, project and organization. When I switch the platform to Linux or Android, the stack can be displayed

dryyy27 avatar Apr 09 '25 06:04 dryyy27

Thanks for the repro. The stack trace doesn't work even on webgl outside wechat? if we just build it for web vs linux we'll see the lack of stack trace you mean?

bruno-garcia avatar Apr 10 '25 12:04 bruno-garcia

It doesn't work even on webgl outside wechat. But when I build android or linux, it work normally.

dryyy27 avatar Apr 11 '25 10:04 dryyy27

We didn't get around trying the report yet but to confirm:

  • Which browser are you using?
  • Is there a stack trace in the browser dev tools?

bruno-garcia avatar Apr 14 '25 12:04 bruno-garcia

Hi guys. We have almost the same issue in our projects. So I can provide my investigation results and share some more details.

  • Package version: 3.1.0
  • Unity Version: 2022.3.19f1
  • Platform: WebGL
  • Tested on the: Google Chrome

We have SentrySDK integrated via Package Manager and instead of using base config we're disabling it with Enable Sentry toggle from the Sentry SDK Options window and doing a runtime initialization via C# using SentrySDK.Init(options) where options parameter contains our custom setup per environment using the same DSN but different Sample Rates and etc. Right after calling SentrySDK.Init we are also calling SentrySDK.CaptureMessage(msg) to test if Custom messages are coming through the dashboard.

As a result:

  • We DO get all errors and messages that were sent from the Unity Editor.
  • We DO NOT get any error/warning or custom events we sent from the browser.

Also some extra info we found when we used config initialization instead of C#.

  • We DO get some events, but maybe about 1 of 20 events, event if Sample Rate set to 1(100%) and configuration values set to get all warnings and errors.

Here is the code we use to initialize the SentrySDK:

 private static void InternalInit(string dsn, string environment)
        {
#if SENTRY
            SentrySdk.Close();
            if (string.IsNullOrEmpty(dsn))
            {
                Debug.Log("[SDK_Init] Sentry SDK is disabled by remote config, 'sentry_dsn' is not defined");
                return;
            }

            RuntimeInfo.Environment env = RuntimeInfoManager.ConvertEnvironment(environment);
            float sampleRate = env == RuntimeInfo.Environment.Production
                ? sentry_sample_rate
                : 1;

            Debug.Log($"[SDK_Init] Sentry SDK initialization with '{dsn}', env '{environment}' and '{sampleRate}' sample rate");
            SentrySdk.Init(options =>
            {
                options.Dsn = dsn;
                options.TracesSampleRate = sampleRate;
                options.MaxCacheItems = 30;
                options.InitCacheFlushTimeout = TimeSpan.FromMilliseconds(0);
                options.ShutdownTimeout = TimeSpan.FromSeconds(2);
                options.MaxQueueItems = 30;
                options.Release = $"{RuntimeInfoManager.runtimeInfo.version}";
                options.AutoSessionTrackingInterval = TimeSpan.FromSeconds(30);
                options.Environment = environment;
                options.ProfilesSampleRate = 1;
                options.SampleRate = 1;
            });
            
            Debug.Log($"[SDK_Init] Sentry SDK initialized, SentrySdk.Enabled: {SentrySdk.IsEnabled}");
#else
            Debug.Log("[SDK_Init] Sentry SDK is disabled by define symbol");
#endif
        }

EduardSayGames avatar Apr 16 '25 08:04 EduardSayGames

@EduardSayGames thanks for sharing the details.

One thing I noticed is that. you're using SentrySdk.Init which is the .NET SDK, without any of the Unity stuff we built here. Can you try this out with SentryUnity.Init instead?

bruno-garcia avatar Apr 16 '25 23:04 bruno-garcia

@bruno-garcia Thank you for this option.

Unfortunately, issue is still the same. I do see logs from the Editor, but nothing from the browser. I did try it with Safari as well to check if it isn't only Chrome, same result.

As a side question, wouldn't it be better to make SentrySdk.Init internal to avoid two APIs doing the same if you already have SentryUnity.Init?

However, I can confirm both APIs didn't work for my WebGL builds but they are both working just well in the Editor.

EduardSayGames avatar Apr 17 '25 09:04 EduardSayGames

SentrySdk is a public class of the core API of the dotnet SDK which Unity is built on too. Not sure how we can hide that but agree it's a pitfall having that in combination with SentryUnity.

We'll need to debug this through. Something must have changed in the Unity side I imagine.

bruno-garcia avatar Apr 17 '25 12:04 bruno-garcia

@bruno-garcia Please let me know if I can help with debugging or anything else. Thanks again! We’re looking forward to getting the SDK integrated for production as soon as you’ve figured this out.

EduardSayGames avatar Apr 17 '25 13:04 EduardSayGames

We didn't get around trying the report yet but to confirm:我们还没有尝试报告,但为了确认:

  • Which browser are you using?您正在使用哪个浏览器?
  • Is there a stack trace in the browser dev tools?浏览器开发者工具中是否有堆栈跟踪?

I used Google Chrome。Strack trace like this:

Image

Image

dryyy27 avatar Apr 18 '25 02:04 dryyy27

Could you try SentrySdk.CaptureException? Looking at this with @bitsandfoxes he mentioned messages on WebGL don't have stack traces (Unity doesn't give them out).

e.g:


void Test1() { throw new Exception("test"); }
void Test2() {
try { Test1(): } catch (Exception e) {SentrySdk.CaptureException(e);)}
}

This should work. And if so, it sounds like we need to document bettter where/when Unity provides a stack trace so Sentry can attach that to events, or not.

bruno-garcia avatar Apr 23 '25 15:04 bruno-garcia

like this? but it don't display

Image

dryyy27 avatar Apr 27 '25 11:04 dryyy27

Thank you for confirming. There's definitely something broken here. We'll look into this asap.

bruno-garcia avatar Apr 27 '25 14:04 bruno-garcia

Hey @dryyy27, @EduardSayGames, sorry this took some time to dig into. Thanks for providing the minimal repro, it helped a lot! Sorry to not have any better news but here it goes.

In the provided setup the SDK fails to create the stacktrace with Could not resolve stack frame. here, leading to

Sentry: (Debug) Created DebugStackTrace with 0 frames. 

leading to missing stack traces in the issues on Sentry.

Currently, it looks like there's not much the SDK can do at this point and seems to be a limitation of WebGL and its exception handling. The only option forward I have found at this point is to change the exception settings in the PlayerSettings here:

Image

With Full with Stacktrace the SDK is able to provide a proper stack trace on the events in Sentry.

But when trying to make use of Unity's stringified stack traces available in Unity 6 and newer we're getting this:

  | $WebGLPrintfConsolev(LogType, char const*, void*) | @ | Test.wasm:0x9a033e
  | $printf_consolev(LogType, char const*, void*, bool, LineEndInfoHint) | @ | Test.wasm:0xe6f040
  | $InternalErrorConsoleWithNewline(char const*, ...) | @ | Test.wasm:0xe704fd
  | $DebugStringToFilePostprocessedStacktrace(DebugStringToFileData const&) | @ | Test.wasm:0xe703e3
  | $DebugStringToFile(DebugStringToFileData const&) | @ | Test.wasm:0xe6fb0f
  | $DebugLogHandler_CUSTOM_Internal_Log(LogType, LogOption, BindingsManagedSpan*, void*) | @ | Test.wasm:0xe3090a
  | $dynCall_viiii | @ | Test.wasm:0xe7bbce
  | invoke_viiii | @ | Test.framework.js:9
  | $DebugLogHandler_Internal_Log_m20852F18A88BB18425BA07260545E3968F7EA76C | @ | Test.wasm:0x3bc563
  | $DebugLogHandler_LogFormat_m216B169EF9B669F2ED4C59F6B9F326D4EBBDF821 | @ | Test.wasm:0x3bc722
  | $UnityLogHandlerIntegration_LogFormat_m05B60A465C6F6A98B4E14C2128DED1281EE38DE2 | @ | Test.wasm:0x7a7576
  | $Logger_Log_mEA3D39763D610E92491AA479BA653ECFEE3E9E5C | @ | Test.wasm:0x3c73aa
  | $Debug_LogError_mB00B2B4468EF3CAF041B038D840820FB84C924B2 | @ | Test.wasm:0x3bce38
  | $Main_Start_m5864CE07B60D35921FE23903087087FC5C3CF8FD | @ | Test.wasm:0x944afc
  | $RuntimeInvoker_TrueVoid_t4861ACF8F4594C3437BB48B6E56783494B843915(void (*)(), MethodInfo const*, void*, void**, void*) | @ | Test.wasm:0x97252a
  | $il2cpp::vm::Runtime::InvokeWithThrow(MethodInfo const*, void*, void**) | @ | Test.wasm:0x98c8aa
  | $dynCall_iiii | @ | Test.wasm:0xe7bb48
  | invoke_iiii | @ | Test.framework.js:9
  | $il2cpp::vm::Runtime::Invoke(MethodInfo const*, void*, void**, Il2CppException**) | @ | Test.wasm:0x98b9d2
  | $il2cpp_runtime_invoke | @ | Test.wasm:0x99930
  | $scripting_method_invoke(ScriptingMethodPtr, ScriptingObjectPtr, ScriptingArguments&, ScriptingExceptionPtr*, bool) | @ | Test.wasm:0xe48960
  | $ScriptingInvocation::Invoke(ScriptingExceptionPtr*, bool) | @ | Test.wasm:0xe173d4
  | $MonoBehaviour::InvokeMethodOrCoroutineChecked(ScriptingMethodPtr, ScriptingObjectPtr) | @ | Test.wasm:0xdcce1b
  | $MonoBehaviour::DelayedStartCall(Object*, void*) | @ | Test.wasm:0xdce3e7
  | $DelayedCallManager::Update(int) | @ | Test.wasm:0xb70f80
  | $InitPlayerLoopCallbacks()::EarlyUpdateScriptRunDelayedStartupFrameRegistrator::Forward() | @ | Test.wasm:0xca374e
  | $ExecutePlayerLoop(NativePlayerLoopSystem*) | @ | Test.wasm:0xc34e1b
  | $ExecutePlayerLoop(NativePlayerLoopSystem*) | @ | Test.wasm:0xc34e90
  | $MainLoop() | @ | Test.wasm:0xe50c92
  | $dynCall_v | @ | Test.wasm:0xe7bb50
  | (anonymous)

So this might be something we could capture and attempt to parse in a future update of the SDK.

In Unity 6.1 changing these options is greyed out: Image

bitsandfoxes avatar Apr 29 '25 18:04 bitsandfoxes

We could parse that in the SDK and create a stack trace but there wouldn't be any line numbers unless we add symbolication support. Looking at those file name and hex appended to the line it seems to be memory addresses we could use.

For now I'd add to the docs that we don't support stack traces for Wasm starting on version X of Unity (assuming it broke eventually, since iirc we did have the parsing working originally and had stack traces without line numbers already)

bruno-garcia avatar Apr 29 '25 20:04 bruno-garcia

Thank you @bitsandfoxes @bruno-garcia for sharing this information. But this won't solve my issue because I don't get any error registered in the dashboard, even without stacktraces. So my issue is still open.

FYI: I've updated the SDK to the latest 3.2.1 and it has the same issue.

EduardSayGames avatar Apr 30 '25 13:04 EduardSayGames

I'll have to try and check the conditions under which we can reliably grab and parse the stacktrace and then put together a PR.

bitsandfoxes avatar May 02 '25 13:05 bitsandfoxes

So after digging through it there are two issues tied to the `exceptionSupport:

PlayerSettings.WebGL.exceptionSupport Limitations

None

Not supported. See https://github.com/getsentry/sentry-unity/pull/2141 for more details.

Explicitly Thrown Exceptions Only

Captures and reports exceptions but misses stacktrace.

Full Without Stacktrace

Captures and reports exceptions but misses stacktrace.

Full With Stacktrace

Captures and reports exception and provides a stacktrace (albeit with no line numbers due to lack of IL2CPP backend availability)

Failing to create a new StackTrace

This is happening when setting to Explicitly Thrown Exceptions Only and Full Without Stacktrace. The SDK fails trying to create a new SentryStackTrace the SDK fails with Could not resolve stack frame. This is due to the DebugStackTrace failing to create a managed frame. It relies on the following snippet used in the AotHelper

var stackTrace = new StackTrace(false);
return stackTrace.GetFrame(0)?.GetMethod() is null;

To resolve this we could provide our own dedicated WebGLStackTraceFactory to create a new frame and fall back on stackFrame.ToString() instead of doing nothing. And probably skip a whole lot of other, for WebGL unnecessary checks i.e.

if (CreateFrame(stackFrame) is { } frame)
{
    yield return frame;
}
else
{
    yield return new SentryStackFrame { Function = stackFrame.ToString() };
}

LogError events missing stacktrace

The missing stacktrace also mentioned in https://github.com/getsentry/sentry-unity/issues/2091#issuecomment-2839750715 is also related to the PlayerSettings.WebGL.exceptionSupport. When setting to FullWithStacktrace the SDK is able to provide stacktraces for these events as well. Set to any other option Unity does not pass down a stacktrace to the logging integration and we'd need to create a new stacktrace, bringing us back to the point above.

Recommendation

The current recommendation is to set PlayerSettings.WebGL.exceptionSupport to Full With Stacktrace to get the most out of the SDK. Anything else will require us to build a WebGL specific integration to better handle those cases.

bitsandfoxes avatar May 05 '25 17:05 bitsandfoxes

Superseded by https://github.com/getsentry/sentry-unity/issues/2415

bitsandfoxes avatar Nov 12 '25 15:11 bitsandfoxes