Expand my Community achievements bar.

SOLVED

iOS 10 app crashes while in background

Avatar

Employee Advisor

(From Kamil Kocemba on our former forum platform, 28 Oct 2016)

We are seeing lots of crashes on iOS 10.

It seems like this happens when the app is in background and SDK tries to access its database when it's located in a shared container. The system kills the app (SIGKILL) with code 0xdead10cc, which means unauthorized access to a system resource while in background. Looks like since iOS 10 access to shared container has to be performed in a background task (beginBackgroundTask) or at least it is now strictly enforced. Not sure if this is actual reason - mostly guesswork.

Would really appreciate if you could look into this. Here's link to the issue on Github: https://github.com/Adobe-Marketing-Cloud/mobile-services/issues/187

What's weird though is that this issue is iOS 10 only, used to work fine before.

Topics

Topics help categorize Community content and increase your ability to discover relevant content.

1 Accepted Solution

Avatar

Correct answer by
Employee Advisor

(From Kamil Kocemba on our former forum platform, 2 Nov 2016)

I have some news. Seems I was right. Doing something like this to simulate race condition:

~~~~ 
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        NSString *configurationPath = [[NSBundle mainBundle] pathForResource:@"adobe-development" ofType:@"json"];
        [ADBMobile overrideConfigPath:configurationPath];
        [ADBMobile setDebugLogging:YES];
        [ADBMobile setAppGroup:@"group.sdk.test"];
        [ADBMobile collectLifecycleData];
        [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer *timer) {
              [ADBMobile trackAction:@"TEST" data:nil];
              [ADBMobile trackingClearQueue];
              }];
        return YES;
        }
~~~~

And putting app in background will cause the crash I am talking about. This only happens if you specify app group. Of course this is not how we are using the SDK - just trying to give you something to reproduce the issue. I've put together small sample on Github: https://github.com/kkodev/ADBSDKTest/tree/master/SDKTest. Put SDK config in `adobe-development.json`, update code signing and run on iOS 10 device. Proceed to the second screen and put the app in background. It will most likely crash so when you reopen you are back on first screen. If you navigate to Organizer you will see familiar crash log on device. I tried wrapping the calls in a background task, but seems like database is accessed asynchronously so this needs to be done inside appropriate methods of the SDK itself. If you replace:

~~~~

[NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer *timer) {

         [ADBMobile trackAction:@"TEST" data:nil];

         [ADBMobile trackingClearQueue];

         }];

~~~~

with:

~~~~

[NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer *timer) {

         __block UIBackgroundTaskIdentifier taskId = [application beginBackgroundTaskWithExpirationHandler:^{

                [application endBackgroundTask:taskId];

                taskId = UIBackgroundTaskInvalid;

                }];

       dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

       dispatch_async(queue, ^{

              [ADBMobile trackAction:@"TEST" data:nil];

              [ADBMobile trackingClearQueue];

              // Give SDK extra time to perform async I/O

              dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^{

                     [application endBackgroundTask:taskId];

                     taskId = UIBackgroundTaskInvalid;

                     });

              });

       }];

~~~~

Then the problem goes away (at least I am no longer able to reproduce it). This is obviously a hack, but shows where the problem is. Let me know if it helps.

View solution in original post

2 Replies

Avatar

Employee Advisor

(From Kamil Kocemba on our former forum platform, 1 Nov 2016)

Unfortunately we aren't able to reproduce it reliably either. It does happen fairly often to our customers though. We have tens of thousands of crash reports from ITC and that's only coming from users who opted-in to provide that data. None of the crash reports specifies responsible thread. The app gets killed so it shows as crash in `main()`. I've however gone through what we have and here's my analysis:

  • Every crash is caused by `SIGKILL` with `0xdead10cc` reason code which is received in `main()` on main thread - it looks like the app was in background, because all active threads seem to be spawned from a schedule and there is no trace of app or foreground activity
  • There are multiple other threads, but all of them, except for one, is either sleeping (`__psynch_cvwait`, `__semwait_signal`) or gets killed (`mach_msg_trap`) at the time of crash
  • The one running thread is accessing `sqlite3` database at the time of crash (`fsync` or `__guarded_open_np`)

