Skip to content

Commit

Permalink
feat(rn): Add Native Linked Errors (RN Mixed stack traces) (#3201)
Browse files Browse the repository at this point in the history
  • Loading branch information
krystofwoldrich authored Aug 11, 2023
1 parent a36d5e1 commit e64a66f
Show file tree
Hide file tree
Showing 18 changed files with 1,138 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

### Features

- Add support for React Native mixed stacktraces ([#3201](https://github.com/getsentry/sentry-react-native/pull/3201))

In the current `react-native@nightly` (`0.73.0-nightly-20230809-cb60e5c67`) JS errors from native modules can
contain native JVM or Objective-C exception stack trace. Both JS and native stack trace
are processed by default no configuration needed.

- Add `tracePropagationTargets` option ([#3230](https://github.com/getsentry/sentry-react-native/pull/3230))

This release adds support for [distributed tracing](https://docs.sentry.io/platforms/react-native/usage/distributed-tracing/)
Expand Down
1 change: 1 addition & 0 deletions RNSentryCocoaTester/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ platform :ios, '12.4'
target 'RNSentryCocoaTesterTests' do
use_react_native!()
pod 'RNSentry', :path => '../RNSentry.podspec'
pod 'OCMock', '3.9.1'
end
90 changes: 90 additions & 0 deletions RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
/* Begin PBXFileReference section */
1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.release.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.release.xcconfig"; sourceTree = "<group>"; };
3360898D29524164007C7730 /* RNSentryCocoaTesterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNSentryCocoaTesterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
338739072A7D7D2800950DDD /* RNSentry+initNativeSdk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNSentry+initNativeSdk.h"; sourceTree = "<group>"; };
33F58ACF2977037D008F60EA /* RNSentry+initNativeSdk.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "RNSentry+initNativeSdk.mm"; sourceTree = "<group>"; };
650CB718ACFBD05609BF2126 /* libPods-RNSentryCocoaTesterTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNSentryCocoaTesterTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
E2321E7CFA55AB617247098E /* Pods-RNSentryCocoaTesterTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.debug.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.debug.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -62,6 +63,7 @@
isa = PBXGroup;
children = (
33F58ACF2977037D008F60EA /* RNSentry+initNativeSdk.mm */,
338739072A7D7D2800950DDD /* RNSentry+initNativeSdk.h */,
);
path = RNSentryCocoaTesterTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -249,6 +251,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "\"${PODS_ROOT}/Sentry/Sources/Sentry/include\"";
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
Expand Down Expand Up @@ -301,6 +304,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "\"${PODS_ROOT}/Sentry/Sources/Sentry/include\"";
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
Expand All @@ -316,6 +320,49 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"\"${PODS_ROOT}/Headers/Public\"",
"\"${PODS_ROOT}/Headers/Public/DoubleConversion\"",
"\"${PODS_ROOT}/Headers/Public/FBLazyVector\"",
"\"${PODS_ROOT}/Headers/Public/OCMock\"",
"\"${PODS_ROOT}/Headers/Public/RCT-Folly\"",
"\"${PODS_ROOT}/Headers/Public/RCTRequired\"",
"\"${PODS_ROOT}/Headers/Public/RCTTypeSafety\"",
"\"${PODS_ROOT}/Headers/Public/RNSentry\"",
"\"${PODS_ROOT}/Headers/Public/React-Codegen\"",
"\"${PODS_ROOT}/Headers/Public/React-Core\"",
"\"${PODS_ROOT}/Headers/Public/React-NativeModulesApple\"",
"\"${PODS_ROOT}/Headers/Public/React-RCTAnimation\"",
"\"${PODS_ROOT}/Headers/Public/React-RCTAppDelegate\"",
"\"${PODS_ROOT}/Headers/Public/React-RCTBlob\"",
"\"${PODS_ROOT}/Headers/Public/React-RCTText\"",
"\"${PODS_ROOT}/Headers/Public/React-callinvoker\"",
"\"${PODS_ROOT}/Headers/Public/React-cxxreact\"",
"\"${PODS_ROOT}/Headers/Public/React-debug\"",
"\"${PODS_ROOT}/Headers/Public/React-hermes\"",
"\"${PODS_ROOT}/Headers/Public/React-jsi\"",
"\"${PODS_ROOT}/Headers/Public/React-jsiexecutor\"",
"\"${PODS_ROOT}/Headers/Public/React-jsinspector\"",
"\"${PODS_ROOT}/Headers/Public/React-logger\"",
"\"${PODS_ROOT}/Headers/Public/React-perflogger\"",
"\"${PODS_ROOT}/Headers/Public/React-runtimeexecutor\"",
"\"${PODS_ROOT}/Headers/Public/React-runtimescheduler\"",
"\"${PODS_ROOT}/Headers/Public/React-utils\"",
"\"${PODS_ROOT}/Headers/Public/ReactCommon\"",
"\"${PODS_ROOT}/Headers/Public/Sentry\"",
"\"${PODS_ROOT}/Headers/Public/SocketRocket\"",
"\"${PODS_ROOT}/Headers/Public/Yoga\"",
"\"${PODS_ROOT}/Headers/Public/fmt\"",
"\"${PODS_ROOT}/Headers/Public/glog\"",
"\"${PODS_ROOT}/Headers/Public/hermes-engine\"",
"\"${PODS_ROOT}/Headers/Public/libevent\"",
"\"$(PODS_ROOT)/DoubleConversion\"",
"\"$(PODS_ROOT)/boost\"",
"\"$(PODS_ROOT)/Headers/Private/React-Core\"",
"\"$(PODS_TARGET_SRCROOT)/include/\"",
"\"${PODS_ROOT}/Sentry/Sources/Sentry/include\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = io.sentry.RNSentryCocoaTesterTests;
Expand All @@ -335,6 +382,49 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = (
"$(inherited)",
"\"${PODS_ROOT}/Headers/Public\"",
"\"${PODS_ROOT}/Headers/Public/DoubleConversion\"",
"\"${PODS_ROOT}/Headers/Public/FBLazyVector\"",
"\"${PODS_ROOT}/Headers/Public/OCMock\"",
"\"${PODS_ROOT}/Headers/Public/RCT-Folly\"",
"\"${PODS_ROOT}/Headers/Public/RCTRequired\"",
"\"${PODS_ROOT}/Headers/Public/RCTTypeSafety\"",
"\"${PODS_ROOT}/Headers/Public/RNSentry\"",
"\"${PODS_ROOT}/Headers/Public/React-Codegen\"",
"\"${PODS_ROOT}/Headers/Public/React-Core\"",
"\"${PODS_ROOT}/Headers/Public/React-NativeModulesApple\"",
"\"${PODS_ROOT}/Headers/Public/React-RCTAnimation\"",
"\"${PODS_ROOT}/Headers/Public/React-RCTAppDelegate\"",
"\"${PODS_ROOT}/Headers/Public/React-RCTBlob\"",
"\"${PODS_ROOT}/Headers/Public/React-RCTText\"",
"\"${PODS_ROOT}/Headers/Public/React-callinvoker\"",
"\"${PODS_ROOT}/Headers/Public/React-cxxreact\"",
"\"${PODS_ROOT}/Headers/Public/React-debug\"",
"\"${PODS_ROOT}/Headers/Public/React-hermes\"",
"\"${PODS_ROOT}/Headers/Public/React-jsi\"",
"\"${PODS_ROOT}/Headers/Public/React-jsiexecutor\"",
"\"${PODS_ROOT}/Headers/Public/React-jsinspector\"",
"\"${PODS_ROOT}/Headers/Public/React-logger\"",
"\"${PODS_ROOT}/Headers/Public/React-perflogger\"",
"\"${PODS_ROOT}/Headers/Public/React-runtimeexecutor\"",
"\"${PODS_ROOT}/Headers/Public/React-runtimescheduler\"",
"\"${PODS_ROOT}/Headers/Public/React-utils\"",
"\"${PODS_ROOT}/Headers/Public/ReactCommon\"",
"\"${PODS_ROOT}/Headers/Public/Sentry\"",
"\"${PODS_ROOT}/Headers/Public/SocketRocket\"",
"\"${PODS_ROOT}/Headers/Public/Yoga\"",
"\"${PODS_ROOT}/Headers/Public/fmt\"",
"\"${PODS_ROOT}/Headers/Public/glog\"",
"\"${PODS_ROOT}/Headers/Public/hermes-engine\"",
"\"${PODS_ROOT}/Headers/Public/libevent\"",
"\"$(PODS_ROOT)/DoubleConversion\"",
"\"$(PODS_ROOT)/boost\"",
"\"$(PODS_ROOT)/Headers/Private/React-Core\"",
"\"$(PODS_TARGET_SRCROOT)/include/\"",
"\"${PODS_ROOT}/Sentry/Sources/Sentry/include\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = io.sentry.RNSentryCocoaTesterTests;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#import <Foundation/Foundation.h>
#import <RNSentry/RNSentry.h>

@interface
SentrySDK (PrivateTests)
- (nullable SentryOptions *) options;
@end

@interface SentryBinaryImageInfo : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic) uint64_t address;
@property (nonatomic) uint64_t size;
@end

@interface SentryBinaryImageCache : NSObject
@property (nonatomic, readonly, class) SentryBinaryImageCache *shared;
- (void)start;
- (void)stop;
- (nullable SentryBinaryImageInfo *)imageByAddress:(const uint64_t)address;
@end

@interface SentryDependencyContainer : NSObject
+ (instancetype)sharedInstance;
@property (nonatomic, strong) SentryDebugImageProvider *debugImageProvider;
@end
125 changes: 122 additions & 3 deletions RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentry+initNativeSdk.mm
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#import "RNSentry+initNativeSdk.h"
#import <OCMock/OCMock.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <Sentry/SentryOptions.h>
#import <Sentry/SentryEvent.h>
#import "RNSentry.h"
#import <RNSentry/RNSentry.h>

@interface RNSentryInitNativeSdkTests : XCTestCase

Expand Down Expand Up @@ -168,4 +168,123 @@ - (void)testEventFromSentryReactNativeOriginAndEnvironmentTagsAreOverwritten
XCTAssertEqual(testEvent.tags[@"event.environment"], @"native");
}

void (^expectRejecterNotCalled)(NSString*, NSString*, NSError*) = ^(NSString *code, NSString *message, NSError *error) {
@throw [NSException exceptionWithName:@"Promise Rejector should not be called." reason:nil userInfo:nil];
};

uint64_t MOCKED_SYMBOL_ADDRESS = 123;
char const* MOCKED_SYMBOL_NAME = "symbolicatedname";

int sucessfulSymbolicate(const void *, Dl_info *info){
info->dli_saddr = (void *) MOCKED_SYMBOL_ADDRESS;
info->dli_sname = MOCKED_SYMBOL_NAME;
return 1;
}

- (void)prepareNativeFrameMocksWithLocalSymbolication: (BOOL) debug
{
SentryOptions* sentryOptions = [[SentryOptions alloc] init];
sentryOptions.debug = debug; //no local symbolication

id sentrySDKMock = OCMClassMock([SentrySDK class]);
OCMStub([(SentrySDK*) sentrySDKMock options]).andReturn(sentryOptions);

id sentryBinaryImageInfoMockOne = OCMClassMock([SentryBinaryImageInfo class]);
OCMStub([(SentryBinaryImageInfo*) sentryBinaryImageInfoMockOne address]).andReturn([@112233 unsignedLongLongValue]);
OCMStub([sentryBinaryImageInfoMockOne name]).andReturn(@"testnameone");

id sentryBinaryImageInfoMockTwo = OCMClassMock([SentryBinaryImageInfo class]);
OCMStub([(SentryBinaryImageInfo*) sentryBinaryImageInfoMockTwo address]).andReturn([@112233 unsignedLongLongValue]);
OCMStub([sentryBinaryImageInfoMockTwo name]).andReturn(@"testnametwo");

id sentryBinaryImageCacheMock = OCMClassMock([SentryBinaryImageCache class]);
OCMStub(ClassMethod([sentryBinaryImageCacheMock shared])).andReturn(sentryBinaryImageCacheMock);
OCMStub([sentryBinaryImageCacheMock imageByAddress:[@123 unsignedLongLongValue]]).andReturn(sentryBinaryImageInfoMockOne);
OCMStub([sentryBinaryImageCacheMock imageByAddress:[@456 unsignedLongLongValue]]).andReturn(sentryBinaryImageInfoMockTwo);

NSDictionary* serializedDebugImage = @{
@"uuid": @"mockuuid",
@"debug_id": @"mockdebugid",
@"type": @"macho",
@"image_addr": @"0x000000000001b669",
};
id sentryDebugImageMock = OCMClassMock([SentryDebugMeta class]);
OCMStub([sentryDebugImageMock serialize]).andReturn(serializedDebugImage);

id sentryDebugImageProviderMock = OCMClassMock([SentryDebugImageProvider class]);
OCMStub([sentryDebugImageProviderMock getDebugImagesForAddresses:[NSSet setWithObject:@"0x000000000001b669"] isCrash:false]).andReturn(@[sentryDebugImageMock]);

id sentryDependencyContainerMock = OCMClassMock([SentryDependencyContainer class]);
OCMStub(ClassMethod([sentryDependencyContainerMock sharedInstance])).andReturn(sentryDependencyContainerMock);
OCMStub([sentryDependencyContainerMock debugImageProvider]).andReturn(sentryDebugImageProviderMock);
}

- (void)testFetchNativeStackFramesByInstructionsServerSymbolication
{
[self prepareNativeFrameMocksWithLocalSymbolication:NO];
RNSentry* rnSentry = [[RNSentry alloc] init];
NSDictionary* actual = [rnSentry fetchNativeStackFramesBy: @[@123, @456]
symbolicate: sucessfulSymbolicate];

NSDictionary* expected = @{
@"debugMetaImages": @[
@{
@"uuid": @"mockuuid",
@"debug_id": @"mockdebugid",
@"type": @"macho",
@"image_addr": @"0x000000000001b669",
},
],
@"frames": @[
@{
@"package": @"testnameone",
@"in_app": @NO,
@"platform": @"cocoa",
@"instruction_addr": @"0x000000000000007b", //123
@"image_addr": @"0x000000000001b669", //112233
},
@{
@"package": @"testnametwo",
@"in_app": @NO,
@"platform": @"cocoa",
@"instruction_addr": @"0x00000000000001c8", //456
@"image_addr": @"0x000000000001b669", //445566
},
],
};
XCTAssertTrue([actual isEqualToDictionary:expected]);
}

- (void)testFetchNativeStackFramesByInstructionsOnDeviceSymbolication
{
[self prepareNativeFrameMocksWithLocalSymbolication:YES];
RNSentry* rnSentry = [[RNSentry alloc] init];
NSDictionary* actual = [rnSentry fetchNativeStackFramesBy: @[@123, @456]
symbolicate: sucessfulSymbolicate];

NSDictionary* expected = @{
@"frames": @[
@{
@"function": @"symbolicatedname",
@"package": @"testnameone",
@"in_app": @NO,
@"platform": @"cocoa",
@"symbol_addr": @"0x000000000000007b", //123
@"instruction_addr": @"0x000000000000007b", //123
@"image_addr": @"0x000000000001b669", //112233
},
@{
@"function": @"symbolicatedname",
@"package": @"testnametwo",
@"in_app": @NO,
@"platform": @"cocoa",
@"symbol_addr": @"0x000000000000007b", //123
@"instruction_addr": @"0x00000000000001c8", //456
@"image_addr": @"0x000000000001b669", //445566
},
],
};
XCTAssertTrue([actual isEqualToDictionary:expected]);
}

@end
4 changes: 4 additions & 0 deletions android/src/main/java/io/sentry/react/RNSentryModuleImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,10 @@ public void fetchNativeSdkInfo(Promise promise) {
}
}

public void fetchNativePackageName(Promise promise) {
promise.resolve(packageInfo.packageName);
}

private void setEventOriginTag(SentryEvent event) {
SdkVersion sdk = event.getSdk();
if (sdk != null) {
Expand Down
10 changes: 10 additions & 0 deletions android/src/newarch/java/io/sentry/react/RNSentryModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,14 @@ public WritableMap startProfiling() {
public WritableMap stopProfiling() {
return this.impl.stopProfiling();
}

@Override
public void fetchNativePackageName(Promise promise) {
this.impl.fetchNativePackageName(promise);
}

@Override
public void fetchNativeStackFramesBy(Promise promise) {
// Not used on Android
}
}
10 changes: 10 additions & 0 deletions android/src/oldarch/java/io/sentry/react/RNSentryModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,14 @@ public WritableMap startProfiling() {
public WritableMap stopProfiling() {
return this.impl.stopProfiling();
}

@ReactMethod
public void fetchNativePackageName(Promise promise) {
this.impl.fetchNativePackageName(promise);
}

@ReactMethod
public void fetchNativeStackFramesBy(Promise promise) {
// Not used on Android
}
}
20 changes: 19 additions & 1 deletion ios/RNSentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,31 @@
#import "RCTBridge.h"
#endif

#import <dlfcn.h>

#import <Sentry/Sentry.h>
#import <Sentry/SentryOptions.h>
#import <Sentry/SentryDebugImageProvider.h>

typedef int (*SymbolicateCallbackType)(const void *, Dl_info *);

@interface SentryDebugImageProvider ()
- (NSArray<SentryDebugMeta *> * _Nonnull)getDebugImagesForAddresses:(NSSet<NSString *> * _Nonnull)addresses isCrash:(BOOL)isCrash;
@end

@interface
SentrySDK (Private)
@property (nonatomic, nullable, readonly, class) SentryOptions *options;
@end

@interface RNSentry : NSObject <RCTBridgeModule>

- (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)options
error:(NSError *_Nullable*_Nonnull)errorPointer;

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

- (NSDictionary*_Nonnull) fetchNativeStackFramesBy: (NSArray<NSNumber*>*)instructionsAddr
symbolicate: (SymbolicateCallbackType) symbolicate;

@end
Loading

0 comments on commit e64a66f

Please sign in to comment.