wix-incubator / DetoxSync

Synchronization framework for Detox and other testing frameworks
MIT License
34 stars 16 forks source link

EXC_BAD_ACCESS on clean XCode build #5

Closed michaelgmcd closed 3 years ago

michaelgmcd commented 3 years ago

I'm a little out of my area of expertise with this issue, but while running Detox tests, our app intermittently fails with the infamous "Signal 11 Raised" issue. After debugging in XCode (via the Detox debug documentation), my iOS app crashes in DispatchQueue+DTXSpy.m:

Running manually via XCode (Throws here): Screen Shot 2021-02-03 at 2 23 28 PM

Running via Detox:

detox[29391] ERROR: [Detox.js/APP_CRASH] App crashed in test '...', here are the crash details:
Signal 11 was raised
(
    0   Detox                               0x000000010587bf85 +[NSThread(DetoxUtils) dtx_demangledCallStackSymbols] + 37
    1   Detox                               0x000000010587f190 __DTXHandleCrash + 464
    2   Detox                               0x000000010587f8d5 __DTXHandleSignal + 59
    3   libsystem_platform.dylib            0x00007fff60c815fd _sigtramp + 29
    4   ???                                 0x0000700008f7c580 0x0 + 123145452766592
    5   CoreFoundation                      0x00007fff2048f049 -[__NSDictionaryM dealloc] + 128
    6   libobjc.A.dylib                     0x00007fff2018f834 objc_object::sidetable_release(bool, bool) + 174
    7   libobjc.A.dylib                     0x00007fff2018bb85 _object_remove_assocations + 580
    8   libobjc.A.dylib                     0x00007fff20188f25 objc_destructInstance + 84
    9   libobjc.A.dylib                     0x00007fff2018efb8 -[NSObject dealloc] + 21
    10  libobjc.A.dylib                     0x00007fff2018f834 objc_object::sidetable_release(bool, bool) + 174
    11  CFNetwork                           0x00007fff236cbc58 _CFNetworkHTTPConnectionCacheSetLimit + 163696
    12  CFNetwork                           0x00007fff23515055 CFNetwork + 24661
    13  DetoxSync                           0x000000010662a600 ____detox_sync_dispatch_wrapper_block_invoke + 23
    14  libdispatch.dylib                   0x00007fff2010532f _dispatch_call_block_and_release + 12
    15  libdispatch.dylib                   0x00007fff20106508 _dispatch_client_callout + 8
    16  libdispatch.dylib                   0x00007fff2010c3f9 _dispatch_lane_serial_drain + 715
    17  libdispatch.dylib                   0x00007fff2010cfab _dispatch_lane_invoke + 458
    18  libdispatch.dylib                   0x00007fff20117577 _dispatch_workloop_worker_thread + 782
    19  libsystem_pthread.dylib             0x00007fff60c8aa3d _pthread_wqthread + 290
    20  libsystem_pthread.dylib             0x00007fff60c89b77 start_wqthread + 15
)

I'm happy to provide any additional information you need. We are on the latest version of Detox.

Thanks for all of your work on these projects. We've used Detox for years now and it's been a great tool to work with.

michaelgmcd commented 3 years ago

This may have to do with react-native-firebase. We're still investigating.

LeoNatan commented 3 years ago

The crash seems to originate in CFNetwork, as you can see in your stack trace.

Does this reproduce consistently? If so, please create an minimal example project that reproduces this.

michaelgmcd commented 3 years ago

This is the root cause: https://github.com/firebase/firebase-ios-sdk/issues/7277

The app will crash in the following scenario:

  1. Firebase perf httpMetric is started
  2. React Native code is reloaded before the httpMetric is stopped.

Disabling firebase performance seems to fix this. I'll re-open if I find anything else.

LeoNatan commented 3 years ago

I think the problem might actually be with RN's Flipper. Try disabling that and see if there are crashes. We've had issues with Flipper+Detox also due to bugs in Flipper.

michaelgmcd commented 3 years ago

@LeoNatan I've made a sample app that reproduces this issue: https://github.com/michaelgmcd/DetoxTestApp

The only thing I've added on top of the npx react-native init code is react-native-firebase.

LeoNatan commented 3 years ago

I will take a look, thanks!

michaelgmcd commented 3 years ago

