Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows native config and logging #577

Merged
merged 22 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
dcd539e
feat: standalone native handler - set some missing options
vaind Feb 22, 2022
b91e67b
wip: smoke crash test
vaind Feb 22, 2022
3b13029
feat: standalone sentry-native configuration & debug logging
vaind Feb 23, 2022
611afab
fix: standalone shouldn't reinstall backend - it's unnecessary
vaind Feb 23, 2022
4ec7957
test: Standalone native crash test
vaind Feb 23, 2022
fc6cda9
ci: fix windows integration test
vaind Feb 24, 2022
8e559c0
fix windows integration test
vaind Feb 25, 2022
4771292
ci: windows integration tests - print logs
vaind Feb 25, 2022
1f0bc87
fix integration tests in CI
vaind Feb 25, 2022
dee9399
test: don't fail integration test when the stdout file couldn't be re…
vaind Feb 25, 2022
a7c1c23
ci: decrease windows integration test flakiness
vaind Feb 25, 2022
550c198
ci: further crash test deflaking
vaind Feb 28, 2022
56d3e4f
tmp: try if SentryNativeBridge.ReinstallBackend would help the crash …
vaind Feb 28, 2022
0f7a1c2
test: smoke test - fix logging in dotnet
vaind Mar 1, 2022
1f4ebe6
feat: basic native log forwarding support
vaind Mar 1, 2022
6ebc7d1
feat: native log forwarding - formatting
vaind Mar 1, 2022
3d6283d
ci: cleanup
vaind Mar 1, 2022
737dfcc
ci: crash test - try to sleep after init to see if a background sync …
vaind Mar 1, 2022
7871ec2
chore: update UnitySmokeTestStandalonePlayerIL2CPP to run the integra…
vaind Mar 3, 2022
1ebc6ca
chore: native bridge - remove logger synchronization
vaind Mar 3, 2022
3148f09
ci: try to use a python crash-test-server to avoid errors in CI
vaind Mar 4, 2022
8a96f67
chore: pre-merge cleanups
vaind Mar 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
14 changes: 2 additions & 12 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<StandaloneBuildMethod>Builder.BuildWindowsIl2CPPPlayer</StandaloneBuildMethod>
<StandaloneBuildPath>$(PlayerBuildPath)Windows/IL2CPP_Player.exe</StandaloneBuildPath>
<StandaloneExecutablePath>$(StandaloneBuildPath)</StandaloneExecutablePath>
<StandaloneDataPath>%USERPROFILE%\AppData\LocalLow\DefaultCompany\unity-of-bugs\</StandaloneDataPath>
<StandaloneDataPath>$(USERPROFILE)/AppData/LocalLow/DefaultCompany/unity-of-bugs/</StandaloneDataPath>
<TestDsn>https://94677106febe46b88b9b9ae5efd18a00@o447951.ingest.sentry.io/5439417</TestDsn>
</PropertyGroup>

Expand Down Expand Up @@ -244,17 +244,7 @@ Related: https://forum.unity.com/threads/6572-debugger-agent-unable-to-listen-on
<Target Name="UnitySmokeTestStandalonePlayerIL2CPP" DependsOnTargets="FindUnity" Condition="'$(MSBuildProjectName)' == 'Sentry.Unity'">
<Error Condition="$(UnityRoot) == ''" Text="Couldn't find Unity."></Error>

<Message Importance="High" Text="Running smoke test on player."></Message>

<Exec Command="$(StandaloneExecutablePath) --test smoke" IgnoreExitCode="true" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="SmokeTestExitCode"/>
</Exec>

<Message Condition="'$(StandaloneDataPath)' != ''" Importance="High" Text="Runtime data is found in $(StandaloneDataPath)" />
<Exec Condition="$(StandaloneDataPath) != ''" Command="type &quot;$(StandaloneDataPath)/Player.log&quot;"/>

<Error Text="Smoke test failed with code $(SmokeTestExitCode)" Condition="'$(SmokeTestExitCode)' != '200'" />
<Message Importance="High" Text="Smoke test PASSED" Condition="'$(SmokeTestExitCode)' == '200'" />
<Exec Command="pwsh &quot;$(RepoRoot)/test/Scripts.Integration.Test/integration-run-smoke-test.ps1&quot; -Smoke -Crash &quot;$(StandaloneExecutablePath)&quot; -AppDataDir &quot;$(StandaloneDataPath)&quot;" />
</Target>

