The debug version of your application must be built with the following: The Fix & Continue GCC_ENABLE_FIX_AND_CONTINUE build setting checked Compiled using gcc version 3.3 or later Ful
Trang 1Object Descriptions
Like data formatters, many object - oriented languages have adopted conventions for converting any
object into a textual representation In Java, this is the toString() function Objective - C uses the
- [NSObject description] method If you are using an object that supports one of these standards,
you can use the Run ➪ Variables View ➪ Print Description to Console command The debugger
invokes the standard “ to string ” function on the object and sends the result to the debugger console
WATCHPOINTS
Watchpoints are breakpoints for data You can make any variable a watchpoint Whenever the
debugger detects that the value of that variable has changed, it stops your application
Watchpoints sound great, but they are fairly limited The biggest problem is that your application
can ’ t execute any code where the watchpoint variable is out of context, so they are mostly useful for
global variables that are always in scope and for catching state changes in a loop
You set a watchpoint by fi rst selecting a variable in the variables pane Choose the Run ➪ Variables
View ➪ Watch Variable command This places a magnifying glass icon next to the variable as shown
in Figure 18 - 35 Start the program executing again, and it breaks at the point just before the variable
is altered with a dialog box explaining what is about to happen, also shown in Figure 18 - 35
FIGURE 18-35
You can choose to acknowledge the event and leave the watchpoint set, or disable the watchpoint
by clicking the Disable button Watchpoints are automatically deleted whenever your application
exits the context where the watchpoint variable exists Watchpoints are not retained between debug
sessions
You can create an effect similar to a watchpoint using a breakpoint conditional like i!=0 It ’ s not as convenient as a watchpoint, but it ’ s more durable
To remove a watchpoint, select the variable being watched and choose Run ➪ Variables View ➪
Watch Variable again to remove the check mark
Trang 2CHANGING DATA AND CODE
So far, this chapter has taken a rather passive approach to debugging You ’ ve viewed code and variables in countless ways, but you haven ’ t actually changed anything Xcode lets you alter both data and code while your application is executing This can be a huge time - saver when you ’ re debugging You can change the values of parameters to test specifi c cases, or correct a value that was miscalculated and continue testing
Changing variables is easy Select a primitive variable and choose the Edit Value command from either the Run ➪ Variables View menu or the Right/Control - click contextual menu in the variables pane You can also double - click the value of the variable right in the variables pane Edit the value and press Return The only acceptable forms are decimal, octal (beginning with a zero), or hexadecimal (beginning with 0x ) To enter a character you need to translate that character into a decimal or hexadecimal value The Code Table view of the system ’ s Character Palette is particularly useful in looking up character code values
If the variable is a pointer, you can change the address of the pointer or you can expand the variable and Xcode allows you to change any primitive values to which the pointer points
The Magic Fix
It ’ s simple enough to poke a new value into a variable and continue executing, but what if the code itself is incorrect? Xcode allows you to fi x that too
This bit of magic — and it really is something close to magic — is a feature called Fix & Continue
As the name implies, it enables you to recompile code in your application and continue debugging
it without restarting your program Use of this feature depends on some prerequisites The debug
version of your application must be built with the following:
The Fix & Continue ( GCC_ENABLE_FIX_AND_CONTINUE ) build setting checked Compiled using gcc version 3.3 or later
Full debug symbols
No optimization
If, for any reason, the debugger can ’ t use Fix & Continue, the Fix command will be disabled while debugging
Using this feature is deceptively simple Say, for example, you discover a bug in your source code while you ’ re debugging Listing 18 - 5 shows a common programming mistake: a loop with a missing increment statement
LISTING 18 - 5: Bad loop
Token findOneToken( const char* s ) {
while ( *s!='\0' & & isspace(*s) ) s++;
continues
➤
➤
➤
➤
Trang 3LISTING 18-5 (continued)
Token token;
token.word = s;
token.length = 0;
while ( *s!='\0' )
{
char c = *s;
if (isspace(c))
break;
token.length++;
}
return (token);
}
After stepping through the second loop a few times, it becomes obvious that it gets stuck because
the statement c = *s should have been c = *s++
To correct this code, simply edit the statement so that it reads c = *s++ and choose Run ➪ Fix or
click the Fix button in the debugger ’ s toolbar The source for this fi le is recompiled, the new code
is loaded into your application ’ s code space replacing the old version of findOneToken , and the
program counter changes to point to the equivalent line in the new code
If that was all that needed to be done, you could continue debugging the application Replacing the
buggy code has, unfortunately, created another situation Before you added the increment operator,
the s variable wasn ’ t being incremented — but token.length was The length value now has a
non - zero value and won ’ t agree with the length of the string when the function returns
Can you continue debugging your application without having to restart it? You have two ways of
addressing this The fi rst would be to use the variables pane and simply edit the value of token
.length , setting it back to 0 Another way is to alter the program counter so that the program
continues executing at a different location in the code Here the PC indicator is being dragged
back up to the token.length = 0 statement so that the entire second loop starts over from the
beginning, as shown in Figure 18 - 36
FIGURE 18-36
Trang 4When the execution is continued, the program starts again at the top of the (now bug - free) loop, reinitializes token.length to 0 , and executes correctly
Magic Fix Limitations
Fix & Continue does have some limitations Here are a few:
Fix is not supported by all debuggers Support for Fix & Continue comes and goes in gdb
You cannot redefi ne typedef variables, data structures, classes, or function arguments
You cannot redefi ne the automatic variables on the stack frame
You cannot redefi ne global data variables
You cannot make any change to your application ’ s resources, such as icon or nib fi les
You cannot fi x a bad reference to a function by renaming the function
In short, you can make any change that alters only the executable code of one or more functions.
You can ’ t make a fi x that alters the data types or linkages that are, or could be, used anywhere else
in the application
You should be aware of a couple other caveats about how Fix & Continue works Fix & Continue replaces the code of a function that was executing and changes the current program counter so that execution continues in the new code However, it does not change the program counter in any other stack frame Say that Function A calls Function B If you stop the program in Function B
and fi x Function A, when Function B returns it will return to the old Function A, not the
corrected one The corrected Function A won ’ t be called until something else calls Function A again
Fix & Continue only compiles and replaces the in - memory image of a single fi le If you make changes in several fi les, you will need to perform a Fix & Continue on each one
Also note that Fix & Continue only patches the memory image of the running application
It does not alter the original executable fi le that was produced by the last build If you restart your application the old (bug - ridden) version is executed Worse, the executable code is now out of sync with the modifi ed source fi les Make sure you follow each debugging session where you use Fix & Continue with a new build to incorporate the changes you made into the fi nal product
DEBUGGER CONSOLE
The debugger console has been mentioned several times in this chapter To access it, choose the Run ➪ Console (Shift+Command+R) command, or click the Console button in the debugger window ’ s toolbar This opens the debugger console window, shown in Figure 18 - 37
➤
➤
➤
➤
➤
➤
Trang 5FIGURE 18-37
Like many of Xcode ’ s interfaces, the debugger window is just a graphical front - end to the gdb (or
Java, or AppleScript) debugger that runs underneath it The debugger console window is a shell
window that interacts with the debugger process directly When you click the Continue button in
Xcode ’ s debugger window, Xcode just sends a continue command to gdb Any information that
gdb outputs is visible in the debugger console window
If you are having problems with the debugger, the debugger console window is the fi rst place
to look Problems setting breakpoints, resolving symbols, or evaluating expressions are
logged there
More interesting is that the debugger console window is a fully interactive terminal window
Through this window you can type commands directly into the debugger The debugger provides
many features that are not available through the graphical interface provided by Xcode Of course,
this requires an understanding of the gdb (or Java debugger) commands and their syntax You can
learn the basics by entering the help command at the (gdb) or JavaBug > prompt The AppleScript
debugger has no interactive commands
SHARED LIBRARIES
One miscellaneous debugger tool is the shared library window, shown in Figure 18 - 38 Opened with
the Run ➪ Show ➪ Shared Libraries command, it shows the status of the shared libraries that your
application is linked to Most of the information here concerns how many of the debugging symbols
for each library have been loaded into the debugger
Trang 6The Module column shows the name of each shared library The Address column shows the address
in the application ’ s memory space where the library has been loaded If the fi eld is blank, the library has not been loaded into memory yet The complete path to the selected library is shown at the bottom of the window
The Starting Level and Current Level columns show what level of debugging symbols should be loaded for each library when the debugger starts and right now, respectively The debugger can avoid loading symbols for a library, load only the external declarations, or read all debugging symbols including source fi le line information The less debugging information loaded, the faster the debugger starts up and runs — and the less it knows about your application
Normally, the debugger loads only the external declarations This is the superfi cial information about the library Whenever it needs to know more detailed information, it automatically loads any remaining debugger symbols that describe data structures, source fi le line numbers, and so on
You can watch this process at work Start an application and set a breakpoint very early in the application, like at the fi rst line of main() Open the shared library window and the global variables window Start looking through the libraries in the global variables window As you browse each library for global variables, the status of the loaded symbols in the shared library window changes from None or External to All as you force the debugger to load additional debugging symbols for each library — debug symbol information that the debugger needs to display the global variables
in each library
You can manually load the symbols for a library into memory by changing the setting in the Current Level column The change occurs immediately The Starting Level column determines what the Current Level column will be set to when the library is initially loaded You can set this to
a particular level or use the Default setting If set to Default, the level used will either be the Default Level for System Libraries or User Libraries, as appropriate, set with the two global pop - up menus
at the top of the window The default level of External is known as “ lazy ” symbol loading; the idea
is to get your application running in the debugger as quickly as possible by loading only the minimal amount of information and worrying about the details later You can disable Lazy Symbol Loading
FIGURE 18-38
Trang 7in the Debugger pane of the Xcode Preferences Disabling Lazy Symbol Loading changes the User
Libraries default from External to All
The Reset button at the bottom sets the Starting Level of all libraries to Default
You can manually add or remove libraries from the list by clicking the + and – buttons at the bottom
of the window To add a library, browse to the location of the library and open it Remember that
in the fi le browser, the Shift+Command+G key combination opens the Go To sheet, allowing you to
enter a path to normally invisible directories like /usr/lib
The shared libraries window is mostly informational, but it can be used to give hints to the
debugger telling it to load — or avoid loading — debug symbol information at strategic times If you
are debugging a very large application, this can speed up the debugger by not loading unnecessary
symbols or speed up your debugging workfl ow by preloading symbols you need You cannot use
this window to force libraries to load or unload or to force symbol information that the debugger is
using out of memory
CUSTOM EXECUTABLES
So far you ’ ve been running and debugging simple applications without much thought to
the environment in which those applications were running When you created a target
to produce your application, Xcode also created a matching product and an executable The
executable, which appears in the Executables smart group of the Groups & Files pane, defi nes
the execution environment for your application It defi nes what binary program will be executed
when it is launched, what parameters and environment variables will be passed to it, and what its
I/O fi le descriptors will be attached to You can customize the environment settings of an executable
created by Xcode, or you can create your own
You may want to customize or create a custom executable for several reasons For example:
You need to pass command - line parameters to the process when it is started
You need to set environment variables for the process, or choose a different working directory before the process is started
You want to redirect the input or output of the tool to something other than the run or debugging console windows
You need to debug an executable that Xcode didn ’ t produce or for which Xcode doesn ’ t automatically create a product and executable, such as a program produced by an external build process
Your executable is launched via a script or some other process
The project you are developing is a plug - in or a framework that can ’ t be executed on its own You need to launch an application that will load the plug - in and exercise it
General Settings
Open the Info window for an existing executable, or choose the Project ➪ New Custom Executable
command to create a new one The General tab of the executable ’ s Info window, shown in
Figure 18 - 39, controls the environment for that executable when it is run
➤
➤
➤
➤
➤
➤
Trang 8The Path option is the path, relative to the build product ’ s directory, to the program that will be launched when the executable is started Normally this is the binary application produced by your target Change this if you want a different program executed instead An example would be a UNIX program that is started by a shell script that checks the state of the program, gathers confi guration information, and so on, before launching the binary program If your product is started by such a script, enter the path to the script here
At the bottom of the window is the current or working directory that will be set before the executable is launched This is important to some executables that expect to fi nd resources or perform work on fi les relative to the current directory Normally this is set to the build directory for the product That is, the current directory will be the same directory that contains the executable
The build product directory will change depending on which build confi guration is active You can alternatively choose the project directory or a custom directory Enter the custom directory path, or click the Choose button to browse for a folder
The Use Suffi x When Loading Frameworks option passes a special fl ag to the dynamic linker It tells the system ’ s run time library loader to search for alternate versions of framework libraries Many libraries are provided in alternate versions designed to aid in debugging or profi ling They may include additional integrity checks or log informational messages to the system log that are useful during development When set to No, the loader links your application to the standard system libraries
The Use For Standard Input/Output option determines where the stdout, stdin, and stderr fi le descriptors will be connected when the executable is launched The Pseudo Terminal connects your application to the run or debugging console you ’ ve been using throughout this chapter The Pipe choice is only useful for remote debugging, as described later The System Console choice directs the program ’ s output to the system console Macintosh applications launched by the user, and command - line programs launched without a shell, normally have their output redirected to the
FIGURE 18-39
Trang 9system console log You can review the system console log using the Console utility provided with
Mac OS X When set to the System Console choice, stdin is connected to null
Arguments and Environment
Use the Arguments pane to pass additional arguments and environment variables to your program
This can be extremely useful for testing command - line applications or setting special features of the
run time system
To add an argument, click the + button beneath the Arguments pane and type in the argument value,
as shown in Figure 18 - 40 You can later edit arguments by double - clicking their values and reorder
them by dragging The check box in the left column is an enabled setting Only arguments that are
enabled are passed to the executable This makes it easy to keep several commonly used arguments in
the list and quickly select just the ones you want Select an argument and click the – button to delete
it entirely
The environment variables pane works exactly the same way as the arguments, except that this
pane defi nes named variables that are defi ned in the environment of the process The values of
environment variables can also reference any of the following build settings: SYMROOT , SRCROOT ,
OBJROOT , BUILT_PRODUCTS_DIR , and TARGET_TEMP_DIR Chapter 17 covers referencing build
settings in general and the meaning of these build settings in particular
FIGURE 18-40
Debugging
The Debugging pane, shown in Figure 18 - 41, controls additional settings that affect the execution
environment of your program when you launch it under the control of the debugger The When Using
option controls which debugger Xcode will start to debug your application The debugger chosen
must be capable of debugging the kind of application that the executable produces The Java debugger
cannot debug a binary executable Xcode sets this appropriately for executable products produced
from targets For custom executables, you need to tell Xcode which debugger is appropriate
Trang 10The Use For Standard Input/Output option controls the connections to the program ’ s I/O
You may want to set this to System Console if the output of the program is obscuring the output
of the debugger itself in the Debugger Console window It is also possible to distinguish between the output of the debugger and your program by coloring their text differently in the Debugger Console window (See the Debugger pane settings in the Xcode preferences.) If you are doing remote debugging, this option must be set to Pipe
The next two options confi gure the gdb debugger for remote execution The “ Remote Debugging ” section in this chapter explains how to confi gure a program for remote debugging
The Start Executable After Starting Debugger option automatically starts your application running
as soon as the debugger is loaded Normally this is checked, but you may not want this to happen
Turning this option off launches the debugger, but performs no further action This permits you the opportunity of making special adjustments in the debugging environment, such as setting breakpoints or editing static variables, before the program starts running You can even use the debugger command to attach to an already running instance of your application, rather than launching a new one
The Break on Debugger() And DebugStr() option sets the USERBREAK environment variable before your application is started The presence of this environment variable causes the Debugger() and DebugStr() functions defi ned in Core Services to send a SIGINT signal to your program if either of these functions are called Without this setting, these functions do nothing or just log a message to the console When running under the debugger, a SIGINT signal suspends your program just as if it hit a breakpoint This option sets this environment variable only when the executable is launched for debugging To have it set all of the time, set the USERBREAK environment variable to 1 in the Arguments pane
FIGURE 18-41