Mac OS X Reference Library Apple Developer
Search

Viewing Variables and Memory

This chapter describes the various ways in which you can view the values of variables as you debug your programs.

Setting the Variable Display Format

You can view the value of a variable in a variety of formats, including hexadecimal, octal, and unsigned decimal. To display the value of a variable in a different numeric format:

  1. Open the Debugger window and select the variable in the Variable list.

  2. From Run > Variables View choose one of these options:

    • Natural

    • Hexadecimal

    • OSType

    • Decimal

    • Unsigned Decimal

    • Octal

    • Binary

You can also cast a variable to a type that’s not included in the menu. For example, a variable may be declared as void *, yet you know it contains a char * value. To cast a variable to a type, select the variable, choose Run > Variables View > View Value As.

Using Data Formatters

Xcode allows you to customize how variables are displayed in debugger datatips and the Variable list in the debugger by specifying your own format strings for the Value or Summary columns. In this way, you can display program data in a readable format. Xcode includes a number of built-in data formatters for data types defined by various Mac OS X system frameworks. You can edit these format strings or create your own data formatters.

To turn data formatters on or off, choose Run > Variables View > Enable Data Formatters.

Writing Data Formatters

You can provide your own data formatters to display data types defined by your program. To edit the formatter associated with a variable value or variable summary, select the variable in the Variable list in the debugger and double-click in the appropriate column of the Variable list in the debugger. You can also choose Run > Variables View > Edit Summary Format.

Data formatters can contain literal text and references to certain values and expressions.

Data Formatter Macros

These are the macros through which you can access dynamic structures in data formatters:

Tips for Using Data Formatters

These are some tips and important concepts to keep in mind when using data formatters in your projects:

Data Formatter Example

The following example uses the CGRect data type to illustrate how you can build format strings using member references and expressions. (Note that because Apple provides format strings for the CGRect data type, Xcode already knows how to display the contents of variables of that type). The CGRect data type is defined as follows:

struct CGRect { CGPoint origin; CGSize size; }; typedef struct CGRect CGRect;

Assuming that the goal is to create a format string that displays the origin and size of variables of type CGRect, there are many ways you can write such a format string. For example, you can reference members of the origin and size fields directly. Of course, each of these two fields also contains data structures, so simply referencing the values of those fields isn’t very interesting; the values you want are in the data structures themselves. One way you can access those values is to include the full path to the desired field from the CGRect type. For example, to access the height and width of the rectangle, in the height and width fields of the CGSize structure in the size field you could use the references %size.height% and %size.width%. A sample format string using these references might be similar to the following:

height = %size.height%, width = %size.width%

You could write a similar reference to access the x and y coordinates of the origin. Or, if you already have a data formatter for values of type CGPoint that displays the x and y coordinates of the point in the Summary column of the Variable list—such as (%x%, %y%)—you can leverage that format string to display the contents of the origin field in the data formatter for the CGRect type. You can do so by referencing the Summary column for CGPoint, as in the following format string:

origin: %origin%:s

When Xcode evaluates this format string, it accesses the origin field and retrieves the contents of the Summary column for the CGPoint data type, substituting it for the reference to the origin field. The end result is equivalent to writing the format string origin: (%origin.x%, %origin.y%).

You can combine this format string with the format string for the size field and create a data format string for the CGRect type similar to the following:

origin: %origin%:s, height = %size.height%, width = %size.width%

For example, a rectangle with the origin (1,2 ), a width of 3, and a height of 4 results in the following display:

origin: (1, 2), width=3, height=4.

You can also write a data formatter to display the same information using an expression such as the following:

origin: {$VAR.origin}:s, height = {$VAR.size.height}, width = {$VAR.size.width}

When Xcode evaluates this expression for a variable, it replaces $VAR with a reference to the variable itself. Of course, using an expression to perform a simple value reference is not necessary. Another example of an expression in a format string is {(NSString *)[$VAR name]}:s to display the name of a notification, of type NSNotification.

When you specify a custom data formatter for a variable of a given type, that format string is also used for all other variables of the same type. Note, however, that you cannot specify a custom format for string types, such as NSString, char*, and so on. Custom data formatters that you enter in the debugger are stored at:

~/
   Library/
      Application Support/
         Developer/
            Shared/
               Xcode/
                  CustomDataViews/
                     CustomDataViews.plist

Using Data-Formatter Bundles

