Skip to content

Commit 217c11c

Browse files
vaindbruno-garcia
andauthored
Feat: native crash reporting (#2887)
Co-authored-by: Bruno Garcia <[email protected]>
1 parent f473889 commit 217c11c

31 files changed

+1000
-95
lines changed

Diff for: .github/workflows/build.yml

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ jobs:
3131
key: sentry-native-${{ runner.os }}-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }}
3232
enableCrossOsArchive: true
3333

34+
- name: Install build dependencies
35+
if: steps.cache.outputs.cache-hit != 'true' && runner.os == 'Linux'
36+
run: |
37+
sudo apt update
38+
sudo apt install libcurl4-openssl-dev
39+
3440
- run: scripts/build-sentry-native.ps1
3541
if: steps.cache.outputs.cache-hit != 'true'
3642
shell: pwsh
@@ -157,6 +163,12 @@ jobs:
157163
name: ${{ github.sha }}
158164
path: src
159165

166+
- name: Install build dependencies
167+
if: runner.os == 'Linux'
168+
run: |
169+
sudo apt update
170+
sudo apt install libcurl4-openssl-dev
171+
160172
- uses: actions/setup-dotnet@v3
161173
with:
162174
dotnet-version: |

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ test_output/
2525
test/**/*.apk
2626
/tools/
2727
*.log
28+
.sentry-native

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- Native crash reporting on NativeAOT published apps (Windows, Linux, macOS). ([#2887](https://github.com/getsentry/sentry-dotnet/pull/2887))
8+
59
### API breaking Changes
610

711
#### Removed APIs

Diff for: CONTRIBUTING.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@ For big feature it's advised to raise an issue to discuss it first.
1414
## Dependencies
1515

1616
* The latest versions of the following .NET SDKs:
17-
- [.NET 7.0](https://dotnet.microsoft.com/download/dotnet/7.0)
17+
- [.NET 8.0](https://dotnet.microsoft.com/download/dotnet/8.0)
1818
- [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0)
1919

20-
*Technically, you only need the full SDK install for the latest version (7.0). If you like, you can install the smaller ASP.NET Core Runtime packages for .NET 6.0 . However, installing the full SDKs will also give you the runtimes.*
20+
*Technically, you only need the full SDK install for the latest version (8.0). If you like, you can install the smaller ASP.NET Core Runtime packages for .NET 6.0 . However, installing the full SDKs will also give you the runtimes.*
2121

2222
*If using an M1 ("Apple silicon") processor, read [the special instructions below](#special-instructions-for-apple-silicon-cpus).*
2323

24+
* You'll need `CMake` somewhere on your PATH. If you don't already have this, one way to get it is to install the [C++ CMake tools for Windows](https://learn.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170#installation)
25+
26+
* You'll need [`pwsh`](https://github.com/PowerShell/PowerShell#get-powershell) on PATH.
27+
2428
* On Windows:
2529
- [.NET Framework](https://dotnet.microsoft.com/download/dotnet-framework) 4.6.2 or higher.
26-
- You'll need `CMake` somewhere on your PATH. If you don't already have this, one way to get it is to install the [C++ CMake tools for Windows](https://learn.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170#installation)
2730
- `Sentry.DiagnosticSource.IntegrationTests.csproj` uses [SQL LocalDb](https://docs.microsoft.com/sql/database-engine/configure-windows/sql-server-express-localdb) - [download SQL LocalDB 2019](https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi). To avoid running these tests, unload `Sentry.DiagnosticSource.IntegrationTests.csproj` from the solution.
2831
* On macOS/Linux
2932
- [Mono 6 or higher](https://www.mono-project.com/download/stable) to run the unit tests on the `net4x` targets.

Diff for: Sentry-CI-Build-Linux.slnf

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj",
1515
"samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj",
1616
"samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj",
17+
"samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj",
1718
"samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj",
1819
"samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj",
1920
"samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj",

Diff for: Sentry-CI-Build-Windows.slnf

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj",
1414
"samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj",
1515
"samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj",
16+
"samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj",
1617
"samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj",
1718
"samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj",
1819
"samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj",

Diff for: Sentry-CI-Build-macOS.slnf

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj",
1616
"samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj",
1717
"samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj",
18+
"samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj",
1819
"samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj",
1920
"samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj",
2021
"samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj",

Diff for: Sentry.NoMobile.sln

+10-3
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TraceEvent", "modules\perfv
161161
EndProject
162162
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastSerialization", "modules\perfview\src\FastSerialization\FastSerialization.csproj", "{8032310D-3C06-442C-A318-F365BCC4C804}"
163163
EndProject
164+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Native", "samples\Sentry.Samples.Console.Native\Sentry.Samples.Console.Native.csproj", "{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}"
165+
EndProject
164166
Global
165167
GlobalSection(SolutionConfigurationPlatforms) = preSolution
166168
Debug|Any CPU = Debug|Any CPU
167169
Release|Any CPU = Release|Any CPU
168170
EndGlobalSection
169-
GlobalSection(SolutionProperties) = preSolution
170-
HideSolutionNode = FALSE
171-
EndGlobalSection
172171
GlobalSection(ProjectConfigurationPlatforms) = postSolution
173172
{8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
174173
{8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
@@ -462,6 +461,13 @@ Global
462461
{8032310D-3C06-442C-A318-F365BCC4C804}.Debug|Any CPU.Build.0 = Debug|Any CPU
463462
{8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.ActiveCfg = Release|Any CPU
464463
{8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.Build.0 = Release|Any CPU
464+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
465+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
466+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
467+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU
468+
EndGlobalSection
469+
GlobalSection(SolutionProperties) = preSolution
470+
HideSolutionNode = FALSE
465471
EndGlobalSection
466472
GlobalSection(NestedProjects) = preSolution
467473
{8328B70C-B808-4ED1-BB16-8555B2752CB6} = {7D4D7A6A-3F5C-4B4C-A198-AC51F9220231}
@@ -538,5 +544,6 @@ Global
538544
{162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D}
539545
{67269916-C417-4CEE-BD7D-CA66C3830AEE} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA}
540546
{8032310D-3C06-442C-A318-F365BCC4C804} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA}
547+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2} = {21B42F60-5802-404E-90F0-AEBCC56760C0}
541548
EndGlobalSection
542549
EndGlobal

Diff for: Sentry.sln

+10-3
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TraceEvent", "modules\perfv
161161
EndProject
162162
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastSerialization", "modules\perfview\src\FastSerialization\FastSerialization.csproj", "{8032310D-3C06-442C-A318-F365BCC4C804}"
163163
EndProject
164+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Native", "samples\Sentry.Samples.Console.Native\Sentry.Samples.Console.Native.csproj", "{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}"
165+
EndProject
164166
Global
165167
GlobalSection(SolutionConfigurationPlatforms) = preSolution
166168
Debug|Any CPU = Debug|Any CPU
167169
Release|Any CPU = Release|Any CPU
168170
EndGlobalSection
169-
GlobalSection(SolutionProperties) = preSolution
170-
HideSolutionNode = FALSE
171-
EndGlobalSection
172171
GlobalSection(ProjectConfigurationPlatforms) = postSolution
173172
{8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
174173
{8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
@@ -462,6 +461,13 @@ Global
462461
{8032310D-3C06-442C-A318-F365BCC4C804}.Debug|Any CPU.Build.0 = Debug|Any CPU
463462
{8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.ActiveCfg = Release|Any CPU
464463
{8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.Build.0 = Release|Any CPU
464+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
465+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
466+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
467+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU
468+
EndGlobalSection
469+
GlobalSection(SolutionProperties) = preSolution
470+
HideSolutionNode = FALSE
465471
EndGlobalSection
466472
GlobalSection(NestedProjects) = preSolution
467473
{8328B70C-B808-4ED1-BB16-8555B2752CB6} = {7D4D7A6A-3F5C-4B4C-A198-AC51F9220231}
@@ -538,5 +544,6 @@ Global
538544
{162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D}
539545
{67269916-C417-4CEE-BD7D-CA66C3830AEE} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA}
540546
{8032310D-3C06-442C-A318-F365BCC4C804} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA}
547+
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2} = {21B42F60-5802-404E-90F0-AEBCC56760C0}
541548
EndGlobalSection
542549
EndGlobal

Diff for: SentryNoMobile.slnf

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj",
1414
"samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj",
1515
"samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj",
16+
"samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj",
1617
"samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj",
1718
"samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj",
1819
"samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj",

Diff for: integration-test/runtime.Tests.ps1

+57-23
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,31 @@ $ErrorActionPreference = 'Stop'
44
. $PSScriptRoot/common.ps1
55

66
Describe 'Console app NativeAOT (<framework>)' -ForEach @(
7-
@{ framework = "net8.0" }
7+
@{ framework = 'net8.0' }
88
) {
99
BeforeAll {
1010
$path = './console-app'
1111
DotnetNew 'console' $path $framework
12-
@"
12+
@'
1313
using Sentry;
1414
using Sentry.Extensibility;
1515
using Sentry.Protocol.Envelopes;
1616
1717
// Initialize the Sentry SDK. (It is not necessary to dispose it.)
1818
SentrySdk.Init(options =>
1919
{
20-
options.Dsn = "http://[email protected]:9999/123";
20+
options.Dsn = args[0];
2121
options.Debug = true;
2222
options.Transport = new FakeTransport();
2323
});
2424
25-
throw new ApplicationException("Something happened!");
25+
if (args.Length > 1 && !string.IsNullOrEmpty(args[1]))
26+
{
27+
#pragma warning disable CS0618
28+
var crashType = (CrashType)Enum.Parse(typeof(CrashType), args[1]);
29+
SentrySdk.CauseCrash(crashType);
30+
#pragma warning restore CS0618
31+
}
2632
2733
internal class FakeTransport : ITransport
2834
{
@@ -32,14 +38,7 @@ internal class FakeTransport : ITransport
3238
return Task.CompletedTask;
3339
}
3440
}
35-
"@ | Out-File $path/Program.cs
36-
37-
# Publish once, then run the executable in actual tests.
38-
dotnet publish console-app -c Release --nologo --framework $framework | ForEach-Object { Write-Host $_ }
39-
if ($LASTEXITCODE -ne 0)
40-
{
41-
throw "Failed to publish the test app project."
42-
}
41+
'@ | Out-File $path/Program.cs
4342

4443
function getConsoleAppPath()
4544
{
@@ -58,33 +57,49 @@ internal class FakeTransport : ITransport
5857
}
5958
}
6059

61-
function runConsoleApp([bool]$IsAOT = $true)
60+
function runConsoleApp([bool]$IsAOT = $true, [string]$CrashType = 'Managed', [string]$Dsn = 'http://[email protected]:9999/123')
6261
{
63-
$executable = $IsAOT ? { & (getConsoleAppPath) } : { dotnet run --project $path -c Release --framework $framework }
62+
if ($IsAOT)
63+
{
64+
$executable = getConsoleAppPath
65+
If (!(Test-Path $executable))
66+
{
67+
dotnet publish console-app -c Release --nologo --framework $framework | ForEach-Object { Write-Host $_ }
68+
if ($LASTEXITCODE -ne 0)
69+
{
70+
throw 'Failed to publish the test app project.'
71+
}
72+
}
73+
}
74+
else
75+
{
76+
$executable = "dotnet run --project $path -c Release --framework $framework"
77+
}
78+
$executable += " $Dsn $CrashType"
6479
Write-Host "::group::Executing $executable"
6580
try
6681
{
67-
$executable.Invoke() | ForEach-Object {
82+
Invoke-Expression $executable | ForEach-Object {
6883
Write-Host " $_"
6984
$_
7085
}
7186
}
7287
finally
7388
{
74-
Write-Host "::endgroup::"
89+
Write-Host '::endgroup::'
7590
}
7691
}
7792
}
7893

79-
It "sends native debug images" {
94+
It 'sends native debug images' {
8095
runConsoleApp | Should -AnyElementMatch '"debug_meta":{"images":\[{"type":"(pe|elf|macho)","image_addr":"0x[a-f0-9]+","image_size":[0-9]+,"debug_id":"[a-f0-9\-]+"'
8196
}
8297

83-
It "sends stack trace native addresses" {
98+
It 'sends stack trace native addresses' {
8499
runConsoleApp | Should -AnyElementMatch '"stacktrace":{"frames":\[{"image_addr":"0x[a-f0-9]+","instruction_addr":"0x[a-f0-9]+"}'
85100
}
86101

87-
It "publish directory contains expected files" {
102+
It 'publish directory contains expected files' {
88103
$path = getConsoleAppPath
89104
Test-Path $path | Should -BeTrue
90105
$items = Get-ChildItem -Path (Get-Item $path).DirectoryName
@@ -101,6 +116,25 @@ internal class FakeTransport : ITransport
101116
It "'dotnet run' produces an app that's recognized as JIT by Sentry" {
102117
runConsoleApp $false | Should -AnyElementMatch 'This looks like a standard JIT/AOT application build.'
103118
}
119+
120+
It 'Produces the expected exception (Managed, AOT=<_>)' -ForEach @($true, $false) {
121+
runConsoleApp $_ 'Managed' | Should -AnyElementMatch '{"type":"System.ApplicationException","value":"This exception was caused deliberately by SentrySdk.CauseCrash\(CrashType.Managed\)."'
122+
}
123+
124+
It 'Produces the expected exception (Native)' {
125+
# The first run triggers a native error. This error is captured by sentry-native and stored stored for the next run.
126+
runConsoleApp $true 'Native' | Should -AnyElementMatch 'Triggering a deliberate exception'
127+
128+
# On the next run, we use a mock Sentry HTTP server to receive the native crash.
129+
$result = Invoke-SentryServer {
130+
Param([string]$url)
131+
runConsoleApp $true '' ($url.Replace('http://', 'http://key@') + '/0')
132+
}
133+
$result.HasErrors() | Should -BeFalse
134+
$result.ScriptOutput | Should -AnyElementMatch "Native SDK reported: 'crashedLastRun': 'True'"
135+
$type = $IsWindows ? 'EXCEPTION_ACCESS_VIOLATION' : 'SIGSEGV'
136+
$result.Envelopes() | Should -AnyElementMatch "`"exception`":{`"values`":\[{`"type`":`"$type`""
137+
}
104138
}
105139

106140
# This ensures we don't have a regression for https://github.com/getsentry/sentry-dotnet/issues/2825
@@ -109,25 +143,25 @@ Describe 'Console app regression (missing System.Reflection.Metadata)' {
109143
dotnet remove ./net4-console/console-app.csproj package Sentry
110144
}
111145

112-
It "Ensure System.Reflection.Metadata is not missing" {
146+
It 'Ensure System.Reflection.Metadata is not missing' {
113147
$path = './net4-console'
114148
Remove-Item -Recurse -Force -Path @("$path/bin", "$path/obj") -ErrorAction SilentlyContinue
115149
AddPackageReference $path 'Sentry'
116150

117151
function runConsoleApp()
118152
{
119-
$executable = { dotnet run --project $path -c Release }
153+
$executable = "dotnet run --project $path -c Release"
120154
Write-Host "::group::Executing $executable"
121155
try
122156
{
123-
$executable.Invoke() | ForEach-Object {
157+
Invoke-Expression $executable | ForEach-Object {
124158
Write-Host " $_"
125159
$_
126160
}
127161
}
128162
finally
129163
{
130-
Write-Host "::endgroup::"
164+
Write-Host '::endgroup::'
131165
}
132166
}
133167

Diff for: samples/Sentry.Samples.Console.Native/Program.cs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* This sample demonstrates a native crash handling in a NativeAOT published application.
3+
*/
4+
using Sentry;
5+
6+
// Initialize the Sentry SDK. (It is not necessary to dispose it.)
7+
SentrySdk.Init(options =>
8+
{
9+
// A Sentry Data Source Name (DSN) is required.
10+
// See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
11+
// You can set it in the SENTRY_DSN environment variable, or you can set it in code here.
12+
// options.Dsn = "... Your DSN ...";
13+
14+
// When debug is enabled, the Sentry client will emit detailed debugging information to the console.
15+
// This might be helpful, or might interfere with the normal operation of your application.
16+
// We enable it here for demonstration purposes.
17+
// You should not do this in your applications unless you are troubleshooting issues with Sentry.
18+
options.Debug = true;
19+
});
20+
21+
await FirstFunctionAsync();
22+
23+
async Task FirstFunctionAsync()
24+
{
25+
await Task.Delay(100);
26+
await SecondFunctionAsync();
27+
}
28+
29+
async Task SecondFunctionAsync()
30+
{
31+
await Task.Delay(100);
32+
#pragma warning disable CS0618
33+
SentrySdk.CauseCrash(CrashType.Native);
34+
#pragma warning restore CS0618
35+
}

0 commit comments

Comments
 (0)