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

Add Native Linked Errors (RN Mixed stack traces) #3201

Merged
merged 20 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

### 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.

### Fixes

- `Sentry.init` must be called before `Sentry.wrap`([#3227](https://github.com/getsentry/sentry-react-native/pull/3227))
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
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
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
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
Loading