Skip to content

feat: Capturing fatal CPPExceptions via cxa_throw #5256

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

Open
wants to merge 23 commits into
base: main
Choose a base branch
from

Conversation

philipphofmann
Copy link
Member

@philipphofmann philipphofmann commented May 20, 2025

📜 Description

Use fishhook to swap the __cxa_throw C++ exception handler for getting
stacktraces for unhandled C++ exceptions. This implementation is based
on the code in https://github.com/kstenerud/KSCrash.

💡 Motivation and Context

Fixes GH-4517

💚 How did you test it?

Testing Details macOS sample

Without the feature

Screenshot 2025-05-20 at 09 00 00

https://sentry-sdks.sentry.io/issues/6590753526/events/d1bb9784083e40a8abfdb28ac2b9c34e/?project=5428557

With the feature

Invalid Argument

void
internalFunction(void)
{
throw std::invalid_argument("Internal function must not be called.");
}

Screenshot 2025-05-20 at 09 02 18 https://sentry-sdks.sentry.io/issues/6545237791/events/a393feb8d8b2401c8d6cedca5ce181a4/?project=5428557

rethrowNoActiveCPPException

void
Sentry::CppSample::rethrowNoActiveCPPException(void)
{
// Rethrowing an exception when there is no active exception will lead to std::terminate being
// called.
throw;
}

Screenshot 2025-05-20 at 09 06 59

https://sentry-sdks.sentry.io/issues/6617441025/events/30bc9db8d17b4dcd80d6473c57a3c36f/?project=5428557

C++ Exception from BG Thread

Screenshot 2025-05-20 at 09 08 15

https://sentry-sdks.sentry.io/issues/6565152294/events/691f845d8dc04305b0cf376e2d21732b/?project=5428557

NoExcept C++ Exception

void
Sentry::CppSample::noExceptCppException() noexcept
{
throw std::invalid_argument("Invalid Argument.");
}

Screenshot 2025-05-20 at 09 10 17

https://sentry-sdks.sentry.io/issues/6565163235/events/d9dc157068994ed2879842dbaf5f477b/?project=5428557

Unhandled C++Exception from testflight iOS-Swift
https://sentry-sdks.sentry.io/issues/6623122757/events/85451edf3f324ad98a6293f1e2cc4b55/?project=5428557

Screenshot 2025-05-22 at 10 12 16

📝 Checklist

You have to check all boxes before merging:

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

Use fishhook to swap the __cxa_throw C++ exception handler for getting
stacktraces for unhandled C++ exceptions. This implementation is based
on the code in https://github.com/kstenerud/KSCrash.

Fixes GH-4517
Copy link
Contributor

github-actions bot commented May 20, 2025

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 877a487

Copy link

codecov bot commented May 20, 2025

Codecov Report

Attention: Patch coverage is 92.85714% with 63 lines in your changes missing coverage. Please review.

Project coverage is 92.892%. Comparing base (9299104) to head (877a487).

Files with missing lines Patch % Lines
...Crash/Recording/Tools/SentryCrashCxaThrowSwapper.c 84.210% 39 Missing ⚠️
...ts/SentryCrash/SentryCrashCxaThrowSwapper_Tests.mm 91.011% 16 Missing ⚠️
...rding/Monitors/SentryCrashMonitor_CPPException.cpp 73.076% 7 Missing ⚠️
...ntryCrash/SentryCrashMonitor_CppException_Tests.mm 96.666% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@              Coverage Diff              @@
##              main     #5256       +/-   ##
=============================================
- Coverage   92.909%   92.892%   -0.018%     
=============================================
  Files          688       692        +4     
  Lines        86326     87075      +749     
  Branches     31213     31425      +212     
=============================================
+ Hits         80205     80886      +681     
- Misses        6021      6088       +67     
- Partials       100       101        +1     
Files with missing lines Coverage Δ
Sources/Sentry/SentryCrashIntegration.m 99.186% <100.000%> (+0.027%) ⬆️
Sources/Sentry/include/SentryCompiler.h 100.000% <100.000%> (ø)
...es/SentryCrash/Recording/Tools/SentryCrashMach-O.c 100.000% <100.000%> (ø)
...cording/Tools/SentryCrashPlatformSpecificDefines.h 100.000% <100.000%> (ø)
Sources/Swift/Core/SentryExperimentalOptions.swift 60.000% <100.000%> (+10.000%) ⬆️
...es/Swift/Helper/SentryEnabledFeaturesBuilder.swift 100.000% <100.000%> (ø)
...sts/Helper/SentryEnabledFeaturesBuilderTests.swift 100.000% <100.000%> (ø)
...ions/SentryCrash/SentryCrashIntegrationTests.swift 92.598% <100.000%> (+0.374%) ⬆️
...s/SentryTests/SentryCrash/SentryCrashMach-OTests.m 100.000% <100.000%> (ø)
...ntryCrash/SentryCrashMonitor_CppException_Tests.mm 94.392% <96.666%> (+0.886%) ⬆️
... and 3 more