So the only 2 things that are consistent in all these reports are:

  • 1. `SIGKILL 0xdead10cc`
  • 2. That lone thread that was running and trying to access the database.

Which makes me believe that's the thread worth investigating. Methods causing access seem to come from:

  • `__38-[ADBAnalyticsWorker queue:timestamp:]_block_invoke + 108`
  • `__29-[ADBHitQueueBase deleteHit:]_block_invoke + 56`
  • `__29-[ADBHitQueueBase deleteHit:]_block_invoke + 48`

Now there's question about `0xdead10cc` and accessing *system resource*. Database is not a system resource. But perhaps resources in a shared container are now considered system resources. I know it's vague but I was told that *"someone from Apple said"* that access to such resources from background has to be now wrapped in a background task. I'm not sure if that's official response from DTS, I'll report back once I find out. If I'm correct, I'd imagine that it should be relatively straightforward to reproduce the issue by:

  • Creating an app that stores all the data in a shared container
  • Triggering one of the methods from SDK mentioned above while in background (be it `dispatch_after` or any other way)

If you know how to achieve this using public methods in the SDK, I'm happy to try it out myself. I'm sorry for lots of guesswork but I don't really know what else I can do at this point. One way to definitely disprove it would be to ship an app version without the SDK, but it seems like absolutely last resort. I'm quite determined to find a fix or workaround, because it affects tens of thousands of users each day. Let me know if there is anything I can do to help.

Avatar

Correct answer by
Employee Advisor

(From Kamil Kocemba on our former forum platform, 2 Nov 2016)

I have some news. Seems I was right. Doing something like this to simulate race condition:

~~~~ 
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        NSString *configurationPath = [[NSBundle mainBundle] pathForResource:@"adobe-development" ofType:@"json"];
        [ADBMobile overrideConfigPath:configurationPath];
        [ADBMobile setDebugLogging:YES];
        [ADBMobile setAppGroup:@"group.sdk.test"];
        [ADBMobile collectLifecycleData];
        [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer *timer) {
              [ADBMobile trackAction:@"TEST" data:nil];
              [ADBMobile trackingClearQueue];
              }];
        return YES;
        }
~~~~

And putting app in background will cause the crash I am talking about. This only happens if you specify app group. Of course this is not how we are using the SDK - just trying to give you something to reproduce the issue. I've put together small sample on Github: https://github.com/kkodev/ADBSDKTest/tree/master/SDKTest. Put SDK config in `adobe-development.json`, update code signing and run on iOS 10 device. Proceed to the second screen and put the app in background. It will most likely crash so when you reopen you are back on first screen. If you navigate to Organizer you will see familiar crash log on device. I tried wrapping the calls in a background task, but seems like database is accessed asynchronously so this needs to be done inside appropriate methods of the SDK itself. If you replace:

~~~~

[NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer *timer) {

         [ADBMobile trackAction:@"TEST" data:nil];

         [ADBMobile trackingClearQueue];

         }];

~~~~

with:

~~~~

[NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer *timer) {

         __block UIBackgroundTaskIdentifier taskId = [application beginBackgroundTaskWithExpirationHandler:^{

                [application endBackgroundTask:taskId];

                taskId = UIBackgroundTaskInvalid;

                }];

       dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

       dispatch_async(queue, ^{

              [ADBMobile trackAction:@"TEST" data:nil];

              [ADBMobile trackingClearQueue];

              // Give SDK extra time to perform async I/O

              dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^{

                     [application endBackgroundTask:taskId];

                     taskId = UIBackgroundTaskInvalid;

                     });

              });

       }];

~~~~

Then the problem goes away (at least I am no longer able to reproduce it). This is obviously a hack, but shows where the problem is. Let me know if it helps.