Skip to content

Commit

Permalink
Merge branch 'master' into feat/extra-controller-breadcrumb-data
Browse files Browse the repository at this point in the history
* master:
  ref: Remove unused SentryCrashDeadlock (#1941)
  feat: Add screenshot at crash (#1920)
  Add code docs for SentryScope (#1942)
  • Loading branch information
kevinrenskers committed Jul 7, 2022
2 parents 87a13de + c0b8d5e commit 6ed0aac
Show file tree
Hide file tree
Showing 35 changed files with 462 additions and 460 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Features

- Add screenshot at crash (#1920)
- Track timezone changes as breadcrumbs (#1930)
- Enhance the UIViewController breadcrumbs with more data (#1945)

Expand Down
20 changes: 8 additions & 12 deletions Sentry.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions Sources/Sentry/Public/SentryScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

NS_ASSUME_NONNULL_BEGIN

/**
* The scope holds useful information that should be sent along with the event. For instance tags or
* breadcrumbs are stored on the scope.
*
* For more information see:
* https://docs.sentry.io/platforms/apple/enriching-events/scopes/#whats-a-scope-whats-a-hub
*/
NS_SWIFT_NAME(Scope)
@interface SentryScope : NSObject <SentrySerializable>

Expand Down
1 change: 0 additions & 1 deletion Sources/Sentry/SentryCrashReportConverter.m
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ - (SentryEvent *_Nullable)convertReportToEvent
[NSString stringWithFormat:@"Could not convert report:%@", exception.description];
[SentryLog logWithMessage:errorMessage andLevel:kSentryLevelError];
}

return nil;
}

Expand Down
12 changes: 11 additions & 1 deletion Sources/Sentry/SentryCrashReportSink.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "SentryCrashReportSink.h"
#import "SentryAttachment.h"
#import "SentryClient.h"
#import "SentryCrash.h"
#import "SentryCrashReportConverter.h"
Expand All @@ -9,6 +10,7 @@
#import "SentryLog.h"
#import "SentrySDK+Private.h"
#import "SentrySDK.h"
#import "SentryScope.h"
#import "SentryThread.h"

@interface
Expand All @@ -33,7 +35,15 @@ - (void)handleConvertedEvent:(SentryEvent *)event
sentReports:(NSMutableArray *)sentReports
{
[sentReports addObject:report];
[SentrySDK captureCrashEvent:event];
SentryScope *scope = [[SentryScope alloc] initWithScope:SentrySDK.currentHub.scope];

if (report[SENTRYCRASH_REPORT_SCREENSHOT_ITEM]) {
for (NSString *ssPath in report[SENTRYCRASH_REPORT_SCREENSHOT_ITEM]) {
[scope addAttachment:[[SentryAttachment alloc] initWithPath:ssPath]];
}
}

[SentrySDK captureCrashEvent:event withScope:scope];
}

- (void)filterReports:(NSArray *)reports
Expand Down
11 changes: 8 additions & 3 deletions Sources/Sentry/SentryHub.m
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,18 @@ - (nullable SentrySession *)incrementSessionErrors
return sessionCopy;
}

- (void)captureCrashEvent:(SentryEvent *)event
{
[self captureCrashEvent:event withScope:self.scope];
}

/**
* If autoSessionTracking is enabled we want to send the crash and the event together to get proper
* numbers for release health statistics. If there are multiple crash events to be sent on the start
* of the SDK there is currently no way to know which one belongs to the crashed session so we just
* send the session with the first crashed event we receive.
*/
- (void)captureCrashEvent:(SentryEvent *)event
- (void)captureCrashEvent:(SentryEvent *)event withScope:(SentryScope *)scope
{
event.isCrashEvent = YES;

Expand All @@ -226,13 +231,13 @@ - (void)captureCrashEvent:(SentryEvent *)event
// It can be that there is no session yet, because autoSessionTracking was just enabled and
// there is a previous crash on disk. In this case we just send the crash event.
if (nil != crashedSession) {
[client captureCrashEvent:event withSession:crashedSession withScope:self.scope];
[client captureCrashEvent:event withSession:crashedSession withScope:scope];
[fileManager deleteCrashedSession];
return;
}
}

[client captureCrashEvent:event withScope:self.scope];
[client captureCrashEvent:event withScope:scope];
}

- (SentryId *)captureTransaction:(SentryTransaction *)transaction withScope:(SentryScope *)scope
Expand Down
5 changes: 5 additions & 0 deletions Sources/Sentry/SentrySDK.m
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ + (void)captureCrashEvent:(SentryEvent *)event
[SentrySDK.currentHub captureCrashEvent:event];
}

+ (void)captureCrashEvent:(SentryEvent *)event withScope:(SentryScope *)scope
{
[SentrySDK.currentHub captureCrashEvent:event withScope:scope];
}

+ (SentryId *)captureEvent:(SentryEvent *)event
{
return [SentrySDK captureEvent:event withScope:SentrySDK.currentHub.scope];
Expand Down
55 changes: 37 additions & 18 deletions Sources/Sentry/SentryScreenshot.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,9 @@ @implementation SentryScreenshot

- (NSArray<NSData *> *)appScreenshots
{
__block NSMutableArray *result;
__block NSArray *result;

void (^takeScreenShot)(void) = ^{
NSArray<UIWindow *> *windows =
[SentryDependencyContainer.sharedInstance.application windows];

result = [NSMutableArray arrayWithCapacity:windows.count];

for (UIWindow *window in windows) {
UIGraphicsBeginImageContext(window.frame.size);

if ([window drawViewHierarchyInRect:window.bounds afterScreenUpdates:false]) {
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
[result addObject:UIImagePNGRepresentation(img)];
}

UIGraphicsEndImageContext();
}
};
void (^takeScreenShot)(void) = ^{ result = [self takeScreenshots]; };

if ([NSThread isMainThread]) {
takeScreenShot();
Expand All @@ -38,6 +22,41 @@ @implementation SentryScreenshot
return result;
}

- (void)saveScreenShots:(NSString *)path
{
// This function does not dispatch the screenshot to the main thread.
// The caller should be aware of that.
// We did it this way because we use this function to save screenshots
// during signal handling, and if we dispatch it to the main thread,
// that is probably blocked by the crash event, we freeze the application.
[[self takeScreenshots] enumerateObjectsUsingBlock:^(NSData *obj, NSUInteger idx, BOOL *stop) {
NSString *name = idx == 0
? @"screenshot.png"
: [NSString stringWithFormat:@"screenshot-%li.png", (unsigned long)idx + 1];
NSString *fileName = [path stringByAppendingPathComponent:name];
[obj writeToFile:fileName atomically:YES];
}];
}

- (NSArray<NSData *> *)takeScreenshots
{
NSArray<UIWindow *> *windows = [SentryDependencyContainer.sharedInstance.application windows];

NSMutableArray *result = [NSMutableArray arrayWithCapacity:windows.count];

for (UIWindow *window in windows) {
UIGraphicsBeginImageContext(window.frame.size);

if ([window drawViewHierarchyInRect:window.bounds afterScreenUpdates:false]) {
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
[result addObject:UIImagePNGRepresentation(img)];
}

UIGraphicsEndImageContext();
}
return result;
}

@end

#endif
44 changes: 39 additions & 5 deletions Sources/Sentry/SentryScreenshotIntegration.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import "SentryScreenshotIntegration.h"
#import "SentryAttachment.h"
#import "SentryClient+Private.h"
#import "SentryCrashC.h"
#import "SentryDependencyContainer.h"
#import "SentryEvent+Private.h"
#import "SentryEvent.h"
Expand All @@ -10,6 +11,35 @@
#import "SentrySDK+Private.h"

#if SENTRY_HAS_UIKIT

void
saveScreenShot(const char *path)
{
NSString *reportPath = [NSString stringWithUTF8String:path];
NSError *error = nil;

if (![NSFileManager.defaultManager fileExistsAtPath:reportPath]) {
[NSFileManager.defaultManager createDirectoryAtPath:reportPath
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error != nil)
return;
} else {
// We first delete any screenshot that could be from an old crash report
NSArray *oldFiles = [NSFileManager.defaultManager contentsOfDirectoryAtPath:reportPath
error:&error];

if (!error) {
[oldFiles enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
[NSFileManager.defaultManager removeItemAtPath:obj error:nil];
}];
}
}

[SentryDependencyContainer.sharedInstance.screenshot saveScreenShots:reportPath];
}

@implementation SentryScreenshotIntegration

- (void)installWithOptions:(nonnull SentryOptions *)options
Expand All @@ -21,6 +51,13 @@ - (void)installWithOptions:(nonnull SentryOptions *)options

SentryClient *client = [SentrySDK.currentHub getClient];
client.attachmentProcessor = self;

sentrycrash_setSaveScreenshots(&saveScreenShot);
}

- (void)uninstall
{
sentrycrash_setSaveScreenshots(NULL);
}

- (NSArray<SentryAttachment *> *)processAttachments:(NSArray<SentryAttachment *> *)attachments
Expand All @@ -39,11 +76,8 @@ - (void)installWithOptions:(nonnull SentryOptions *)options
[result addObjectsFromArray:attachments];

for (int i = 0; i < screenshot.count; i++) {
NSString *name;
if (i == 0)
name = @"screenshot.png";
else
name = [NSString stringWithFormat:@"screenshot-%i.png", i + 1];
NSString *name
= i == 0 ? @"screenshot.png" : [NSString stringWithFormat:@"screenshot-%i.png", i + 1];

SentryAttachment *att = [[SentryAttachment alloc] initWithData:screenshot[i]
filename:name
Expand Down
2 changes: 2 additions & 0 deletions Sources/Sentry/include/SentryHub+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ SentryHub (Private)

- (void)captureCrashEvent:(SentryEvent *)event;

- (void)captureCrashEvent:(SentryEvent *)event withScope:(SentryScope *)scope;

- (void)setSampleRandomValue:(NSNumber *)value;

- (void)closeCachedSessionWithTimestamp:(NSDate *_Nullable)timestamp;
Expand Down
2 changes: 2 additions & 0 deletions Sources/Sentry/include/SentrySDK+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ SentrySDK (Private)

+ (void)captureCrashEvent:(SentryEvent *)event;

+ (void)captureCrashEvent:(SentryEvent *)event withScope:(SentryScope *)scope;

/**
* SDK private field to store the state if onCrashedLastRun was called.
*/
Expand Down
1 change: 1 addition & 0 deletions Sources/Sentry/include/SentryScreenshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable NSArray<NSData *> *)appScreenshots;

- (void)saveScreenShots:(NSString *)path;
@end

NS_ASSUME_NONNULL_END
Expand Down
5 changes: 0 additions & 5 deletions Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#include "SentryCrashDebug.h"
#include "SentryCrashMonitor_AppState.h"
#include "SentryCrashMonitor_CPPException.h"
#include "SentryCrashMonitor_Deadlock.h"
#include "SentryCrashMonitor_MachException.h"
#include "SentryCrashMonitor_NSException.h"
#include "SentryCrashMonitor_Signal.h"
Expand Down Expand Up @@ -73,10 +72,6 @@ static Monitor g_monitors[] = {
.monitorType = SentryCrashMonitorTypeNSException,
.getAPI = sentrycrashcm_nsexception_getAPI,
},
{
.monitorType = SentryCrashMonitorTypeMainThreadDeadlock,
.getAPI = sentrycrashcm_deadlock_getAPI,
},
{
.monitorType = SentryCrashMonitorTypeZombie,
.getAPI = sentrycrashcm_zombie_getAPI,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ static const struct {
MONITORTYPE(SentryCrashMonitorTypeSignal),
MONITORTYPE(SentryCrashMonitorTypeCPPException),
MONITORTYPE(SentryCrashMonitorTypeNSException),
MONITORTYPE(SentryCrashMonitorTypeMainThreadDeadlock),
MONITORTYPE(SentryCrashMonitorTypeUserReported),
MONITORTYPE(SentryCrashMonitorTypeSystem),
MONITORTYPE(SentryCrashMonitorTypeApplicationState),
Expand Down
14 changes: 3 additions & 11 deletions Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ extern "C" {
* - Fatal signal
* - Uncaught C++ exception
* - Uncaught Objective-C NSException
* - Deadlock on the main thread
* - User reported custom exception
*/
typedef enum {
Expand All @@ -52,9 +51,6 @@ typedef enum {
/* Captures and reports NSExceptions. */
SentryCrashMonitorTypeNSException = 0x08,

/* Detects and reports a deadlock in the main thread. */
SentryCrashMonitorTypeMainThreadDeadlock = 0x10,

/* Accepts and reports user-generated exceptions. */
SentryCrashMonitorTypeUserReported = 0x20,

Expand All @@ -71,11 +67,8 @@ typedef enum {
#define SentryCrashMonitorTypeAll \
(SentryCrashMonitorTypeMachException | SentryCrashMonitorTypeSignal \
| SentryCrashMonitorTypeCPPException | SentryCrashMonitorTypeNSException \
| SentryCrashMonitorTypeMainThreadDeadlock | SentryCrashMonitorTypeUserReported \
| SentryCrashMonitorTypeSystem | SentryCrashMonitorTypeApplicationState \
| SentryCrashMonitorTypeZombie)

#define SentryCrashMonitorTypeExperimental (SentryCrashMonitorTypeMainThreadDeadlock)
| SentryCrashMonitorTypeUserReported | SentryCrashMonitorTypeSystem \
| SentryCrashMonitorTypeApplicationState | SentryCrashMonitorTypeZombie)

#define SentryCrashMonitorTypeDebuggerUnsafe \
(SentryCrashMonitorTypeMachException | SentryCrashMonitorTypeSignal \
Expand All @@ -96,8 +89,7 @@ typedef enum {
/** Monitors that are safe to use in a production environment.
* All other monitors should be considered experimental.
*/
#define SentryCrashMonitorTypeProductionSafe \
(SentryCrashMonitorTypeAll & (~SentryCrashMonitorTypeExperimental))
#define SentryCrashMonitorTypeProductionSafe (SentryCrashMonitorTypeAll)

/** Production safe monitors, minus the optional ones. */
#define SentryCrashMonitorTypeProductionSafeMinimal \
Expand Down

0 comments on commit 6ed0aac

Please sign in to comment.