It's worth noting that react-native-firebase has its own suite of detox tests. This includes a pretty massive patch for Detox. It's a little too cryptic for me to really understand what's going on, but you may have some better luck: https://github.com/invertase/react-native-firebase/blob/master/tests/patches/detox%2B17.14.6.patch

michaelgmcd commented 3 years ago

This patch seems to only deal with android though so it likely has no effect.

michaelgmcd commented 3 years ago

This seems to just be a Firebase issue: https://github.com/firebase/firebase-ios-sdk/issues/6734. Closing again (for now).

LeoNatan commented 3 years ago

Didn't see a stack trace in that thread. Is there one?

michaelgmcd commented 3 years ago

This thread has a similar one: https://github.com/firebase/firebase-ios-sdk/issues/6886

However, it was supposed to have been fixed in FirebasePerformance 7.0.1 and the example is well above that. Apologies for the constant close/re-opening.

LeoNatan commented 3 years ago

That crash doesn’t seem related to this one. You see the stack trace is different.

michaelgmcd commented 3 years ago

Make sense. Any idea what the root cause might be?

LeoNatan commented 3 years ago

Not yet. I’ll look at it probably tomorrow.

LeoNatan commented 3 years ago

Running your app without any DetoxSync linked, it still crashes:

Screen Shot 2021-02-09 at 22 27 00

This is due to terrible Flipper.

LeoNatan commented 3 years ago

Running the app with Flopper disabled, but with DetoxSync, the app does not crash. I do see this error in the log:

2021-02-09 22:29:20.245657+0200 DetoxTestApp[22862:1694078] 7.5.0 - [Firebase/Crashlytics][I-CLS000000] Failed to download settings. If this is your first time launching the app, make sure you have enabled Crashlytics in the Firebase Console. Error Domain=FIRCLSNetworkError Code=-5 "(null)" UserInfo={status_code=404, type=2, request_id=, content_type=text/html; charset=utf-8}
LeoNatan commented 3 years ago

Reproduced by adding fetch("https://github.com/wix/DetoxSync/issues/5"); somewhere in the JS. Investigating.

LeoNatan commented 3 years ago

I don't believe this is an issue in DetoxSync. I think it's something in the Google SDK. It seems like memory isn't managed correctly for some objects that are created inside GULObjectSwizzler, which comes from the SDK. I suggest opening an issue there with the following information:

(lldb) bt
* thread #10, queue = 'com.apple.NSURLSession-work', stop reason = EXC_BAD_ACCESS (code=2, address=0x6040130001b6)
  * frame #0: 0x00007fff2018f768 libobjc.A.dylib`objc_release + 8
    frame #1: 0x00007fff20490126 CoreFoundation`cow_cleanup + 152
    frame #2: 0x00007fff20490049 CoreFoundation`-[__NSDictionaryM dealloc] + 128
    frame #3: 0x00007fff2018f834 libobjc.A.dylib`objc_object::sidetable_release(bool, bool) + 174
    frame #4: 0x00007fff2018bb85 libobjc.A.dylib`_object_remove_assocations + 580
    frame #5: 0x00007fff20188f25 libobjc.A.dylib`objc_destructInstance + 84
    frame #6: 0x00007fff20430bc7 CoreFoundation`-[NSObject(NSObject) __dealloc_zombie] + 159
    frame #7: 0x00007fff2018f834 libobjc.A.dylib`objc_object::sidetable_release(bool, bool) + 174
    frame #8: 0x00007fff236d0b10 CFNetwork`___lldb_unnamed_symbol8355$$CFNetwork + 355
    frame #9: 0x00007fff23519ec5 CFNetwork`___lldb_unnamed_symbol114$$CFNetwork + 21
    frame #10: 0x000000010dc66c4b libclang_rt.asan_iossim_dynamic.dylib`__wrap_dispatch_async_block_invoke + 203
    frame #11: 0x000000010faab7ec libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #12: 0x000000010faac9c8 libdispatch.dylib`_dispatch_client_callout + 8
    frame #13: 0x000000010fab3296 libdispatch.dylib`_dispatch_lane_serial_drain + 796
    frame #14: 0x000000010fab3f9d libdispatch.dylib`_dispatch_lane_invoke + 493
    frame #15: 0x000000010fabfde2 libdispatch.dylib`_dispatch_workloop_worker_thread + 882
    frame #16: 0x00007fff611684c0 libsystem_pthread.dylib`_pthread_wqthread + 314
    frame #17: 0x00007fff61167493 libsystem_pthread.dylib`start_wqthread + 15
