Skip to content

Commit

Permalink
Final updates for new API.
Browse files Browse the repository at this point in the history
Reorganized the workspace structure a bit. All example apps are in a separate Example-Apps project.

Added two more example projects that cover the most common use cases.

More unit tests.

Better documentation.

Updated example reports.

Memory introspection is now controllable. You can disable it for certain classes, or disable it entirely.

Minor bug fixes.
  • Loading branch information
kstenerud committed Mar 10, 2013
1 parent 95a2809 commit 5a7660e
Show file tree
Hide file tree
Showing 168 changed files with 20,580 additions and 25,500 deletions.
74 changes: 74 additions & 0 deletions Architecture.md
@@ -0,0 +1,74 @@
KSCrash Architecture
====================

KSCrash is implemented as a layered architecture. Each layer can in theory be
compiled without the layers adjacent or above.

+-------------------------------------------------------------+
| Installation |
| +----------------------------------------------------+
| | KSCrash |
| +--------------------------------------------------------+
| | Crash Reporting | Crash Recording | Crash Report Store |
+----+-----------------+-----------------+--------------------+
| Filters | Sentry |
+----------------------+-----------------+


### Installation

This top level layer provides a "clean" interface to the crash system.
It is expected that the API at this level will be largely idiomatic to
the backend system it will be communicating with.

Primary entry points: KSCrashInstallation.h, KSCrashInstallationXYZ.h


### KSCrash

Handles high level configuration and installation of the crash recording and
crash reporting systems.

Primary entry point: KSCrash.h


### Crash Report Store

Provides storage and retrieval of crash reports and other configuration data.
Also provides file paths for more primitive access by other layers.

Primary entry point: KSCrashReportStore.h


### Crash Recording

Records a single crash event. This layer is implemented in async-safe C.

Primary entry point: KSCrashC.h


### Crash Reporting

Processes, transforms, and sends reports to a remote system.

Primary entry point: KSCrash.h


### Sentry

Traps application errors and passes control to a supplied function.
It handles the following errors:

* Mach Exception
* Signal
* NSException
* Main Thread Deadlock

Primary entry point: KSCrashSentry.h


### Filters

Low level interface for transforming, processing, and sending crash reports.

Primary entry points: KSCrashReportFilter.h, KSCrashReportFilterXYZ.h
493 changes: 0 additions & 493 deletions CrashTester/CrashTester.xcodeproj/project.pbxproj

This file was deleted.

14 changes: 14 additions & 0 deletions Example-Apps/Advanced-Example/Advanced-Example-Prefix.pch
@@ -0,0 +1,14 @@
//
// Prefix header for all source files of the 'Advanced-Example' target in the 'Advanced-Example' project
//

#import <Availability.h>

#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif

#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#endif
15 changes: 15 additions & 0 deletions Example-Apps/Advanced-Example/AppDelegate.h
@@ -0,0 +1,15 @@
//
// AppDelegate.h
// Advanced-Example
//

#import <UIKit/UIKit.h>

@class KSCrashInstallation;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow* window;
@property (strong, nonatomic) KSCrashInstallation* crashInstallation;

@end
156 changes: 156 additions & 0 deletions Example-Apps/Advanced-Example/AppDelegate.m
@@ -0,0 +1,156 @@
//
// AppDelegate.m
// Advanced-Example
//

#import "AppDelegate.h"

#import <KSCrash/KSCrashInstallationStandard.h>
#import <KSCrash/KSCrashInstallationQuincyHockey.h>
#import <KSCrash/KSCrashInstallationEmail.h>
#import <KSCrash/KSCrash.h>
#import <KSCrash/KSCrashAdvanced.h>


/* More advanced crash reporting example.
*
* This example creates an installation (standard, email, quincy, or hockey),
* but defers showing the main VC until crash reporting has completed.
*
* This mitigates issues where the app crashes during initialization (in which
* case you'd never see a crash report).
*
* This example also enables some more advanced features of KSCrash. See
* configureAdvancedSettings.
*/


@implementation AppDelegate

- (BOOL) application:(__unused UIApplication *) application
didFinishLaunchingWithOptions:(__unused NSDictionary *) launchOptions
{
[self installCrashHandler];

return YES;
}

// ======================================================================
#pragma mark - Basic Crash Handling -
// ======================================================================

- (void) installCrashHandler
{
// This can be useful when debugging on the simulator.
// Normally, there's no way to see console messages in the simulator,
// except when running in the debugger, which disables the crash handler.
// This feature redirects KSCrash console messages to a log file instead.
// [[KSCrash sharedInstance] redirectConsoleLogsToDefaultFile];


// Create an installation (choose one)
// KSCrashInstallation* installation = [self makeStandardInstallation];
self.crashInstallation = [self makeEmailInstallation];
// self.crashInstallation = [self makeHockeyInstallation];
// self.crashInstallation = [self makeQuincyInstallation];


// Install the crash handler. This should be done as early as possible.
// This will record any crashes that occur, but it doesn't automatically send them.
[self.crashInstallation install];

// You may also optionally configure some more advanced settings if you like.
[self configureAdvancedSettings];

// Crash reports will be sent by LoaderVC.
}

- (KSCrashInstallation*) makeEmailInstallation
{
NSString* emailAddress = @"your@email.here";

KSCrashInstallationEmail* email = [KSCrashInstallationEmail sharedInstance];
email.recipients = @[emailAddress];
email.subject = @"Crash Report";
email.message = @"This is a crash report";
email.filenameFmt = @"crash-report-%d.txt.gz";

[email addConditionalAlertWithTitle:@"Crash Detected"
message:@"The app crashed last time it was launched. Send a crash report?"
yesAnswer:@"Sure!"
noAnswer:@"No thanks"];

return email;
}

