Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xcode leaving zombie processes after running iOS tests/simulator

After working in Xcode on an iOS app for a few days, I've noticed that there are over 100 zombie processes hanging around. It appears that there is one for each time that I ran unit tests, and possibly one for each time I ran the full app in the simulator. Here's a sample (cleaned and truncated):

> ps -efj | grep $PRODUCT_NAME
  502  2794   236   0 Wed12AM ??         0:00.00 (MyProduct)  me            2794      0    1 Z      ?? 
  502  2843   236   0 Wed01AM ??         0:00.00 (MyProduct)  me            2843      0    1 Z      ?? 
  502  2886   236   0 Wed01AM ??         0:00.00 (MyProduct)  me            2886      0    1 Z      ?? 
...
  502 13711   236   0 Thu11PM ??         0:00.00 (MyProduct)  me           13711      0    1 Z      ?? 
  502 13770   236   0 Thu11PM ??         0:00.00 (MyProduct)  me           13770      0    1 Z      ?? 
  502 14219   236   0 10:35AM ??         0:00.00 (MyProduct)  me           14219      0    1 Z      ?? 
  502 14280   236   0 10:38AM ??         0:00.00 (MyProduct)  me           14280      0    1 Z      ?? 

The Z in the second to last column indicates they are zombie processes. The 236 in the third column is the parent PID, which belongs to my user's launchd in this case.

Note that some of the processes are multiple days old. I have quit and reopened Xcode a few times during this time period.

Does anyone know why this happens, or whether this should be cause for alarm?

like image 732
Mike Mertsock Avatar asked Nov 03 '12 13:11

Mike Mertsock


2 Answers

After some particularly heavy Xcode sessions where my MBP sounded like it was being asked to execute the STARNET Init Procedure, I decided to spend a few minutes looking into this zombie process nonsense... after all, a Unix box that can't fork is a useless Unix box. I may have some good news. Hopefully, we'll see. Running Xcode 4.6 on 10.8.2 here.

The zombie issue appears to happen regardless of GDB or LLDB usage. The app running within the simulator is owned by the debug process -- either GDB, or "debugserver" in the case of LLDB. When you hit "Stop" the app process running in the simulator goes zombie. This sounds like an unclean shutdown sequence.

On a hunch rather than hitting "Stop", I paused the app, and in the debug console (LLDB in my case) I unattached from the running app using "process detach". A quick ps verifies debugserver is no longer running... so far so good! Now, the app is still running in the simulator itself, just not under debug. In fact, hitting the Stop button now is a no-op.

In the simulator hit the home button to get back to springboard, then double-click the home button and manually close the app. Go to your command line and look for the zombie... no zombie! Yay.

So... next step is to see if there's a reasonable way to execute this or similar shutdown procedure via python script etc. 'Course that doesn't help if you're on GDB. If I can do a clean shutdown via a single debug console command then it's just a matter of getting used to not hitting the broken Stop button. Maybe there's a resource hack to disable it altogether... :)

EDIT #1: couple of interesting tidbits...

1.) Hitting Stop button in xcode just outright kills the debug and app processes -- there's no attempt to do a clean shutdown whatsoever. Printf debug in app delegate applicationWillTerminate and applicationDidEnterBackground shows that the running app is killed with prejudice -- no NSLogs shown in the console.

2.) In debug console calling [UIApplication terminateWithSuccess] will cause the app to terminate "properly", but still leaves a zombie... interestingly if you have a breakpoint set, the app won't terminate:

(lldb) expression
Enter expressions, then terminate with an empty line to evaluate:
[(UIApplication *)[UIApplication sharedApplication] terminateWithSuccess]

error: Execution was interrupted, reason: breakpoint 2.1.
The process has been returned to the state before execution.
(lldb) breakpoint disable 2.1
1 breakpoints disabled.
(lldb) expression
Enter expressions, then terminate with an empty line to evaluate:
[(UIApplication *)[UIApplication sharedApplication] terminateWithSuccess]

2013-03-25 01:28:00.186 iPhone Testbed[9481:c07] -[AppDelegate applicationWillTerminate:]
(lldb) 

So app goes through some sort of shutdown process, and the will terminate shows in console, but we still have a zombie, so it's still not a clean shutdown.

I'm thinking this whole thing has to do with the app going to the background within iOS runtime. When munging processes directly (via Stop button, kill command, debug console stuff, etc. etc.) iOS runtime is not allowed to do proper shutdown and cleanup -- in fact, springboard still thinks the app is running in the background even when the process no longer exists. It just so happens our iOS and OS X runtimes are one in the same -- hence launchd owning the zombie.

So I think the solution to all of this is determining a clean shutdown procedure at the iOS level and at least be able to execute that via debug console. Gonna look more into UIApplicationExitsOnSuspend flag, see if I can set the necessary bits at runtime (as opposed to plist) to get the app shutdown cleanly upon debug detach...

like image 85
kbaccki Avatar answered Dec 03 '22 03:12

kbaccki


They don't particularly take up a lot of room.

This appears to be an artifact of the Xcode machinery improperly killing the subprocesses.

I have the same problem, but I note that the zombies belong to, in my case, ppid 271 which is an invocation of launchd under my name, instead the whole system.

I'm curious as to what might happen if I kill or hup that process.

In any case, logging out will probably clear the zombies. And certainly rebooting will, but thats to be avoided, in my book.


Oh, that went quite poorly. Don't kill your launchd, it unceremoniously kills your session, but does nothing to let you get it back, like give you a login screen.

I shall have to look and see of I left the zombies behind because of stopping Xcode. It seems like there might be a couple of dumb things here. It your process doesn't wait for a child, it gets zombied. If the parent dies, the next thing up the line gets it, I think, which in this case is launchd. Launchd ought to wait() for it, but maybe thats getting confused?

like image 35
Hack Saw Avatar answered Dec 03 '22 03:12

Hack Saw