(lldb) p $arg1
(unsigned long) $0 = 105827997000656
(lldb) memory history 105827997000656
  thread #4294967295: tid = 0x000a, 0x000000010dc67b56 libclang_rt.asan_iossim_dynamic.dylib`wrap_free + 166, name = 'Memory deallocated by Thread 10'
    frame #0: 0x000000010dc67b56 libclang_rt.asan_iossim_dynamic.dylib`wrap_free + 166
    frame #1: 0x00007fff20181bf3 libobjc.A.dylib`free_class(objc_class*) + 1062
    frame #2: 0x00007fff201888b5 libobjc.A.dylib`objc_disposeClassPair + 334
    frame #3: 0x000000010a4cf84a DetoxTestApp`-[GULObjectSwizzler dealloc](self=<unavailable>, _cmd=<unavailable>) at GULObjectSwizzler.m:166:7
    frame #4: 0x00007fff2018f833 libobjc.A.dylib`objc_object::sidetable_release(bool, bool) + 173
    frame #5: 0x00007fff2018bb84 libobjc.A.dylib`_object_remove_assocations + 579
    frame #6: 0x00007fff20188f24 libobjc.A.dylib`objc_destructInstance + 83
    frame #7: 0x00007fff20430bc6 CoreFoundation`-[NSObject(NSObject) __dealloc_zombie] + 158
    frame #8: 0x00007fff2018f833 libobjc.A.dylib`objc_object::sidetable_release(bool, bool) + 173
    frame #9: 0x00007fff236d0b0f CFNetwork`___lldb_unnamed_symbol8355$$CFNetwork + 354
    frame #10: 0x00007fff23519ec4 CFNetwork`___lldb_unnamed_symbol114$$CFNetwork + 20
    frame #11: 0x000000010dc66c4a libclang_rt.asan_iossim_dynamic.dylib`__wrap_dispatch_async_block_invoke + 202
    frame #12: 0x000000010faab7eb libdispatch.dylib`_dispatch_call_block_and_release + 11
    frame #13: 0x000000010faac9c7 libdispatch.dylib`_dispatch_client_callout + 7
    frame #14: 0x000000010fab3295 libdispatch.dylib`_dispatch_lane_serial_drain + 795
    frame #15: 0x000000010fab3f9c libdispatch.dylib`_dispatch_lane_invoke + 492
    frame #16: 0x000000010fabfde1 libdispatch.dylib`_dispatch_workloop_worker_thread + 881
    frame #17: 0x00007fff611684bf libsystem_pthread.dylib`_pthread_wqthread + 313
    frame #18: 0x00007fff61167492 libsystem_pthread.dylib`start_wqthread + 14
  thread #4294967295: tid = 0x0001, 0x000000010dc67de2 libclang_rt.asan_iossim_dynamic.dylib`wrap_calloc + 162, name = 'Memory allocated by Thread 1'
    frame #0: 0x000000010dc67de2 libclang_rt.asan_iossim_dynamic.dylib`wrap_calloc + 162
    frame #1: 0x00007fff20188489 libobjc.A.dylib`objc_allocateClassPair + 149
    frame #2: 0x000000010a4cdb11 DetoxTestApp`-[GULObjectSwizzler initWithObject:](self=<unavailable>, _cmd=<unavailable>, object=<unavailable>) at GULObjectSwizzler.m:95:23
    frame #3: 0x00000001097175ed DetoxTestApp`-[FPRObjectInstrumentor initWithObject:](self=<unavailable>, _cmd=<unavailable>, object=<unavailable>) at FPRObjectInstrumentor.m:40:23
    frame #4: 0x00000001096f85f2 DetoxTestApp`__52-[FPRNSURLSessionDelegateInstrument registerObject:]_block_invoke(.block_descriptor=<unavailable>) at FPRNSURLSessionDelegateInstrument.m:240:43
    frame #5: 0x000000010edafd0f DetoxSync`____detox_sync_dispatch_wrapper_block_invoke(.block_descriptor=<unavailable>) at DispatchQueue+DTXSpy.m:45:3
    frame #6: 0x000000010faac9c7 libdispatch.dylib`_dispatch_client_callout + 7
    frame #7: 0x000000010fabbbfd libdispatch.dylib`_dispatch_lane_barrier_sync_invoke_and_complete + 131
    frame #8: 0x000000010edada6b DetoxSync`__detox_sync_dispatch_sync [inlined] __detox_sync_dispatch_wrapper(func=<unavailable>, name=<unavailable>, copy=<unavailable>, param1=<unavailable>, _param2=<unavailable>) at DispatchQueue+DTXSpy.m:42:2
    frame #9: 0x000000010edad4f4 DetoxSync`__detox_sync_dispatch_sync(queue=<unavailable>, block=<unavailable>) at DispatchQueue+DTXSpy.m:78
    frame #10: 0x00000001096f82c0 DetoxTestApp`-[FPRNSURLSessionDelegateInstrument registerObject:](self=<unavailable>, _cmd=<unavailable>, object=<unavailable>) at FPRNSURLSessionDelegateInstrument.m:236:3
    frame #11: 0x0000000109708d5d DetoxTestApp`__InstrumentSessionWithConfigurationDelegateDelegateQueue_block_invoke(.block_descriptor=<unavailable>, session=<unavailable>, configuration=<unavailable>, delegate=<unavailable>, queue=<unavailable>) at FPRNSURLSessionInstrument.m:159:11
    frame #12: 0x000000010a897685 DetoxTestApp`-[RCTMultipartDataTask startTask](self=<unavailable>, _cmd=<unavailable>) at RCTMultipartDataTask.m:38:27
    frame #13: 0x000000010a81f3f6 DetoxTestApp`attemptAsynchronousLoadOfBundleAtURL(scriptURL=<unavailable>, onProgress=<unavailable>, onComplete=<unavailable>) block_pointer, void (NSError*, RCTSource*) block_pointer) at RCTJavaScriptLoader.mm:322:3
    frame #14: 0x000000010a81dc5f DetoxTestApp`+[RCTJavaScriptLoader loadBundleAtURL:onProgress:onComplete:](self=<unavailable>, _cmd=<unavailable>, scriptURL=<unavailable>, onProgress=<unavailable>, onComplete=<unavailable>) at RCTJavaScriptLoader.mm:87:5
    frame #15: 0x000000010edd06da DetoxSync`__detox_sync_loadBundleAtURL_onProgress_onComplete(self=<unavailable>, _cmd=<unavailable>, url=<unavailable>, onProgress=<unavailable>, onComplete=<unavailable>) at DTXReactNativeSupport.m:111:2
    frame #16: 0x000000010a7511de DetoxTestApp`-[RCTCxxBridge loadSource:onProgress:](self=<unavailable>, _cmd=<unavailable>, _onSourceLoad=<unavailable>, onProgress=<unavailable>) at RCTCxxBridge.mm:428:5
    frame #17: 0x000000010a74cb69 DetoxTestApp`-[RCTCxxBridge start](self=<unavailable>, _cmd=<unavailable>) at RCTCxxBridge.mm:364:3
    frame #18: 0x000000010a6d777d DetoxTestApp`-[RCTBridge setUp](self=<unavailable>, _cmd=<unavailable>) at RCTBridge.m:321:3
    frame #19: 0x000000010a6d45df DetoxTestApp`-[RCTBridge initWithDelegate:bundleURL:moduleProvider:launchOptions:](self=<unavailable>, _cmd=<unavailable>, delegate=<unavailable>, bundleURL=<unavailable>, block=<unavailable>, launchOptions=<unavailable>) at RCTBridge.m:175:5
    frame #20: 0x000000010a6d3885 DetoxTestApp`-[RCTBridge initWithDelegate:launchOptions:](self=<unavailable>, _cmd=<unavailable>, delegate=<unavailable>, launchOptions=<unavailable>) at RCTBridge.m:154:10
    frame #21: 0x000000010931da0f DetoxTestApp`-[AppDelegate application:didFinishLaunchingWithOptions:](self=<unavailable>, _cmd=<unavailable>, application=<unavailable>, launchOptions=<unavailable>) at AppDelegate.m:39:23
    frame #22: 0x00007fff24692fdc UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 231
    frame #23: 0x00007fff24694b5e UIKitCore`-[UIApplication _callInitializationDelegatesWithActions:forCanvas:payload:fromOriginatingProcess:] + 3918
    frame #24: 0x00007fff2469a56c UIKitCore`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1236
    frame #25: 0x00007fff23cc36f6 UIKitCore`-[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:] + 121
    frame #26: 0x00007fff24251d1d UIKitCore`_UIScenePerformActionsWithLifecycleActionMask + 87
    frame #27: 0x00007fff23cc4205 UIKitCore`__101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke + 197
    frame #28: 0x00007fff23cc3cc9 UIKitCore`-[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:] + 473
    frame #29: 0x00007fff23cc4036 UIKitCore`-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:] + 818
    frame #30: 0x00007fff23cc38ca UIKitCore`-[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:] + 344
