diff --git a/CODE_TOUR.md b/CODE_TOUR.md index b85d6f5fd..031b2089c 100644 --- a/CODE_TOUR.md +++ b/CODE_TOUR.md @@ -16,11 +16,11 @@ These are the main parts of `KSCrashC.c`: #### Installation -`kscrash_install()` installs and prepares the KSCrash system to handle crashes. You can configure KSCrash using the various configuration functions in this file (`kscrash_setMonitoring() and such`) before or after install. +`kscrash_install()` installs and prepares the KSCrash system to handle crashes. You configure KSCrash by creating and populating a `KSCrashCConfiguration` struct with your desired settings. This configuration struct is then passed to the installation function, allowing you to set up all crash handling parameters in one step. #### Configuration -All of the main configuration settings are set via `kscrash_setXYZ()`. +All of the main configuration settings are set via `KSCrashCConfiguration`. #### App State diff --git a/README.md b/README.md index 2c72265e2..a249b19ce 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ +# KSCrash + [![Run Unit Tests](https://github.com/kstenerud/KSCrash/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/kstenerud/KSCrash/actions/workflows/unit-tests.yml) [![CocoaPods Lint](https://github.com/kstenerud/KSCrash/actions/workflows/cocoapods-lint.yml/badge.svg)](https://github.com/kstenerud/KSCrash/actions/workflows/cocoapods-lint.yml) - -KSCrash -======= +[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fkstenerud%2FKSCrash%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/kstenerud/KSCrash) +[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fkstenerud%2FKSCrash%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/kstenerud/KSCrash) #### The Ultimate Crash Reporter - -### Another crash reporter? Why? +## Another crash reporter? Why? Because while the existing crash reporters do report crashes, there's a heck of a lot more that they COULD do. Here are some key features of KSCrash: @@ -37,7 +37,7 @@ of a lot more that they COULD do. Here are some key features of KSCrash: * Supports including extra data that the programmer supplies (before and during a crash). -#### KSCrash handles the following kinds of crashes: +### KSCrash handles the following kinds of crashes: * Mach kernel exceptions * Fatal signals @@ -46,16 +46,9 @@ of a lot more that they COULD do. Here are some key features of KSCrash: * Main thread deadlock (experimental) * Custom crashes (e.g. from scripting languages) -#### KSCrash can report to the following servers: - -* Email - [Here are some examples of the reports it can generate.](https://github.com/kstenerud/KSCrash/tree/master/Example-Reports/_README.md) - -### What's New? - -#### Call for help! +## Call for help! My life has changed enough over the past few years that I can't keep up with giving KSCrash the love it needs. @@ -63,31 +56,173 @@ My life has changed enough over the past few years that I can't keep up with giv I'm looking for someone to help me maintain this package, make sure issues get handled, merges are properly vetted, and code quality remains high. Please contact me personally (kstenerud at my gmail address) or comment in https://github.com/kstenerud/KSCrash/issues/313 +## How to Install KSCrash + +### Swift Package Manager (SPM) + +#### Option 1: Using Xcode UI + +1. In Xcode, go to File > Add Packages... +2. Enter: `https://github.com/kstenerud/KSCrash.git` +3. Select the desired version/branch +4. Choose your target(s) +5. Click "Add Package" + +#### Option 2: Using Package.swift + +Add the following to your `Package.swift` file: + +```swift +dependencies: [ + .package(url: "https://github.com/kstenerud/KSCrash.git", from: "2.0.0-alpha.2") +] +``` + +Then, include "Installations" as a dependency for your target: + +```swift +targets: [ + .target( + name: "YourTarget", + dependencies: [ + .product(name: "Installations", package: "KSCrash"), + ]), +] +``` + +### CocoaPods + +1. Add to your `Podfile`: + ```ruby + pod 'KSCrash' + ``` + +2. Run: + ``` + $ pod install + ``` + +3. Use the generated `.xcworkspace` file. + +### Post-Installation Setup + +Add the following to your `AppDelegate.swift` file: + +#### Import KSCrash + +For Swift Package Manager: + +```swift +import KSCrashInstallations +``` + +For CocoaPods: + +```swift +import KSCrash +``` + +#### Configure AppDelegate + +```swift +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + let installation = CrashInstallationStandard.shared() + installation.url = URL(string: "http://put.your.url.here")! + + // Optional: Add an alert confirmation (recommended for email installation) + installation.addConditionalAlert( + withTitle: "Crash Detected", + message: "The app crashed last time it was launched. Send a crash report?", + yesAnswer: "Sure!", + noAnswer: "No thanks" + ) + + // Install the crash reporting system + let config = KSCrashConfiguration() + config.monitors = [.machException, .signal] + installation.install(with: config) // set `nil` for default config + + return true + } +} +``` + +#### Other Installation Types + +##### Email Installation + +```swift +let installation = CrashInstallationEmail.shared() +installation.recipients = ["some@email.address"] // Specify recipients for email reports +// Optional: Send Apple-style reports instead of JSON +installation.setReportStyle(.apple, useDefaultFilenameFormat: true) +``` + +##### Console Installation + +```swift +let installation = CrashInstallationConsole.shared() +installation.printAppleFormat = true // Print crash reports in Apple format for testing +``` + +#### Sending Reports + +To send any outstanding crash reports, call: + +```swift +installation.sendAllReports { reports, completed, error in + // Stuff to do when report sending is complete +} +``` + +### Optional Modules + +KSCrash includes two optional modules: `BootTimeMonitor` and `DiscSpaceMonitor`. These modules are not included by default and must be explicitly added if needed. They contain privacy-concerning APIs that require showing crash reports to the user before sending this information off the device. + +To include these modules: + +- With CocoaPods: + ```ruby + pod 'KSCrash/BootTimeMonitor' + pod 'KSCrash/DiscSpaceMonitor' + ``` + +- With SPM, add to your target dependencies: + ```swift + .product(name: "BootTimeMonitor", package: "KSCrash"), + .product(name: "DiscSpaceMonitor", package: "KSCrash"), + ``` + +If these modules are linked, they act automatically and require no additional setup. It is the responsibility of the library user to implement the necessary UI for user consent. +For more information, see Apple's documentation on [Disk space APIs](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api#4278397) and [System boot time APIs](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api#4278394). -#### VERY VERY VERY preliminary Android support. +## What's New? -And I do mean PRELIMINARY. Most stuff doesn't work, but it does compile. -Look in the Android subdir if you want to play around with it. +### Out-of-Memory Crash Detection -#### C++ Exception Handling +KSCrash now includes advanced memory tracking capabilities to help detect and prevent out-of-memory crashes. The new `KSCrashAppMemoryTracker` allows you to monitor your app's memory usage, pressure, and state transitions in real-time. This feature enables proactive memory management, helping you avoid system-initiated terminations due to excessive memory use. Check out the "Advanced Usage" section for more details on how to implement this in your app. + +### C++ Exception Handling That's right! Normally if your app terminates due to an uncaught C++ exception, all you get is this: - Thread 0 name: Dispatch queue: com.apple.main-thread - Thread 0 Crashed: - 0 libsystem_kernel.dylib 0x9750ea6a 0x974fa000 + 84586 (__pthread_kill + 10) - 1 libsystem_sim_c.dylib 0x04d56578 0x4d0f000 + 292216 (abort + 137) - 2 libc++abi.dylib 0x04ed6f78 0x4ed4000 + 12152 (abort_message + 102) - 3 libc++abi.dylib 0x04ed4a20 0x4ed4000 + 2592 (_ZL17default_terminatev + 29) - 4 libobjc.A.dylib 0x013110d0 0x130b000 + 24784 (_ZL15_objc_terminatev + 109) - 5 libc++abi.dylib 0x04ed4a60 0x4ed4000 + 2656 (_ZL19safe_handler_callerPFvvE + 8) - 6 libc++abi.dylib 0x04ed4ac8 0x4ed4000 + 2760 (_ZSt9terminatev + 18) - 7 libc++abi.dylib 0x04ed5c48 0x4ed4000 + 7240 (__cxa_rethrow + 77) - 8 libobjc.A.dylib 0x01310fb8 0x130b000 + 24504 (objc_exception_rethrow + 42) - 9 CoreFoundation 0x01f2af98 0x1ef9000 + 204696 (CFRunLoopRunSpecific + 360) - ... + Thread 0 name: Dispatch queue: com.apple.main-thread + Thread 0 Crashed: + 0 libsystem_kernel.dylib 0x9750ea6a 0x974fa000 + 84586 (__pthread_kill + 10) + 1 libsystem_sim_c.dylib 0x04d56578 0x4d0f000 + 292216 (abort + 137) + 2 libc++abi.dylib 0x04ed6f78 0x4ed4000 + 12152 (abort_message + 102) + 3 libc++abi.dylib 0x04ed4a20 0x4ed4000 + 2592 (_ZL17default_terminatev + 29) + 4 libobjc.A.dylib 0x013110d0 0x130b000 + 24784 (_ZL15_objc_terminatev + 109) + 5 libc++abi.dylib 0x04ed4a60 0x4ed4000 + 2656 (_ZL19safe_handler_callerPFvvE + 8) + 6 libc++abi.dylib 0x04ed4ac8 0x4ed4000 + 2760 (_ZSt9terminatev + 18) + 7 libc++abi.dylib 0x04ed5c48 0x4ed4000 + 7240 (__cxa_rethrow + 77) + 8 libobjc.A.dylib 0x01310fb8 0x130b000 + 24504 (objc_exception_rethrow + 42) + 9 CoreFoundation 0x01f2af98 0x1ef9000 + 204696 (CFRunLoopRunSpecific + 360) + ... No way to track what the exception was or where it was thrown from! @@ -111,103 +246,27 @@ Now with KSCrash, you get the uncaught exception type, description, and where it 10 CoreFoundation 0x01f2bf44 0x1efa000 + 204612 (CFRunLoopRunSpecific + 276) ... -#### Custom Crashes & Stack Traces +### Custom Crashes & Stack Traces You can now report your own custom crashes and stack traces (think scripting languages): ```objective-c - (void) reportUserException:(NSString*) name - reason:(NSString*) reason - lineOfCode:(NSString*) lineOfCode - stackTrace:(NSArray*) stackTrace - terminateProgram:(BOOL) terminateProgram; + reason:(NSString*) reason + lineOfCode:(NSString*) lineOfCode + stackTrace:(NSArray*) stackTrace + terminateProgram:(BOOL) terminateProgram; ``` See KSCrash.h for details. - ### Unstable Features The following features should be considered "unstable" and are disabled by default: - Deadlock detection - - -How to Build KSCrash --------------------- - -1. Select the **KSCrash** scheme. -2. Choose **iOS Device**. -3. Select **Archive** from the **Products** menu. - -When it has finished building, it will show you the framework in Finder. You -can use it like you would any other framework. - - - -How to Use KSCrash ------------------- - -1. Add the framework to your project (or add the KSCrash project as a - dependency) - -2. Add the following system frameworks & libraries to your project: - * libc++.dylib (libc++.tbd in newer versions) - * libz.dylib (libz.tbd in newer versions) - * MessageUI.framework (iOS only) - * SystemConfiguration.framework - -3. Add the flag "-ObjC" to **Other Linker Flags** in your **Build Settings** - -4. Add the following to your **[application: didFinishLaunchingWithOptions:]** - method in your app delegate: - - -```objective-c -#import -// Include to use the standard reporter. -#import -// Include to use the email reporter. -#import - -- (BOOL)application:(UIApplication*) application didFinishLaunchingWithOptions:(NSDictionary*) launchOptions -{ -KSCrashInstallationStandard* installation = [KSCrashInstallationStandard sharedInstance]; -installation.url = [NSURL URLWithString:@"http://put.your.url.here"]; - -// OR: - -KSCrashInstallationEmail* installation = [KSCrashInstallationEmail sharedInstance]; -installation.recipients = @[@"some@email.address"]; - -// Optional (Email): Send Apple-style reports instead of JSON -[installation setReportStyle:KSCrashEmailReportStyleApple useDefaultFilenameFormat:YES]; - -// Optional: Add an alert confirmation (recommended for email installation) -[installation addConditionalAlertWithTitle:@"Crash Detected" - message:@"The app crashed last time it was launched. Send a crash report?" - yesAnswer:@"Sure!" - noAnswer:@"No thanks"]; -} -``` - -This will install the crash monitor system (which intercepts crashes and stores -reports to disk). Note that there are other properties you can and probably -will want to set for the various installations. - -Once you're ready to send any outstanding crash reports, call the following: - -```objective-c -[installation sendAllReportsWithCompletion:^(NSArray *filteredReports, BOOL completed, NSError *error) -{ - // Stuff to do when report sending is complete -}]; -``` - - -Recommended Reading -------------------- +## Recommended Reading If possible, you should read the following header files to fully understand what features KSCrash has, and how to use them: @@ -217,17 +276,34 @@ what features KSCrash has, and how to use them: * KSCrashInstallation(SPECIFIC TYPE).h * Architecture.md +## Understanding the KSCrash Codebase + +KSCrash is structured into several modules, divided into public and private APIs: + +### Public API Modules + +1. **Recording**: `KSCrashRecording` - Handles crash event recording. +2. **Reporting**: + - **Filters**: `KSCrashFilters` - Processes crash reports. + - **Sinks**: `KSCrashSinks` - Manages report destinations. + - **Installations**: `KSCrashInstallations` - Provides easy-to-use setups for different reporting scenarios. +### Optional Modules -Understanding the KSCrash Codebase ----------------------------------- +- **DiscSpaceMonitor**: `KSCrashDiscSpaceMonitor` - Monitors available disk space. +- **BootTimeMonitor**: `KSCrashBootTimeMonitor` - Tracks device boot time. -I've written a quick code tour [here](CODE_TOUR.md) +### Private API Modules +- `KSCrashRecordingCore`: Core functionality for crash recording. +- `KSCrashReportingCore`: Core functionality for crash reporting. +- `KSCrashCore`: Core system capabilities logic. +Users should interact with the public API modules, while the private modules handle internal operations. The optional modules can be included for additional functionality as needed. -Advanced Usage --------------- +**Also see a quick code tour [here](CODE_TOUR.md).** + +## Advanced Usage ### Enabling on-device symbolication @@ -236,20 +312,17 @@ build. To enable this, go to your app's build settings and set **Strip Style** to **Debugging Symbols**. Doing so increases your final binary size by about 5%, but you get on-device symbolication. - ### Enabling advanced functionality: KSCrash has advanced functionality that can be very useful when examining crash reports in the wild. Some involve minor trade-offs, so most of them are disabled by default. - #### Custom User Data (userInfo in KSCrash.h) You can store custom user data to the next crash report by setting the **userInfo** property in KSCrash.h. - #### Zombie Tracking (KSCrashMonitorTypeZombie in KSCrashMonitorType.h) KSCrash has the ability to detect zombie instances (dangling pointers to @@ -267,7 +340,6 @@ to debug your app, so this feature can be quite handy at times. Trade off: Zombie tracking at the cost of adding very slight overhead to object deallocation, and having some memory reserved. - #### Deadlock Detection (KSCrashMonitorTypeMainThreadDeadlock in KSCrashMonitorType.h) **WARNING WARNING WARNING WARNING WARNING WARNING WARNING** @@ -292,7 +364,6 @@ become impatient and shut down the app manually before the watchdog triggers! Trade off: Deadlock detection, but you must be a lot more careful about what runs on the main thread! - #### Memory Introspection (introspectMemory in KSCrash.h) When an app crashes, there are usually objects and strings in memory that are @@ -303,7 +374,6 @@ in the crash report. You can also specify a list of classes that should not be introspected by setting the **doNotIntrospectClasses** property in KSCrash. - #### Custom crash handling code (onCrash in KSCrash.h) If you want to do some extra processing after a crash occurs (perhaps to add @@ -319,26 +389,37 @@ report anyway, though your custom handler code may not fully run. Trade off: Custom crash handling code, but you must be careful what you put in it! - #### KSCrash log redirection This takes whatever KSCrash would have printed to the console, and writes it to a file instead. I mostly use this for debugging KSCrash itself, but it could be useful for other purposes, so I've exposed an API for it. +#### Out-of-Memory Crash Detection (KSCrashAppMemoryTracker) + +KSCrash now includes advanced memory tracking capabilities to help detect and prevent out-of-memory crashes. The `KSCrashAppMemoryTracker` class monitors your app's memory usage, pressure, and state transitions. It provides real-time updates on memory conditions, allowing you to respond dynamically to different memory states (Normal, Warn, Urgent, Critical, Terminal). By implementing the `KSCrashAppMemoryTrackerDelegate` protocol, you can receive notifications about memory changes and take appropriate actions to reduce memory usage, potentially avoiding system-initiated terminations due to memory pressure. +To use this feature: -Examples --------- +```swift +let memoryTracker = AppMemoryTracker() +memoryTracker.delegate = self +memoryTracker.start() +``` -The workspace includes some example apps, which demonstrate common KSCrash -usage. Please look at the top of AppDelegate.m in each app for a description -of what it does. +In your delegate method: +```swift +func appMemoryTracker(_ tracker: AppMemoryTracker, memory: AppMemory, changed changes: AppMemoryTrackerChangeType) { + if changes.contains(.level) { + // Respond to memory level changes + } +} +``` +This feature helps you implement proactive memory management strategies in your app. -License -------- +## License Copyright (c) 2012 Karl Stenerud