A data-formatter bundle is a plug-in that contains functions you can invoke from the data formatters described in “Using Data Formatters.” You use data-formatter bundles when you need more power to create a textual representation of a variable than data-formatter expressions provide.

Data-Formatter Bundle Requirements

A data-formatter bundle must contain these items:

Data-formatter bundles and the data-formatter function they define must meet these requirements:

Data-Formatter Bundle Example

The following steps create a data-formatter bundle that formats a structure containing temperature data into its Celsius or Fahrenheit representation. Figure 8-1 shows the structure of the finished project and highlights the files that need to be added the project.

image: ../art/temperatureformatter_project_structure.jpgimage: ../art/temperatureformatter_project_structure.jpg
  1. Create a Mac OS X Bundle project based on the Core Foundation framework.

    New Project dialog, selecting a Mac OS X Core Foundation bundle project.
  2. Name the project TemperatureFormatter.

  3. Delete the External Frameworks and Libraries group from the TemperatureFormatter group in the Groups & Files list.

Create the TemperatureFormatter Data-Formatter Bundle

The following steps create the data-formatter bundle.

  1. Add the Temperature.h header file, which defines the Temperature data type.



    Listing 8-1  Temperature.h

    // Temperature.h
     
    typedef struct {
       int  degrees;
       char scale;
    } Temperature;
     

    Save the file

  2. Add the Temperature_formatter.c file, which implements the Temperature data formatter. (This implementation file doesn’t require a corresponding header file.)



    Listing 8-2  Temperature_formatter.c

    // Temperature_formatter.c
     
    // Change /Developer with the path to your <Xcode> directory if you've placed it elsewhere.
    #import "/Developer/Applications/Xcode.app/Contents/PlugIns/GDBMIDebugging.xcplugin/Contents/Headers/DataFormatterPlugin.h"
     
    #include <stdlib.h>
    #include <string.h>
    #include "Temperature.h"
     
    // Plug-in function list (required symbol).
    _pbxgdb_plugin_function_list *_pbxgdb_plugin_functions;
     
    // Error message.
    static char *nullPluginFunctions = "CFDataFormatter plugin error: _pbxgdb_plugin_functions not set!";
     
    // Formats Temperature data.
    char *format_Temperature(Temperature temperature, int identifier) {
       char *formatter_buff;
       if (_pbxgdb_plugin_functions) {
          formatter_buff = (char *)(_pbxgdb_plugin_functions->allocate(identifier, 255));
          formatter_buff[0] = 0;
          //strcpy(formatter_buff, "Hello");
          sprintf(formatter_buff, "%i degrees %s", temperature.degrees, temperature.scale == 'C'? "Celsius":"Fahrenheit");
       }
       else {
          formatter_buff = nullPluginFunctions;
       }
     
       // Debugging aid.
       printf("format_Temperature(identifier: %i)\n", identifier);
     
       return formatter_buff;
    }

    Save the file.

  3. Add a property list file named CustomDataViews.plist. Enter the following properties to the file:

    File Version (String): 1
    Temperature (Dictionary):
       SummmaryString (String): {(char *)format_Temperature((Temperature) $VAR, (int) $ID)}:s
    Property list editor window displaying CustomDataViews.plist file.

    Save the file.

  4. Set the Installation Build Products Location target build setting for all configurations to:

    $(DEVELOPER_DIR)
  5. Set the Installation Directory target build setting for all configurations to:

    /Library/Xcode/CustomDataViews
  6. Delete the Fix & Continue target build setting for all configurations.

  7. Build the bundle, and ensure that there are no build errors.

  8. Build the bundle from the command line to install it.

    Launch Terminal and perform these commands:

    > cd <TemperatureFormatter_project_directory>
    > /Developer/usr/bin/xcodebuild install -target TemperatureFormatter

    The Temperature data-formatter plug-in is installed in /Developer/Library/Xcode/CustomDataViews.

  9. Restart Xcode.

Test the TemperatureFormatter Data-Formatter Bundle

