Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7a09c89
feat: add iOS/macOS fatal hang detection (#51)
bobbyg603 Apr 18, 2026
d56d530
fix: hang demo blocks forever with confirmation dialog
bobbyg603 Apr 18, 2026
5979733
fix: avoid deadlocking the hang watchdog when main is actually hung
bobbyg603 Apr 18, 2026
9b5229f
docs: note simulator / macOS termination in hang dialogs
bobbyg603 Apr 18, 2026
43bae9e
fix: capture main thread in hang report; remove C++ UB in hang demo
bobbyg603 May 16, 2026
928aae1
fix: discard orphan hang crash on meta-write failure; doc fixes
bobbyg603 May 16, 2026
7525e3e
feat(samples-swiftui): rebuild demo UI to match Android refresh
bobbyg603 May 16, 2026
401a7f2
feat(samples-uikit): rebuild UIKit-Swift + UIKit-ObjC demo UIs
bobbyg603 May 16, 2026
38c33cb
fix(samples-uikit): pin wordmark width to aspect ratio
bobbyg603 May 16, 2026
9f5e5ef
feat(samples-macos): rebuild demo UI as desktop variant
bobbyg603 May 16, 2026
f9feed5
fix(samples-macos): tighten top spacing
bobbyg603 May 16, 2026
85266d3
fix(samples-objc): make crash card actually crash
bobbyg603 May 16, 2026
fb63a07
fix(samples): hang demo now actually demos a fatal hang upload
bobbyg603 May 16, 2026
8da7b19
fix(samples-macos): make splat-keyboard gesture fire on succession
bobbyg603 May 16, 2026
190bae2
fix(samples): suppress splat during feedback; don't record empty subm…
bobbyg603 May 16, 2026
cab5c31
feat(samples-ios): wire up shake-to-feedback on all three samples
bobbyg603 May 16, 2026
0271d47
fix(samples-macos): splat tuning, Edit menu, cap recent activity
bobbyg603 May 16, 2026
a0fd613
fix(hang-detector): widen suspension-overshoot guard; per-thread stop…
bobbyg603 May 18, 2026
376cbc9
Merge remote-tracking branch 'origin/feat/ios-hang-detection' into fe…
bobbyg603 May 18, 2026
b3e6376
feat(hang-detector): expose hangDetectionThreshold as a public property
bobbyg603 May 18, 2026
8b2186c
Merge remote-tracking branch 'origin/feat/ios-hang-detection' into fe…
bobbyg603 May 18, 2026
0db91fc
fix(hang-detector): skip suspension guard on the first poll
bobbyg603 May 18, 2026
9993ccc
Merge remote-tracking branch 'origin/feat/ios-hang-detection' into fe…
bobbyg603 May 18, 2026
743dacb
test(hang-detector): widen timing for parallel CI runners
bobbyg603 May 18, 2026
71629a4
Merge remote-tracking branch 'origin/feat/ios-hang-detection' into fe…
bobbyg603 May 18, 2026
0855d84
ci(tests): disable parallel testing for iOS suite
bobbyg603 May 18, 2026
b8bd721
Merge remote-tracking branch 'origin/feat/ios-hang-detection' into fe…
bobbyg603 May 18, 2026
ae5d701
refactor(hang-detector): inject clock + recovery dispatcher; determin…
bobbyg603 May 18, 2026
64f6072
Merge remote-tracking branch 'origin/feat/ios-hang-detection' into fe…
bobbyg603 May 18, 2026
a03859e
refactor(hang-detector): drop runloop observer for the ping pattern
bobbyg603 May 18, 2026
ca37130
refactor(hang-detector): drop runloop observer for the ping pattern
bobbyg603 May 18, 2026
7195793
Merge remote-tracking branch 'origin/feat/ios-hang-detection' into fe…
bobbyg603 May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion BugSplat+Testing.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,20 @@

#import "BugSplatTestSupport.h"
#import "BugSplatUploadService.h"
#import "BugSplatHangTracker.h"

NS_ASSUME_NONNULL_BEGIN

/**
* Class extension to expose private methods for testing.
* These methods are implemented in the main BugSplat class.
*/
@interface BugSplat ()
@interface BugSplat () <BugSplatHangTrackerDelegate>

- (BOOL)shouldSendCrashSilently:(NSDictionary *)metadata;
- (NSString *)resolvedApplicationName;
- (NSString *)resolvedApplicationVersion;
- (nullable NSString *)crashesDirectoryPath;

@end

Expand Down Expand Up @@ -83,6 +85,22 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)setDebuggerAttachedOverride:(nullable NSNumber *)value;

#pragma mark - Hang Detection Testing

/**
* Create the internal plumbing (serial queue, launch id) that `-start` would
* normally set up for hang detection. Lets tests exercise the hang delegate
* methods without starting the real tracker or enabling the crash reporter.
*/
- (void)setupHangInfrastructureForTesting;

/// Serial queue that hang delegate callbacks dispatch onto; `dispatch_sync` on
/// this queue to wait for pending hang work to drain.
- (nullable dispatch_queue_t)hangQueueForTesting;

/// The basename of the hang report most recently persisted by the hang delegate.
- (nullable NSString *)currentHangFilename;

@end

NS_ASSUME_NONNULL_END
43 changes: 43 additions & 0 deletions BugSplat.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,49 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, assign) BOOL autoSubmitCrashReport;

/**
* Enable detection and reporting of fatal main-thread hangs.
*
* When set to YES before `-start` is invoked, BugSplat monitors the main runloop for
* prolonged unresponsive periods while the app is active in the foreground. If a hang
* is detected and the app is subsequently terminated without the main thread recovering
* (launch/resume watchdog kills, user force-quit), a hang report is uploaded on the next
* launch using the same pipeline as crash reports.
*
* If the main thread resumes after a hang is detected, the persisted report is discarded -
* non-fatal hangs are not reported in this version.
*
* Hang reports carry the exception name `App Hang (Fatal)` and include attributes prefixed
* with `bugsplat-hang-` (duration, detection time, app state, launch id) that can be used
* to correlate with crashes from the same launch.
*
* Detection is suppressed when a debugger is attached or the app is not active. As a
* consequence, hangs that begin while the app is in the background (including those
* terminated by background-task expiration) are not reported.
*
* This property is a no-op inside app extensions.
*
* When this property is YES, `-start` must be invoked on the main thread - the
* main thread's Mach port is captured there so the hang report identifies the
* correct thread. Debug builds assert; Release builds will silently capture the
* wrong thread.
*
* Default: NO
*/
@property (nonatomic, assign) BOOL enableHangDetection;

/**
* Threshold in seconds for declaring the main thread hung when `enableHangDetection` is YES.
*
* Must be set before `-start` is invoked. Values below 0.1 are clamped to 0.1 by the
* underlying tracker. Typical production values are 1.0-5.0 seconds; choose a value above
* any work the app may legitimately do on the main thread (image decoding, JSON parsing,
* etc.) to avoid false positives.
*
* Default: 2.0
*/
@property (nonatomic, assign) NSTimeInterval hangDetectionThreshold;

/**
* Add an attribute and value to a dictionary of attributes that will potentially be included in a crash report.
* If the attribute is an invalid XML entity name, or the attribute+value pair cannot be set,
Expand Down
Loading