This is probably newbie stuff for hardcore C programmers, but I’m logging here for posterity and for my own benefit. I don’t pretend to be one, but recently found myself needing to find out the cause of a curious message while running Python on my Mac OS X 10.5.8:
Break on __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__() to debug.
The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec().
Okay, this means that my program forked, and the child did not immediately
exec(). Instead, it went on to call some Core Foundation function. Naturally, I’d want to find out what is it that it’s doing! Time to fire up our trusty
macmac:~ $ sudo gdb -p 52772
GNU gdb 6.3.50-20050815 (Apple version gdb-960) (Sun May 18 18:38:33 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".
Attaching to process 52772.
Reading symbols for shared libraries . done
Reading symbols for shared libraries .................................... done
0x9354a6fa in select$DARWIN_EXTSN ()
(gdb) b __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__
Breakpoint 1 at 0x938df314
Breakpoint 1, 0x938df314 in __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__ ()
#0 0x938df314 in __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__ ()
#1 0x938d97bd in CFRunLoopGetCurrent ()
#2 0x95bf09fa in +[NSThread currentThread] ()
#3 0x95befe39 in _NSInitializePlatform ()
#4 0x918568b8 in _class_initialize ()
#5 0x9185678c in _class_initialize ()
#6 0x91855239 in _class_lookupMethodAndLoadCache ()
#7 0x918656d6 in objc_msgSend ()
#8 0x9185dbdf in call_load_methods ()
#9 0x918570d3 in load_images ()
#10 0x8fe02e38 in __dyld__ZN4dyld12notifySingleE17dyld_image_statesPK11mach_headerPKcl ()
#11 0x8fe0e7cf in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEj ()
#12 0x8fe0e775 in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEj ()
#13 0x8fe0e775 in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEj ()
#14 0x8fe0e775 in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEj ()
#15 0x8fe0e775 in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEj ()
#16 0x8fe0e775 in __dyld__ZN11ImageLoader23recursiveInitializationERKNS_11LinkContextEj ()
#17 0x8fe0e8c9 in __dyld__ZN11ImageLoader15runInitializersERKNS_11LinkContextE ()
#18 0x8fe02202 in __dyld__ZN4dyld15runInitializersEP11ImageLoader ()
#19 0x8fe0bbdd in __dyld_dlopen ()
#20 0x935042c2 in dlopen ()
#21 0x001b09ac in _PyImport_GetDynLoadFunc ()
#22 0x001a35a4 in _PyImport_LoadDynamicModule ()
.. (snipped) ..
This tells us that Python was trying to call the
dlopen() function to load a dynamic library. I know that because the stacktrace went from a bunch of CPython functions to
dlopen() (at stack #20) and eventually led up to the function that we were told to break on. Now it’s time to find out which library it is trying to load.
(gdb) frame 20
#20 0x935042c2 in dlopen ()
(gdb) info frame
Stack level 20, frame at 0xb007d6f0:
eip = 0x935042c2 in dlopen; saved eip 0x1b09ac
called by frame at 0xb007d9b0, caller of frame at 0xb007d6d0
Arglist at 0xb007d6e8, args:
Locals at 0xb007d6e8, Previous frame's sp is 0xb007d6f0
ebx at 0xb007d6e4, ebp at 0xb007d6e8, eip at 0xb007d6ec
If you had the source code for
gdb would’ve helpfully printed the arguments for you, but alas we don’t have the source code!
Thankfully, we know what the function looks like from the
dlopen man page:
void *dlopen(const char* path, int mode);
So we know that the library we’re looking for is in the first argument, which is a pointer to a C string. And we will find it.
Some googling later, I found the layout for darwin stackframes:
Sweet! According to this, the first function argument is in
(gdb) info registers ebp
ebp 0xb007d6e8 0xb007d6e8
Aha! But you didn’t really need to do that; the
info frame command output already told us that locals are at
0xb007d6e8 + 8 = 0xb007d6f0, so we shall get the address stored at this address:
(gdb) x/a 0xb007d6f0
Now print the string at the target address:
(gdb) x/s 0xb007da67
Et voila! This happens to be the glue code for Python to access the Mac OS X Internet Config settings. In some previous foray, I learned (to my surprise) that Python’s
urllib (on Darwin) actually uses IC to find out your proxy settings and use it! For some reason, I never expected it to do so but I guess it makes sense for a seamless user experience.
Did I find out why it’s giving me that fork/exec message? No, not really but it doesn’t matter much.
(Update: I do know the reason for this; it’s because some code started fetching from HTTP after fork, but that’s the expected behavior, and I don’t wish to reexec. Program continues to work, so I just have to ignore it in my development environment. This will be deployed to a Linux or FreeBSD which is why I said it doesn’t matter much.)
Now, I’m convinced that there is a better way to do it so if you have any idea, please leave a comment.
p.s. This can probably be easily achieved with DTrace, but it makes my head spin.