Improving your productivity using the IBM Debugger for AIX

The IBM® Debugger solution is packaged in a variety of IBM software development products. The tool features an interactive source level debugger that helps you to debug applications written in different languages and running on different platforms. The key feature of the IBM Debugger is that its single user interface client can talk to several different debug engines. The debug engine always runs on the native platform, while the user interface client runs on a remote machine. The debug engine connects to the user interface client through a network connection to start a debug session. This article will show you how to leverage these core competencies to help improve your software development productivity.

Sean Kennedy (seanpk@ca.ibm.com), Software Developer, IBM

author photoSean Kennedy began his career writing structural analysis software in Fortran and C++ for a small ISV. Kennedy Joined the IBM® company as a service and support specialist in 2006 for the XL C, C++, and Fortran compilers, and is currently developing debug technology for the IBM Rational® division.; he is the Rational Debugger for Native Code on IBM Power™ Systems Lead.



Morris Kwan (mkwan@ca.ibm.com), Software Developer, IBM

author photoMorris Kwan is a software developer working at IBM® Toronto Lab. Since joining IBM in 1997, Kwan has been working on development tools for various languages and platforms, including VisualAge C++ integrated development environment (IDE) for AIX, Eclipse CDT and the XSLT processor. Kwan is now developing web services debugging tools for Rational Application Developer product.



Natasha D'Silva (ndsilva@ca.ibm.com), Software Developer, IBM

author photoNatasha D’Silva joined the IBM® company as an intern in 2006 and has been working with IBM debug technology ever since. After graduating from the University of Toronto in 2008, D’Silva continued in her role as a developer for compiled language debuggers. She is now working on the debug component of Rational Application Developer solution, producing and supporting tooling for debugging web applications.



04 May 2010

Also available in Chinese Russian

Today's software development organizations are under pressure to deliver high-quality products, that meet the needs of their users, as soon as possible. One way companies are achieving this goal is by leveraging the IBM Debugger for AIX to meet challenging quality targets and improve their time-to-solution. This article will show you how to use the IBM Debugger for AIX, for success.

Supported platforms and languages

While components of the IBM® Debugger for AIX are shipped in a number of IBM products, this article specifically focuses on using the IBM Debugger with the IBM XL C/C++ and COBOL compilers for AIX®. (See the Resource section at the end of the article for links to the IBM products that provide debug support using technology included in the IBM Debugger for AIX, as well as additional capabilities.)

The IBM Debugger for AIX allows you to debug AIX programs that are written in C, C++ and/or COBOL languages. The user interface client runs Microsoft® Windows® and is connected remotely through a network connection to a debugger engine running on AIX.

How it works

The IBM Debugger tool uses a client/server design model, where the user interface client (UI) connects to a remote debug engine to establish a debug session. It is this debug model that is one of its major advantages of this solution, as the UI can talk to several different debug engines at the same time. This communication is handled through a daemon running within the UI. The debug engines are where the "real" work of debugging is done — for example, the debug engines control execution of the target code, read debug information from the target's executable load modules, set breakpoints, evaluate expressions, and disassemble the program's instructions. In order to do all of this, the debug engine must have exhaustive knowledge about the format of the executable being debugged, in addition to the environment in which it is running.

Starting a debug session

Compiling the program with debug information

In order to debug your program at the source code level, you need to compile your program with certain compiler options that instruct the compiler to generate symbolic information and debug hooks in the object file. For the IBM XL C/C++ compiler Enterprise Edition for AIX, compile without optimization (-O0) and with the -g option.

Setting up the daemon

The UI contains a daemon that listens for debug engine connections. The daemon is the link between the UI and the debug engine, and must be configured before starting debug sessions. The debug daemon listens on a port for debug engine connections. To begin debugging, you need to make sure the daemon is ready to accept connections. You can observe and change the state of the debug daemon from the daemon button in the Debug view toolbar in the UI. If the daemon is listening, the icon appears as a icon if daemon is listening. If the daemon is not listening, the icon appears as a icon if daemon is not listening.

