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

Capture trace of error reporting thread and identify with boolean flag #303

Merged
merged 5 commits into from
Sep 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ Changelog

## 5.16.3 (14 Aug 2018)

* Capture trace of error reporting thread and identify with boolean flag [#303](https://github.com/bugsnag/bugsnag-cocoa/pull/303)

### Bug Fixes

* Deregister notification observers and listeners before application termination [#301](https://github.com/bugsnag/bugsnag-cocoa/pull/301)
Expand Down
48 changes: 25 additions & 23 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
GIT
remote: https://github.com/bugsnag/maze-runner
revision: f7123450d5a75b719911c6dd3baa0507e6062c2d
revision: a480183979a48aa2fc9f4891989a1237b757bb91
specs:
bugsnag-maze-runner (1.0.0)
cucumber (~> 3.1.0)
cucumber-expressions (= 5.0.15)
minitest (~> 5.0)
rack (~> 2.0.0)
rake (~> 12.3.0)
test-unit (~> 3.2.0)

GEM
remote: https://rubygems.org/
specs:
CFPropertyList (2.3.6)
CFPropertyList (3.0.0)
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
atomos (0.1.2)
atomos (0.1.3)
backports (3.11.3)
builder (3.2.3)
claide (1.0.2)
cocoapods (1.4.0)
cocoapods (1.5.3)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.4.0)
cocoapods-core (= 1.5.3)
cocoapods-deintegrate (>= 1.0.2, < 2.0)
cocoapods-downloader (>= 1.1.3, < 2.0)
cocoapods-downloader (>= 1.2.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
Expand All @@ -37,21 +38,21 @@ GEM
escape (~> 0.0.4)
fourflusher (~> 2.0.1)
gh_inspector (~> 1.0)
molinillo (~> 0.6.4)
molinillo (~> 0.6.5)
nap (~> 1.0)
ruby-macho (~> 1.1)
xcodeproj (>= 1.5.4, < 2.0)
cocoapods-core (1.4.0)
xcodeproj (>= 1.5.7, < 2.0)
cocoapods-core (1.5.3)
activesupport (>= 4.0.2, < 6)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.2)
cocoapods-downloader (1.1.3)
cocoapods-downloader (1.2.1)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.3.0)
cocoapods-trunk (1.3.1)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.1.0)
Expand All @@ -78,32 +79,33 @@ GEM
fourflusher (2.0.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
gherkin (5.0.0)
gherkin (5.1.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
minitest (5.11.3)
molinillo (0.6.4)
molinillo (0.6.6)
multi_json (1.13.1)
multi_test (0.1.2)
nanaimo (0.2.3)
nanaimo (0.2.6)
nap (1.1.0)
netrc (0.11.0)
power_assert (1.1.1)
rack (2.0.4)
power_assert (1.1.3)
rack (2.0.5)
rake (12.3.1)
rouge (2.0.7)
ruby-macho (1.1.0)
test-unit (3.2.7)
ruby-macho (1.2.0)
test-unit (3.2.8)
power_assert
thread_safe (0.3.6)
tzinfo (1.2.5)
thread_safe (~> 0.1)
xcodeproj (1.5.6)
CFPropertyList (~> 2.3.3)
atomos (~> 0.1.2)
xcodeproj (1.6.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.3)
xcpretty (0.2.8)
nanaimo (~> 0.2.6)
xcpretty (0.3.0)
rouge (~> 2.0.7)

PLATFORMS
Expand Down
46 changes: 28 additions & 18 deletions Source/BugsnagCrashReport.m
Original file line number Diff line number Diff line change
Expand Up @@ -553,12 +553,13 @@ - (NSDictionary *)generateSessionDict {
// Build all stacktraces for threads and the error
- (NSArray *)serializeThreadsWithException:(NSMutableDictionary *)exception {
NSMutableArray *bugsnagThreads = [NSMutableArray array];
for (NSDictionary *thread in [self threads]) {

for (NSDictionary *thread in self.threads) {
NSArray *backtrace = thread[@"backtrace"][@"contents"];
BOOL stackOverflow = [thread[@"stack"][@"overflow"] boolValue];
BOOL isCrashedThread = [thread[@"crashed"] boolValue];
BOOL isReportingThread = [thread[@"crashed"] boolValue];

if (isCrashedThread) {
if (isReportingThread) {
NSUInteger seen = 0;
NSMutableArray *stacktrace = [NSMutableArray array];

Expand All @@ -579,29 +580,38 @@ - (NSArray *)serializeThreadsWithException:(NSMutableDictionary *)exception {
BSGFormatFrame(mutableFrame, [self binaryImages]));
}
}

BSGDictSetSafeObject(exception, stacktrace, BSGKeyStacktrace);
} else {
NSMutableArray *threadStack = [NSMutableArray array];
}
[self serialiseThread:bugsnagThreads thread:thread backtrace:backtrace reportingThread:isReportingThread];
}
return bugsnagThreads;
}

for (NSDictionary *frame in backtrace) {
- (void)serialiseThread:(NSMutableArray *)bugsnagThreads
thread:(NSDictionary *)thread
backtrace:(NSArray *)backtrace
reportingThread:(BOOL)isReportingThread {
NSMutableArray *threadStack = [NSMutableArray array];

for (NSDictionary *frame in backtrace) {
BSGArrayInsertIfNotNil(
threadStack, BSGFormatFrame(frame, [self binaryImages]));
}

NSMutableDictionary *threadDict = [NSMutableDictionary dictionary];
BSGDictSetSafeObject(threadDict, thread[@"index"], BSGKeyId);
BSGDictSetSafeObject(threadDict, threadStack, BSGKeyStacktrace);
BSGDictSetSafeObject(threadDict, DEFAULT_EXCEPTION_TYPE, BSGKeyType);
// only if this is enabled in BSG_KSCrash.
if (thread[BSGKeyName]) {
BSGDictSetSafeObject(threadDict, thread[BSGKeyName], BSGKeyName);
}
NSMutableDictionary *threadDict = [NSMutableDictionary dictionary];
BSGDictSetSafeObject(threadDict, thread[@"index"], BSGKeyId);
BSGDictSetSafeObject(threadDict, threadStack, BSGKeyStacktrace);
BSGDictSetSafeObject(threadDict, DEFAULT_EXCEPTION_TYPE, BSGKeyType);

BSGArrayAddSafeObject(bugsnagThreads, threadDict);
}
// only if this is enabled in BSG_KSCrash.
if (thread[BSGKeyName]) {
BSGDictSetSafeObject(threadDict, thread[BSGKeyName], BSGKeyName);
}
return bugsnagThreads;
if (isReportingThread) {
BSGDictSetSafeObject(threadDict, @YES, @"errorReportingThread");
}

BSGArrayAddSafeObject(bugsnagThreads, threadDict);
}

- (NSString *_Nullable)enhancedErrorMessageForThread:(NSDictionary *_Nullable)thread {
Expand Down
2 changes: 1 addition & 1 deletion Tests/BugsnagSinkTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ - (void)testExceptionStacktrace {

- (void)testEventThreadCount {
NSArray *threads = [self.processedData[@"events"] firstObject][@"threads"];
XCTAssert(threads.count == 8);
XCTAssertTrue(threads.count == 9);
}

- (void)testEventDevice {
Expand Down
2 changes: 1 addition & 1 deletion examples/objective-c-ios/Bugsnag Test App/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ - (void)startBugsnagWithConfiguration {
}

- (void)startBugsnagWithAPIKey {
[Bugsnag startBugsnagWithApiKey:@"6ef10e3707a961373e8592ae65d68ff1"];
[Bugsnag startBugsnagWithApiKey:@"5d1ec8bd39a74caa1267142706a7fb20"];
[Bugsnag configuration].releaseStage = @"production";
[Bugsnag configuration].notifyReleaseStages = @[@"production"];
}
Expand Down
4 changes: 2 additions & 2 deletions examples/objective-c-ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PODS:
- Bugsnag (5.16.0)
- Bugsnag (5.16.2)

DEPENDENCIES:
- Bugsnag (from `../..`)
Expand All @@ -9,7 +9,7 @@ EXTERNAL SOURCES:
:path: "../.."

SPEC CHECKSUMS:
Bugsnag: 47bcc70b43e3c616ec35d30c2ca94497b957199f
Bugsnag: 4d84527ed289f9cda5d8291393918732372d50bd

PODFILE CHECKSUM: 4c48f26cc704429f747c4af7a40e026b20fdc83e

Expand Down
7 changes: 7 additions & 0 deletions features/error_reporting_thread.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: Error Reporting Thread

Scenario: Only 1 thread is flagged as the error reporting thread
When I run "HandledErrorScenario" with the defaults on "iPhone8-11.2"
Then I should receive a request
And the request is a valid for the error reporting API
And the thread with id "0" contains the error reporting flag
4 changes: 4 additions & 0 deletions iOS/Bugsnag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@
E7EC041A1F4CC97200C2E9D5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E79FEBE61F4CB1320048FAD6 /* Foundation.framework */; };
F4295168CDC7A77A832C9475 /* BugsnagApiClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F4295E2778677786239F2B28 /* BugsnagApiClient.m */; };
F429522FC5D3D54364D51F3F /* BugsnagApiClient.h in Headers */ = {isa = PBXBuildFile; fileRef = F42950F4A741305B77E95389 /* BugsnagApiClient.h */; };
F42952D83435C02F8D891C40 /* BugsnagThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F429551527EAE3AFE1F605FE /* BugsnagThreadTest.m */; };
F42953297D561ACAB878DEB8 /* BugsnagFileStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F42958B2E67C338E3086EAC2 /* BugsnagFileStore.m */; };
F42954D219B725C18DA1084F /* BugsnagSessionTrackingApiClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F4295FBD23F478FC6216A006 /* BugsnagSessionTrackingApiClient.m */; };
F42954D2337A7CAE1FE9B308 /* BugsnagFileStore.m in Sources */ = {isa = PBXBuildFile; fileRef = F42958B2E67C338E3086EAC2 /* BugsnagFileStore.m */; };
Expand Down Expand Up @@ -592,6 +593,7 @@
F42954ACC6FFDDE3C8471495 /* BugsnagSessionFileStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagSessionFileStore.m; path = ../Source/BugsnagSessionFileStore.m; sourceTree = SOURCE_ROOT; };
F42954B7D892334E7551F0F3 /* RegisterErrorDataTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegisterErrorDataTest.m; sourceTree = "<group>"; };
F42955025DBE1DCEFD928CAA /* BugsnagSessionFileStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagSessionFileStore.h; path = ../Source/BugsnagSessionFileStore.h; sourceTree = SOURCE_ROOT; };
F429551527EAE3AFE1F605FE /* BugsnagThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagThreadTest.m; sourceTree = "<group>"; };
F429554A50F3ABE60537F70E /* BugsnagKSCrashSysInfoParserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagKSCrashSysInfoParserTest.m; sourceTree = "<group>"; };
F42958B2E67C338E3086EAC2 /* BugsnagFileStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagFileStore.m; path = ../Source/BugsnagFileStore.m; sourceTree = SOURCE_ROOT; };
F4295E2778677786239F2B28 /* BugsnagApiClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagApiClient.m; path = ../Source/BugsnagApiClient.m; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -729,6 +731,7 @@
8A2C8F291C6BBD2300846019 /* TestsInfo.plist */,
F429554A50F3ABE60537F70E /* BugsnagKSCrashSysInfoParserTest.m */,
F42954B7D892334E7551F0F3 /* RegisterErrorDataTest.m */,
F429551527EAE3AFE1F605FE /* BugsnagThreadTest.m */,
);
name = Tests;
path = BugsnagTests;
Expand Down Expand Up @@ -1241,6 +1244,7 @@
E78C1EF11FCC2F1700B976D3 /* BugsnagSessionTrackerTest.m in Sources */,
F4295995C3259BF7D9730BC4 /* BugsnagKSCrashSysInfoParserTest.m in Sources */,
F4295F017754324FD52CCE46 /* RegisterErrorDataTest.m in Sources */,
F42952D83435C02F8D891C40 /* BugsnagThreadTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
90 changes: 90 additions & 0 deletions iOS/BugsnagTests/BugsnagThreadTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// Created by Jamie Lynch on 02/08/2018.
// Copyright (c) 2018 Bugsnag. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>

#import "BugsnagCrashReport.h"

@interface BugsnagThreadTest : XCTestCase
@end

@interface BugsnagCrashReport ()
- (NSArray *)serializeThreadsWithException:(NSMutableDictionary *)exception;

@property(nonatomic, readonly, copy, nullable) NSArray *threads;
@end

@implementation BugsnagThreadTest

- (void)testEmptyThreads {
BugsnagCrashReport *report = [self generateReportWithThreads:@[]];
NSArray *threads = [report serializeThreadsWithException:nil];
XCTAssertTrue(threads.count == 0);
}

- (void)testThreadSerialisation {
NSArray *trace = @[
@{
@"backtrace": @{
@"contents": @[
@{
@"instruction_addr": @4438096107,
@"object_addr": @4438048768,
@"object_name": @"Bugsnag Test App",
@"symbol_addr": @4438048768,
@"symbol_name": @"_mh_execute_header",
}
],
@"skipped": @NO,
},
@"crashed": @YES,
@"current_thread": @YES,
@"index": @0
},
@{
@"backtrace": @{
@"contents": @[
@{
@"instruction_addr": @4510040722,
@"object_addr": @4509921280,
@"object_name": @"libsystem_kernel.dylib",
@"symbol_addr": @4510040712,
@"symbol_name": @"__workq_kernreturn",
}
],
@"skipped": @NO,
},
@"crashed": @NO,
@"current_thread": @NO,
@"index": @1
},
];

BugsnagCrashReport *report = [self generateReportWithThreads:trace];
NSArray *threads = [report serializeThreadsWithException:nil];
XCTAssertTrue(threads.count == 2);

// first thread is crashed, should be serialised and contain 'errorReportingThread' flag
NSDictionary *firstThread = threads[0];
XCTAssertEqualObjects(@0, firstThread[@"id"]);
XCTAssertEqualObjects(@"cocoa", firstThread[@"type"]);
XCTAssertNotNil(firstThread[@"stacktrace"]);
XCTAssertTrue(firstThread[@"errorReportingThread"]);

// second thread is not crashed, should not contain 'errorReportingThread' flag
NSDictionary *secondThread = threads[1];
XCTAssertEqualObjects(@1, secondThread[@"id"]);
XCTAssertEqualObjects(@"cocoa", secondThread[@"type"]);
XCTAssertNotNil(secondThread[@"stacktrace"]);
XCTAssertNil(secondThread[@"errorReportingThread"]);
}

- (BugsnagCrashReport *)generateReportWithThreads:(NSArray *)threads {
return [[BugsnagCrashReport alloc] initWithKSReport:@{@"crash": @{@"threads": threads}}];
}


@end