From dcd539e0b3415464d1c397929eb59c6314b9881d Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 22 Feb 2022 19:34:00 +0100 Subject: [PATCH 01/22] feat: standalone native handler - set some missing options --- src/Sentry.Unity.Native/SentryNativeBridge.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index 47ba6502c..b71bbbbb0 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -29,6 +29,21 @@ public static void Init(SentryUnityOptions options) sentry_options_set_release(cOptions, options.Release); } + if (options.Environment is not null) + { + sentry_options_set_environment(cOptions, options.Environment); + } + + sentry_options_set_debug(cOptions, options.Debug ? 1 : 0); + + if (options.SampleRate.HasValue) + { + sentry_options_set_sample_rate(cOptions, options.SampleRate.Value); + } + + // Disabling the native in favor of the C# layer for now + sentry_options_set_auto_session_tracking(cOptions, 0); + sentry_init(cOptions); } @@ -42,6 +57,18 @@ public static void Init(SentryUnityOptions options) [DllImport("sentry")] private static extern void sentry_options_set_release(IntPtr options, string release); + [DllImport("sentry")] + private static extern void sentry_options_set_debug(IntPtr options, int debug); + + [DllImport("sentry")] + private static extern void sentry_options_set_environment(IntPtr options, string environment); + + [DllImport("sentry")] + private static extern void sentry_options_set_sample_rate(IntPtr options, double rate); + + [DllImport("sentry")] + private static extern void sentry_options_set_auto_session_tracking(IntPtr options, int debug); + // TODO we could set a logger for sentry-native, forwarding the logs to `options.DiagnosticLogger?` // [DllImport("sentry")] // private static extern void sentry_options_set_logger(IntPtr options, IntPtr logger, IntPtr userData); From b91e67b7497c0c1ad77aebeed4665fdb58fe2316 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 22 Feb 2022 19:36:34 +0100 Subject: [PATCH 02/22] wip: smoke crash test --- .../Assets/Scripts/SmokeTester.cs | 120 ++++++++++++++---- .../crash-test-server.ps1 | 37 ++++++ 2 files changed, 129 insertions(+), 28 deletions(-) create mode 100644 test/Scripts.Integration.Test/crash-test-server.ps1 diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index 0535c0504..c3b4ae0ba 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -3,6 +3,8 @@ #define SENTRY_NATIVE_IOS #elif UNITY_ANDROID #define SENTRY_NATIVE_ANDROID +#elif UNITY_STANDALONE_WIN && ENABLE_IL2CPP +#define SENTRY_NATIVE_WINDOWS #endif #endif @@ -10,6 +12,8 @@ using Sentry.Unity.iOS; #elif UNITY_ANDROID using Sentry.Unity.Android; +#elif SENTRY_NATIVE_WINDOWS +using Sentry.Unity.Native; #endif #if UNITY_IOS @@ -23,6 +27,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Sentry; @@ -34,16 +39,13 @@ public class SmokeTester : MonoBehaviour { public void Start() { + string arg = null; #if SENTRY_NATIVE_ANDROID using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using (var currentActivity = unityPlayer.GetStatic("currentActivity")) using (var intent = currentActivity.Call("getIntent")) { - var text = intent.Call ("getStringExtra", "test"); - if (text == "smoke") - { - SmokeTest(); - } + arg = intent.Call ("getStringExtra", "test"); } #elif UNITY_IOS string pListPath = Path.Combine(Application.dataPath.Substring(0, Application.dataPath.LastIndexOf('/')), "Info.plist"); @@ -53,8 +55,8 @@ public void Start() var key = "RunSentrySmokeTest"; if (rawPlist.Contains(key)) { - Debug.Log("Key " + key + " found on Info.plistm starting Smoke test."); - SmokeTest(); + arg = "smoke"; + Debug.Log("Key " + key + " found on Info.plist starting Smoke test."); } else { @@ -65,12 +67,51 @@ public void Start() var args = Environment.GetCommandLineArgs(); if (args.Length > 2 && args[1] == "--test") { - if (args[2] == "smoke") - { - SmokeTest(); - } + arg = args[2]; + } +#endif + if (arg == null) + { + Debug.Log($"SmokeTest not executed - no argument given"); + } + else if (arg == "smoke") + { + SmokeTest(); + } + else if (arg == "smoke-crash") + { + SmokeTestCrash(); + } + else + { + Debug.Log($"Unknown command line argument: {arg}"); + } + } + + public static void InitSentry(SentryUnityOptions options, bool requireNative = true) + { +#if SENTRY_NATIVE_IOS + Debug.Log("SMOKE TEST: Configure Native iOS."); + options.IosNativeSupportEnabled = true; + SentryNativeIos.Configure(options); +#elif SENTRY_NATIVE_ANDROID + Debug.Log("SMOKE TEST: Configure Native Android."); + options.AndroidNativeSupportEnabled = true; + SentryNativeAndroid.Configure(options, new SentryUnityInfo()); +#elif SENTRY_NATIVE_WINDOWS + Debug.Log("SMOKE TEST: Configure Native support."); + options.WindowsNativeSupportEnabled = true; + SentryNative.Configure(options); +#else + if (requireNative) { + throw new Exception("Given platform is not supported"); } #endif + + Debug.Log("SMOKE TEST: SentryUnity Init."); + SentryUnity.Init(options); + Debug.Log("SMOKE TEST: SentryUnity Init OK."); + } public static void SmokeTest() @@ -88,23 +129,7 @@ public static void SmokeTest() options.DiagnosticLogger = new ConsoleDiagnosticLogger(SentryLevel.Debug); options.CreateHttpClientHandler = () => t; - var sentryUnityInfo = new SentryUnityInfo(); - -#if SENTRY_NATIVE_IOS - Debug.Log("SMOKE TEST: Configure Native iOS."); - SentryNativeIos.Configure(options); -#elif SENTRY_NATIVE_ANDROID - Debug.Log("SMOKE TEST: Configure Native Android."); - SentryNativeAndroid.Configure(options, sentryUnityInfo); -#elif SENTRY_NATIVE_WINDOWS - Debug.Log("SMOKE TEST: Configure Native Windows."); - SentryNative.Configure(options); -#endif - - Debug.Log("SMOKE TEST: SentryUnity Init."); - SentryUnity.Init(options); - - Debug.Log("SMOKE TEST: SentryUnity Init OK."); + InitSentry(options, false); var currentMessage = 0; t.ExpectMessage(currentMessage, "'type':'session'"); @@ -166,6 +191,45 @@ public static void SmokeTest() } } + public static void SmokeTestCrash() + { + Debug.Log("SMOKE TEST: Start"); + + InitSentry(new SentryUnityOptions() + { + Dsn = "http://key@localhost:8000/project", + Debug = true, + DiagnosticLogger = new ConsoleDiagnosticLogger(SentryLevel.Debug) + }); + + SentrySdk.AddBreadcrumb("crumb", "bread", "error", new Dictionary() { { "foo", "bar" } }, BreadcrumbLevel.Critical); + SentrySdk.ConfigureScope((Scope scope) => + { + scope.SetExtra("extra-key", 42); + scope.AddBreadcrumb("scope-crumb"); + scope.SetTag("tag-key", "tag-value"); + scope.User = new User() + { + Username = "username", + Email = "email@example.com", + IpAddress = "::1", + Id = "user-id", + Other = new Dictionary() { { "role", "admin" } } + }; + }); + + Debug.Log("SMOKE TEST: Issuing a native crash (c++ unhandled exception)"); + throw_cpp(); + + // shouldn't execute because the previous call should have failed + Debug.Log("SMOKE TEST: FAIL - unexpected code executed..."); + Application.Quit(-1); + } + + // CppPlugin.cpp + [DllImport("__Internal")] + private static extern void throw_cpp(); + private class TestHandler : HttpClientHandler { private List _requests = new List(); diff --git a/test/Scripts.Integration.Test/crash-test-server.ps1 b/test/Scripts.Integration.Test/crash-test-server.ps1 new file mode 100644 index 000000000..17d527231 --- /dev/null +++ b/test/Scripts.Integration.Test/crash-test-server.ps1 @@ -0,0 +1,37 @@ +# Start an HTTP server +$uri = "http://localhost:8000/" +$separator = "--------------------------------------------------------------------" +$httpServer = [System.Net.HttpListener]::new() +$httpServer.Prefixes.Add($uri) +$httpServer.Start() + +write-Host "HTTP server listening on $uri" +Write-Host $separator + +try { + while ($httpServer.IsListening) { + # allow ctrl+c interrupt using the AsyncWaitHandle + $contextAsync = $httpServer.GetContextAsync() + while (-not $contextAsync.AsyncWaitHandle.WaitOne(100)) { } + + # current request info + $context = $contextAsync.GetAwaiter().GetResult() + + # print the request to stdout + write-Host "$($context.Request.HttpMethod) $($context.Request.Url)" + if ($context.Request.HttpMethod -eq 'POST') { + $payload = [System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd() + Write-Host -NoNewline $payload + } + Write-Host $separator + + # send an empty response + $context.Response.ContentLength64 = 0 + $context.Response.OutputStream.Close() + } +} +finally { + # This is always called when ctrl+c is used + write-Host "HTTP server stopping!" + $httpServer.Stop() +} From 3b13029aa7ffd112d2c10751d35e1e34b506d87c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 23 Feb 2022 19:59:21 +0100 Subject: [PATCH 03/22] feat: standalone sentry-native configuration & debug logging --- src/Sentry.Unity.Native/SentryNativeBridge.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index b71bbbbb0..030c6fa62 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Runtime.InteropServices; using Sentry.Extensibility; @@ -31,19 +32,38 @@ public static void Init(SentryUnityOptions options) if (options.Environment is not null) { + options.DiagnosticLogger?.LogDebug("Setting Environment: {0}", options.Environment); sentry_options_set_environment(cOptions, options.Environment); } + options.DiagnosticLogger?.LogDebug("Setting Debug: {0}", options.Debug); sentry_options_set_debug(cOptions, options.Debug ? 1 : 0); if (options.SampleRate.HasValue) { + options.DiagnosticLogger?.LogDebug("Setting Sample Rate: {0}", options.SampleRate.Value); sentry_options_set_sample_rate(cOptions, options.SampleRate.Value); } // Disabling the native in favor of the C# layer for now + options.DiagnosticLogger?.LogDebug("Disabling native auto session tracking"); sentry_options_set_auto_session_tracking(cOptions, 0); + if (options.CacheDirectoryPath is not null) + { + var dir = Path.Combine(options.CacheDirectoryPath, "SentryNative"); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + options.DiagnosticLogger?.LogDebug("Setting CacheDirectoryPath on Windows: {0}", dir); + sentry_options_set_database_pathw(cOptions, dir); + } + else + { + options.DiagnosticLogger?.LogDebug("Setting CacheDirectoryPath: {0}", dir); + sentry_options_set_database_path(cOptions, dir); + } + } + sentry_init(cOptions); } @@ -66,6 +86,14 @@ public static void Init(SentryUnityOptions options) [DllImport("sentry")] private static extern void sentry_options_set_sample_rate(IntPtr options, double rate); + + [DllImport("sentry")] + private static extern void sentry_options_set_database_path(IntPtr options, string path); + + + [DllImport("sentry")] + private static extern void sentry_options_set_database_pathw(IntPtr options, [MarshalAs(UnmanagedType.LPWStr)] string path); + [DllImport("sentry")] private static extern void sentry_options_set_auto_session_tracking(IntPtr options, int debug); From 611afaba59664456c17a9212eb0a8d522fb06996 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 23 Feb 2022 19:59:42 +0100 Subject: [PATCH 04/22] fix: standalone shouldn't reinstall backend - it's unnecessary --- src/Sentry.Unity.Native/SentryNative.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Sentry.Unity.Native/SentryNative.cs b/src/Sentry.Unity.Native/SentryNative.cs index 2e1cec61f..6ddf25ee0 100644 --- a/src/Sentry.Unity.Native/SentryNative.cs +++ b/src/Sentry.Unity.Native/SentryNative.cs @@ -36,9 +36,6 @@ public static void Configure(SentryUnityOptions options) // return crashedLastRun.Value; // }; - // At this point Unity has taken the signal handler and will not invoke the original handler (Sentry) - // So we register our backend once more to make sure user-defined data is available in the crash report. - SentryNativeBridge.ReinstallBackend(); } } } From 4ec795724a73629876d9e2b0ac8860e89923a009 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 23 Feb 2022 20:28:38 +0100 Subject: [PATCH 05/22] test: Standalone native crash test --- Directory.Build.targets | 15 +- .../Assets/Scripts/SmokeTester.cs | 66 ++++----- .../crash-test-server.ps1 | 5 + .../integration-run-smoke-test.ps1 | 135 +++++++++++++----- 4 files changed, 141 insertions(+), 80 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index dc433e6bf..f188d5a99 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -47,7 +47,7 @@ Builder.BuildWindowsIl2CPPPlayer $(PlayerBuildPath)Windows/IL2CPP_Player.exe $(StandaloneBuildPath) - %USERPROFILE%\AppData\LocalLow\DefaultCompany\unity-of-bugs\ + $(USERPROFILE)\AppData\LocalLow\DefaultCompany\unity-of-bugs\ https://94677106febe46b88b9b9ae5efd18a00@o447951.ingest.sentry.io/5439417 @@ -255,6 +255,19 @@ Related: https://forum.unity.com/threads/6572-debugger-agent-unable-to-listen-on + + diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index c3b4ae0ba..e1a86d756 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -85,11 +85,18 @@ public void Start() else { Debug.Log($"Unknown command line argument: {arg}"); + Application.Quit(-1); } } public static void InitSentry(SentryUnityOptions options, bool requireNative = true) { + options.Dsn = "http://publickey@localhost:8000/12345"; + options.Debug = true; + options.DebugOnlyInEditor = false; + options.DiagnosticLevel = SentryLevel.Debug; + options.DiagnosticLogger = new ConsoleDiagnosticLogger(SentryLevel.Debug); + #if SENTRY_NATIVE_IOS Debug.Log("SMOKE TEST: Configure Native iOS."); options.IosNativeSupportEnabled = true; @@ -99,11 +106,13 @@ public static void InitSentry(SentryUnityOptions options, bool requireNative = t options.AndroidNativeSupportEnabled = true; SentryNativeAndroid.Configure(options, new SentryUnityInfo()); #elif SENTRY_NATIVE_WINDOWS - Debug.Log("SMOKE TEST: Configure Native support."); + Debug.Log("SMOKE TEST: Configure Native Windows."); options.WindowsNativeSupportEnabled = true; SentryNative.Configure(options); #else - if (requireNative) { + if (requireNative) + { + Debug.Log("SMOKE TEST: Given platform is not supported for native sentry configuration"); throw new Exception("Given platform is not supported"); } #endif @@ -111,7 +120,6 @@ public static void InitSentry(SentryUnityOptions options, bool requireNative = t Debug.Log("SMOKE TEST: SentryUnity Init."); SentryUnity.Init(options); Debug.Log("SMOKE TEST: SentryUnity Init OK."); - } public static void SmokeTest() @@ -121,15 +129,7 @@ public static void SmokeTest() { Debug.Log("SMOKE TEST: Start"); - var options = new SentryUnityOptions(); - options.Dsn = "https://key@sentry/project"; - options.Debug = true; - // TODO: Must be set explicitly for the time being. - options.RequestBodyCompressionLevel = CompressionLevelWithAuto.Auto; - options.DiagnosticLogger = new ConsoleDiagnosticLogger(SentryLevel.Debug); - options.CreateHttpClientHandler = () => t; - - InitSentry(options, false); + InitSentry(new SentryUnityOptions() { CreateHttpClientHandler = () => t }, false); var currentMessage = 0; t.ExpectMessage(currentMessage, "'type':'session'"); @@ -152,21 +152,7 @@ public static void SmokeTest() t.ExpectMessage(currentMessage, guid); var ex = new Exception("Exception & context test"); - SentrySdk.AddBreadcrumb("crumb", "bread", "error", new Dictionary() { { "foo", "bar" } }, BreadcrumbLevel.Critical); - SentrySdk.ConfigureScope((Scope scope) => - { - scope.SetExtra("extra-key", 42); - scope.AddBreadcrumb("scope-crumb"); - scope.SetTag("tag-key", "tag-value"); - scope.User = new User() - { - Username = "username", - Email = "email@example.com", - IpAddress = "::1", - Id = "user-id", - Other = new Dictionary() { { "role", "admin" } } - }; - }); + AddContext(); SentrySdk.CaptureException(ex); t.ExpectMessage(++currentMessage, "'type':'event'"); t.ExpectMessage(currentMessage, "'message':'crumb','type':'error','data':{'foo':'bar'},'category':'bread','level':'critical'}"); @@ -195,13 +181,20 @@ public static void SmokeTestCrash() { Debug.Log("SMOKE TEST: Start"); - InitSentry(new SentryUnityOptions() - { - Dsn = "http://key@localhost:8000/project", - Debug = true, - DiagnosticLogger = new ConsoleDiagnosticLogger(SentryLevel.Debug) - }); + InitSentry(new SentryUnityOptions()); + + AddContext(); + + Debug.Log("SMOKE TEST: Issuing a native crash (c++ unhandled exception)"); + throw_cpp(); + // shouldn't execute because the previous call should have failed + Debug.Log("SMOKE TEST: FAIL - unexpected code executed..."); + Application.Quit(-1); + } + + private static void AddContext() + { SentrySdk.AddBreadcrumb("crumb", "bread", "error", new Dictionary() { { "foo", "bar" } }, BreadcrumbLevel.Critical); SentrySdk.ConfigureScope((Scope scope) => { @@ -217,13 +210,6 @@ public static void SmokeTestCrash() Other = new Dictionary() { { "role", "admin" } } }; }); - - Debug.Log("SMOKE TEST: Issuing a native crash (c++ unhandled exception)"); - throw_cpp(); - - // shouldn't execute because the previous call should have failed - Debug.Log("SMOKE TEST: FAIL - unexpected code executed..."); - Application.Quit(-1); } // CppPlugin.cpp diff --git a/test/Scripts.Integration.Test/crash-test-server.ps1 b/test/Scripts.Integration.Test/crash-test-server.ps1 index 17d527231..71728f0c0 100644 --- a/test/Scripts.Integration.Test/crash-test-server.ps1 +++ b/test/Scripts.Integration.Test/crash-test-server.ps1 @@ -21,6 +21,11 @@ try { write-Host "$($context.Request.HttpMethod) $($context.Request.Url)" if ($context.Request.HttpMethod -eq 'POST') { $payload = [System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd() + if ($context.Request.Url.ToString().Contains("minidump")) { + $payload = "minidump payload length: $($payload.Length)`n" + } + else { + } Write-Host -NoNewline $payload } Write-Host $separator diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index e2a49024e..5370d7976 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -1,52 +1,109 @@ -. ./test/Scripts.Integration.Test/IntegrationGlobals.ps1 +param ( + [Parameter(Position = 0)] + [string] $TestAppPath = "", + [Parameter()] + [string] $AppDataDir = "" +) +. ./test/Scripts.Integration.Test/IntegrationGlobals.ps1 -If ($IsMacOS) -{ - $testAppPath = "$NewProjectBuildPath/test.app/Contents/MacOS/$NewProjectName" +if ("$AppDataDir" -ne "") { + Write-Warning "Removing AppDataDir '$AppDataDir'" + Remove-Item -Recurse $AppDataDir } -ElseIf ($IsWindows) -{ - $testAppPath = "$NewProjectBuildPath/test.exe" -} -ElseIf ($IsLinux) -{ - $testAppPath = "$NewProjectBuildPath/test" -} -Else -{ - Throw "Unsupported build" +else { + Write-Warning "No AppDataDir param given - if you're running this after a previous smoke-crash test, the smoke test will fail." + Write-Warning "You can provide AppDataDir and it will be deleted before running the test." + Write-Warning 'On windows, this would normally be: -AppDataDir "$env:UserProfile\AppData\LocalLow\DefaultCompany\unity-of-bugs\"' } -$process = Start-Process -FilePath "$testAppPath" -ArgumentList "--test", "smoke" -PassThru -If ($null -eq $process) -{ - Throw "Process not found." +if ("$TestAppPath" -eq "") { + If ($IsMacOS) { + $TestAppPath = "$NewProjectBuildPath/test.app/Contents/MacOS/$NewProjectName" + } + ElseIf ($IsWindows) { + $TestAppPath = "$NewProjectBuildPath/test.exe" + } + ElseIf ($IsLinux) { + $TestAppPath = "$NewProjectBuildPath/test" + } + Else { + Throw "Unsupported build" + } } -# Wait for 1 minute (sleeps for 500ms per iteration) -$timeout = 60 * 2 -$processName = $process.Name -Write-Host -NoNewline "Waiting for $processName" +Set-Strictmode -Version latest -While (!$process.HasExited -and $timeout -gt 0) -{ - Start-Sleep -Milliseconds 500 - Write-Host -NoNewline "." - $timeout-- -} +function RunTest([string] $type) { + Write-Host "Running $TestAppPath --test $type" -# ExitCode 200 is the status code indicating success inside SmokeTest.cs -If ($process.ExitCode -eq 200) -{ - Write-Host "`nPASSED" + $process = Start-Process "$TestAppPath" -ArgumentList "--test", $type -PassThru + If ($null -eq $process) { + Throw "Process not found." + } + + # Wait for the test to finish + $timedOut = $null # reset any previously set timeout + $process | Wait-Process -Timeout 60 -ErrorAction SilentlyContinue -ErrorVariable timedOut + + # ExitCode 200 is the status code indicating success inside SmokeTest.cs + If ($process.ExitCode -eq 200) { + Write-Host "$type test: PASSED" -ForegroundColor Green + } + ElseIf ($timedOut) { + $process | Stop-Process + Throw "Test process timed out." + } + Else { + $info = "Test process finished with status code $($process.ExitCode)." + If ($type -ne "smoke-crash") { + throw $info + } + Write-Host $info + } } -ElseIf ($timeout -eq 0) -{ - Throw "Test process timed out." + +function RunApiServer() { + $result = "" | Select-Object -Property process, outFile + Write-Host "Starting the HTTP server (dummy API server)" + $result.outFile = New-TemporaryFile + $errFile = New-TemporaryFile + + $result.process = Start-Process "powershell" -ArgumentList "-command", "$PSScriptRoot/crash-test-server.ps1" -NoNewWindow -PassThru -RedirectStandardOutput $result.outFile -RedirectStandardError $errFile + + # The process shouldn't finish by itself, if it did, there was an error, so let's check that + Start-Sleep -Second 1 + if ($result.process.HasExited) { + Write-Error "Couldn't start the HTTP server" + Write-Host "Standard Output:" -ForegroundColor Yellow + Get-Content $result.outFile + Write-Host "Standard Error:" -ForegroundColor Yellow + Get-Content $errFile + Remove-Item $result.outFile + Remove-Item $errFile + exit 1 + } + + return $result } -Else -{ - Throw "Test process failed with status code $($process.ExitCode)." + +# Simple smoke test +RunTest "smoke" + +# Native crash test +$httpServer = RunApiServer +RunTest "smoke-crash" +$httpServer.process | Stop-Process +$output = Get-Content $httpServer.outFile -Raw +Remove-Item $httpServer.outFile +Write-Host "Standard Output:" -ForegroundColor Yellow +$output + +if ($output.Contains("POST http://localhost:8000/api/12345/minidump/")) { + Write-Host "smoke-crash test: PASSED" -ForegroundColor Green } +else { + Write-Host "smoke-crash test: FAILED" -ForegroundColor Red + exit 1 +} \ No newline at end of file From fc6cda9bbf115058576cf50d2f4f8a5870df8675 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 24 Feb 2022 10:29:53 +0100 Subject: [PATCH 06/22] ci: fix windows integration test --- test/Scripts.Integration.Test/integration-update-sentry.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Scripts.Integration.Test/integration-update-sentry.ps1 b/test/Scripts.Integration.Test/integration-update-sentry.ps1 index 7b605e37f..8b6a12ae2 100644 --- a/test/Scripts.Integration.Test/integration-update-sentry.ps1 +++ b/test/Scripts.Integration.Test/integration-update-sentry.ps1 @@ -43,4 +43,5 @@ Remove-Item -Path "$NewProjectAssetsPath/Scripts/SmokeTester.cs" Remove-Item -Path "$NewProjectAssetsPath/Scripts/SmokeTester.cs.meta" Copy-Item "$PackageReleaseAssetsPath/Scripts/SmokeTester.cs" -Destination "$NewProjectAssetsPath/Scripts" Copy-Item "$PackageReleaseAssetsPath/Scripts/SmokeTester.cs.meta" -Destination "$NewProjectAssetsPath/Scripts" +Copy-Item "$PackageReleaseAssetsPath/Scripts/NativeSupport/CppPlugin.*" -Destination "$NewProjectAssetsPath/Scripts/" Write-Host " OK" From 8e559c03d5f01e56479e8b5b75a3a2001f6db0b0 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 25 Feb 2022 09:12:00 +0100 Subject: [PATCH 07/22] fix windows integration test --- .../Assets/Scripts/SmokeTester.cs | 4 +- .../SentryEditorWindowInstrumentation.cs | 4 +- .../integration-run-smoke-test.ps1 | 10 ++-- .../integration-update-sentry.ps1 | 58 ++++++++++--------- 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index e1a86d756..1a1cdef94 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -136,10 +136,10 @@ public static void SmokeTest() t.ExpectMessage(currentMessage, "'init':"); // if first message was init:false, wait for another one with init:true (this happens on windows...) - if (t.GetMessage(currentMessage).Contains("\"init\":false")) + int waitToInitLimit = 10; + while (t.GetMessage(currentMessage).Contains("\"init\":false") && waitToInitLimit-- > 0) { t.ExpectMessage(++currentMessage, "'type':'session'"); - t.ExpectMessage(currentMessage, "'init':true"); } var guid = Guid.NewGuid().ToString(); diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs index 5266f5235..2ce8f8e43 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs @@ -19,7 +19,7 @@ private static void ConfigureOptions(Dictionary args, [CallerMem if (!EditorApplication.ExecuteMenuItem("Tools/Sentry")) { - throw new Exception("Menu item 'Tools -> Sentry' not found. Was the Sentry UPM package installed?"); + throw new Exception($"{functionName} failed: Menu item 'Tools -> Sentry' not found. Was the Sentry UPM package installed?"); } var optionsWindow = EditorWindow.GetWindow(); @@ -27,7 +27,7 @@ private static void ConfigureOptions(Dictionary args, [CallerMem if (options is null) { - throw new InvalidOperationException("SentryOptions not found"); + throw new InvalidOperationException($"{functionName} failed: SentryOptions not found"); } Debug.LogFormat("{0}: Found SentryOptions", functionName); diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index 5370d7976..f8778bbac 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -58,6 +58,9 @@ function RunTest([string] $type) { Else { $info = "Test process finished with status code $($process.ExitCode)." If ($type -ne "smoke-crash") { + if ("$AppDataDir" -ne "") { + Get-Content "$AppDataDir/Player.log" + } throw $info } Write-Host $info @@ -75,7 +78,7 @@ function RunApiServer() { # The process shouldn't finish by itself, if it did, there was an error, so let's check that Start-Sleep -Second 1 if ($result.process.HasExited) { - Write-Error "Couldn't start the HTTP server" + Write-Host "Couldn't start the HTTP server" -ForegroundColor Red Write-Host "Standard Output:" -ForegroundColor Yellow Get-Content $result.outFile Write-Host "Standard Error:" -ForegroundColor Yellow @@ -94,7 +97,7 @@ RunTest "smoke" # Native crash test $httpServer = RunApiServer RunTest "smoke-crash" -$httpServer.process | Stop-Process +$httpServer.process | Stop-Process -Force $output = Get-Content $httpServer.outFile -Raw Remove-Item $httpServer.outFile Write-Host "Standard Output:" -ForegroundColor Yellow @@ -104,6 +107,5 @@ if ($output.Contains("POST http://localhost:8000/api/12345/minidump/")) { Write-Host "smoke-crash test: PASSED" -ForegroundColor Green } else { - Write-Host "smoke-crash test: FAILED" -ForegroundColor Red - exit 1 + Write-Error "smoke-crash test: FAILED" } \ No newline at end of file diff --git a/test/Scripts.Integration.Test/integration-update-sentry.ps1 b/test/Scripts.Integration.Test/integration-update-sentry.ps1 index 8b6a12ae2..c846b2cce 100644 --- a/test/Scripts.Integration.Test/integration-update-sentry.ps1 +++ b/test/Scripts.Integration.Test/integration-update-sentry.ps1 @@ -4,37 +4,40 @@ param($arg) $unityPath = FormatUnityPath $arg -If (-not(Test-Path -Path "$PackageReleaseOutput")) -{ +If (-not(Test-Path -Path "$PackageReleaseOutput")) { Throw "Path $PackageReleaseOutput does not exist. Be sure to run ./test/Scripts.Integration.Test/integration-create-project." } -ClearUnityLog - -Write-Host -NoNewline "Starting Unity process:" -$UnityProcess = RunUnity $unityPath @("-batchmode", "-projectPath ", "$NewProjectPath", "-logfile", "$NewProjectLogPath", "-installSentry", "Disk") -Write-Host " OK" +function RunUnityAndExpect([string] $name, [string] $successMessage, [string] $failMessage, [string[]] $arguments) { + ClearUnityLog + + Write-Host -NoNewline "$name | Starting Unity process:" + $UnityProcess = RunUnity $unityPath $arguments + Write-Host " OK" + + WaitForLogFile 30 + + Write-Host "$name | Waiting for Unity to finish." + $stdout = SubscribeToUnityLogFile $UnityProcess $successMessage $failMessage + + Write-Host $stdout + If ($UnityProcess.ExitCode -ne 0) { + $exitCode = $UnityProcess.ExitCode + Write-Error "$name | Unity exited with code $exitCode" + } + ElseIf ($null -ne ($stdout | select-string $successMessage)) { + Write-Host "`n$name | SUCCESS" -ForegroundColor Green + } + Else { + Write-Error "$name | Unity exited without an error but the successMessage was not found in the output ('$successMessage')" + } +} -WaitForLogFile 30 -$successMessage = "Sentry Package Installation:" +RunUnityAndExpect "AddSentryPackage" "Sentry Package Installation:" "Sentry setup: FAILED" @( ` + "-batchmode", "-projectPath ", "$NewProjectPath", "-logfile", "$NewProjectLogPath", "-installSentry", "Disk") -Write-Host "Waiting for Unity to add Sentry to the project." -$stdout = SubscribeToUnityLogFile $UnityProcess $successMessage "Sentry setup: FAILED" - -Write-Host $stdout -If ($UnityProcess.ExitCode -ne 0) -{ - $exitCode = $UnityProcess.ExitCode - Throw "Unity exited with code $exitCode" -} -ElseIf ($null -ne ($stdout | select-string $successMessage)) -{ - Write-Host "`nSentry added!!" -} -Else -{ - Throw "Unity exited but failed to add the Sentry package." -} +RunUnityAndExpect "ConfigureSentryOptions" "ConfigureOptions: Sentry options Configured" "ConfigureOptions failed" @( ` + "-quit", "-batchmode", "-nographics", "-projectPath ", "$NewProjectPath", "-logfile", "$NewProjectLogPath", "-executeMethod", "Sentry.Unity.Editor.ConfigurationWindow.SentryEditorWindowInstrumentation.ConfigureOptions", "-sentryOptions.Dsn", "http://publickey@localhost:8000/12345") Write-Host -NoNewline "Updating test files " # We were previously using an empty SmokeTester to not generate Build errors. @@ -44,4 +47,5 @@ Remove-Item -Path "$NewProjectAssetsPath/Scripts/SmokeTester.cs.meta" Copy-Item "$PackageReleaseAssetsPath/Scripts/SmokeTester.cs" -Destination "$NewProjectAssetsPath/Scripts" Copy-Item "$PackageReleaseAssetsPath/Scripts/SmokeTester.cs.meta" -Destination "$NewProjectAssetsPath/Scripts" Copy-Item "$PackageReleaseAssetsPath/Scripts/NativeSupport/CppPlugin.*" -Destination "$NewProjectAssetsPath/Scripts/" -Write-Host " OK" + +Write-Host " Unity configuration finished successfully" -ForegroundColor Green From 477129265755cd7c0ef65f4174fe6cd49ac20861 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 25 Feb 2022 10:46:19 +0100 Subject: [PATCH 08/22] ci: windows integration tests - print logs --- .../integration-run-smoke-test.ps1 | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index f8778bbac..38b88c666 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -7,23 +7,15 @@ ) . ./test/Scripts.Integration.Test/IntegrationGlobals.ps1 -if ("$AppDataDir" -ne "") { - Write-Warning "Removing AppDataDir '$AppDataDir'" - Remove-Item -Recurse $AppDataDir -} -else { - Write-Warning "No AppDataDir param given - if you're running this after a previous smoke-crash test, the smoke test will fail." - Write-Warning "You can provide AppDataDir and it will be deleted before running the test." - Write-Warning 'On windows, this would normally be: -AppDataDir "$env:UserProfile\AppData\LocalLow\DefaultCompany\unity-of-bugs\"' -} - - if ("$TestAppPath" -eq "") { If ($IsMacOS) { $TestAppPath = "$NewProjectBuildPath/test.app/Contents/MacOS/$NewProjectName" } ElseIf ($IsWindows) { $TestAppPath = "$NewProjectBuildPath/test.exe" + if ("$AppDataDir" -eq "") { + $AppDataDir = "$env:UserProfile\AppData\LocalLow\DefaultCompany\$NewProjectName\" + } } ElseIf ($IsLinux) { $TestAppPath = "$NewProjectBuildPath/test" @@ -33,6 +25,16 @@ if ("$TestAppPath" -eq "") { } } +if ("$AppDataDir" -ne "") { + Write-Warning "Removing AppDataDir '$AppDataDir'" + Remove-Item -Recurse $AppDataDir +} +else { + Write-Warning "No AppDataDir param given - if you're running this after a previous smoke-crash test, the smoke test will fail." + Write-Warning "You can provide AppDataDir and it will be deleted before running the test." + Write-Warning 'On windows, this would normally be: -AppDataDir "$env:UserProfile\AppData\LocalLow\DefaultCompany\unity-of-bugs\"' +} + Set-Strictmode -Version latest function RunTest([string] $type) { @@ -47,6 +49,13 @@ function RunTest([string] $type) { $timedOut = $null # reset any previously set timeout $process | Wait-Process -Timeout 60 -ErrorAction SilentlyContinue -ErrorVariable timedOut + if ("$AppDataDir" -ne "") { + Write-Host "$type test: Player.log contents:" -ForegroundColor Yellow + Get-Content "$AppDataDir/Player.log" + Write-Host "================================================================================" -ForegroundColor Yellow + Write-Host "$type test: Player.log contents END" -ForegroundColor Yellow + } + # ExitCode 200 is the status code indicating success inside SmokeTest.cs If ($process.ExitCode -eq 200) { Write-Host "$type test: PASSED" -ForegroundColor Green From 1f0bc870962fe2c01586784ec728327ef2e30ef0 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 25 Feb 2022 12:43:02 +0100 Subject: [PATCH 09/22] fix integration tests in CI --- .../Assets/Scripts/SmokeTester.cs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index 1a1cdef94..01cf5e08e 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -133,18 +133,23 @@ public static void SmokeTest() var currentMessage = 0; t.ExpectMessage(currentMessage, "'type':'session'"); - t.ExpectMessage(currentMessage, "'init':"); - // if first message was init:false, wait for another one with init:true (this happens on windows...) - int waitToInitLimit = 10; - while (t.GetMessage(currentMessage).Contains("\"init\":false") && waitToInitLimit-- > 0) + var guid = Guid.NewGuid().ToString(); + Debug.LogError(guid); + + // Skip the session init requests (there may be multiple of othem). We can't skip them by a "positive" + // because they're also repeated with standard events (in an envelope). + Debug.Log("Skipping all non-event requests"); + for (; currentMessage < 10; currentMessage++) { - t.ExpectMessage(++currentMessage, "'type':'session'"); + if (t.CheckMessage(currentMessage, "'type':'event'")) + { + break; + } } + Debug.Log($"Done skipping non-event requests. Last one was: #{currentMessage}"); - var guid = Guid.NewGuid().ToString(); - Debug.LogError(guid); - t.ExpectMessage(++currentMessage, "'type':'event'"); + t.ExpectMessage(currentMessage, "'type':'event'"); t.ExpectMessage(currentMessage, guid); SentrySdk.CaptureMessage(guid); @@ -305,11 +310,13 @@ public string GetMessage(int index) } } - public void ExpectMessage(int index, String substring) + public bool CheckMessage(int index, String substring) { var message = GetMessage(index); - Expect($"HTTP Request #{index} contains \"{substring}\".", - message.Contains(substring) || message.Contains(substring.Replace("'", "\""))); + return message.Contains(substring) || message.Contains(substring.Replace("'", "\"")); } + + public void ExpectMessage(int index, String substring) => + Expect($"HTTP Request #{index} contains \"{substring}\".", CheckMessage(index, substring)); } } From dee9399782c7715419b8e9b4b933bd3132c7f14b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 25 Feb 2022 18:35:43 +0100 Subject: [PATCH 10/22] test: don't fail integration test when the stdout file couldn't be removed --- test/Scripts.Integration.Test/integration-run-smoke-test.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index 38b88c666..57f2d44bf 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -108,7 +108,7 @@ $httpServer = RunApiServer RunTest "smoke-crash" $httpServer.process | Stop-Process -Force $output = Get-Content $httpServer.outFile -Raw -Remove-Item $httpServer.outFile +Remove-Item $httpServer.outFile -ErrorAction Continue Write-Host "Standard Output:" -ForegroundColor Yellow $output From a7c1c23cb2cb46cca1880a6c59e48ba181624f8d Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 25 Feb 2022 20:13:27 +0100 Subject: [PATCH 11/22] ci: decrease windows integration test flakiness --- .github/workflows/ci.yml | 15 +++-- .../integration-run-smoke-test.ps1 | 65 ++++++++++++++----- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6213e347..fc1e988a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -278,20 +278,23 @@ jobs: - name: Make symbolic link for Unity (Windows) run: New-Item -ItemType SymbolicLink -Path "C:\${{ steps.env.outputs.unityVersion }}" -Target "C:\Program Files\Unity\Hub\Editor\${{ steps.env.outputs.unityVersion }}" - - name: Integration Test - Create new Project + - name: Create new Project run: ./test/Scripts.Integration.Test/integration-create-project.ps1 "${{ env.UNITY_PATH }}" - - name: Integration Test - Build Standalone Player without Sentry SDK + - name: Build Standalone Player without Sentry SDK run: ./test/Scripts.Integration.Test/integration-build-project.ps1 "${{ env.UNITY_PATH }}" - - name: Integration Test - Add Sentry to test project + - name: Add Sentry to test project run: ./test/Scripts.Integration.Test/integration-update-sentry.ps1 "${{ env.UNITY_PATH }}" - - name: Integration Test - Build Standalone Player Sentry SDK + - name: Build Standalone Player Sentry SDK run: ./test/Scripts.Integration.Test/integration-build-project.ps1 "${{ env.UNITY_PATH }}" - - name: Integration Test - Run Player - Smoke Test - run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 + - name: Run Player - Smoke Test + run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Smoke + + - name: Run Player - Crash Test + run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Crash android-smoke-test: needs: [build] diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index 57f2d44bf..7bb2c2d6d 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -3,10 +3,20 @@ [string] $TestAppPath = "", [Parameter()] - [string] $AppDataDir = "" + [string] $AppDataDir = "", + + [Parameter()] + [switch] $Smoke, + + [Parameter()] + [switch] $Crash ) . ./test/Scripts.Integration.Test/IntegrationGlobals.ps1 +If (!$Smoke -and !$Crash) { + Write-Error "Select one of the following tests (or both): -Smoke or -Crash" +} + if ("$TestAppPath" -eq "") { If ($IsMacOS) { $TestAppPath = "$NewProjectBuildPath/test.app/Contents/MacOS/$NewProjectName" @@ -21,13 +31,15 @@ if ("$TestAppPath" -eq "") { $TestAppPath = "$NewProjectBuildPath/test" } Else { - Throw "Unsupported build" + Write-Error "Unsupported build" } } if ("$AppDataDir" -ne "") { - Write-Warning "Removing AppDataDir '$AppDataDir'" - Remove-Item -Recurse $AppDataDir + if (Test-Path $AppDataDir) { + Write-Warning "Removing AppDataDir '$AppDataDir'" + Remove-Item -Recurse $AppDataDir + } } else { Write-Warning "No AppDataDir param given - if you're running this after a previous smoke-crash test, the smoke test will fail." @@ -101,20 +113,37 @@ function RunApiServer() { } # Simple smoke test -RunTest "smoke" +if ($Smoke) { + RunTest "smoke" +} # Native crash test -$httpServer = RunApiServer -RunTest "smoke-crash" -$httpServer.process | Stop-Process -Force -$output = Get-Content $httpServer.outFile -Raw -Remove-Item $httpServer.outFile -ErrorAction Continue -Write-Host "Standard Output:" -ForegroundColor Yellow -$output - -if ($output.Contains("POST http://localhost:8000/api/12345/minidump/")) { - Write-Host "smoke-crash test: PASSED" -ForegroundColor Green -} -else { - Write-Error "smoke-crash test: FAILED" +if ($Crash) { + $httpServer = RunApiServer + RunTest "smoke-crash" + + $successMessage = "POST http://localhost:8000/api/12345/minidump/" + + for ($i = 0; $i -lt 100; $i++) { + $output = Get-Content $httpServer.outFile -Raw + + if ($output.Contains($successMessage)) { + break + } + Start-Sleep -Milliseconds 100 + } + + $httpServer.process | Stop-Process -ErrorAction SilentlyContinue + $httpServer.process | Stop-Process -ErrorAction SilentlyContinue -Force + Remove-Item $httpServer.outFile -ErrorAction Continue + + Write-Host "Standard Output:" -ForegroundColor Yellow + $output + + if ($output.Contains($successMessage)) { + Write-Host "smoke-crash test: PASSED" -ForegroundColor Green + } + else { + Write-Error "smoke-crash test: FAILED" + } } \ No newline at end of file From 550c1985eec819c3e02d26684f43c8dc47aa1204 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 28 Feb 2022 09:33:37 +0100 Subject: [PATCH 12/22] ci: further crash test deflaking --- .github/workflows/ci.yml | 4 +- .../crash-test-server.ps1 | 6 ++- .../integration-run-smoke-test.ps1 | 51 ++++++++++++------- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc1e988a1..e42f92650 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -294,7 +294,9 @@ jobs: run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Smoke - name: Run Player - Crash Test - run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Crash + # We need to run through bash so that the game-generated Player.log will contain [debug] output messages. + shell: bash + run: pwsh -c "./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Crash" android-smoke-test: needs: [build] diff --git a/test/Scripts.Integration.Test/crash-test-server.ps1 b/test/Scripts.Integration.Test/crash-test-server.ps1 index 71728f0c0..ccfd24a9e 100644 --- a/test/Scripts.Integration.Test/crash-test-server.ps1 +++ b/test/Scripts.Integration.Test/crash-test-server.ps1 @@ -33,10 +33,14 @@ try { # send an empty response $context.Response.ContentLength64 = 0 $context.Response.OutputStream.Close() + if ($context.Request.Url.PathAndQuery -eq "/STOP") { + break + } } } finally { - # This is always called when ctrl+c is used + # This is always called when ctrl+c is used - note, this doesn't seem to be 100 % working... + # instead, you can send a GET request to http://localhost:8000/STOP write-Host "HTTP server stopping!" $httpServer.Stop() } diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index 7bb2c2d6d..7ece71070 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -119,31 +119,44 @@ if ($Smoke) { # Native crash test if ($Crash) { - $httpServer = RunApiServer - RunTest "smoke-crash" + $runs = 1 # You can increase this to run the crash test multiple times in a loop (until it fails) + for ($run = 1; $run -le $runs; $run++) { + $httpServer = RunApiServer + RunTest "smoke-crash" - $successMessage = "POST http://localhost:8000/api/12345/minidump/" + $httpServerUri = "http://localhost:8000" + $successMessage = "POST $httpServerUri/api/12345/minidump/" - for ($i = 0; $i -lt 100; $i++) { - $output = Get-Content $httpServer.outFile -Raw + # Wait for 1 minute (600 * 100 milliseconds) until the expected message comes in + for ($i = 0; $i -lt 600; $i++) { + $output = Get-Content $httpServer.outFile -Raw - if ($output.Contains($successMessage)) { - break + if ($output.Contains($successMessage)) { + break + } + Start-Sleep -Milliseconds 100 } - Start-Sleep -Milliseconds 100 - } - $httpServer.process | Stop-Process -ErrorAction SilentlyContinue - $httpServer.process | Stop-Process -ErrorAction SilentlyContinue -Force - Remove-Item $httpServer.outFile -ErrorAction Continue + # Stop the HTTP server + Write-Host "Stopping the dummy API server ..." -NoNewline + try { + (Invoke-WebRequest -URI "$httpServerUri/STOP").StatusDescription + } + catch { + $httpServer.process | Stop-Process -Force -ErrorAction SilentlyContinue + } + Start-Sleep -Milliseconds 500 + $output = Get-Content $httpServer.outFile -Raw + Remove-Item $httpServer.outFile -ErrorAction Continue - Write-Host "Standard Output:" -ForegroundColor Yellow - $output + Write-Host "Standard Output:" -ForegroundColor Yellow + $output - if ($output.Contains($successMessage)) { - Write-Host "smoke-crash test: PASSED" -ForegroundColor Green - } - else { - Write-Error "smoke-crash test: FAILED" + if ($output.Contains($successMessage)) { + Write-Host "smoke-crash test $run/$runs : PASSED" -ForegroundColor Green + } + else { + Write-Error "smoke-crash test $run/$runs : FAILED" + } } } \ No newline at end of file From 56d3e4f6555e3f16ce27cc941cd1cbf7395ba080 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 28 Feb 2022 17:19:24 +0100 Subject: [PATCH 13/22] tmp: try if SentryNativeBridge.ReinstallBackend would help the crash test to pass in CI --- src/Sentry.Unity.Native/SentryNative.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Sentry.Unity.Native/SentryNative.cs b/src/Sentry.Unity.Native/SentryNative.cs index 6ddf25ee0..2e1cec61f 100644 --- a/src/Sentry.Unity.Native/SentryNative.cs +++ b/src/Sentry.Unity.Native/SentryNative.cs @@ -36,6 +36,9 @@ public static void Configure(SentryUnityOptions options) // return crashedLastRun.Value; // }; + // At this point Unity has taken the signal handler and will not invoke the original handler (Sentry) + // So we register our backend once more to make sure user-defined data is available in the crash report. + SentryNativeBridge.ReinstallBackend(); } } } From 0f7a1c2c8c4f2cf12fe01c9c78c6c4fc59de5fed Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 1 Mar 2022 10:03:18 +0100 Subject: [PATCH 14/22] test: smoke test - fix logging in dotnet avoid console.write which doesn't show up in player.log --- samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs | 2 -- test/Scripts.Integration.Test/integration-create-project.ps1 | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index 01cf5e08e..18ca76a4a 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -94,8 +94,6 @@ public static void InitSentry(SentryUnityOptions options, bool requireNative = t options.Dsn = "http://publickey@localhost:8000/12345"; options.Debug = true; options.DebugOnlyInEditor = false; - options.DiagnosticLevel = SentryLevel.Debug; - options.DiagnosticLogger = new ConsoleDiagnosticLogger(SentryLevel.Debug); #if SENTRY_NATIVE_IOS Debug.Log("SMOKE TEST: Configure Native iOS."); diff --git a/test/Scripts.Integration.Test/integration-create-project.ps1 b/test/Scripts.Integration.Test/integration-create-project.ps1 index e9136cf9e..2f4a307b9 100644 --- a/test/Scripts.Integration.Test/integration-create-project.ps1 +++ b/test/Scripts.Integration.Test/integration-create-project.ps1 @@ -76,5 +76,10 @@ Else Copy-Item -Recurse "$IntegrationScriptsPath/SentrySetup.*" -Destination "$NewProjectAssetsPath/Editor/" Write-Host " OK" + # Don't print stack traces in debug logs. See ./samples/unity-of-bugs/ProjectSettings/PresetManager.asset + $projectSettingsPath = "$NewProjectPath/ProjectSettings/ProjectSettings.asset" + (Get-Content $projectSettingsPath) -replace "m_StackTraceTypes: ?[01]+", "m_StackTraceTypes: 010000000000000000000000000000000100000001000000" | ` + Out-File $projectSettingsPath + Write-Host "`nProject created!!" } From 1f4ebe64454731490c59fb2a08e821f51233ddeb Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 1 Mar 2022 14:10:16 +0100 Subject: [PATCH 15/22] feat: basic native log forwarding support --- .../Assets/Scripts/SmokeTester.cs | 3 + src/Sentry.Unity.Native/SentryNativeBridge.cs | 68 ++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index 18ca76a4a..cab3024fb 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -94,6 +94,9 @@ public static void InitSentry(SentryUnityOptions options, bool requireNative = t options.Dsn = "http://publickey@localhost:8000/12345"; options.Debug = true; options.DebugOnlyInEditor = false; + // Need to set the logger explicitly so it's available already for the native .Configure() methods. + // SentryUnity.Init() would set it to the same value, but too late for us. + options.DiagnosticLogger = new UnityLogger(options); #if SENTRY_NATIVE_IOS Debug.Log("SMOKE TEST: Configure Native iOS."); diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index 030c6fa62..fbe4af0c4 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -2,6 +2,8 @@ using System.IO; using System.Runtime.InteropServices; using Sentry.Extensibility; +using AOT; +using System.Threading; namespace Sentry.Unity { @@ -64,6 +66,22 @@ public static void Init(SentryUnityOptions options) } } + var prevLogger = _getLogger(); + if (options.DiagnosticLogger is null) + { + if (prevLogger is not null) + { + prevLogger.LogDebug("Unsetting the current native logger"); + } + _setLogger(null); + } + else + { + options.DiagnosticLogger.LogDebug($"{(prevLogger is null ? "Setting a" : "Replacing the")} native logger"); + _setLogger(options.DiagnosticLogger); + sentry_options_set_logger(cOptions, new sentry_logger_function_t(nativeLog), IntPtr.Zero); + } + sentry_init(cOptions); } @@ -97,9 +115,53 @@ public static void Init(SentryUnityOptions options) [DllImport("sentry")] private static extern void sentry_options_set_auto_session_tracking(IntPtr options, int debug); - // TODO we could set a logger for sentry-native, forwarding the logs to `options.DiagnosticLogger?` - // [DllImport("sentry")] - // private static extern void sentry_options_set_logger(IntPtr options, IntPtr logger, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)] + private delegate void sentry_logger_function_t(int level, string message, IntPtr argsAddress, IntPtr userData); + + [DllImport("sentry")] + private static extern void sentry_options_set_logger(IntPtr options, sentry_logger_function_t logger, IntPtr userData); + + // The logger we should forward native messages to. + private static IDiagnosticLogger? _logger; + private static Mutex _loggerMutex = new Mutex(); + + private static IDiagnosticLogger? _getLogger() + { + _loggerMutex.WaitOne(); + var result = _logger; + _loggerMutex.ReleaseMutex(); + return result; + } + private static void _setLogger(IDiagnosticLogger? logger) + { + _loggerMutex.WaitOne(); + _logger = logger; + _loggerMutex.ReleaseMutex(); + } + + // This method is called from the C library + [MonoPInvokeCallback(typeof(sentry_logger_function_t))] + private static void nativeLog(int cLevel, string message, IntPtr argsAddress, IntPtr userData) + { + var logger = _getLogger(); + if (logger is null) + return; + + // see sentry.h: sentry_level_e + var level = cLevel switch + { + -1 => SentryLevel.Debug, + 0 => SentryLevel.Info, + 1 => SentryLevel.Warning, + 2 => SentryLevel.Error, + 3 => SentryLevel.Fatal, + _ => SentryLevel.Info, + }; + + // TODO args + logger.Log(level, $"Native: {message}"); + } + [DllImport("sentry")] private static extern void sentry_init(IntPtr options); From 6ebc7d187695070373a971cf6d905fcd616dc4d2 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 1 Mar 2022 15:52:40 +0100 Subject: [PATCH 16/22] feat: native log forwarding - formatting --- src/Sentry.Unity.Native/SentryNativeBridge.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index fbe4af0c4..0b86defa3 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -4,6 +4,7 @@ using Sentry.Extensibility; using AOT; using System.Threading; +using System.Text; namespace Sentry.Unity { @@ -141,7 +142,7 @@ private static void _setLogger(IDiagnosticLogger? logger) // This method is called from the C library [MonoPInvokeCallback(typeof(sentry_logger_function_t))] - private static void nativeLog(int cLevel, string message, IntPtr argsAddress, IntPtr userData) + private static void nativeLog(int cLevel, string message, IntPtr args, IntPtr userData) { var logger = _getLogger(); if (logger is null) @@ -158,10 +159,24 @@ private static void nativeLog(int cLevel, string message, IntPtr argsAddress, In _ => SentryLevel.Info, }; - // TODO args + if (!logger.IsEnabled(level)) + return; + + if (message.Contains("%")) + { + var formattedLength = vsnprintf(null, UIntPtr.Zero, message, args); + var buffer = new StringBuilder(formattedLength + 1); + vsprintf(buffer, message, args); + message = buffer.ToString(); + } logger.Log(level, $"Native: {message}"); } + [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + private static extern int vsprintf(StringBuilder buffer, string format, IntPtr args); + + [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + private static extern int vsnprintf(string? buffer, UIntPtr bufferSize, string format, IntPtr args); [DllImport("sentry")] private static extern void sentry_init(IntPtr options); From 3d6283d140e900c56de8bbd67952ad8c9be452d3 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 1 Mar 2022 15:55:12 +0100 Subject: [PATCH 17/22] ci: cleanup --- .github/workflows/ci.yml | 2 -- src/Sentry.Unity.Native/SentryNativeBridge.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e42f92650..752b27b02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -294,8 +294,6 @@ jobs: run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Smoke - name: Run Player - Crash Test - # We need to run through bash so that the game-generated Player.log will contain [debug] output messages. - shell: bash run: pwsh -c "./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Crash" android-smoke-test: diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index 0b86defa3..caf9fa584 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -105,11 +105,9 @@ public static void Init(SentryUnityOptions options) [DllImport("sentry")] private static extern void sentry_options_set_sample_rate(IntPtr options, double rate); - [DllImport("sentry")] private static extern void sentry_options_set_database_path(IntPtr options, string path); - [DllImport("sentry")] private static extern void sentry_options_set_database_pathw(IntPtr options, [MarshalAs(UnmanagedType.LPWStr)] string path); From 737dfcc7812284b7478c09c720f2feb12368ab52 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 1 Mar 2022 18:14:20 +0100 Subject: [PATCH 18/22] ci: crash test - try to sleep after init to see if a background sync causes the flakiness --- samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index cab3024fb..d30ebdd5a 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -189,6 +189,10 @@ public static void SmokeTestCrash() InitSentry(new SentryUnityOptions()); + const int sleepMs = 5000; + Debug.Log($"SMOKE TEST: Sleep for {sleepMs} ms to avoid failure caused by the background data sync during sentry init."); + Thread.Sleep(sleepMs); + AddContext(); Debug.Log("SMOKE TEST: Issuing a native crash (c++ unhandled exception)"); From 7871ec2e9660d6ad7e961aa24f8132844b4cacb8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 3 Mar 2022 10:03:17 +0100 Subject: [PATCH 19/22] chore: update UnitySmokeTestStandalonePlayerIL2CPP to run the integratino-run-smoke-test.psq --- .github/workflows/ci.yml | 2 +- Directory.Build.targets | 27 ++----------------- .../integration-run-smoke-test.ps1 | 8 +++++- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 752b27b02..fc1e988a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -294,7 +294,7 @@ jobs: run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Smoke - name: Run Player - Crash Test - run: pwsh -c "./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Crash" + run: ./test/Scripts.Integration.Test/integration-run-smoke-test.ps1 -Crash android-smoke-test: needs: [build] diff --git a/Directory.Build.targets b/Directory.Build.targets index f188d5a99..a52838994 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -47,7 +47,7 @@ Builder.BuildWindowsIl2CPPPlayer $(PlayerBuildPath)Windows/IL2CPP_Player.exe $(StandaloneBuildPath) - $(USERPROFILE)\AppData\LocalLow\DefaultCompany\unity-of-bugs\ + $(USERPROFILE)/AppData/LocalLow/DefaultCompany/unity-of-bugs/ https://94677106febe46b88b9b9ae5efd18a00@o447951.ingest.sentry.io/5439417 @@ -244,30 +244,7 @@ Related: https://forum.unity.com/threads/6572-debugger-agent-unable-to-listen-on - - - - - - - - - - - - - + diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index 7ece71070..95f54869c 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -11,7 +11,13 @@ [Parameter()] [switch] $Crash ) -. ./test/Scripts.Integration.Test/IntegrationGlobals.ps1 +. $PSScriptRoot/IntegrationGlobals.ps1 + +Write-Host "Given parameters:" +Write-Host " TestAppPath: $TestAppPath" +Write-Host " AppDataDir: $AppDataDir" +Write-Host " Smoke: $Smoke" +Write-Host " Crash: $Crash" If (!$Smoke -and !$Crash) { Write-Error "Select one of the following tests (or both): -Smoke or -Crash" From 1ebc6cac84119f98ccebdf6058fb6c9e9ce13af9 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 3 Mar 2022 19:57:23 +0100 Subject: [PATCH 20/22] chore: native bridge - remove logger synchronization --- src/Sentry.Unity.Native/SentryNativeBridge.cs | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index caf9fa584..d8d6eabed 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -67,19 +67,15 @@ public static void Init(SentryUnityOptions options) } } - var prevLogger = _getLogger(); if (options.DiagnosticLogger is null) { - if (prevLogger is not null) - { - prevLogger.LogDebug("Unsetting the current native logger"); - } - _setLogger(null); + _logger?.LogDebug("Unsetting the current native logger"); + _logger = null; } else { - options.DiagnosticLogger.LogDebug($"{(prevLogger is null ? "Setting a" : "Replacing the")} native logger"); - _setLogger(options.DiagnosticLogger); + options.DiagnosticLogger.LogDebug($"{(_logger is null ? "Setting a" : "Replacing the")} native logger"); + _logger = options.DiagnosticLogger; sentry_options_set_logger(cOptions, new sentry_logger_function_t(nativeLog), IntPtr.Zero); } @@ -122,29 +118,16 @@ public static void Init(SentryUnityOptions options) // The logger we should forward native messages to. private static IDiagnosticLogger? _logger; - private static Mutex _loggerMutex = new Mutex(); - - private static IDiagnosticLogger? _getLogger() - { - _loggerMutex.WaitOne(); - var result = _logger; - _loggerMutex.ReleaseMutex(); - return result; - } - private static void _setLogger(IDiagnosticLogger? logger) - { - _loggerMutex.WaitOne(); - _logger = logger; - _loggerMutex.ReleaseMutex(); - } // This method is called from the C library [MonoPInvokeCallback(typeof(sentry_logger_function_t))] private static void nativeLog(int cLevel, string message, IntPtr args, IntPtr userData) { - var logger = _getLogger(); + var logger = _logger; if (logger is null) + { return; + } // see sentry.h: sentry_level_e var level = cLevel switch @@ -158,7 +141,9 @@ private static void nativeLog(int cLevel, string message, IntPtr args, IntPtr us }; if (!logger.IsEnabled(level)) + { return; + } if (message.Contains("%")) { From 3148f0908c8385969f02e215aa161663378fb391 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 4 Mar 2022 14:27:22 +0100 Subject: [PATCH 21/22] ci: try to use a python crash-test-server to avoid errors in CI --- .../Assets/Scripts/SmokeTester.cs | 24 +++++++------- src/Sentry.Unity.Native/SentryNativeBridge.cs | 7 ++-- .../crash-test-server.ps1 | 3 +- .../crash-test-server.py | 33 +++++++++++++++++++ .../integration-run-smoke-test.ps1 | 33 +++++++++++-------- 5 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 test/Scripts.Integration.Test/crash-test-server.py diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index d30ebdd5a..a146688e3 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -89,7 +89,7 @@ public void Start() } } - public static void InitSentry(SentryUnityOptions options, bool requireNative = true) + public static void InitSentry(SentryUnityOptions options, bool sentryDotNet) { options.Dsn = "http://publickey@localhost:8000/12345"; options.Debug = true; @@ -111,16 +111,19 @@ public static void InitSentry(SentryUnityOptions options, bool requireNative = t options.WindowsNativeSupportEnabled = true; SentryNative.Configure(options); #else - if (requireNative) + Debug.LogWarning("SMOKE TEST: Given platform is not supported for native sentry configuration"); + if (!sentryDotNet) { - Debug.Log("SMOKE TEST: Given platform is not supported for native sentry configuration"); throw new Exception("Given platform is not supported"); } #endif - Debug.Log("SMOKE TEST: SentryUnity Init."); + // if (sentryDotNet) + // { + Debug.Log("SMOKE TEST: SentryUnity (.net) Init."); SentryUnity.Init(options); - Debug.Log("SMOKE TEST: SentryUnity Init OK."); + Debug.Log("SMOKE TEST: SentryUnity (.net) Init OK."); + // } } public static void SmokeTest() @@ -130,7 +133,7 @@ public static void SmokeTest() { Debug.Log("SMOKE TEST: Start"); - InitSentry(new SentryUnityOptions() { CreateHttpClientHandler = () => t }, false); + InitSentry(new SentryUnityOptions() { CreateHttpClientHandler = () => t }, true); var currentMessage = 0; t.ExpectMessage(currentMessage, "'type':'session'"); @@ -187,11 +190,10 @@ public static void SmokeTestCrash() { Debug.Log("SMOKE TEST: Start"); - InitSentry(new SentryUnityOptions()); - - const int sleepMs = 5000; - Debug.Log($"SMOKE TEST: Sleep for {sleepMs} ms to avoid failure caused by the background data sync during sentry init."); - Thread.Sleep(sleepMs); + // Note: we're disabling sentr-dotnet initialization in the crash test because the HTTP minidump isn't being + // received consistently by our simple powershell HTTP server (crash-test-server.ps1) in CI. This is likely an + // issue in the server, not the SDK but may require further investigation. + InitSentry(new SentryUnityOptions(), false); AddContext(); diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index d8d6eabed..1942fb1fd 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -116,10 +116,10 @@ public static void Init(SentryUnityOptions options) [DllImport("sentry")] private static extern void sentry_options_set_logger(IntPtr options, sentry_logger_function_t logger, IntPtr userData); - // The logger we should forward native messages to. + // The logger we should forward native messages to. This is referenced by nativeLog() which in turn for. private static IDiagnosticLogger? _logger; - // This method is called from the C library + // This method is called from the C library and forwards incoming messages to the currently set _logger. [MonoPInvokeCallback(typeof(sentry_logger_function_t))] private static void nativeLog(int cLevel, string message, IntPtr args, IntPtr userData) { @@ -145,6 +145,9 @@ private static void nativeLog(int cLevel, string message, IntPtr args, IntPtr us return; } + // If the message contains any "formatting" modifiers (that should be substituted by `args`), we need + // to apply the formatting. However, we cannot access C var-arg (va_list) in c# thus we pass it back to + // vsnprintf (to find out the length of the resulting buffer) & vsprintf (to actually format the message). if (message.Contains("%")) { var formattedLength = vsnprintf(null, UIntPtr.Zero, message, args); diff --git a/test/Scripts.Integration.Test/crash-test-server.ps1 b/test/Scripts.Integration.Test/crash-test-server.ps1 index ccfd24a9e..57301070a 100644 --- a/test/Scripts.Integration.Test/crash-test-server.ps1 +++ b/test/Scripts.Integration.Test/crash-test-server.ps1 @@ -5,7 +5,8 @@ $httpServer = [System.Net.HttpListener]::new() $httpServer.Prefixes.Add($uri) $httpServer.Start() -write-Host "HTTP server listening on $uri" +Write-Host "HTTP server listening on $uri" +Write-Host "To stop the server, execute a GET request to ${uri}STOP" Write-Host $separator try { diff --git a/test/Scripts.Integration.Test/crash-test-server.py b/test/Scripts.Integration.Test/crash-test-server.py new file mode 100644 index 000000000..bda147765 --- /dev/null +++ b/test/Scripts.Integration.Test/crash-test-server.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +import sys +from typing import get_type_hints +import threading + + +class Handler(BaseHTTPRequestHandler): + def commonServe(self): + self.send_response(200, "") + self.end_headers() + sys.stdout.flush() + sys.stderr.flush() + + if (self.path == "/STOP"): + print("HTTP server stopping!") + threading.Thread(target=self.server.shutdown).start() + + def do_GET(self): + self.commonServe() + + def do_POST(self): + self.commonServe() + + +host = '127.0.0.1' +port = 8000 +uri = 'http://{}:{}'.format(host, port) +print("HTTP server listening on {}".format(uri)) +print("To stop the server, execute a GET request to {}/STOP".format(uri)) +httpd = ThreadingHTTPServer((host, port), Handler) +target = httpd.serve_forever() diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index 95f54869c..8042fd71b 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -95,12 +95,12 @@ function RunTest([string] $type) { } function RunApiServer() { - $result = "" | Select-Object -Property process, outFile + $result = "" | Select-Object -Property process, outFile, errFile Write-Host "Starting the HTTP server (dummy API server)" $result.outFile = New-TemporaryFile - $errFile = New-TemporaryFile + $result.errFile = New-TemporaryFile - $result.process = Start-Process "powershell" -ArgumentList "-command", "$PSScriptRoot/crash-test-server.ps1" -NoNewWindow -PassThru -RedirectStandardOutput $result.outFile -RedirectStandardError $errFile + $result.process = Start-Process "python3" -ArgumentList "$PSScriptRoot/crash-test-server.py" -NoNewWindow -PassThru -RedirectStandardOutput $result.outFile -RedirectStandardError $result.errFile # The process shouldn't finish by itself, if it did, there was an error, so let's check that Start-Sleep -Second 1 @@ -109,9 +109,9 @@ function RunApiServer() { Write-Host "Standard Output:" -ForegroundColor Yellow Get-Content $result.outFile Write-Host "Standard Error:" -ForegroundColor Yellow - Get-Content $errFile + Get-Content $result.errFile Remove-Item $result.outFile - Remove-Item $errFile + Remove-Item $result.errFile exit 1 } @@ -131,13 +131,12 @@ if ($Crash) { RunTest "smoke-crash" $httpServerUri = "http://localhost:8000" - $successMessage = "POST $httpServerUri/api/12345/minidump/" + $successMessage = "POST /api/12345/minidump/" # Wait for 1 minute (600 * 100 milliseconds) until the expected message comes in for ($i = 0; $i -lt 600; $i++) { - $output = Get-Content $httpServer.outFile -Raw - - if ($output.Contains($successMessage)) { + $output = (Get-Content $httpServer.outFile -Raw) + (Get-Content $httpServer.errFile -Raw) + if ("$output".Contains($successMessage)) { break } Start-Sleep -Milliseconds 100 @@ -149,14 +148,20 @@ if ($Crash) { (Invoke-WebRequest -URI "$httpServerUri/STOP").StatusDescription } catch { + Write-Host "/STOP request failed, killing the server process" $httpServer.process | Stop-Process -Force -ErrorAction SilentlyContinue } - Start-Sleep -Milliseconds 500 - $output = Get-Content $httpServer.outFile -Raw - Remove-Item $httpServer.outFile -ErrorAction Continue + $httpServer.process | Wait-Process -Timeout 10 -ErrorAction Continue - Write-Host "Standard Output:" -ForegroundColor Yellow - $output + Write-Host "Server stdout:" -ForegroundColor Yellow + Get-Content $httpServer.outFile -Raw + + Write-Host "Server stderr:" -ForegroundColor Yellow + Get-Content $httpServer.errFile -Raw + + $output = (Get-Content $httpServer.outFile -Raw) + (Get-Content $httpServer.errFile -Raw) + Remove-Item $httpServer.outFile -ErrorAction Continue + Remove-Item $httpServer.errFile -ErrorAction Continue if ($output.Contains($successMessage)) { Write-Host "smoke-crash test $run/$runs : PASSED" -ForegroundColor Green From 8a96f67c0824556cbe40f97fe6b5844f205edf2a Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 4 Mar 2022 15:57:54 +0100 Subject: [PATCH 22/22] chore: pre-merge cleanups --- .../Assets/Scripts/SmokeTester.cs | 17 ++----- .../crash-test-server.ps1 | 47 ------------------- .../crash-test-server.py | 1 - .../integration-run-smoke-test.ps1 | 5 +- 4 files changed, 7 insertions(+), 63 deletions(-) delete mode 100644 test/Scripts.Integration.Test/crash-test-server.ps1 diff --git a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs index a146688e3..74a98dbea 100644 --- a/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs +++ b/samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs @@ -89,7 +89,7 @@ public void Start() } } - public static void InitSentry(SentryUnityOptions options, bool sentryDotNet) + public static void InitSentry(SentryUnityOptions options) { options.Dsn = "http://publickey@localhost:8000/12345"; options.Debug = true; @@ -112,18 +112,12 @@ public static void InitSentry(SentryUnityOptions options, bool sentryDotNet) SentryNative.Configure(options); #else Debug.LogWarning("SMOKE TEST: Given platform is not supported for native sentry configuration"); - if (!sentryDotNet) - { - throw new Exception("Given platform is not supported"); - } + throw new Exception("Given platform is not supported"); #endif - // if (sentryDotNet) - // { Debug.Log("SMOKE TEST: SentryUnity (.net) Init."); SentryUnity.Init(options); Debug.Log("SMOKE TEST: SentryUnity (.net) Init OK."); - // } } public static void SmokeTest() @@ -133,7 +127,7 @@ public static void SmokeTest() { Debug.Log("SMOKE TEST: Start"); - InitSentry(new SentryUnityOptions() { CreateHttpClientHandler = () => t }, true); + InitSentry(new SentryUnityOptions() { CreateHttpClientHandler = () => t }); var currentMessage = 0; t.ExpectMessage(currentMessage, "'type':'session'"); @@ -190,10 +184,7 @@ public static void SmokeTestCrash() { Debug.Log("SMOKE TEST: Start"); - // Note: we're disabling sentr-dotnet initialization in the crash test because the HTTP minidump isn't being - // received consistently by our simple powershell HTTP server (crash-test-server.ps1) in CI. This is likely an - // issue in the server, not the SDK but may require further investigation. - InitSentry(new SentryUnityOptions(), false); + InitSentry(new SentryUnityOptions()); AddContext(); diff --git a/test/Scripts.Integration.Test/crash-test-server.ps1 b/test/Scripts.Integration.Test/crash-test-server.ps1 deleted file mode 100644 index 57301070a..000000000 --- a/test/Scripts.Integration.Test/crash-test-server.ps1 +++ /dev/null @@ -1,47 +0,0 @@ -# Start an HTTP server -$uri = "http://localhost:8000/" -$separator = "--------------------------------------------------------------------" -$httpServer = [System.Net.HttpListener]::new() -$httpServer.Prefixes.Add($uri) -$httpServer.Start() - -Write-Host "HTTP server listening on $uri" -Write-Host "To stop the server, execute a GET request to ${uri}STOP" -Write-Host $separator - -try { - while ($httpServer.IsListening) { - # allow ctrl+c interrupt using the AsyncWaitHandle - $contextAsync = $httpServer.GetContextAsync() - while (-not $contextAsync.AsyncWaitHandle.WaitOne(100)) { } - - # current request info - $context = $contextAsync.GetAwaiter().GetResult() - - # print the request to stdout - write-Host "$($context.Request.HttpMethod) $($context.Request.Url)" - if ($context.Request.HttpMethod -eq 'POST') { - $payload = [System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd() - if ($context.Request.Url.ToString().Contains("minidump")) { - $payload = "minidump payload length: $($payload.Length)`n" - } - else { - } - Write-Host -NoNewline $payload - } - Write-Host $separator - - # send an empty response - $context.Response.ContentLength64 = 0 - $context.Response.OutputStream.Close() - if ($context.Request.Url.PathAndQuery -eq "/STOP") { - break - } - } -} -finally { - # This is always called when ctrl+c is used - note, this doesn't seem to be 100 % working... - # instead, you can send a GET request to http://localhost:8000/STOP - write-Host "HTTP server stopping!" - $httpServer.Stop() -} diff --git a/test/Scripts.Integration.Test/crash-test-server.py b/test/Scripts.Integration.Test/crash-test-server.py index bda147765..a65d42adf 100644 --- a/test/Scripts.Integration.Test/crash-test-server.py +++ b/test/Scripts.Integration.Test/crash-test-server.py @@ -2,7 +2,6 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import sys -from typing import get_type_hints import threading diff --git a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 index 8042fd71b..def8d51e9 100644 --- a/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 +++ b/test/Scripts.Integration.Test/integration-run-smoke-test.ps1 @@ -133,6 +133,7 @@ if ($Crash) { $httpServerUri = "http://localhost:8000" $successMessage = "POST /api/12345/minidump/" + Write-Host "Waiting for the expected message to appear in the server output logs ..." # Wait for 1 minute (600 * 100 milliseconds) until the expected message comes in for ($i = 0; $i -lt 600; $i++) { $output = (Get-Content $httpServer.outFile -Raw) + (Get-Content $httpServer.errFile -Raw) @@ -143,7 +144,7 @@ if ($Crash) { } # Stop the HTTP server - Write-Host "Stopping the dummy API server ..." -NoNewline + Write-Host "Stopping the dummy API server ... " -NoNewline try { (Invoke-WebRequest -URI "$httpServerUri/STOP").StatusDescription } @@ -170,4 +171,4 @@ if ($Crash) { Write-Error "smoke-crash test $run/$runs : FAILED" } } -} \ No newline at end of file +}