You can see where you can debug from almost anywhere in the Xcode interface, but you can also debug your application anytime; you can launch your application normally and then later deci
Trang 1The debugger window includes an editing pane, so everything you can do in an editing pane can
also be done in the debugger window In addition, the debugger window provides:
A structured list of all in - scope variables The CPU registers
Access to global variables Advanced data inspectors
A stack frame list Thread selection The debugger window is where you turn if you want to explore other stack frames, switch to
another thread, want to see a list of all variables in scope simultaneously, see global variables, want
to examine variables in more detail, or want to modify variables
The third interface is the mini - debugger It ’ s a minimal debugging interface designed for use with
full - screen applications and other situations where getting to the debugger window is awkward or
inconvenient The mini - debugger is described in later sections
You can see where you can debug from almost anywhere in the Xcode interface, but you can also debug
your application anytime; you can launch your application normally and then later decide that you want
to debug it; Xcode will interrupt the process, attach its debugger, and hand over control to you
In fact, that ’ s really the primary reason for the Run ➪ Run - Breakpoints Off (Option+ Command+R)
command After starting your application, all you have to do is create, enable, or reactivate (Run ➪
Activate Breakpoints) a breakpoint; Xcode will invoke the debugger, have it attach itself to your
running executable (if needed), set the requested breakpoints, and let the debugger take over — just
as if you had started your application under the control of the debugger in the fi rst place In Mac
OS X 10.6 (Snow Leopard), Xcode keeps the GDB debugging running all the time, so it ’ s even more
responsive
Before any serious debugging can take place, you must fi rst prepare your project for debugging
BUILT TO BE DEBUGGED
The take - home message of this section is this:
Before debugging, profi ling, or analyzing your code, you must fi rst build it using the Debug build confi guration
It ’ s an essential requirement for doing any kind of debugging or analysis If you ’ re in a hurry,
switch your active build confi guration to Debug and skip to the next section If you ’ re interested in
knowing why, keep reading
How you build your application affects its ability to be debugged The quintessential quality of a
modern programming language is that it allows a developer to express procedures symbolically,
letting the compiler deal with the ugly details of how to accomplish those procedures in machine
code Listing 18 - 1 shows just how obtuse the machine code for a few “ simple ” lines of programming
source can be The source code is shown in the listing, followed by the resulting Intel machine code
➤
➤
➤
➤
➤
➤
➤
Trang 2The debugger has the unenviable job of reversing this process — it must examine the raw machine code and translate that back into something that corresponds to the functions, methods, code blocks, classes, structures, and variable names defi ned in your source code (You see this process at work later in Figure 18 - 6.)
LISTING 18 - 1: Compiled source code
SOURCE CODE
- (void)dealloc {
free(byteArray);
[super dealloc];
}
COMPILED ASSEMBLY CODE
pushl %ebp movl %esp, %ebp pushl %ebx subl $36, %esp movl 8(%ebp), %ebx movl 4(%ebx), %eax movl %eax, (%esp) call _free movl %ebx, -16(%ebp) movl L_OBJC_CLASS_SieveOfEratosthenes+4, %eax movl %eax, -12(%ebp)
leal -16(%ebp), %edx movl L_OBJC_SELECTOR_REFERENCES_2, %eax movl %eax, 4(%esp)
movl %edx, (%esp) call _objc_msgSendSuper addl $36, %esp
popl %ebx leave ret
To accomplish this feat, the debugger needs a lot of help That help comes in the form of debugger symbols produced by the compiler Debugger symbols are a kind of massive cross - index They contain information like “ the machine instruction at byte offset 12,738 corresponds to line 83 of the source fi le breakme.c ” If you set a breakpoint at line 83 of breakme.c , the debugger knows it needs to stop your program at the instruction found at offset 12,738 If your program crashes at (or near) the machine instruction at offset 12,738, the debugger can tell you that your program crashed
at (or near) line 83 of breakme.c The debugger symbols contain similar information about data structures, classes, automatic variables, and so on You must request that these debugger symbols be produced when your application is compiled If you have created your project using one of the Xcode templates, you should have a Release and a Debug build confi guration The Debug build confi guration, shown in Figure 18 - 5, for your program ’ s target has the following build settings:
Generate Debug Symbols: On
➤
Trang 3Optimization Level: None Fix & Continue: Off
➤
➤
FIGURE 18-5
Generate Debug Symbols enables the full array of debugger symbols in the compiler, detailing every
aspect of your code for the debugger Without it, the debugger is next to useless This information
is produced when each source fi le is compiled, it takes a little extra time to compile, and produces
a lot of data — quite often more data than your actual program — which the debugger has to load
All of this slows down both compilation and launch time If you are debugging a massive amount of
code, you may elect to generate debug symbols only for some modules and not others For example,
you may generate debugger symbols for your main application code but not some well - tested library
routines This speeds up building and debugging, but limits your debugging to the portion of your
code that has debugger symbols
The Debug Information Format defi nes which fi le format to use to write the debugging information
DWARF with dSYM File is the modern format and should be your fi rst choice DWARF and Stabs
are older formats that embed the debugging information in the executable
If you ’ re using the modern DWARF with dSYM File debug symbols format, the legacy build settings Strip Linked Product and Strip Debug Symbols During Copy are largely superfl uous Legacy debugging symbols were stored in the executable fi les themselves, and later stripped off during the deployment phase
of your Release build confi guration The modern dSYM fi le writes the debugging information to a separate symbol fi le, so the executable is essentially already
“ stripped ” of its debug information
If you ’ re developing a legacy application created with an earlier version of Xcode, update your debug format to DWARF with dSYM File and make sure the Strip Debug Symbols During Copy build setting is set to NO Leaving Strip Debug Symbols During Copy on will interfere with code signing, which is critical to iPhone and modern Mac OS X applications
Trang 4While you ’ re debugging, the optimization of your code should be set to None The reason why goes back to how debuggers work Optimization, by its very nature, is logic in the compiler that reorganizes, reorders, rewrites, and often eliminates code that it fi nds to be ineffi cient or redundant
Take the following code fragment as an example:
Line 100: int i=0;
Line 101: for (i=1; i < argc; i++)
With optimization turned on, the compiler eliminates the statement i=0 because the result of that assignment is never used In the debugger, it now becomes impossible to set a breakpoint at line
100 because the code that corresponds to that line of source code doesn ’ t exist in your compiled application More advanced optimization techniques, such as loop unrolling or instruction reordering, can produce even more bizarre aberrations, such as programs whose statements execute out of order (for example, line 101, then 103, then 102) It might be impossible to stop an application at a certain point in your source code, or step through your code one line at a time
Skeptical readers are invited to enable full optimization and then attempt to debug their application Fix & Continue is a feature, covered later in “ The Magic Fix, ” that allows you to make limited code changes in your application while your application is running That is, you don ’ t have to stop your program, change your code, rebuild, and restart You simply change your code and keep executing
To use this feature, your compiled object code must contain additional information that the Fix &
Continue feature needs If you leave this build setting off, you can still debug your code, but the Fix & Continue feature is disabled
The Release build confi guration for new projects has the opposite build settings: no debug symbols, symbols stripped, normal optimization, and no Fix & Continue If you have created your own targets or build confi gurations, you ’ ll need to set them up accordingly
Note that Xcode project templates defi ne many of these settings in the target ’ s build settings
If you have a multiple target project, you can easily misconfi gure the settings in one target and not realize it Or, you can set them in the project build settings, where they will be promptly ignored For these reasons, I recommend moving these basic debug settings into the project build settings level for multi - target projects, and then override these settings only in those targets that require something different (which is rare) This way, you can adjust the level of debugger symbols produced (for example) with a single build setting, rather than having to change this setting in every target
TIP TO REMEMBER
If you think you ’ re having problems getting your build settings right, create a temporary build confi guration to experiment with After you have everything working, you can compare that to the original, or just delete the one that doesn ’ t work If you ’ ve made a lot of changes and really want to know what the differences are, export all of your build settings by copying and pasting them into two separate text fi les You can then compare them visually or using a diff tool
Trang 5DEBUGGING EXECUTABLES
With the preliminaries out of the way, you ’ re ready to debug your application Getting started is as
easy as running your program Choose any of these commands to start your application under the
control of the debugger:
Build ➪ Build and Debug (Command+Return) Build ➪ Build and Debug - Breakpoints On (Command+Y) Run ➪ Debug (Option+Command+Return)
Run ➪ Debug - Breakpoints On (Option+Command+Y)
As mentioned in the “ Running Your Application ” section, the two unqualifi ed “ Debug ” commands
change to “ Run ” commands when breakpoints are inactive The two “ Breakpoints On ” commands
simply activate breakpoints and start debugging, identical to Run ➪ Activate Breakpoints, followed
by Build/Run ➪ Debug
All of these commands start your program under the control of the debugger The time from which
the debugger starts your program until it fi nishes is referred to as the debug session
An alternative is available in the targets group if you would like to build and then run or debug a
target that is not the active target or executable Right/Control - click the target ’ s icon in the target
smart group and choose either the Build and Start or the Build and Debug command The effects
are the same as changing the active target to the one selected, issuing a Build and Run/Debug
command, and then switching the active target back to what it was originally
You can have only one debug session active for a project However, you can have concurrent debug
sessions from separate projects A Client and a Server application, both built using separate projects,
could each be running in separate debug sessions simultaneously
The Process Behind the Curtain
Like most modern integrated development environments (IDEs), the Xcode application doesn ’ t
perform the actual debugging — or compilation, or linking, or profi ling — of your application
Xcode simply provides an intelligent interface to the command - line tools that do the bulk of the
work In the case of debugging, the work is usually done by gdb , the GNU Debugger
I say usually, because Xcode can also use other debuggers It understands the JavaBug and
AppleScript debuggers, but if you ’ re doing Mac OS X or iPhone OS development, you ’ ll be
using gdb
Xcode doesn ’ t try to hide the debugger; there are plenty of places where you can interact with
the debugger directly What it tries to do is eliminate much of the tedium of using command - line
debuggers by replacing the command line with an interactive, graphical user interface
So while you ’ re working your way through this chapter, keep this in mind:
Almost every debugging task you undertake in Xcode is eventually translated into a com-mand sent to gdb , or whatever debugger you ’ re using
➤
➤
➤
➤
➤
Trang 6Debugger Spoken Here
Another great advantage to using an intermediate debugger is that your debugger interface remains largely the same regardless of the target application environment Xcode can debug an application
in any of the following environments:
A local process running in Mac OS X
A remote process running on another Mac OS X system
An iPhone application running in a local simulator process
An iPhone application running on a remote device Debugging an application running on the same computer system is the most common scenario, but
by no means the only one The section “ Remote Debugging, ” later in this chapter, shows you how
to debug a process interactively that is running on a different computer system
iPhone application development would seem like it is worlds apart from desktop application development While the design and implementation might be substantially different, the development tools remain almost identical
You might have expected a substantial portion of this chapter to be dedicated to the iPhone simulator and iPhone native debugging, but there ’ s really very little to say Though there ’ s some prerequisite confi guration required before an iPhone or iPod Touch can debug applications (see Chapter 22), there is virtually no difference between debugging an iPhone application running
on an iPod Touch and debugging a Cocoa application running on your development system Almost everything in this chapter applies to both
ATTACHING TO RUNNING EXECUTABLES
As mentioned earlier, the Xcode debugger can attach itself to an already running process on your
local system, extracting debug information and taking control of its execution Xcode may do this for one of four reasons:
You create a new breakpoint or enable an existing breakpoint You reactivate existing breakpoints
An application you started in Xcode crashes or encounters a trap You choose a process from the Run ➪ Attach to Process menu The fi rst three all apply to the currently running executable started via Xcode, but the behavior is
a little different depending on which operating system you ’ re running If you ’ re running Mac OS
X 10.5 (Leopard) or earlier, your application is launched normally and the gdb debugger is started and attached when requested If your running Mac OS X 10.6 (Snow Leopard) or later, Xcode preemptively starts gdb when it launches your application, but lets your application “ run free ” When you enable breakpoints, gdb steps in, enables breakpoints, and assumes control of your application ’ s execution The primary advantage is speed; gdb is launched quietly in the background, and springs instantly into action when requested
➤
➤
➤
➤
➤
➤
➤
➤
Trang 7Whether gdb is preemptively started ahead of time or launched post hoc is largely immaterial, except in a few circumstances Some of the system frameworks include anti - piracy code that either resists being debugged or disables certain features in the presence of the debugger If you ’ re working with QuickTime or similar libraries, you may have to manually launch your application and then use the Run ➪ Attach to Process command at some strategic time
Once your application is running, creating or enabling any breakpoint signals to Xcode that you
want to take control of the application with the debugger The workfl ow you ’ d typically use this in
looks something like this:
1. Write some code
2. Build and run the application
3. Discover that something ’ s not working right
4. Start the debugger and have it attach to the already running application
5. Debug the problem
The Run ➪ Attach to Process menu lets you attach the Xcode debugger to a process that Xcode
didn ’ t launch In this menu are all of the running applications If you need to attach to a background
process, choose the Run ➪ Attach to Process ➪ Process ID command and enter the process ID you
want to debug These commands are useful when you want to debug a process that you started
outside of Xcode (say, from the Finder) or for processes started programmatically; for instance,
you ’ ve written a Cocoa application that executes a command - line tool
When attaching to a running process, Xcode does its best to match the executable with a target in
the current project If it can ’ t it will still attach to the process, but the amount of useful debugging
information will be extremely limited
The third reason Xcode will attach to your running application involves a trap, uncaught signal,
or other exception that would normally cause the process to exit immediately Xcode automatically
intercepts these exceptions and — rather than letting your application terminate — attaches the
debugger You are left in a debug session with the program suspended at the location that caused
the fatal problem You can examine the threads, stack, variables, and other information in an
attempt to determine what went wrong
You might fi nd it useful to intentionally cause a trap programmatically To do this, you can add a
debugger trap or a hard trap instruction to your code The Debugger() or DebugStr() functions
request the debugger For the most part, they act like programmatically defi ned breakpoints
They will cause the debugger to stop the application, or attach the debugger to an already running
application If left in your code and run in the absence of a debugger, these functions will
write a message to the system console every time they are executed — but they won ’ t terminate your
application
Trang 8A machine trap instruction is essentially a programmatic crash It can be inserted using an assembly directive, as depicted in the following listing:
#if defined( ppc ) || defined( ppc64 ) asm { trap }
#endif
#if defined( i386 ) || defined( x86_64 ) asm { int 3 }
#endif
These machine language trap instructions cause your application to immediately stop If launched from Xcode, Xcode will attach the debugger as it would for any unrecoverable exception If a trap
is executed outside of Xcode, your program will immediately terminate The system will treat your application as if it had crashed
Also see the “ Custom Executables ” section Whether Xcode stops for Debugger() calls or automatically attaches itself when the process crashes can be disabled for individual executables
IN - EDITOR DEBUGGING
Xcode ’ s in - editor debugging tools let you perform basic and common debugging actions right
in your editing window This means that in many situations you can edit, compile, run, and debug your application without ever switching to another Xcode window or view
When a debugging session starts, the debugger strip appears at the top of your editor pane, as shown in Figure 18 - 6 Combined with breakpoint controls in the gutter (which, more than likely,
were already visible) and additional symbol inspectors (called datatips ), the in - editor debugging
controls allow you to do three things:
Modify breakpoints Control execution Inspect variables
➤
➤
➤
FIGURE 18-6
A setting in the debugging pane of the Xcode preferences enables the in - editor debugging controls If you don ’ t see the in - editor debugging controls, check your preferences