Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 11 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
11
Dung lượng
3,04 MB
Nội dung
490 ❘ CHAPTER 18 DEBUGGING Object 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. c18.indd 490c18.indd 490 1/22/10 12:55:54 PM1/22/10 12:55:54 PM Download at getcoolebook.com CHANGING 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 ➤ ➤ ➤ ➤ Changing Data and Code ❘ 491 c18.indd 491c18.indd 491 1/22/10 12:56:00 PM1/22/10 12:56:00 PM Download at getcoolebook.com 492 ❘ CHAPTER 18 DEBUGGING LISTING 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 c18.indd 492c18.indd 492 1/22/10 12:56:01 PM1/22/10 12:56:01 PM Download at getcoolebook.com When 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. ➤ ➤ ➤ ➤ ➤ ➤ Debugger Console ❘ 493 c18.indd 493c18.indd 493 1/22/10 12:56:02 PM1/22/10 12:56:02 PM Download at getcoolebook.com 494 ❘ CHAPTER 18 DEBUGGING FIGURE 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. c18.indd 494c18.indd 494 1/22/10 12:56:03 PM1/22/10 12:56:03 PM Download at getcoolebook.com The 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 Shared Libraries ❘ 495 c18.indd 495c18.indd 495 1/22/10 12:56:04 PM1/22/10 12:56:04 PM Download at getcoolebook.com 496 ❘ CHAPTER 18 DEBUGGING in 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. ➤ ➤ ➤ ➤ ➤ ➤ c18.indd 496c18.indd 496 1/22/10 12:56:05 PM1/22/10 12:56:05 PM Download at getcoolebook.com The 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 Custom Executables ❘ 497 c18.indd 497c18.indd 497 1/22/10 12:56:05 PM1/22/10 12:56:05 PM Download at getcoolebook.com 498 ❘ CHAPTER 18 DEBUGGING system 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. c18.indd 498c18.indd 498 1/22/10 12:56:06 PM1/22/10 12:56:06 PM Download at getcoolebook.com The 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 Custom Executables ❘ 499 c18.indd 499c18.indd 499 1/22/10 12:56:07 PM1/22/10 12:56:07 PM Download at getcoolebook.com [...]... 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... 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... 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... 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 configure a few common debugging features Download at getcoolebook.com c18.indd 500 1/22/10 12:56:08 PM ... 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 . ➤ ➤ ➤ ➤ ➤ ➤ Debugger Console ❘ 4 93 c18.indd 493c18.indd 4 93 1/22/10 12:56:02 PM1/22/10 12:56:02 PM Download at getcoolebook.com 494 ❘ CHAPTER 18 DEBUGGING FIGURE 18 -37 Like many of Xcode ’ s interfaces,. 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. 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