michaelgmcd commented 3 years ago

Thanks for the help here @LeoNatan. I'll follow up.

sam-barker commented 3 years ago

@michaelgmcd did you manage to follow up? if so could you link to an issue you have created for the google sdk?

michaelgmcd commented 3 years ago

I have not yet. Our current workaround is to wrap "launchApp" and "reloadReactNative" in retries. I've spent several days debugging this and it's always difficult to open issues on the firebase side when using react-native-firebase.

LeoNatan commented 3 years ago

I’d say the RN wrapper doesn’t play a role. The issue is in the Google Objective C code.

riskpp commented 3 years ago

Hi @LeoNatan, unfortunately I have similar issue and I don't use firebase or Flipper. I found that if comment out rebindings inside _install_dispatchqueue_spy crash disappears. And I didn't notice this crash before, when version 17.x was used. Could you please explain why these rebindings needed and may be is it possible to switch off them by some setting?

LeoNatan commented 3 years ago

@riskpp The dispatch queue rebindings are necessary for tracking dispatch queues. Otherwise a background task might be missed. If you can, please open a new issue with a reproduction, and I’ll take a look. Thanks

mehdyouras commented 3 years ago

@michaelgmcd Can you give me an example of your retries please?

LeoNatan commented 3 years ago

@riskpp Can you please add an example project of where DetoxSync is crashing for you?