- (KSCrashInstallation*) makeHockeyInstallation
{
NSString* hockeyAppIdentifier = @"PUT_YOUR_HOCKEY_APP_ID_HERE";

KSCrashInstallationHockey* hockey = [KSCrashInstallationHockey sharedInstance];
hockey.appIdentifier = hockeyAppIdentifier;
hockey.userID = @"ABC123";
hockey.contactEmail = @"nobody@nowhere.com";
hockey.crashDescription = @"Something broke!";

// Don't wait until reachable because the main VC won't show until the process completes.
hockey.waitUntilReachable = NO;

return hockey;
}

- (KSCrashInstallation*) makeQuincyInstallation
{
NSURL* quincyURL = [NSURL URLWithString:@"http://localhost:8888/quincy/crash_v200.php"];

KSCrashInstallationQuincy* quincy = [KSCrashInstallationQuincy sharedInstance];
quincy.url = quincyURL;
quincy.userID = @"ABC123";
quincy.contactEmail = @"nobody@nowhere.com";
quincy.crashDescription = @"Something broke!";

// Don't wait until reachable because the main VC won't show until the process completes.
quincy.waitUntilReachable = NO;

return quincy;
}

- (KSCrashInstallation*) makeStandardInstallation
{
NSURL* url = [NSURL URLWithString:@"http://put.your.url.here"];

KSCrashInstallationStandard* standard = [KSCrashInstallationStandard sharedInstance];
standard.url = url;

return standard;
}


// ======================================================================
#pragma mark - Advanced Crash Handling (optional) -
// ======================================================================

static void advanced_crash_callback(const KSCrashReportWriter* writer)
{
// You can add extra user data at crash time if you want.
writer->addBooleanElement(writer, "some_bool_value", NO);
}

- (void) configureAdvancedSettings
{
KSCrash* handler = [KSCrash sharedInstance];

// Settings in KSCrash.h
handler.zombieCacheSize = 16384;
handler.deadlockWatchdogInterval = 8;
handler.userInfo = @{@"someKey": @"someValue"};
handler.onCrash = advanced_crash_callback;
handler.printTraceToStdout = YES;

// Do not introspect class SensitiveInfo (see MainVC)
// When added to the "do not introspect" list, the Objective-C introspector
// will only record the class name, not its contents.
handler.doNotIntrospectClasses = @[@"SensitiveInfo"];
}

@end
File renamed without changes
File renamed without changes
10 changes: 10 additions & 0 deletions Example-Apps/Advanced-Example/LoaderVC.h
@@ -0,0 +1,10 @@
//
// LoaderVC.h
// Advanced-Example
//

#import <UIKit/UIKit.h>

@interface LoaderVC : UIViewController

@end
51 changes: 51 additions & 0 deletions Example-Apps/Advanced-Example/LoaderVC.m
@@ -0,0 +1,51 @@
//
// LoaderVC.m
// Advanced-Example
//

#import "LoaderVC.h"

#import "AppDelegate.h"
#import <KSCrash/KSCrashInstallation.h>

/**
* Defers application loading until all error reports have been sent.
* This allows error reports to be sent even if the app's initialization
* code is causing a crash.
*
* Normally you'd just have this view display Default.png so that it looks
* no different from the launch view.
*/
@implementation LoaderVC

- (void) viewDidAppear:(BOOL) animated
{
[super viewDidAppear:animated];

// Send all outstanding reports, then show the main view controller.
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate.crashInstallation sendAllReportsWithCompletion:^(NSArray* reports, BOOL completed, NSError* error)
{
if(completed)
{
NSLog(@"Sent %d reports", [reports count]);
}
else
{
NSLog(@"Failed to send reports: %@", error);
}

// If you added an alert to the installation, it will interfere with replacing
// the root view controller. Delaying by 0.3 seconds mitigates this.
[self performSelector:@selector(showMainVC) withObject:nil afterDelay:0.3];
}];

}

- (void) showMainVC
{
UIViewController* vc = [self.storyboard instantiateViewControllerWithIdentifier:@"MainVC"];
[UIApplication sharedApplication].keyWindow.rootViewController = vc;
}

@end
10 changes: 10 additions & 0 deletions Example-Apps/Advanced-Example/MainVC.h
@@ -0,0 +1,10 @@
//
// MainVC.h
// Advanced-Example
//

#import <UIKit/UIKit.h>

@interface MainVC : UIViewController

@end
55 changes: 55 additions & 0 deletions Example-Apps/Advanced-Example/MainVC.m
@@ -0,0 +1,55 @@
//
// MainVC.m
// Advanced-Example
//

#import "MainVC.h"


/**
* Some sensitive info that should not be printed out at any time.
*
* If you have Objective-C introspection turned on, it would normally
* introspect this class, unless you add it to the list of
* "do not introspect classes" in KSCrash. We do precisely this in
* -[AppDelegate configureAdvancedSettings]
*/
@interface SensitiveInfo: NSObject

@property(nonatomic, readwrite, strong) NSString* password;

@end

@implementation SensitiveInfo

@end



@interface MainVC ()

@property(nonatomic, readwrite, strong) SensitiveInfo* info;

@end

@implementation MainVC

- (id) initWithCoder:(NSCoder *)aDecoder
{
if((self = [super initWithCoder:aDecoder]))
{
// This info could be leaked during introspection unless you tell KSCrash to ignore it.
// See -[AppDelegate configureAdvancedSettings] for more info.
self.info = [SensitiveInfo new];
self.info.password = @"it's a secret!";
}
return self;
}

- (IBAction) onCrash:(__unused id) sender
{
char* invalid = (char*)-1;
*invalid = 1;
}

@end

0 comments on commit 5a7660e

Please sign in to comment.