kstenerud / KSCrash

The Ultimate iOS Crash Reporter
MIT License
4.23k stars 705 forks source link

Filter out NSException in cxa_throw #493

Closed xuezhulian closed 2 months ago

xuezhulian commented 3 months ago

There are numerous C++ exceptions in the iOS system that are caught and then rethrown by the system, or caught and directly terminated. Therefore, obtaining a C++ crash stack trace on iOS is relatively troublesome. A common solution is to hook __cxa_throw.

In KSCrash, there are two ways to implement the hook. When g_cxaSwapEnabled is true, a technique similar to fishhook or dyld interposing is used to implement the hook. At the same time, __cxa_throw is also overridden. This method only works for the main executable file. When obtaining the stack trace in the hooked __cxa_throw method, it is stored in a global variable and is not thread-local. When multiple crashes are triggered, incorrect stack traces may be obtained. Therefore, it is necessary to reduce method reentry for __cxa_throw.

For NSException, the stack trace can be obtained through system APIs, and it is not necessary to obtain it in __cxa_throw. Therefore, this filtering is valuable and also provides some performance optimization.

GLinnik21 commented 3 months ago

Please provide a detailed description of the problem. I am particularly interested in the monitors and flags used in the installation.

xuezhulian commented 3 months ago

Additionally, even if we hook __cxa_throw, due to the dyld shared cache, some system libraries will still fail, resulting in some C++ stack traces being unobtainable. I am currently working on solving this issue, and once the solution is stable and reliable, I will discuss it with you further.

GLinnik21 commented 3 months ago

It looks like __attribute__ ((weak)) doesn't work with SPM, probably because of Clang modules. When only CocoaPods was used and a podspec was a single module, this attribute worked fine. It does work with SPM if you remove the attribute, but that can cause symbol duplicates, which isn't ideal. So, the only good method left for SPM seems to be a fishhook-like swap. Both @bamx23 and I are thinking of enabling this by default for C++. The latest update has made this solution more flexible, keeping up with Apple's constant changes to the Mach-O layout.

xuezhulian commented 3 months ago

👍🏻 In fact, we've been using fishhook to replace __cxa_throw for quite some time now. Here's a suggestion: don't perform this operation at startup, as it might trigger the watchdog.

GLinnik21 commented 3 months ago

Here's a suggestion: don't perform this operation at startup, as it might trigger the watchdog.

We thought about moving it to a separate thread. Would that help with startup performance?

xuezhulian commented 3 months ago

The approach I'm currently using involves an asynchronous thread with a delay. Fishhook frequently calls dyld's APIs, which use global locks. During the app startup process, loading dyld also involves these locks. Executing fishhook during this process might cause the main thread to wait for these locks.

GLinnik21 commented 3 months ago

For some reason, my checks show that accessing symbols and checking memory protection takes up most of the time.

image
xuezhulian commented 3 months ago

Due to time constraints, I am unable to locate the previous watchdog stack trace. From what I remember, this issue has never been reproduced offline, and the lengthy duration is caused by the page fault.

GLinnik21 commented 2 months ago

@xuezhulian let's fix PR checks and merge

xuezhulian commented 2 months ago

@GLinnik21 @bamx23 Thank you for your modification suggestions. It seems that the lint failure is due to a timeout, but I haven't found an option to retry.

bamx23 commented 2 months ago

@xuezhulian thanks for your contribution!