<!-- Build an Android player with IL2CPP: dotnet msbuild /t:UnityBuildPlayerAndroidIL2CPP src/Sentry.Unity -->
Expand Down
184 changes: 123 additions & 61 deletions samples/unity-of-bugs/Assets/Scripts/SmokeTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
#define SENTRY_NATIVE_IOS
#elif UNITY_ANDROID
#define SENTRY_NATIVE_ANDROID
#elif UNITY_STANDALONE_WIN && ENABLE_IL2CPP
#define SENTRY_NATIVE_WINDOWS
#endif
#endif

#if SENTRY_NATIVE_IOS
using Sentry.Unity.iOS;
#elif UNITY_ANDROID
using Sentry.Unity.Android;
#elif SENTRY_NATIVE_WINDOWS
using Sentry.Unity.Native;
#endif

#if UNITY_IOS
Expand All @@ -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;
Expand All @@ -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<AndroidJavaObject>("currentActivity"))
using (var intent = currentActivity.Call<AndroidJavaObject>("getIntent"))
{
var text = intent.Call<String> ("getStringExtra", "test");
if (text == "smoke")
{
SmokeTest();
}
arg = intent.Call<String> ("getStringExtra", "test");
}
#elif UNITY_IOS
string pListPath = Path.Combine(Application.dataPath.Substring(0, Application.dataPath.LastIndexOf('/')), "Info.plist");
Expand All @@ -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
{
Expand All @@ -65,83 +67,98 @@ 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}");
Application.Quit(-1);
}
}

public static void SmokeTest()
public static void InitSentry(SentryUnityOptions options, bool requireNative = true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I can follow the purpose of requireNative? Could you elaborate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look at the call sites, there are two: one from SmokeTest() which is plain .net and doesn't require native to be configured. The other is from the crash that does require the native part.
Basically, this was originally meant to be able to run the smoke test (.net) also on Linux/Mac, but that was removed in the end so is actually not necessary per se, but may still come in handy.

{
var t = new TestHandler();
try
{
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;

var sentryUnityInfo = new SentryUnityInfo();
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.");
SentryNativeIos.Configure(options);
Debug.Log("SMOKE TEST: Configure Native iOS.");
options.IosNativeSupportEnabled = true;
SentryNativeIos.Configure(options);
#elif SENTRY_NATIVE_ANDROID
Debug.Log("SMOKE TEST: Configure Native Android.");
SentryNativeAndroid.Configure(options, sentryUnityInfo);
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 Windows.");
SentryNative.Configure(options);
Debug.Log("SMOKE TEST: Configure Native Windows.");
options.WindowsNativeSupportEnabled = true;
SentryNative.Configure(options);
#else
if (requireNative)
{
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.");
SentryUnity.Init(options);
Debug.Log("SMOKE TEST: SentryUnity Init.");
SentryUnity.Init(options);
Debug.Log("SMOKE TEST: SentryUnity Init OK.");
}

public static void SmokeTest()
{
var t = new TestHandler();
try
{
Debug.Log("SMOKE TEST: Start");

Debug.Log("SMOKE TEST: SentryUnity Init OK.");
InitSentry(new SentryUnityOptions() { CreateHttpClientHandler = () => t }, false);

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...)
if (t.GetMessage(currentMessage).Contains("\"init\":false"))
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'");
t.ExpectMessage(currentMessage, "'init':true");
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);
t.ExpectMessage(++currentMessage, "'type':'event'");
t.ExpectMessage(currentMessage, guid);

var ex = new Exception("Exception & context test");
SentrySdk.AddBreadcrumb("crumb", "bread", "error", new Dictionary<string, string>() { { "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<string, string>() { { "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'}");
Expand All @@ -166,6 +183,49 @@ public static void SmokeTest()
}
}

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.");
vaind marked this conversation as resolved.
Show resolved Hide resolved
Thread.Sleep(sleepMs);

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<string, string>() { { "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<string, string>() { { "role", "admin" } }
};
});
}

// CppPlugin.cpp
[DllImport("__Internal")]
private static extern void throw_cpp();

private class TestHandler : HttpClientHandler
{
private List<string> _requests = new List<string>();
Expand Down Expand Up @@ -255,11 +315,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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ private static void ConfigureOptions(Dictionary<string, string> 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<SentryWindow>();
var options = optionsWindow.Options;

if (options is null)
{
throw new InvalidOperationException("SentryOptions not found");
throw new InvalidOperationException($"{functionName} failed: SentryOptions not found");
}
Debug.LogFormat("{0}: Found SentryOptions", functionName);

Expand Down
Loading