Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

My iOS app is getting kicked out of the suspended state too quickly. I can't figure out what's hogging the CPU in the background

I need help figuring out what code is getting executed in the background in my app. If I run the Activity Monitor in Instruments on my app, even after I press the home button it uses about 1% of the CPU consistently.

This is, I'm assuming, causing iOS to kick my app out of the suspended state as soon as it's backgroundTimeRemaining has expired. Even on screens where apparently nothing is happening, the app still uses CPU time and gets kicked out of the suspended state even if I just put the phone to sleep for a few minutes.

I've already gone through and done a project-wide search on NSTimers in my app to make sure nothing is getting fired after I leave the app, as well as put breakpoints in popular places for the app to receive network messages, but nothing seems to get hit.

I've confirmed this still happens when it isn't connected to the debugger and other apps seem to go to 0 or much closer to 0 CPU utilization when I press the home button.

What tool can help me achieve my goal and find out what code is running in the background?

Edit 1: After Petesh's comment I played around with the Time Profiler in Instruments a bit more and discovered I could set the Inspection Range and look at only at code run after I put the app in the suspended state. I found some code that shouldn't have been running and I fixed it, but the app is still kicked out of the suspended state after just a few minutes!

The Activity Monitor now reveals only 0.1-0% CPU usage while the app is backgrounded. Running the Time Profiler again reveals that the only thing running is the main run loop I think. 277ms over about an hour of time in the background. If I check the "Invert Call Tree" box it reveals the time is mostly spent in mach_msg_trap with 46ms total over that hour. I'm not sure if this is normal behavior or not.

I've solved some part of my question, but the most important part remains. What is preventing my app from staying in the suspended state?

Edit 2 (FIXED!): After some deeper digging I've found the culprit. A third party library was calling beginBackgroundTaskWithExpirationHandler: and then never calling endBackgroundTask: when it was finished. Therefore, I believe iOS thought the app was still trying to do a background task and after a certain background time was up it just terminated the app even though it wasn't really doing anything.

The Activity Monitor in Instruments now really shows 0% the entire time after the initial background code is run.

I'm not sure if it was just this or a combination of all the things I fixed since I first asked this question, but either way, it's fixed!

EDIT 3: After even a bit more research I found out it wasn't actually the third party library's fault. The library was listening for the applicationDidEnterBackground notification so it could flush it's queue. But in our own applicationDidEnterBackground we also had a force flush for the queue. Therefore it was trying to start the background task twice and only finishing it only once. Oops!

like image 757
teradyl Avatar asked Nov 02 '22 04:11

teradyl


1 Answers

If you read through all the edits on the original question you can see the details and my thought process for solving the problem, but I'm writing this answer so people get the main takeaways.

To figure out what's hogging the CPU in the background: Just use the Inspection Window in the Instruments Time Profiler. I was able to zero in on exactly the part I needed. This didn't actually help, however, in figuring out why my app was terminated prematurely.

To figure out why my app was getting terminated: The real answer was right in front of me. I just had to step through all the code wherever I had a background handler. For every call to beginBackgroundTaskWithExpirationHandler: there needs to be an opposite call for endBackgroundTask:.

There are still a few open issues in my case specifically. I want to run more memory profiling in Instruments to make sure my app can stay in the suspended state longer with even multiple apps running. But that can be a never-ending process since you never really know exactly where that threshold is.

like image 154
teradyl Avatar answered Nov 15 '22 06:11

teradyl