Skip to content

Commit

Permalink
Feat: native crash reporting (#2887)
Browse files Browse the repository at this point in the history
Co-authored-by: Bruno Garcia <bruno@brunogarcia.com>
  • Loading branch information
vaind and bruno-garcia authored Nov 29, 2023
1 parent f473889 commit 217c11c
Show file tree
Hide file tree
Showing 31 changed files with 1,000 additions and 95 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ jobs:
key: sentry-native-${{ runner.os }}-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }}
enableCrossOsArchive: true

- name: Install build dependencies
if: steps.cache.outputs.cache-hit != 'true' && runner.os == 'Linux'
run: |
sudo apt update
sudo apt install libcurl4-openssl-dev
- run: scripts/build-sentry-native.ps1
if: steps.cache.outputs.cache-hit != 'true'
shell: pwsh
Expand Down Expand Up @@ -157,6 +163,12 @@ jobs:
name: ${{ github.sha }}
path: src

- name: Install build dependencies
if: runner.os == 'Linux'
run: |
sudo apt update
sudo apt install libcurl4-openssl-dev
- uses: actions/setup-dotnet@v3
with:
dotnet-version: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ test_output/
test/**/*.apk
/tools/
*.log
.sentry-native
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- Native crash reporting on NativeAOT published apps (Windows, Linux, macOS). ([#2887](https://github.com/getsentry/sentry-dotnet/pull/2887))

### API breaking Changes

#### Removed APIs
Expand Down
9 changes: 6 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ For big feature it's advised to raise an issue to discuss it first.
## Dependencies

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

*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.*
*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.*

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

* 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)

* You'll need [`pwsh`](https://github.com/PowerShell/PowerShell#get-powershell) on PATH.

* On Windows:
- [.NET Framework](https://dotnet.microsoft.com/download/dotnet-framework) 4.6.2 or higher.
- 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)
- `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.
* On macOS/Linux
- [Mono 6 or higher](https://www.mono-project.com/download/stable) to run the unit tests on the `net4x` targets.
Expand Down
1 change: 1 addition & 0 deletions Sentry-CI-Build-Linux.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj",
"samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj",
"samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj",
"samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj",
"samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj",
"samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj",
"samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj",
Expand Down
1 change: 1 addition & 0 deletions Sentry-CI-Build-Windows.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj",
"samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj",
"samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj",
"samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj",
"samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj",
"samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj",
"samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj",
Expand Down
1 change: 1 addition & 0 deletions Sentry-CI-Build-macOS.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj",
"samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj",
"samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj",
"samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj",
"samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj",
"samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj",
"samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj",
Expand Down
13 changes: 10 additions & 3 deletions Sentry.NoMobile.sln
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TraceEvent", "modules\perfv
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastSerialization", "modules\perfview\src\FastSerialization\FastSerialization.csproj", "{8032310D-3C06-442C-A318-F365BCC4C804}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
Expand Down Expand Up @@ -462,6 +461,13 @@ Global
{8032310D-3C06-442C-A318-F365BCC4C804}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.Build.0 = Release|Any CPU
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8328B70C-B808-4ED1-BB16-8555B2752CB6} = {7D4D7A6A-3F5C-4B4C-A198-AC51F9220231}
Expand Down Expand Up @@ -538,5 +544,6 @@ Global
{162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D}
{67269916-C417-4CEE-BD7D-CA66C3830AEE} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA}
{8032310D-3C06-442C-A318-F365BCC4C804} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA}
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2} = {21B42F60-5802-404E-90F0-AEBCC56760C0}
EndGlobalSection
EndGlobal
13 changes: 10 additions & 3 deletions Sentry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TraceEvent", "modules\perfv
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastSerialization", "modules\perfview\src\FastSerialization\FastSerialization.csproj", "{8032310D-3C06-442C-A318-F365BCC4C804}"
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8328B70C-B808-4ED1-BB16-8555B2752CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
Expand Down Expand Up @@ -462,6 +461,13 @@ Global
{8032310D-3C06-442C-A318-F365BCC4C804}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8032310D-3C06-442C-A318-F365BCC4C804}.Release|Any CPU.Build.0 = Release|Any CPU
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8328B70C-B808-4ED1-BB16-8555B2752CB6} = {7D4D7A6A-3F5C-4B4C-A198-AC51F9220231}
Expand Down Expand Up @@ -538,5 +544,6 @@ Global
{162A1CAE-ACEE-45CA-A6D0-7702ADE4D3DE} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D}
{67269916-C417-4CEE-BD7D-CA66C3830AEE} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA}
{8032310D-3C06-442C-A318-F365BCC4C804} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA}
{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2} = {21B42F60-5802-404E-90F0-AEBCC56760C0}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions SentryNoMobile.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj",
"samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj",
"samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj",
"samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj",
"samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj",
"samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj",
"samples\\Sentry.Samples.GenericHost\\Sentry.Samples.GenericHost.csproj",
Expand Down
80 changes: 57 additions & 23 deletions integration-test/runtime.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,31 @@ $ErrorActionPreference = 'Stop'
. $PSScriptRoot/common.ps1

Describe 'Console app NativeAOT (<framework>)' -ForEach @(
@{ framework = "net8.0" }
@{ framework = 'net8.0' }
) {
BeforeAll {
$path = './console-app'
DotnetNew 'console' $path $framework
@"
@'
using Sentry;
using Sentry.Extensibility;
using Sentry.Protocol.Envelopes;
// Initialize the Sentry SDK. (It is not necessary to dispose it.)
SentrySdk.Init(options =>
{
options.Dsn = "http://key@127.0.0.1:9999/123";
options.Dsn = args[0];
options.Debug = true;
options.Transport = new FakeTransport();
});
throw new ApplicationException("Something happened!");
if (args.Length > 1 && !string.IsNullOrEmpty(args[1]))
{
#pragma warning disable CS0618
var crashType = (CrashType)Enum.Parse(typeof(CrashType), args[1]);
SentrySdk.CauseCrash(crashType);
#pragma warning restore CS0618
}
internal class FakeTransport : ITransport
{
Expand All @@ -32,14 +38,7 @@ internal class FakeTransport : ITransport
return Task.CompletedTask;
}
}
"@ | Out-File $path/Program.cs

# Publish once, then run the executable in actual tests.
dotnet publish console-app -c Release --nologo --framework $framework | ForEach-Object { Write-Host $_ }
if ($LASTEXITCODE -ne 0)
{
throw "Failed to publish the test app project."
}
'@ | Out-File $path/Program.cs

function getConsoleAppPath()
{
Expand All @@ -58,33 +57,49 @@ internal class FakeTransport : ITransport
}
}

function runConsoleApp([bool]$IsAOT = $true)
function runConsoleApp([bool]$IsAOT = $true, [string]$CrashType = 'Managed', [string]$Dsn = 'http://key@127.0.0.1:9999/123')
{
$executable = $IsAOT ? { & (getConsoleAppPath) } : { dotnet run --project $path -c Release --framework $framework }
if ($IsAOT)
{
$executable = getConsoleAppPath
If (!(Test-Path $executable))
{
dotnet publish console-app -c Release --nologo --framework $framework | ForEach-Object { Write-Host $_ }
if ($LASTEXITCODE -ne 0)
{
throw 'Failed to publish the test app project.'
}
}
}
else
{
$executable = "dotnet run --project $path -c Release --framework $framework"
}
$executable += " $Dsn $CrashType"
Write-Host "::group::Executing $executable"
try
{
$executable.Invoke() | ForEach-Object {
Invoke-Expression $executable | ForEach-Object {
Write-Host " $_"
$_
}
}
finally
{
Write-Host "::endgroup::"
Write-Host '::endgroup::'
}
}
}

It "sends native debug images" {
It 'sends native debug images' {
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\-]+"'
}

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

It "publish directory contains expected files" {
It 'publish directory contains expected files' {
$path = getConsoleAppPath
Test-Path $path | Should -BeTrue
$items = Get-ChildItem -Path (Get-Item $path).DirectoryName
Expand All @@ -101,6 +116,25 @@ internal class FakeTransport : ITransport
It "'dotnet run' produces an app that's recognized as JIT by Sentry" {
runConsoleApp $false | Should -AnyElementMatch 'This looks like a standard JIT/AOT application build.'
}

It 'Produces the expected exception (Managed, AOT=<_>)' -ForEach @($true, $false) {
runConsoleApp $_ 'Managed' | Should -AnyElementMatch '{"type":"System.ApplicationException","value":"This exception was caused deliberately by SentrySdk.CauseCrash\(CrashType.Managed\)."'
}

It 'Produces the expected exception (Native)' {
# The first run triggers a native error. This error is captured by sentry-native and stored stored for the next run.
runConsoleApp $true 'Native' | Should -AnyElementMatch 'Triggering a deliberate exception'

# On the next run, we use a mock Sentry HTTP server to receive the native crash.
$result = Invoke-SentryServer {
Param([string]$url)
runConsoleApp $true '' ($url.Replace('http://', 'http://key@') + '/0')
}
$result.HasErrors() | Should -BeFalse
$result.ScriptOutput | Should -AnyElementMatch "Native SDK reported: 'crashedLastRun': 'True'"
$type = $IsWindows ? 'EXCEPTION_ACCESS_VIOLATION' : 'SIGSEGV'
$result.Envelopes() | Should -AnyElementMatch "`"exception`":{`"values`":\[{`"type`":`"$type`""
}
}

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

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

