launchdarkly / ios-client-sdk

LaunchDarkly Client-side SDK for iOS (Swift and Obj-C)
https://docs.launchdarkly.com/sdk/client-side/ios
Other
68 stars 82 forks source link

flagStore.updateStore(updatedFlag: featureFlag) - EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000000 #381

Closed Reedyuk closed 3 weeks ago

Reedyuk commented 1 month ago

Describe the bug We are getting 800 users getting a crash - its a small amount of users but enough for investigation.

To reproduce Unable to reproduce.

Expected behavior Not to crash

Logs

Crashed: com.apple.root.default-qos
0  libswiftCore.dylib             0x4065b4 swift_isUniquelyReferenced_nonNull_native + 38
1  UNiDAYS                        0xbddf3c specialized static ObjcLDClient.start(configuration:context:completion:) + 596 (LDClient.swift:596)
2  UNiDAYS                        0xbdd240 @objc static ObjcLDClient.start(configuration:context:completion:) + 4382528064 (<compiler-generated>:4382528064)

Refers to line 596 = https://github.com/launchdarkly/ios-client-sdk/blob/2711aa79dcc24f72b5e7e8928f9e40b4d1e28676/LaunchDarkly/LaunchDarkly/LDClient.swift#L596

Which seems to be around the flagStore.updateStore, maybe flagStore is not being retained?

Library version 9.6.0

XCode and Swift version Xcode 15.1 swift 5.4

Platform the issue occurs on iphone

Additional context We are using the KMM wrapper of the SDK but under the hood this crash is happening within the launch darkly ios sdk. https://github.com/MyUNiDAYS/launch-darkly-kotlin-sdk

tanderson-ld commented 1 month ago

Thank you for reporting this and providing context for the usage. We will take a look in the next day or so and let you know if we find anything.

tanderson-ld commented 1 month ago

@Reedyuk, just to confirm, is that stack trace from a crash that occurred on version 9.6.0? The line number you linked to is the head of our v9 development branch and is different on 9.6.0.

Have you used any other versions of the LaunchDarkly Client SDK and seen this crash?

What percentage of user sessions do you see this crash in?

Are you able to determine from your crash reporting tooling if this is happening in the foreground, in the background, or during a specific portion of your app's lifecycle? Extra information here can help greatly in tracking down the cause.

Thank you!

Reedyuk commented 1 month ago

Its around 0.30% of our users, but we aim to have 99.80% of our users to be crash free, so its a concern. This is the first time integrating and using LaunchDarkly, so 9.6.0 is the first version we have used.

It looks like its happening during the app being open/foreground (not backgrounded).

Nothing seems to standout particularly from the crash.

Reedyuk commented 1 month ago

To add more info to this, it doesnt seem to be iOS version related and its not device related. We have a good spread from ios 17.x to 16.x with the same crash. A mixture of iphones and even ipads too.

tanderson-ld commented 1 month ago

Could you provide an overview of how your application uses the allFlags member of the LDClient.ios.kt file? Does this get invoked in a coroutine? If so, could you provide the details of the coroutine scope and how that executes in KMM?

Could you also share if/when your application invokes close? It isn't explicitly required to call close, it does however give the SDK a signal to send any buffered evaluation events up to our servers. For very short lived applications this can be important for the integrity of event related data.

My inclination right now is to understand the threading behavior of the code and to determine if there are any race conditions or shared references.

Reedyuk commented 1 month ago

The launch darkly wrapper does this for allFlags: https://github.com/MyUNiDAYS/launch-darkly-kotlin-sdk/blob/ad4e4adb867ac4f53656d61b9f34fa473a294e50/library/src/iosMain/kotlin/com/myunidays/launchdarkly/LDClient.ios.kt#L23 Its not being launched from a coroutine inside the wrapper, but looking at our implementation code we do access it from within a coroutine. BUT that said, the stacktrace is crashing before any value is being fetched.

Crashed: com.apple.root.default-qos
0  libswiftCore.dylib             0x4065b4 swift_isUniquelyReferenced_nonNull_native + 38
1  UNiDAYS                        0xbddf3c specialized static ObjcLDClient.start(configuration:context:completion:) + 596 (LDClient.swift:596)
2  UNiDAYS                        0xbdd240 @objc static ObjcLDClient.start(configuration:context:completion:) + 4353610304 (<compiler-generated>:4353610304)
3  UNiDAYS                        0x1350308 kfun:com.myunidays.mp.config.LaunchDarklyConfig#setup#suspend(com.myunidays.mp.config.context.ConfigContext;kotlin.coroutines.Continuation<kotlin.Unit>){}kotlin.Any + 30 (LDClient.ios.kt:30)
4  UNiDAYS                        0x13520cc kfun:com.myunidays.mp.config.LaunchDarklyConfig.$setContextCOROUTINE$0.invokeSuspend#internal + 54 (LaunchDarklyConfig.kt:54)

This stacktrace infers the crash is occurring when we perform the 'start' of Launch Darkly, and its the start that is being called from a coroutine. Which coroutine should we be calling the start method from? We are currently using this coroutine CoroutineScope(dispatcherProvider.default() + SupervisorJob()).launch { Maybe iOS needs to be ran from the main thread?

Close is never invoked so we can ignore that.

tanderson-ld commented 1 month ago

Hi again @Reedyuk. I spent several hours today investigating and did not see any likely culprits in the swift code that would not be more widespread and reported by other customers. I also tried creating a KMM project to try stressing out different error cases, but didn't see any issues in my simple application.

I'm not sure what coroutine you should be calling start from. From my previous experience with KMM, I recall there being a constraint on iOS where instances can not be shared across threads. That is a path to investigate further, but I don't think we will be very helpful as we aren't KMM experts and we also don't have all your code to examine.

If you provide more code snippets and explanation for how your code makes varies calls to the KMM wrapper library you created, that may help us.

Another question: What does your application do when there is no network connectivity? I believe the LDClient.start(...) completion will not be invoked in that case, so your onReady: () -> Unit callback won't fire and the ios instance won't be set here.

Reedyuk commented 1 month ago

Its a tricky one and yes unfortunately there is many additional factors when we involve KMM.

The way we have things in our app, we actually abstract the direct use of launch darkly and other remote config providers by putting them under a 'Config' module, this module can then support multiple 'Config Providers' e.g. Launch Darkly, firebase etc, but without the consuming code knowing.(This is more for your context).

The onready, ensures that when we try to get config values for keys, that we don't do this until launch darkly is 'ready', so in the above abstraction, we immediately ignore launch darkly until it is in a position to fetch. So say we request key 'launch-darkly-cool-flag' and onready hasnt been called, then we would get a null value in our calling code. Not the most elegant solution but it works.

tanderson-ld commented 1 month ago

We have been working with @Reedyuk on this issue through our support channels. We believe this should be fixed now in 9.7.1 for other customers seeing this issue. It may be a few weeks for confirmation as the new version rolls out.