Occasionally you will need to concurrently run your application under OmniObjectAlloc and under the debugger. OOA provides two means of accomplishing this.
After opening your application's PB.project, click on the 'Settings...' button. This will bring up a panel with a 'Wait For Debugger Attach' switch. Selecting this switch will cause the OOA initialization code to pause your application once it has been started. It will loop endlessly repeating a message containing the pid of your application. You may then start gdb in another Terminal window and issue the command 'attach pid' where 'pid' is the process it in the OOA document output view.
gdb will connect to the already running process. You may then issue the command 'p OOAShouldWait = NO' to cause the OOA initialization code to cease waiting. You may then set any breakpoints or perform any other debugging tasks you would normally. Once you are ready to continue, simply issue a 'continue' command.
Unfortunately, gdb does not always deal well with attaching to a running process. In particular, OOA uses the environment variable
DYLD_INSERT_LIBRARIES
to cause the client-side support code to be dynamically linked into your application. gdb seems to not like this occasionally. There are two approaches that I've found to be helpful in working around this bug.
First, if you start the debugger, set a breakpoint at
main()
, run your program and the kill it upon reaching
main()
, gdb will have cached all of the symbols other than the symbols in the OOA client framework that is linked via
DYLD_INSERT_LIBRARIES
. This will often avoid the crasher in gdb.
If you still run into problems with gdb, you can try statically linking the OOA initialization code. The
DYLD_INSERT_LIBRARIES
environment variable is used to make typical use of OOA easier, but it is not necessary. Additionally,
dyld
is smart enough to link the framework twice if you statically linked it. You will find this framework in the appropriate subdirectory of /LocalLibrary/Omni/Frameworks
.
One major problem with this approach is that the OpenStep/Mach and Rhapsody/RDR kernels seems to have some problems in this area. If, for example, you run OOA under the debugger, have it launch another process and then attach to that child process under another debugger, you must be very careful about how you terminate all of the processes involved. In particular, if you quit OOA before the child process has died, you will likely put your machine into a weird state. The manifestation of this is that if you log out and attempt to reboot your machine (if you are going to play Quake2, perhaps), it will hang. Under Rhapsody/RDR, you may get a kernel panic.
On other problem seems to be that the NSPipe/NSFileHandle constructs that OOA uses to gather the output of your application will not work correctly if your application redirects is stdout/stderr using dup()
or dup2
. Rather, you will get zillions of 'Attempt to remove unrecognized exception handler' messages . This appears to be a minor bug in NSPipe or NSFileHandle, but I haven't pinpointed it yet.
The second means of debugging your application is much more reliable, but it involves a little more work.
On the same Settings panel mentioned above, if you click the 'Start Child Task Manually' switch and then hit 'Start' on the document window, OOA will not automatically start the child process for you. Instead, it will print out a message containing some environment variable settings for you to use when starting your application.
For each environment variable given in the output view, you should use the 'set env' command in gdb
to cause gdb
to put the given setting in the environment of the child process.
Under this approach, you must statically link the OOA client framework or use the DYLD_INSERT_LIBRARIES environment variable yourself (OOA will not request that you set this environment variable).
Once you start your application under its own debugger, you will have avoided using the 'attach' command and any weird chains of processes and debuggers. I have not received any weird behaviour using this method.
The OOA client framework support code sends messages to OmniObjectAlloc in a very optimized fashion. One part of this optimization is batching the messages into larger packages to avoid the messaing overhead as much as possible. The OOA client framework will guarantee that any messages more than a few seconds (2 currently) will get flushed to OmniObjectAlloc. But, if you set a breakpoint and hit it while your application is running under OmniObjectAlloc, some number of batched messages may be sitting in your application waiting to be delivered to OmniObjectAlloc. If you step through some code, the OOA client thread may get a chance to run and flush out some of the recent messages, but as you step through code, you could end up adding queuing more messages for delivery.
In order to be certain that any pending messages have been delivered to OmniObjectAlloc, you may call the function OOAFlushAllocationEvents()
. If you fail to do so, you may see objects in OmniObjectAlloc you know have been allocated/deallocated/retained/released (or whatever) that don't report all of their events.