The following steps test the bundle created in “Create the TemperatureFormatter Data-Formatter Bundle.”

  1. Add a Shell Tool target named TemperatureFormatterTesting to the TemperatureFormatter project.

  2. Delete the Fix & Continue (GCC_ENABLE_FIX_AND_CONTINUE) TemperatureFormatterTesting target build setting in all configurations.

  3. Set the active target to TemperatureFormatterTesting.

  4. Add the TemperatureFormatter_test_main.c file to the TemperatureFormatterTesting target. (This implementation file doesn’t require a header file.)



    Listing 8-3  TemperatureFormatter_test_main.c

    // TemperatureFormatter_test_main.c
     
    #include <stdlib.h>
    #include "Temperature.h"
     
     
    int main(int argc, char *argv[]) {
       Temperature temperature_1, temperature_2;
       temperature_1.degrees = 35;
       temperature_1.scale   = 'C';
       temperature_2.degrees = 98;
       temperature_2.scale = 'F';
       return 0;
    }
  5. Add a breakpoint to the return 0 statement of the main function.

  6. Build and debug the TemperatureFormatterTesting target.

  7. Verify that the Variable list in the debugger displays the formatted data, as shown in Figure 8-1.

    Figure 8-1  Testing the TemperatureFormatter data-formatter bundle

    Debugger window with TemperatureFormatter_test_main.c file in editor pane. Program is stopped at the return 0 statement line. Variable list shows TemperatureFormatter is applied to the temperature_1 and temperature_2 variables.Debugger window with TemperatureFormatter_test_main.c file in editor pane. Program is stopped at the return 0 statement line. Variable list shows TemperatureFormatter is applied to the temperature_1 and temperature_2 variables.

Monitoring the Value of an Expression

Using the Expressions window, you can view and track the value of an expression. For example, you can track a global value or a function result over the course of a debugging session.

To open the Expressions window, choose Run > Show > Expressions.

Type the expression you want to track in the Expression field. Xcode adds the expression, evaluates it, and displays the value and summary for that expression. The display format of the value and summary information is determined by any data formatters in effect.

The expression can include any variables that are in scope at the current statement and can use any function in your project. To view processor registers, enter an expression such as '$r0', '$r1'.

In the debugger you can add a variable to the Expressions window by selecting the variable in the Variable list and choosing Run > Variables View > View Variable As Expression.

To remove an expression from the Expressions window, select it and press Delete.

Tips on using the Expressions window

For best results, follow these tips when using the Expressions window:

Browsing Memory

When execution of the current program is paused, you can browse the contents of memory using a memory browser.

To open a memory browser, like the one shown in Figure 8-2, choose Run > Show > Memory Browsers.

Figure 8-2  A memory browser

The memory browser window

You can also open a memory browser to the location of a particular variable:

  1. In the debugger, select the variable.

  2. Choose Run > Variables View > View in Memory Browser.

In memory browsers, you can see:

Viewing Shared Libraries

You can see which libraries have been loaded by the program using the Shared Libraries window, shown in Figure 8-3. To open this window, choose Run > Show > Shared Libraries.

Figure 8-3  Shared Libraries window

The Shared Libraries window

The Module Information table lists all the individual libraries the executable links against. In this table, you can see the name and address of each shared library, as well as the symbols the debugger has loaded for that library. The Starting Level column shows which symbols the debugger loads by default for a given library when the current executable is running. The Current Level column shows which symbols the debugger has loaded for the library during the current debugging session. When an entry has a value in the Address and Current Level columns, the library has been loaded in the debugging session.

The path at the bottom of the window shows where the currently selected library is located in the file system. You can quickly locate a particular library by using the search field to filter the list of libraries by name.

Using the Shared Libraries window you can also choose which symbols the debugger loads for a shared library. This can help the debugger load your project faster. You can specify a default symbol level for all system and user libraries; you can also change which symbols the debugger loads for individual libraries.

For any shared library, you can choose one of three levels of debugging information:

You can specify a different symbol level for system libraries and user libraries. User libraries are any libraries produced by a target in the current project. System libraries are all other libraries.

By default, the debugger loads only external symbols for system and user libraries, and automatically loads additional symbols as needed. Turning off the “Load symbols lazily” option, described in “Debugging Preferences,” changes the default symbol level for User Libraries to All. This is a per-user setting and affects all executables you define. You can also customize the default symbol level settings for system and user libraries on a per-executable basis, using the Default Level pop-up menus in the Shared Libraries window.

For some special cases—applications with a large number of symbols—you may want to customize the default symbol level for individual libraries when running with a particular executable. To set the initial symbol level to a value other than the default, make a selection in the Starting Level column. While debugging, you can increase the symbol level using the Current Level column. This can be useful if you need more symbol information while using GDB commands in the console. Clicking Reset sets all the starting symbol levels for the libraries in the Module Information table to the default value.




Last updated: 2009-10-19

Did this document help you? Yes It's good, but... Not helpful...