If you are using the mini - debugger, the best choice is to open it automatically; by the very nature of programs that you ’ d want to use the mini - debugger with, opening it after you
Trang 1500 ❘ CHAPTER 18 DEBUGGING
The Auto - Attach Debugger on Crash option causes the debugger to attach to the executable ’ s
process should it crash This is equivalent to stopping your executable immediately after it crashes,
but before the process is terminated, and issuing the Run ➪ Attach To Process command
The Auto - Attach Debugger on Crash option is really only meaningful in Mac
OS X 10.5 (Leopard) and earlier In 10.6 (Snow Leopard), Xcode preemptively starts the gdb debugger every time you launch your application from within Xcode, so in effect it ’ s already attached
The Additional Directories to Find Source File In pane lists the paths to where the debugger
can fi nd the source fi le used to build the executable being debugged Normally you don ’ t need
to add anything here because Xcode automatically searches all of the source directories in your
project However, if you have included source fi les outside your project or the executable was
built from source fi les that Xcode doesn ’ t know about — fi les in an externally built target, for
instance — add those directories here You can click the + button and type in the path, or drag a
folder from the Finder and drop it into the list
Selecting an Executable
The active executable that you select using the Project ➪ Set Active Executable menu is the
executable that will be launched when you choose any of the Run or Debug commands This is
typically the product produced by the active target, but it doesn ’ t have to be After selecting a target,
you can change the active executable to an executable produced by another target or to a custom
executable that you ’ ve created
When you switch targets, Xcode examines the active executable If the active executable is the
one created for the product produced by the current target, and the target you are switching to
produces an executable product, the active executable is changed to match the new active target
For most projects, this means that the active executable will “ follow ” the active target as you change
between them However, if you have targets that don ’ t produce an executable, or have created and
made custom executables active, changing the target may not change the executable You need to
be especially watchful if you have created aggregate targets An aggregate target that builds both a
client and server application will not select an active executable when you make that target active
You must specify which executable, the client or the server, needs to be launched when you choose
Run or Debug
DEBUGGER PREFERENCES
You can use the Debugging pane of the Xcode preferences, shown in Figure 18 - 42, to confi gure a
few common debugging features
Trang 2Starting on the left are the Fonts and Colors preferences Select a category of text from the pop
-up menu, and then change the font, style, and color of the font using the Set Font button With these settings, you can alter the appearance of text that appears in the run and Debugger Console windows This makes it possible, or at least easier, to differentiate between the text output by the debugger and the text output by your program By default, Xcode colors the debugger ’ s prompt and bolds the text sent to the debugger All output appears the same
The Instruction Pointer Highlight color is the color used to highlight the currently executing line of source code in the debugger If you prefer to see a different color, click the color well or drag a color into the color well to change it
The On Start setting lets you choose to automatically open certain windows when you start your application using the debugger The choices are:
Do Nothing Show Console Show Debugger Show Console & Debugger Show Mini Debugger
If you tend to use the in - editor debugging controls, set this to Show Console Otherwise, choose Show Debugger or Show Console & Debugger so that the debugger window will automatically open when you begin debugging
If you are using the mini - debugger, the best choice is to open it automatically; by the very nature
of programs that you ’ d want to use the mini - debugger with, opening it after you ’ ve started your application can be awkward
The GDB Log setting will optionally write an extremely detailed log of your debugging session to a text fi le for later analysis This is particularly useful if you ’ re trying to diagnose a problem with gdb
➤
➤
➤
➤
➤
FIGURE 18-42
Trang 3502 ❘CHAPTER 18 DEBUGGING
commands, breakpoint actions, and so on Enter the path to where you want the fi le written — just
remember that this is a global setting that affects all projects If the path is left empty, Xcode writes
to a temporary fi le
Load Symbols Lazily controls the default level of symbols that load when modules and dynamic
libraries are loaded into memory Enabling lazy loading causes only the minimal amount of
debug information to be loaded for each module initially, deferring the loading of more complete
symbol information until it ’ s needed Turning it off causes the debugger to immediately load
everything it knows about every library loaded into memory This makes starting the debugger
slower, but makes more complete debug information available See the “ Shared Libraries ” section
for more details
The Disassembly Style setting controls the output of the Build ➪ Show Assembly Code command
Clearing the In - Editor Debugger Controls will turn off the normal in - editor debugging features in
each editor pane You ’ ll have to use the debugger window for your debugging
REMOTE DEBUGGING
The gdb debugger supports remote debugging The debugger runs on one computer, while your
application runs on a different computer What actually happens is that another copy of the
debugger is started on the remote computer along with your program, and the two debuggers
communicate via a network connection The remote instance of the debugger transmits all of the
pertinent information about your application to the local debugger so you can see what ’ s going
on Commands issued to the local debugger are, similarly, forwarded to the remote debugger for
execution
Remote debugging permits you to test your application in an environment different from that of
your development system A typical requirement is the need to debug your application using an
earlier version of the operating system Xcode, and even the computer you ’ re developing on, may
not be compatible with the OS you need to run under Even if it were, building your application
and then rebooting your computer into an older version of the OS to test it is both tedious and
unproductive
Remote debugging is also useful for debugging interactive code Video games and drag - and - drop
handlers can be nearly impossible to debug on a single machine, because the sequence of user events
needed to test the problem are interrupted by the debugger itself The mini - debugger is a great tool,
but it still requires user interaction on the same system running the application
Debugging your application remotely requires some special confi guration of both computers
Specifi cally, you must:
Pre - authorize an ssh login account on the remote computer
Create a shared build location accessible by both computers via the same path
Confi gure the executable for remote debugging
Remote debugging works through the Secure Shell ( ssh ) remote login facility built into Mac
OS X ssh provides secure communications paths using a public - key encryption system The
primary reason for using ssh for remote debugger communications is not security — although
➤
➤
➤
Trang 4that ’ s valuable if you need it Instead, Xcode leverages a powerful feature of ssh called tunneling
that lets it communicate with a remote debugger process as if it were running locally But the side effect of using ssh is that it requires a secure connection to be established fi rst, and that requires authentication Normally this is done interactively using a password For debugging, this is awkward To get around the need for a password, you need pre - authorized access to the remote machine so that the local computer can connect directly to the remote computer without any human intervention
You create pre - authorized ssh logins by manually generating and exchanging parts of a public/
private key pair (If you are curious, a typical ssh login authenticates a user and then spontaneously generates a temporary public/private key pair for that session Pre - creating a public/private key pair skips both of these steps.) To create a pre - authorized login on the remote computer, follow these steps (the commands you would enter are in bold):
1. On the local computer, open a Terminal window and generate an RSA public/private key pair using the ssh - keygen tool:
local:~ james$ ssh-keygen -b 2048 -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/james/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
2. Press Return when Xcode asks for a fi lename This uses the default RSA fi lename for your
account If Xcode asks to overwrite the fi le, answer with a y Enter a passphrase or press
Return to leave it blank (A blank passphrase is less secure, but is still acceptable and more convenient in a low - security environment.) Confi rm the passphrase by entering it again If you are successful, a new private key is written to ~/.ssh/id_rsa and your public key is written to ~/.ssh/id_rsa.pub , as follows:
Your identifi cation has been saved in /Users/james/.ssh/id_rsa.
Your public key has been saved in /Users/james/.ssh/id_rsa.pub.
3. On the remote computer, make sure Remote Login is enabled in the Sharing pane of the System Preferences This allows ssh connections from other computers
4. Log in to the remote computer using the account you plan to debug under This verifi es that the network connection works and that the remote computer is confi gured to accept ssh logins In this example, I ’ m logging in to the computer whiterabbit using a special account I created just for testing Use the account name and address of your remote computer in place
of test and whiterabbit.local
local:~ james$ ssh test@whiterabbit.local
Password:
Last login: Wed Sep 21 15:39:42 2005 Welcome to Darwin!
whiterabbit:~ test$
5. You now need to transfer the public key you just generated to the remote computer
One way is to use ssh ’ s fi le transfer capability to send the id_rsa.pub fi le to the remote computer Open a second Terminal window (you still have more work to do in
Trang 5504 ❘CHAPTER 18 DEBUGGING
the remote shell you just connected to, so leave that alone for the moment) and enter the following command:
local:~ james$ scp ~/.ssh/id_rsa.pub test@whiterabbit.local:development_rsa.pub
Password:
id_rsa.pub 100% 1123 1.1KB/s 00:00
Again, supply the password of the remote account and substitute the correct account name and computer address This command copies the id_rsa.pub fi le from the local .ssh
directory into the development_rsa.pub fi le in the home folder of the remote computer
6. Return to the Terminal window with the ssh shell session on the remote computer Use the
ls command to verify that the development_rsa.pub fi le was transferred
7. You now need to append the public encryption key in the development_rsa.pub fi le to the
list of authorized computers for this account To do this, use the following commands:
whiterabbit:~ test$ mkdir ~/.ssh
whiterabbit:~ test$ cat ~/development_rsa.pub > > ~/.ssh/authorized_keys
whiterabbit:~ test$ rm ~/development_rsa.pub
whiterabbit:~ test$ chmod go-rwx ~/.ssh/authorized_keys
The .ssh directory and authorized_keys fi le may already exist, in which case you don ’ t want to overwrite them You just want to append the new key to the existing fi le This is a text fi le, so it can also be edited using nano , vim , or your favorite text editor The last two commands delete the public key fi le that was transferred and rescind all non - user access to the authorized_keys fi le for security purposes
8. The remote computer is now pre - authorized to accept secure connections from your current
account on the local computer to the account you just confi gured on the remote computer
Verify this by logging out of the current remote session and connecting again, like this:
whiterabbit:~ test$ exit
logout Connection to whiterabbit.local closed
local:~ james$ ssh test@whiterabbit.local
Enter passphrase for key ‘/Users/james/.ssh/id_rsa’:
Last login: Tue Oct 25 09:49:46 2005 from marchhare.local Welcome to Darwin!
whiterabbit:~ test$
This time, ssh prompted for the passphrase used to generate the key, not for the password
of the test account on the local computer If successful, you know that ssh used the key for this computer to connect to the test account on the remote computer If you want to change any of these variables in the future — you want to connect from a different development machine or from a different account or to a different account — you must repeat these steps
The next step is to create a shared build location accessible to both computers Both the
development and remote computer must have direct access to the entire build folder containing both
the fi nal product as well as all intermediate build fi les More importantly, the UNIX path to the
folder must be identical on both computers You have three easy ways of accomplishing this
The fi rst, and probably simplest, solution is to employ a third - party fi le server Create a build folder on a fi le server separate from your local or remote computer (The “ Build Locations ” section in Chapter 17 discussed different ways of relocating your project ’ s build
➤
Trang 6folder.) You can now mount the build folder on both the local and remote computer using the same path
The second is a hybrid approach Confi gure your project to build to a local folder in a com-mon, publicly accessible folder like /Users/Shared/Projects/DistantBugs Turn on the
fi le sharing services of OS X and connect to it from the local machine Now create a sym-bolic link on the remote computer so that the build folder can be reached on both machines using the same path You must use the command - line tools to create symbolic links The following example mounts the main volume of a development system (Griffi n, in this exam-ple) as a network volume on a remote computer, and a symbol link is created in the remote computer ’ s Shared folder that links to the same folder on the development system:
ln -s /Volumes/Griffin/Users/Shared/Projects /Users/Shared/Projects
Now, any build folders that are created in the development computer ’ s /Users/Shared/
Projects folder will appear at the same location in the remote computer ’ s fi le system
The third method of getting both computers access to the same build folder would be to simply copy the entire build folder to the remote computer For a one - shot test, this might
be the easiest solution However, if you were constantly rebuilding the project, this would be both inconvenient and ineffi cient You could automate the process by creating a target script phrase to copy the contents of the build folder to the remote computer If you decide to go this route, consider using a utility like rsync to quickly transfer only the portions of the build folder that change after each build Remember that the location of the copy must reside at the same location as the original, or at least have an equivalent UNIX path
The last step in this process is to confi gure Xcode to start the debugging session remotely You do this is in the Debugging pane of the executable ’ s Info window, previously shown in Figure 18 - 41
Check the Debug Executable Remotely Via SSH option When you do this, the Standard Input/
Output option changes to Pipe Leave it that way; this choice must be set to Pipe for remote
debugging to work
Start your debugging session as you normally would The fi rst time you do, Xcode asks for your passphrase to decode the private key you generated earlier, as shown in Figure 18 - 43 Once it has your private key, it connects to the remote computer and starts the debugging session After this point, debugging your application remotely isn ’ t signifi cantly different from debugging it locally
➤
➤
FIGURE 18-43
Trang 7506 ❘ CHAPTER 18 DEBUGGING
If anything goes wrong — problems connecting to the remote computer, accessing the product on
the remote computer, or starting the debugger — consult the debugger console window for clues
Both the ssh client and the debugger output copious diagnostic messages to the console Anything
that goes wrong should be documented there
DEBUGGING AIDES
A number of miscellaneous tools and features are scattered around the debugger, Xcode, and the
operating system itself that will help you fi nd bugs in your code The “ Custom Executables ” section
covered loading the debug variant of frameworks and enabling Debugger() and DebugStr() calls
The following sections describe a few more Xcode facilities
Catching a Throw
The command Run ➪ Stop on Objective - C Exceptions command enables an implied breakpoint
whenever an Objective - C exception is thrown You can enable or disable this breakpoint at any time
during your debugging
Stopping for Debugger() and DebugStr()
The Run ➪ Stop on Debugger()/DebugStr() command sets an implied breakpoint whenever your
application calls the Debugger() or DebugStr() commands Normally these function calls are
ignored The command in the Debug menu enables this feature for all of the executables in your
project If you want to have Debugger() and DebugStr() break only in certain executables, disable
the menu item and enable the same setting for selected executables in their Debugger pane
Guard Malloc
A very useful debugging feature for C programmers is the Guard Malloc library Choosing the
Run ➪ Enable Guard Malloc command before you start the debugging session causes your
executable to be linked against the Guard Malloc ( libgmalloc ) library instead of the normal
malloc routines provided by the system The Guard Malloc library uses the virtual memory features
of the CPU to map every block of memory allocated using malloc( into its own address space
If your program attempts to access any data outside the immediate boundaries of the allocated
block, an EXC_BAD_ACCESS error occurs, crashing your program at the exact point where the illegal
access occurred
It should be noted that Guard Malloc can signifi cantly slow down your application, but the
additional execution time is usually worth it, because out - of - bounds memory accesses are
particularly diffi cult to debug
Debug Variables
Debug variables are either environment variables or preference property values that invoke special
behavior in the libraries and frameworks and are useful for debugging
Trang 8There is also a wide range of debugging support functions that your application can call directly, but a discussion of those is beyond the scope of this book The best resource is Apple ’ s Technical Note #2124, Mac OS X Debugging Magic You can fi nd it in the Xcode documentation
Environment variables can be set using the Arguments pane of the executable ’ s Info window
Preference values can be set using the defaults command - line tool You can read the man page on the defaults tool for the details of setting user default values, but here ’ s a simple example:
defaults write com.my.SimpleApp NSShowAllViews YES
The com.my.SimpleApp fi le is the user preferences fi le associated with the application By default, this is the Identifi er set in the target ’ s Properties pane The command sets the NSShowAllViews
property to YES , which causes the AppKit framework to draw a colored border around every NSView Setting property values only works for Carbon and Cocoa applications that use the user defaults framework
The following table describes a few environment variables useful for C programming:
MallocScribble Fills deallocated memory with 0x55s If your program reads the data again,
it should be obvious in the debugger that the data is from a stale block
MallocGuardEdges Adds guard pages before and after large allocation blocks This will
catch attempts to access data well beyond the edge of each allocated block It can be used independently of the Guard Malloc library
MallocStackLogging Logs the stack state for each allocated block There are tools that will
read this log and assist you in debugging memory allocation problems, especially memory leaks
If you think you have a problem with loading dynamic libraries, try some of the environment variables described in the following table:
DYLD_IMAGE_SUFFIX Searches for libraries with this suffi x fi rst Use this to load alternate
variants of libraries A lot of the system libraries include debug versions that can be loaded by setting the suffi x to _debug
DYLD_PRINT_LIBRARIES Logs the names of each library, as it is loaded If you think you ’ re
loading the wrong dynamic library, set this variable to 1
DYLD_PRINT_LIBRARIES _POST_LAUNCH
Logs the names of loaded libraries, but it only starts logging after your application has started executing This avoids logging a lot of the core libraries
DYLD_PREBIND_DEBUG Logs pre - binding diagnostics information about your application
Trang 9508 ❘CHAPTER 18 DEBUGGING
If you ’ re debugging memory leak or retain/release problems in Cocoa, consider setting some of the
environment variables described in the following table:
NSZombieEnabled NO If this is set to YES , NSObjects are “ zombifi ed ”
instead of being deallocated A zombie object has all of its message handlers replaced with a call that will break into the debugger This will catch an attempt to send a message to an object that has already been released and deallocated
NSDeallocateZombies NO Set this to YES and the memory for zombie
objects will actually be released The NO setting
is the safest, but can result in memory leaks that themselves may cause your application to misbehave
NSHangOnUncaughtException NO Normally an uncaught NSException will cause a
Cocoa application to terminate Set this variable
to YES and it will simply hang instead, allowing you to break into it using the debugger and examine its state
NSEnableAutoreleasePool YES Setting this value to NO defeats the functionality
of auto - release pools Use this if you want to keep around all of the objects that an auto -release pool would normally -release
NSAutoreleaseFreedObject
CheckEnabled
NO This is a very handy setting for fi nding double
-release bugs Setting this variable to YES will cause the auto - release pool to log an error message if the pool contains an object that has already been released
NSAutoreleaseHighWaterMark This is a useful diagnostics tool to check for
situations where you are putting an excessive number of objects into an auto - release pool Set
it to a number other than 0 and the system will log a warning whenever more than that number of objects have been added to the pool
NSAutoreleaseHighWater
Resolution
0 Use this setting to log a warning message for
every N number of objects above the high - water mark level The high - water mark level emits a single warning when the number exceeds that value This setting emits a warning for every increment Setting the high - water mark to 1000 and the resolution to 50 would log a message when there were 1000, 1050, 1100, 1050, objects in any auto - release pool
Trang 10The two preference settings described in the following table are useful when you ’ re debugging an AppKit application that is having drawing or layout problems You must set preferences in the preference fi le of the application before you launch it
NSShowAllViews Defi ne this preference and set it to YES and the AppKit window manager
will draw a colored border around each NSView in the application This makes it very easy to see spacing and layout problems
NSShowAllDrawing Defi ne this preference and set it to YES and AppKit will draw a colored
rectangle before drawing each NSView This makes it very easy to see what components in your windows are drawing, when, and in what order
Also check out the various Quartz Debug features
This is just the tip of the iceberg There are literally hundreds of variables, system calls, and development tools to help you track down and debug your application Most are collected and
maintained in Apple Technical Note #2124, Mac OS X Debugging Magic , which you can fi nd
in the Xcode documentation or read online at http://developer.apple.com/mac/library/
technotes/tn2004/tn2124.html
SUMMARY
You should now have at your disposal a cornucopia of tools and techniques for isolating, trapping, examining, and correcting any aberrant behavior in your application You are likely to spend a lot of time using the debugger Knowing how to harness the debugger ’ s capabilities can save you hours of time
Getting your application to build and execute correctly may be all you intended to accomplish For some applications, even this isn ’ t the end of the development process You may not be satisfi ed that
your application merely runs You want it to run fast — and that is the subject of the next chapter