The default port used by the daemon is 8001. If you are running the UI on a multi-user machine, the default daemon port might already be in use by a different debug session. If you get a message saying that the debug daemon failed to start, you can change the daemon port number from the Debug view or from the Debug Daemon preference page by specifying either a different port number or a range of ports.

Remote debugging

You can debug a program remotely by starting the UI and the engine separately. Start the UI on a system running Microsoft Windows and use a remote debug engine running on AIX to connect to the UI.

Perform the following steps to start a debug session:

  1. Start the debugger user interface on the client machine.
    Do not specify a program name when starting the debugger user interface.
  2. Make sure the debug daemon is in listening state.
    If it is not already in listening state, click on the daemon icon to start listening for debug engine connections. If the port is already used for another engine connection, you will have to change the port number or specify a range of ports. For more information see the previous section, "Setting up the daemon".
  3. Find the host name or IP address of the client machine.
    The client machine is the machine on which you are running the debugger user interface. You can find the client IP address by selecting the down arrow to the right of the daemon icon and selecting Get Workstation IP from the menu. The host name or client IP address is needed to start the debug engine.
  4. Start the debug engine using the following command line:
    $ irmtdbgc -qhost=<daemon host>[:port] <program name> <program parameters>
    Where daemon host is the host name or IP address of the client machine that you found in the previous step. The port is the port number that the debug daemon is listening on.

The command irmtdbgc supports a number of command line options and recognizes a number of environment variables. For example, the environment variable DER_DBG_ADDR contains the default value for the -qhost option. You can type irmtdbgc -help on the command line to get help on how to use irmtdbgc.

Using the IBM Debugger

Once the connection between the UI and the debug engine is made, you are ready to start debugging your source code. The following screen capture shows the UI for a debug session where the user is debugging a program called sort:

Figure 1. Successful connection from engine to UI
Picture shows debug session with program source.

The debugger displays a lot of information, and it is grouped into different sections called views. The sections below will discuss those that are most important as they relate to various debugging related tasks.

The view you will likely use the most is the Debug view, which is the main control center for your debug session. In addition to the Debug view, the UI contains several other views for displaying special information, including the Monitors, Breakpoints, Memory and Modules, Variables, and Registers views. The icons in each view's toolbar provide additional useful actions, and you can place your mouse over each of them to display a tooltip describing the action. To learn more, experiment with each of them.

Debug view

The debug view is the starting point for any debug session. Each new UI-engine connection is added to the debug view for management. For each process under debug, the debug view displays all its running threads. In turn, for each suspended thread, a list of stack frames is visible, representing the call hierarchy. In addition to the debug view, the UI contains several other views for displaying special information, including the Variables, Breakpoints, Registers, Monitors, Memory and Modules views. Also, the source file currently being debugged is displayed in the Source editor. The contents of almost all the other views are dependent on the "active" debug session. A debug session is made active by selecting the debug target, or any of its components (for example, threads, stack frames) in the debug view. When a debug session is activated, the content of the remaining views in the UI are updated.

Setting breakpoints

The breakpoints view is the central location for controlling breakpoints. You can always see a list of breakpoints you have set in the Breakpoints view, and can add a new breakpoint by right-clicking in the view and selecting one of the entries from the Add Breakpoint sub menu. Different types of breakpoints are supported, and these are discussed more in the section below, "Various breakpoint types". Breakpoints can be temporarily made inactive by disabling them. In the breakpoints view, enabled breakpoints have a check-mark beside them, and breakpoints can be enabled and disabled by toggling that check-box.

Stepping through your program

Figure 2. Controls for stepping your program can be found in the Debug view toolbar
Screenshot shows the debug view toolbar.

Beginning with the first yellow arrow, they are: Step Into, Step Over, and Step Return.

These commands are also available from the run menu, and by using the keyboard shortcuts: F5, F6, and F7, respectively. If you direct the program to Run (F8), execution will resume until either a breakpoint is hit, or the program terminates.

Examining variables

The variables view lists in-scope variables each time a thread or the program stops.