function runConsoleApp()
{
$executable = { dotnet run --project $path -c Release }
$executable = "dotnet run --project $path -c Release"
Write-Host "::group::Executing $executable"
try
{
$executable.Invoke() | ForEach-Object {
Invoke-Expression $executable | ForEach-Object {
Write-Host " $_"
$_
}
}
finally
{
Write-Host "::endgroup::"
Write-Host '::endgroup::'
}
}

Expand Down
35 changes: 35 additions & 0 deletions samples/Sentry.Samples.Console.Native/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* This sample demonstrates a native crash handling in a NativeAOT published application.
*/
using Sentry;

// Initialize the Sentry SDK. (It is not necessary to dispose it.)
SentrySdk.Init(options =>
{
// A Sentry Data Source Name (DSN) is required.
// See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
// You can set it in the SENTRY_DSN environment variable, or you can set it in code here.
// options.Dsn = "... Your DSN ...";
// When debug is enabled, the Sentry client will emit detailed debugging information to the console.
// This might be helpful, or might interfere with the normal operation of your application.
// We enable it here for demonstration purposes.
// You should not do this in your applications unless you are troubleshooting issues with Sentry.
options.Debug = true;
});

await FirstFunctionAsync();

async Task FirstFunctionAsync()
{
await Task.Delay(100);
await SecondFunctionAsync();
}

async Task SecondFunctionAsync()
{
await Task.Delay(100);
#pragma warning disable CS0618
SentrySdk.CauseCrash(CrashType.Native);
#pragma warning restore CS0618
}
Loading

0 comments on commit 217c11c

Please sign in to comment.