Skip to content

Commit

Permalink
Merge pull request #303 from bugsnag/thread-id
Browse files Browse the repository at this point in the history
Capture trace of error reporting thread and identify with boolean flag
  • Loading branch information
fractalwrench authored Sep 3, 2018
2 parents 2fb5e43 + c3b7491 commit 86399b8
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 45 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,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

0 comments on commit 86399b8

Please sign in to comment.