Also, after every program suspend, changes to in-scope variables will be highlighted in the variables view. There are several actions available in the variables view. To get an idea of what can be done, bring up the context menu by right clicking anywhere in the screen view. Variable values can also be changed in the view, and if you edit a variable from the variables view, the new value assigned becomes effective immediately. You can also change the representation of a variable; for example, display an integer in binary format instead of the default decimal, by using the Change representation context menu of a selected variable.

Source lookup

Naturally, being able to view the source of the program you are debugging is critical to debug. The debug engine tries to find, or "look up" the source for the program based on the debug information in your program. The default source lookup path simply contains the debug engine. This means that the debug engine, based on the file path information stored in a program compiled with the -g option, will attempt to find the source on the server. If the debug engine is unable to find the source for the program using the debug information, you can edit the source lookup path. This is a list of locations in which the debug engine will search for the missing source. You can direct the debugger to search for the missing source in more locations by editing the source lookup path. This is achieved by selecting the Edit Source Lookup... entry from the program's context menu in the debug view.

Figure 3. Accessing the source lookup path editor
Graphic shows the Edit source lookup action.

Aside from source file display, there are two other options for viewing the debugger source; you can choose to show the disassembly for the program, and step through disassembly instead, or you can use the mixed view to display the source and disassembly for the program simultaneously. The debugger help contains instructions on how to choose what works best for your program. Note that stepping in disassembly or mixed view is done by assembly instruction, whereas stepping in a source file is done by statement.

Restart

Sometimes you may find your program has run past a point of interest and you need to restart your debug session. Fortunately, that can be done without leaving the friendly confines of the UI. Simply set a breakpoint on your point of interest, hit the Restart program button, and run the program until your breakpoint is hit.

Figure 4. Restarting an active debug session
Picture shows the restart program button.

Getting help

The debugger UI is built on the Eclipse open source language, and benefits from its advanced help system. For example, hovering over a tool bar button will produce a tool tip, and pressing F1 will bring up the context sensitive help for the view.

Selecting the Key assist... entry from the Help menu is a great way to see all the available shortcut keys.

Figure 5. Active keyboard shortcuts
Picture shows tooltip window, depicting shortcuts.

Advanced debugging

This section will discuss some of the more advanced features available in the IBM Debugger tool. These allow for faster problem determination, and are part of the major advantages of using the IBM Debugger solution.

Breakpoint conditions

Sometimes a piece of code is called many times, even from different threads, but you only want to stop in that code if a particular condition is met. This is where the conditional page of the breakpoint wizards is very useful. This second, optional page (you get there by selecting Next instead of Finish on the main page) allows you to handle these situations. You can control the thread on which you wish to stop, the frequency of suspension, and even define a boolean expression that must be true before suspending. You may also have multiple conditional breakpoints at the same location; they can be easily enabled and disabled from the breakpoints view.

Various breakpoint types

The IBM Debugger for AIX tool provides the following breakpoint types.

  • Address: Suspends the program when the execution pointer reaches a particular address in the program. This breakpoint type is also created when you set a breakpoint from the source view in disassembly or mixed modes. This allows you fine grained control of exactly which instruction the debugger should suspend at.
  • Entry: Suspends the program when execution is about to enter the given function. This breakpoint type can be created by right clicking on an entry in the Modules view. If the function in question is not listed in the modules view because the containing executable has not been loaded, you can create a deferred entry breakpoint.
  • Line: Suspends the program when execution reaches the given source line, and can also be set from the source view. You can set line and entry breakpoints for locations that have not yet been reached and modules that have not been loaded, by creating these breakpoints as deferred. See the documentation for detailed instructions on how to do so.
  • Load: Suspend the program when the given library is loaded.
  • Watch: Suspend the program when the selected portion of memory changes.

All breakpoints can be created from the breakpoints view context menu.

Modules

The modules view provides a representation of the source files that were used to create the program, including the function definitions they provided. View these files in the source editor by opening them from the modules view. Set entry breakpoints by right clicking on the function elements in the view.