michaelgmcd commented 3 years ago
import { retry } from 'ts-retry-promise';

const buildDetoxURLBlacklistRegex = () =>
  `(${constants.BLACKLISTED_URLS.map(urlRegex => `\\"${urlRegex}\\"`).join(',')})`;

export const launchApp = async () => {
  await retry(
    async () => {
      try {
        await device.launchApp({
          newInstance: true,
          launchArgs: {
            detoxPrintBusyIdleResources: 'YES',
            detoxURLBlacklistRegex: buildDetoxURLBlacklistRegex(),
          },
        });
      } catch (error) {
        error.message = `Failed to launch app with error: ${error.message}`;
        throw error;
      }
    },
    { retries: 5 },
  );
};

export const reloadReactNative = async () => {
  await retry(
    async () => {
      try {
        await device.reloadReactNative();
      } catch (error) {
        // eslint-disable-next-line no-console
        console.error('Failed to reload react native with error', error);
        await launchApp();
      }
    },
    { retries: 5 },
  );
};
iduuck commented 3 years ago

So, for now there is no workaround like patching the firebase package? So I am not able to use Firebase and Detox together?

d4vidi commented 3 years ago

All, we are currently on a break and a bit short-staffed at the moment, in general. We hope to get on top of this within 1-2 weeks. Here are similar discussions that could be relevant, in the meantime:

SandroMachado commented 3 years ago

@d4vidi do you have any updates?

d4vidi commented 3 years ago

I wish I had any but we're currently running low-powered on iOS. I hope we could revisit this soon. In the mean time, we have @alon-ha working on https://github.com/wix/Detox/issues/2746 - which would allow a snappy setup of apps where issues like these reproduce (we would appreciate your help in regards...).

ball-hayden commented 3 years ago

I'm currently putting together a simple app that has RNFirebase installed and a couple of simple Detox tests.

The issue seems to happen pretty reliably when repeatedly launching the app with Firebase perf installed: https://github.com/PlayerData/DetoxFirebaseRepro

alexsegura commented 3 years ago

Since I removed @react-native-firebase/perf, this error is gone.

d4vidi commented 3 years ago

@alexsegura thanks for letting us know. This makes a stronger case of that indeed the problem lies in the way the two projects integrate. @ball-hayden @SandroMachado Would you consider trying to set up apps based on https://github.com/wix-incubator/DetoxTemplate?

ball-hayden commented 3 years ago

@d4vidi - there's one already over on https://github.com/wix/Detox/issues/2641#issuecomment-881421844

d4vidi commented 3 years ago

Thanks - got lost there for a minute