... and 23 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9299104...877a487. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

github-actions bot commented May 20, 2025

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1224.49 ms 1239.52 ms 15.03 ms
Size 23.76 KiB 825.23 KiB 801.47 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
deeb22c 1233.90 ms 1250.19 ms 16.29 ms
fb7eac9 1236.33 ms 1245.08 ms 8.76 ms
e778bd2 1224.66 ms 1252.16 ms 27.50 ms
a0bb631 1200.55 ms 1214.20 ms 13.65 ms
fa38a2b 1217.92 ms 1235.52 ms 17.60 ms
df50619 1227.12 ms 1243.49 ms 16.37 ms
533c68f 1236.54 ms 1256.68 ms 20.14 ms
b2c9166 1246.86 ms 1255.28 ms 8.42 ms
a6f8b18 1238.54 ms 1265.56 ms 27.02 ms
83acf3e 1240.06 ms 1261.38 ms 21.31 ms

App size

Revision Plain With Sentry Diff
deeb22c 21.58 KiB 612.11 KiB 590.53 KiB
fb7eac9 23.76 KiB 861.22 KiB 837.46 KiB
e778bd2 20.76 KiB 426.15 KiB 405.39 KiB
a0bb631 21.58 KiB 546.19 KiB 524.61 KiB
fa38a2b 21.58 KiB 418.70 KiB 397.12 KiB
df50619 21.58 KiB 671.58 KiB 650.00 KiB
533c68f 21.58 KiB 630.28 KiB 608.70 KiB
b2c9166 21.58 KiB 630.26 KiB 608.68 KiB
a6f8b18 20.76 KiB 431.87 KiB 411.11 KiB
83acf3e 21.58 KiB 539.87 KiB 518.29 KiB

Previous results on branch: fix/cpp-exceptions-v3

Startup times

Revision Plain With Sentry Diff
fa4ec70 1233.90 ms 1253.20 ms 19.30 ms
12a2c05 1213.24 ms 1232.41 ms 19.16 ms
1a55563 1219.78 ms 1245.86 ms 26.08 ms
a87cd4b 1225.20 ms 1245.16 ms 19.96 ms
0143072 1219.62 ms 1252.60 ms 32.98 ms
e102ea7 1239.84 ms 1254.65 ms 14.82 ms

App size

Revision Plain With Sentry Diff
fa4ec70 23.76 KiB 825.20 KiB 801.44 KiB
12a2c05 23.76 KiB 825.23 KiB 801.47 KiB
1a55563 23.76 KiB 825.24 KiB 801.48 KiB
a87cd4b 23.76 KiB 825.30 KiB 801.55 KiB
0143072 23.76 KiB 825.26 KiB 801.49 KiB
e102ea7 23.76 KiB 825.28 KiB 801.51 KiB

@philipphofmann philipphofmann self-assigned this May 21, 2025
@philipphofmann philipphofmann marked this pull request as ready for review May 22, 2025 08:20
@philprime
Copy link
Contributor

I'll wait for @supervacuus feedback on your PR comment before doing the review in case it needs another revision first.

@philipphofmann
Copy link
Member Author

I'll wait for @supervacuus feedback on your PR comment before doing the review in case it needs another revision first.

This is mostly native stuff. I would wait for the review of @supervacuus before adding your review.

Copy link

@supervacuus supervacuus left a comment

Choose a reason for hiding this comment

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

Quick update: I have already started "investigating" the changes. I hope that my response to your comment makes sense. This conversation should ideally lead to inline comments.

I am still in the process, but most parts look relatively sane. A first questionable impression is the lack of thread synchronization, considering that we access quite a few globals. cxa_throw itself can and will be called from multiple threads simultaneously, which might be less of a problem if we only read. However, there is still a chance that setup and teardown happen concurrently. Did you consider these possible interactions already? It could be fine if you were sure to delineate reads and writes with certainty, but that is rarely the case.

Copy link

@supervacuus supervacuus left a comment

Choose a reason for hiding this comment

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

This looks great—only minor things. The only unanswered aspect I currently see is that during sentry initialization, when you rebind __cxa_throw with your decorator, another thread could already be throwing and entering your decorator. At the same time, the g_cxa_originals structure is still not finished.

The problem with that is that we must call an "original"; otherwise, we crash. We can either guarantee that the SDK initialization is completed before any other thread can throw, or we create an explicit abort() path that logs the situation, so users aren't confronted with an indecipherable crash in this scenario. And to be clear, this is an edge case, but still a race.

@philipphofmann
Copy link
Member Author

I have no clue why, but the unit tests for Mac Catalyst keep failing. I need to figure out why that is before merging this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve C++ Exception Handling with Fishhook
4 participants