Figure 6. The modules view
Picture of functions listed, grouped by source file.

Monitors

The monitors view allows you to keep a collection of variables of interest, for easy access and modification. If, for example, you would like to examine the value of a particular variable or expression every time the application suspends, you could create a monitor for the variable or expression, and this monitor would subsequently be visible in the monitors view. This is especially useful for observing global variables as they change throughout your debug session. You can monitor any valid expression, such as a simple local variable, to a particular index in an array.

In the example below, imagine that you have a breakpoint at line 22 of the application, within a loop. Whenever the breakpoint is hit, you can determine whether i is even or odd.

Figure 7. An expression to monitor
Screenshot shows line 22: if ((i % 2) == 0) {.

To do this, you would add a monitor for the expression "(i % 2) == 0". There are several ways to do this. In the editor, highlight and right-click the expression, right click, and select Monitor Expression from the pop-up menu.

Figure 8. The monitor in the monitors view
Picture of monitor (i % 2) == 0 = true.

After every step, the value of the monitor is updated and if it has changed this will be indicated for easy identification. Monitors can be added in other ways: you can monitor a local variable from the Variables view or from the monitors view by clicking the green plus symbol button in the monitors view and typing the expression to be monitored.

If you want to monitor one or more global variables, choose them from a list of all the global variables in scope by clicking Select Globals list from the context menu in the monitors view. You can also change the value of a monitored variable from the monitors view, and this change is effective immediately.

Memory rendering and mapping

The memory view allows you to examine and modify the contents of memory at a specific address, usually that of a variable. You can also choose which format the memory will be displayed in: Hex, ASCII, EBCDIC, signed integer, and unsigned integer. The expression whose address is used as the basis for the expression is called a memory monitor, and you can use the monitors pane in the memory view to add and remove memory monitors. Note that memory monitors are different from the monitors used in the monitors view and discussed in the preceding section.

The image below shows the Signed Integer memory rendering of a variable named "it". The variable's address in memory is 0x2FF22938, and its current value is 10.

Figure 9. Monitoring variables in the memory view
Picture shows memory at 0x2FF22938 changed to 10.

You can view the contents of memory in different formats simultaneously.

Figure 10. Viewing the variable as a signed integer and in hexadecimal
Picture shows formats side by side in memory view.

A useful feature in the memory view is the Reset to Base Address action in the context menu. If you have moved away from the start address of the rendering, you can use this action to easily jump to that location.

Mapping memory

The memory mapping feature enables you to display a region of memory according to a layout defined in an XML file. This is useful, for example, if you would like to display blocks of memory defined outside your program.

To create a memory map rendering, you need to select the file on the file system that defines the layout you want to use. To create a mapping for a variable: Select the variable or monitor in the Variables or Monitors view, and Choose Monitor Memory > Map... from the context menu, and browse to select the XML file to use as the layout for the mapping. This will (a) create a new memory monitor for the variable in the Memory view, and (b) create a new memory map rendering using that file.

In a simple case, the memory map tool enables you to define a layout for a complex variable, such as a union or a structure.

Given the following structure:

typedef struct {
  unsigned short ushort_val;
  short short_val;
  unsigned long ulong_val;
  long long_val;
  char string_val[12];
  char char_val;
} _test;

The corresponding layout file would appear as follows:

<?xml version="1.0"?>
  <LAYOUT Header = "My Structure Layout" length = "25" >
  <FIELD Header="ushort_val" Type="16_BIT_UINT" length = "2"/>
  <FIELD Header="short_val" Type="16_BIT_INT" length="2"/>
  <FIELD Header="ulong_val" Type="32_BIT_UINT" length="4" 
         description="this is a sample description for this field"/>
  <FIELD Header="long_val" Type="32_BIT_INT" length="4"/>
  <FIELD Header="string_val" Type="ASCII" length="12"/>
  <FIELD Header="char_val" Type="ASCII" length="1"/>
</LAYOUT>

This is more useful than any of the other rendering types, since it allows for a logical organization of the memory being examined. Each "field" defines a named region of memory 1 or more bytes long, with an optional text description that is meaningful within the context of your program. The type attribute is used to indicate how the memory should be rendered, in HEX, Decimal, ASCII, etc. An added benefit is that the memory mapping can be displayed alongside a raw, column-based rendering.

Using the XML file above, the screen capture below shows the memory map rendering an instance of this structure, shown below.

Figure 11. Memory map displayed in the memory view
Screenshot shows a memory map layout.

Taking a look at the memory map rendering in detail, you can see that there are several columns that provide detail about each field.

The Value column shows, for each field, the contents of memory at that particular offset. The Offset column displays the offset of each field, from the start address of the mapping. The Description column displays user supplied information about each field. This is useful for adding extra information to portions within the block that are of interest, especially if the maps are shared between developers. The description can be edited in the rendering, and the new description can be optionally saved to the corresponding XML file automatically. To enable this feature, open the Memory map preferences page from the memory view menu, and make sure the box beside When editing groups and descriptions always save the changes to the file is checked.

For easy identification, the memory rendering uses color indicators to highlight one or more bytes that have changed. You can also have two different renderings displayed simultaneously, side by side, as shown below.

Figure 12. Changes in memory shown in multiple simultaneous renderings
Picture shows the changed blocks of memory.

The region on the left, labeled "A" is the memory map rendering for the monitor, while the rendering on the right, labeled "B" contains the signed integer representation of the same region in memory. This allows you to view a logical arrangement of the blocks of memory using the memory map, but also the contents of memory before and after the structure.

The memory mapping tool has other features and uses, and you can learn more about these by consulting the official product documentation.

Displaying registers

You can inspect and modify the contents of various registers using the registers view. The registers in the application are divided into three groups: General, Floating Point, and Special Purpose. Special purpose registers are system defined, and the registers listed in the screenshot are specific to AIX. The IAR, or Instruction Address Register, contains the address of the current instruction, that is, the instruction about to be executed. The registers view is similar to the variables view in that it indicates register changes by highlighting the changed items. Registers can also be added to the monitors view and observed for changes. In addition, their memory can be observed using the memory view.

Figure 13. The registers that are accessible from your program are visible in the registers view
Picture depicts contents in the registers view.

Debug console

The debug console allows command line debug of an existing debug session. You can issue commands directly to the debug engine and view the resulting output in the debug console, which helps enable a fairly interactive debug. The commands supported are debug engine dependent, and on AIX, they can be displayed by typing "help" in the debug console.

On AIX, you can enable "echo" and the commands you enter can be exported and imported at a later time. This allows you to "record" your actions in the debugger, and generate a script that can be reused to put the program that you are debugging in a desired state. In the screen capture below, the column on the right is the command list pane, which is a list of all the commands that have been entered, and the left column is the command history pane, which displays the output from entered commands. The toolbar buttons can be used to clear the command history and list panes, import, export, and run lists of commands.

Figure 14. The debug console
Picture shows the debug console with command line.

Debugging multiple applications

Regardless of how you start a debug session (through the command line or a dialog), the debugger UI can host any number of debug sessions, simultaneously. As was mentioned before, each new debug session is represented by a debug target in the debug view. The contents of almost all the other views, including the source editor, Monitors, Registers, Modules, Variables and Memory views, will be updated based on the active debug session. Changing the selected debug target in the debug view will switch between sessions and update the remaining views. The following screen capture shows the debugger with two active debug sessions.

Figure 15. Multiple debug sessions running in a single UI
Picture shows two debug sessions in the debug view.

Summary

The IBM Debugger tool supports debugging applications written in several different programming languages on several platforms, all from within a single debugger user interface. In addition to the basic debug functionality, such as stepping and setting line breakpoints, the tool offers numerous advanced features, including memory mapping, remote restart, and multiple breakpoint types. To leverage these properties, and many others, see the IBM Debugger documentation for detailed explanation of these, and other features.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Rational software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Rational
ArticleID=486266
ArticleTitle=Improving your productivity using the IBM Debugger for AIX
publish-date=05042010