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

feat: Capture async stack traces #998

Merged
merged 36 commits into from
Mar 31, 2021
Merged

feat: Capture async stack traces #998

merged 36 commits into from
Mar 31, 2021

Conversation

Swatinem
Copy link
Member

@Swatinem Swatinem commented Mar 12, 2021

📜 Description

libdispatch on Apple has its own introspection support. However in order to use that, the user has to actually load the introspection capable version of the library, which is the case when doing debugging in XCode, but is not the case for normal runtime.

See https://github.com/apple/swift-corelibs-libdispatch/blob/main/dispatch/introspection.h

Instead, we rely on https://github.com/facebook/fishhook to replace the async libdispatch functions with our own wrappers, similar to how https://github.com/tiantianbobo/XBAsyncStackTrace works.

Creating the async stack traces is based on these hooks. We capture a stacktrace in the caller, and pass that trace into the async context via an async closure (block). There, we save it in a hashtable keyed by the thread id.
We use that "thread-local" to chain multiple async calls together, and also to attach the async caller to any thread that is being captured by SentryCrash.

When SentryCrash is walking (serializing) the stacktrace, we chain those async callers together.

💡 Motivation and Context

When done, this will allow us to capture the chained stacktrace of async dispatched blocks/functions.

💚 How did you test it?

📝 Checklist

  • I reviewed the submitted code
  • I added tests to verify the changes
  • I updated the CHANGELOG if needed
  • I updated the docs if needed
  • Review from the native team if needed
  • No breaking changes

🔮 Next steps

fixes #852

libdispatch on Apple has its own introspection support. However in order to use that, the user has to actually load the introspection capable version of the library, which is the case when doing debugging in XCode, but is not the case for normal runtime.

See https://github.com/apple/swift-corelibs-libdispatch/blob/main/dispatch/introspection.h

Instead, we rely on https://github.com/facebook/fishhook to replace the async libdispatch functions with our own wrappers, similar to how https://github.com/tiantianbobo/XBAsyncStackTrace works.
@philipphofmann
Copy link
Member

philipphofmann commented Mar 17, 2021

Testing session

Mac OS

Objective-C Sample

iOS-Swift

tvOS-Swift

@codecov-io
Copy link

codecov-io commented Mar 17, 2021

Codecov Report

Merging #998 (935b57a) into master (c9b7cbe) will increase coverage by 0.14%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #998      +/-   ##
==========================================
+ Coverage   95.08%   95.23%   +0.14%     
==========================================
  Files          88       88              
  Lines        3787     3795       +8     
==========================================
+ Hits         3601     3614      +13     
+ Misses        186      181       -5     
Impacted Files Coverage Δ
Sources/Sentry/SentryCrashIntegration.m 100.00% <100.00%> (ø)
Sources/Sentry/SentryTransportFactory.m 100.00% <0.00%> (ø)
Sources/Sentry/SentryDispatchQueueWrapper.m 100.00% <0.00%> (ø)
Sources/Sentry/SentryRequestOperation.m 81.81% <0.00%> (+15.15%) ⬆️

Continue to review full report at Codecov.

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

@Swatinem
Copy link
Member Author

The crash on 14.5 beta seems to be an upstream problem: facebook/fishhook#82

@philipphofmann
Copy link
Member

iPad Air (4th gen) iOS 14.5 beta 4: seems to work fine now

@Swatinem Swatinem changed the title WIP: feat: Capture async stack traces feat: Capture async stack traces Mar 19, 2021
@Swatinem Swatinem marked this pull request as ready for review March 19, 2021 08:57
Swatinem added a commit to getsentry/sentry that referenced this pull request Mar 22, 2021
This feature will soon land as part of getsentry/sentry-cocoa#998 and the way it wraps async calls creates a few well-known stackframes which we want to classify as not in-app.
@philipphofmann
Copy link
Member

Testing session

Mac OS

Objective-C Sample

iOS-Swift

tvOS-Swift

  • Apple TV Arch:x86 Simulator works
  • Apple TV (4th generation) clicking the crash button leaves the app hanging. Doesn't work. Normal crash works though

@Swatinem Swatinem requested a review from a team March 23, 2021 09:59
Swatinem added a commit to getsentry/sentry that referenced this pull request Mar 23, 2021
This feature will soon land as part of getsentry/sentry-cocoa#998 and the way it wraps async calls creates a few well-known stackframes which we want to classify as not in-app.
Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

RSLGTM

@philipphofmann
Copy link
Member

Testing session

Mac OS

Objective-C Sample

iOS-Swift

tvOS-Swift

I had two cases where the device context was missing, but I'm not sure if this is related to this PR.

Copy link
Member

@philipphofmann philipphofmann left a comment

Choose a reason for hiding this comment

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

I think for an alpha this is good enough. Before GA we still need to figure out why on some devices the stacktrace is not complete and why the device data is sometimes missing. A review from someone from the native team is IMO required before merging this.

ahmedetefy pushed a commit to getsentry/sentry that referenced this pull request Mar 26, 2021
This feature will soon land as part of getsentry/sentry-cocoa#998 and the way it wraps async calls creates a few well-known stackframes which we want to classify as not in-app.
if (!bt) {
return;
}
__atomic_fetch_add(&bt->refcount, 1, __ATOMIC_SEQ_CST);
Copy link

Choose a reason for hiding this comment

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

i suspect SEQ_CST is overkill, but this is hard so probably fine to leave it until this is actually a perf bottleneck

Copy link
Member Author

Choose a reason for hiding this comment

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

We use basically the same all over the native SDK. Getting the memory orderings right is quite difficult, so IMO there is nothing wrong with being too conservative.

BTW, there is a nice series on these patterns on LWN which I’m reading these days: https://lwn.net/Articles/844224/

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.

Async Stack Traces for libdispatch
7 participants