Debugging Programs

You can select a topic from this diagram:

Glossary What's New Utilities Used by the IDE Getting System Information Using Code Coverage Common Wizards Reference Preparing Your Target Developing Photon Applications Developing C/C++ Programs Where Files Are Stored Building OS and Flash Images Migrating to the 6.3 Release Tutorials IDE Concepts About This Guide Analyzing Your System With Kernel Tracing Profiling an Application Finding Memory Errors Debugging Programs Managing Source Code Launch Configurations Reference

Workflow diagram with debug chapter highlighted


This chapter shows you how to work with the debugger.

In this chapter:

Introduction

One of the most frequently used tools in the traditional design-develop-debug cycle is the source-level debugger. In the IDE, this powerful tool provides an intuitive debugging environment that's completely integrated with the other workbench tools, giving you the flexibility you need to best address the problems at hand.

Have you ever had to debug several programs simultaneously? Did you have to use separate tools when the programs were written in different languages or for different processors? The IDE's source debugger provides a unified environment for multiprocess and multithreaded debugging of programs written in C, C++, Embedded C++, or Java. You can debug such programs concurrently on one or multiple remote target systems, or locally if you're doing Neutrino self-hosted development.

In order to use the full power of the Debug perspective, you must use executables compiled for debugging. These executables contain additional debug information that lets the debugger make direct associations between the source code and the binaries generated from that original source. In the IDE, you'll see different icons: a running man for executables that weren't compiled for debugging, or a bug for those that were.

The IDE debugger uses GDB as the underlying debug engine. It translates each GUI action into a sequence of GDB commands, and then processes the output from GDB to display the current state of the program being debugged.

The IDE updates the views in the Debug perspective only when the program is suspended.


Note: Editing your source after compiling causes the line numbering to be out of step because the debug information is tied directly to the source. Similarly, debugging an optimized binary can also cause unexpected jumps in the execution trace.

Debugging your program

Building an executable for debugging

Although you can debug a regular executable, you'll get much more information and control by building debug variants of the executables. To build an executable with debugging information, you must pass the -g option to the compiler. If you're using a QNX Make project, the filename for the debug variant has _g appended to it.

To specify the -g option from the project options:

  1. In the C/C++ Projects view (or the Navigator view), right-click the project and select Properties.
  2. In the left pane, select QNX C/C++ Project.
  3. In the right pane, select the Build Variants tab.
  4. Under your selected build variants, make sure Debug is enabled:

    Properties dialog; Build Variants tab

  5. Click Apply.
  6. Click OK.
  7. Rebuild your project (unless you're using the IDE's autobuild feature).

For more information about setting project options, see the Common Wizards Reference chapter.

Launching your program


Note: For a full description of starting your programs and the launch configuration options, see the Launch Configurations Reference chapter.

After building a debug-enabled executable, your next step is to create a launch configuration for that executable so you can run and debug it:

  1. From the main menu, select Run-->Debug.... The launch configurations dialog appears.

    Debug launch configurations

  2. Create a launch configuration as you normally would, but don't click OK.
  3. Select the Debugger tab.

    Debugger tab


    Note: For Neutrino, if you want to perform a local debug of your project, you can't use the default QNX GDB debugger option. Instead, select GDB Debugger from the Debugger list.

  4. Make sure Stop at main() on startup is set.
  5. Click Apply.
  6. Click Debug.

If launching a debugging session doesn't work when connected to the target with qconn, ensure that pdebug is on the target, and it is located in one of the directories in the PATH that qconn uses (typically /usr/bin).


Note: By default:
  • The IDE automatically changes to the Debug perspective when you debug a program. If the default is no longer set, or if you wish to change to a different perspective when you debug, see the "Setting execution options" section in the Launch Configurations Reference chapter.
  • The IDE removes terminated debugging sessions from the Debug view when you launch a new session. This frees resources on your development host and your debugging target. You can retain the completed debug sessions by unchecking the Remove terminated launches when a new launch is created box in the Run/Debug-->Launching pane of the Preferences dialog.

Controlling your debug session


Note: The contents of all views in the Debug perspective are driven by the selections you make in the Debug view.

The Debug view lets you manage the debugging or running of a program in the workbench. This view displays the stack frame for the suspended threads for each target you're debugging. Each thread in your program appears as a node in the tree. The view displays the process for each program you're running:

Debug view

The Debug view shows the target information in a tree hierarchy as follows (shown here with a sample of the possible icons):

Session item Description Possible icons
Launch instance Launch configuration name and type (e.g. Stack Builder [C/C++ QNX QConn (IP)]) Icon: Debug  launch instance
Debugger instance Debugger name and state (e.g. QNX GDB Debugger (Breakpoint hit)) Icon: Debugger
Thread instance Thread number and state (e.g. Thread[1] (Suspended)) Icon: Thread; suspended Icon: Thread; running Icon: Thread; stopped
Stack frame instance Stack frame number, function, filename, and line number Icon: Stackframe Icon: Stack frame; running

Note: The number beside the thread label is a reference counter for the IDE, not a thread ID (TID) number.

The IDE displays stack frames as child elements, and gives the reason for the suspension (e.g. end of stepping range, breakpoint hit, signal received, and so on). When a program exits, the IDE displays the exit code.

The label includes the thread's state. In the example above, the thread was suspended because the program hit a breakpoint. You can't suspend only one thread in a process; suspension affects all threads.

The Debug view also drives the C/C++ editor; as you step through your program, the C/C++ editor highlights the location of the execution pointer.

Using the controls

After you start the debugger, it stops (by default) in main() and waits for your input. (For information about changing this setting, see the "Debugger tab" section in the Launch Configurations Reference chapter.)

The debugging controls appear in the following places (but not all together in any one place):

The controls are superseded by breakpoints. For example, if you ask the program to step over a function (i.e. run until it finishes that function) and the program hits a breakpoint, the program pauses on that breakpoint, even though it hasn't finished the function.

The icons and menu items are context-sensitive. For example, you can use the Terminate action to kill a process, but not a stack frame.

Action Icon Hotkey Description
Resume Icon: Resume F8 Run the process freely from the current point.
Suspend Icon: Suspend   Regain control of the running process.
Terminate Icon: Terminate   Kill the process.
Restart Icon: Restart   Rerun the process from the beginning.
Resume without signal Icon: Resume without signal   Resume the execution of a process without delivering any pending signals.
Step Into Icon: Step Into F5 Step forward one line, going into function calls.
Step Over Icon: Step Over F6 Step forward one line without going into function calls.
Run to return Icon: Step Return F7 Finish this function.
Resume at line Icon: Resume at line   Resume the execution of the process at the specified line. Using this action to change into a different function may cause unpredictable results.
Toggle Instruction Stepping Toggle Instruction Stepping   Toggle what all the operators work on (machine instructions or source code).

You can control your debug session in various ways:

From the Debug view

You'll probably use the Debug view primarily to control your program flow.

To control your debug execution:

  1. In the Debug view, select the thread you wish to control.
  2. Click one of the stepping icons (e.g. Step Into) in the Debug view's toolbar. Repeat as desired.
  3. Finish the debug session by choosing one of the debug launch controls (e.g. Disconnect). For details, see the section "Debug launch controls" in this chapter.

Using hotkeys

Even if you're running your debug session without the Debug view showing, you can use the hotkeys (or the Run menu) to step through your program. You can enable the debug hotkeys in any perspective.

To see a list of the currently active hotkeys, press Ctrl-Shift-L or choose Help-->Key Assist. To customize the debug hotkeys:

  1. Choose Window-->Preferences from the menu. The Preferences dialog is displayed.
  2. Choose General-->Keys in the list on the left.
  3. Select a command from the list and click Edit.
  4. To assign this command to a new hotkey, click in the Name field in the Key Sequence area of the Keys pane, and then press the key(s) for your new hotkey.
  5. Click the Add button to assign the newly created hotkey to the selected command.
  6. Click OK to activate your new hotkeys.

From the C/C++ editor

You can control your debug session using the C/C++ editor by having the program run until it hits the line your cursor is sitting on. If the program never hits that line, the program runs until it finishes or hits another breakpoint.

You can also use the C/C++ editor's context menu to resume execution at a specific line, or to add a watch expression.

To use the C/C++ editor to debug a program:

  1. In the editor, select a file associated with the process being debugged.
  2. Left-click to insert the cursor where you want to interrupt the execution.
  3. Right-click near the cursor and select Run To Line, Resume at line or Add watch expression.

Note: Note that Run To Line works only in the current stack frame. That is, you can use Run to Line within the currently executing function.

Debug launch controls

In addition to controlling the individual stepping of your programs, you can also control the debug session itself (e.g. terminate the session, stop the program, and so on) using the debug launch controls available in the Debug view (or in the view's right-click menu).

As with the other debug controls, these are context-sensitive; some are disabled depending on whether you've selected a thread, a process, and so on, in the Debug view.

Action Icon Description
Terminate Icon: Terminate Kill the selected process.
Terminate & Remove Icon: Terminate and Remove Kill the selected process and remove it from the Debug view.
Terminate All Icon: Terminate All Kill all active processes in the Debug view.
Disconnect Icon: Disconnect Detach the debugger (i.e. gdb) from the selected process (useful for debugging attached processes).
Remove All Terminated Launches Icon: Remove All Terminated Clear all the killed processes from the Debug view.
Restart Icon: Restart Restart the process.

Note: The debugger keeps the project's files open while the program is running. Be sure to terminate the debug session before you try to rebuild the project, or else the build will fail.

Disassembly mode

You can also examine your program as it steps into functions that you don't have source code for, such as printf(). Normally, the debugger steps over these functions, even when you click Step Into. When the instruction pointer enters functions for which it doesn't have the source, the IDE shows the function in the Disassembly view.

To show the Disassembly view:
From the menu, choose Window-->Show View-->Disassembly.

The workbench adds the Disassembly view to the Debug perspective:

Disassembly view

If you click in this view or use the Toggle Instruction Stepping icon (Toggle Instruction Stepping) to give focus to this view, the operators (e.g. Run to Line) operate on machine instructions instead of the source code.

More debugging features

Besides the Debug view, you'll find several other useful views in the Debug perspective:

To: Use this view:
Inspect variables Variables
Use breakpoints and watchpoints Breakpoints
Evaluate expressions Expressions
Inspect registers Registers
Inspect a process's memory Memory
Inspect shared library usage Modules
Monitor signal handling Signals
View your output Console
Interact with GDB Console

Inspecting variables

The Variables view displays information about the variables in the currently selected stack frame:

Variables view

At the bottom of the view, the Detail pane displays the value of the selected variable.


Note: If you happen to have multiple variables of the same name, the one most in scope is evaluated.

When the execution stops, the changed values are highlighted in yellow (by default). Like the other debug-related views, the Variables view doesn't try to keep up with the execution of a running program; it updates the display only when execution stops.

You can decide whether or not to display the variable type (e.g. int) by clicking the Show Type Names toggle button (Icon: Show Type Names). The Show Type Names button is unavailable when columns are displayed.

You can also control whether or not the IDE tracks all your program's variables. See the "Debugger tab" section in the Launch Configurations Reference chapter.


Note: Tracking all the variables can reduce your program's performance.

Inspecting global variables

By default, global variables aren't displayed in the Variables view. To add global variables to the view:

  1. In the Variables view, click the Add Global Variables button (Add Global  Variables).
  2. Select one or more symbols in the Global Variables dialog.

    Global Variables

  3. Click OK to add the selected global variables to the Variables view.

Changing variable values

While debugging a program, you may wish to manually change the value of a variable to test how your program handles the setting or to speed through a loop.

To change a variable value while debugging:

  1. In the Variables view, right-click the variable and select the Change Value... item.

    Changing a variable's value

  2. Enter the new value in the field.

Note: You can also change a variable's value in the Detail pane at the bottom of the Variables view. Click the value, change it, and then press Ctrl-S to save the new value.

Controlling the display of variables

You can prevent the debugger from reading the value of variables from the target. You might use this feature for variables that are either very sensitive or specified as volatile. This can also improve your program's performance.

To enable or disable a variable:
In the Variables view, right-click the variable and select either Enable or Disable. (You can disable all the variables in your launch configuration. See the "Debugger tab" section in the Launch Configurations Reference chapter.)

To change a variable to a different type:

  1. In the Variables view, right-click the variable.
  2. Select one of the following:
    Cast To Type...
    Cast the variable to the type you specify in the field (e.g. int).

    Casting to another type

    Restore Original Type
    Cancel your Cast To Type command.
    Format, followed by a type
    Display the variable in a different format (e.g. hexadecimal).
    Display As Array
    Display the variable as an array with a length and start index that you specify. This option is available only for pointers.

Using breakpoints and watchpoints

The Breakpoints view lists all the breakpoints and watchpoints you've set in your open projects:

Breakpoints view

A breakpoint makes your program stop whenever a certain point in the program is reached. For each breakpoint, you can add conditions to better control whether or not your program stops.

A watchpoint is a special breakpoint that stops the program's execution whenever the value of an expression changes, without specifying where this may happen. Unlike breakpoints, which are line-specific, watchpoints are event-specific and take effect whenever a specified condition is true, regardless of when or where it occurred.

Object Icon
Breakpoint Icon: Breakpoint
Watchpoint (read) Icon: Watchpoint; read
Watchpoint (write) Icon: Watchpoint; write
Watchpoint (read and write) Icon: Watchpoint; read and write

Note: If the breakpoint or watchpoint is for a connected target, the IDE places a check mark on the icon. For example:

Icon with an overlaid check mark


The rest of this section describes how to:

Adding breakpoints

You set breakpoints on an executable line of a program. When you debug the program, the execution is suspended before that line of code is executed.

To add a breakpoint:

  1. In the editor area, open the file that you want to add the breakpoint to.
  2. Notice that the left edge of the C/C++ editor has a blank space called a marker bar.
  3. With your pointer, hover over the marker bar beside the exact line of code where you want to add a breakpoint. Right-click the marker bar and select Toggle Breakpoint.

    A dot appears, indicating the breakpoint:

    Breakpoints view; breakpoint added

    A corresponding dot also appears in the Breakpoints view, along with the name of the file in which you set the breakpoint:.

To add a breakpoint at the entry of a function:
In either the Outline or C/C++ Projects view, right-click a function and select Toggle Breakpoint.

Adding watchpoints

To add a watchpoint:

  1. Right-click in the Breakpoints view and choose the Add Watchpoint (C/C++)... item.

    Breakpoints view; adding watchpoints

  2. Enter an expression in the field. The expression may be anything that can be evaluated inside an if statement. (e.g. y==1)
  3. If you want the program to stop when it reads the watch expression, check Read; to have the program stop when it writes the expression, check Write.
  4. Click OK. The watchpoint appears in the Breakpoints view list.

Setting properties of breakpoints and watchpoints

After you've set your breakpoint or watchpoint, the IDE unconditionally halts the program when:

To set the properties for a breakpoint or watchpoint:

  1. In the Breakpoints view, right-click the breakpoint or watchpoint and select the Properties... item. (For breakpoints only, in the C/C++ editor, right-click the breakpoint and select Breakpoint Properties....)
  2. Use the Common panel to modify the watchpoint's behavior.

    Common Breakpoint properties

    In the Condition field, enter the Boolean expression to evaluate. The expression may be anything that can be evaluated inside an if statement (e.g. x > y ). The default is TRUE.

    In the Ignore Count field, enter the number of times the breakpoint or watchpoint may be hit before it begins to take effect (not the number of times the condition is true). The default is 0.

  3. To restrict the breakpoint to specific threads, make sure they're selected in the Filtering panel:

    Breakpoints view; setting breakpoint properties

  4. Click OK. When in debug mode, your program stops when it meets the conditions you've set for the breakpoint or watchpoint.

Disabling/enabling breakpoints and watchpoints

You may wish to temporarily deactivate a breakpoint or watchpoint without losing the information it contains.

To disable or enable a breakpoint or watchpoint:
In the Breakpoints view, right-click the breakpoint or watchpoint and select Disable or Enable. Clicking the check box in the Breakpoints view (so the breakpoint is no longer selected) also disables the breakpoint.

For breakpoints only, right-click the breakpoint in the editor area and select Disable Breakpoint or Enable Breakpoint.

To disable or enable multiple breakpoints or watchpoints:

  1. In the Breakpoints view, use any of the following methods to select the breakpoints:
  2. Right-click the highlighted breakpoints/watchpoints and select Disable or Enable.

Removing breakpoints and watchpoints

To remove one or more breakpoints/watchpoints:
Select the breakpoint or watchpoint, right-click, and then select Remove or Remove All.

Evaluating your expressions

The Expressions view lets you evaluate and examine the value of expressions. To display this view, choose Windows-->Show View-->Expressions.

Expressions view


Note: The Expressions view is similar to the Variables view; for more information, see the "Inspecting variables" section in this chapter.

To evaluate an expression:

  1. Right-click the Expressions view, and then choose Add Watch Expression.

    Expressions view; add expressions

  2. Enter the expression you want to evaluate (e.g. (x-5)*3 ).
  3. Click OK. The expression and its value appear in the Expressions view. When the debugger suspends the program's execution, it reevaluates all expressions and highlights the changed values.

Inspecting your registers

The Registers view displays information about the registers in the currently selected stack frame. When the execution stops, the changed values are highlighted. To display this view, choose Windows-->Show View-->Registers.


Note: The Registers view is similar to the Variables view; for more information, see the "Inspecting variables" section in this chapter.

Registers view

You can also customize the colors in the Registers view and change the default value of the Show Type Names option.

Inspecting a process's memory

The Memory view lets you inspect and change your process's memory. To display this view, choose Windows-->Show View-->Memory. The view is initially empty, but after you've added an item to monitor and specified the output format, this view will look something like this:

Memory view


Note: QNX Neutrino uses a virtual-addressing model wherein each process has its own range of valid virtual addresses. This means that the address you enter into the Memory view must be a virtual address that's valid for the process you're debugging (e.g. the address of any variable). For more on QNX Neutrino's memory management, see the Process Manager chapter in the System Architecture guide.

Inspecting memory

The Memory view supports the same addressing as the C language. You can address memory using expressions such as 0x0847d3c, (&y)+1024, and *ptr.

To inspect the memory of a process:

  1. In the Debug view, select a process. Selecting a thread automatically selects its associated process.
  2. In the Memory view's Monitors pane, click the Add Memory Monitor button (Icon: Add Memory Monitor).
  3. In the Enter address or expression to monitor field, type the address or expression, and then select OK.

Configuring output format

You can display memory in hexadecimal or ASCII, or as signed or unsigned integers:

  1. In the Memory view's Renderings pane, click the Add Rendering(s) button (Icon: Add Memory Rendering). The Add Memory Rendering dialog appears:

    Memory Rendering view: Add Memory Rendering

  2. From the dropdown menu, select the memory or expression you wish to add a new memory rendering for, or click Add New... to create a new memory or expression monitor.
  3. Click, Shift-click, or Ctrl-click to choose one or more formats from the Memory Rendering(s) list.
  4. Click OK. Each format you've chosen appears in a separate tab in the Memory view's Renderings pane.

Changing memory

To change a process's memory:

  1. Follow the procedures for inspecting a process's memory and configuring the output format.
  2. In the Memory view's Renderings pane, click in a cell, type the new value for the memory, and then press one of the following:

    The changed memory appears in red.


Caution: Changing a process's memory can make your program crash.

Inspecting shared-library usage

The Modules view shows you information about the shared libraries for the session you select in the Debug view. The view shows the name, base address, and size of each library. To display this view, choose Windows-->Show View-->Modules.

Shared Libraries view

To load a library's symbols:
Right-click a library and select Load Symbols (or Load Symbols for All for all your libraries).

Monitoring signal handling

The Signals view provides a summary of how your debugger handles signals that are intercepted before they're received by your program. To display this view, choose Windows-->Show View-->Signals.

Signals view

The view contains the following fields:

Name
The name of the signal
Pass
The debugger can filter out signals. If the signal is set to "no", the debugger prevents it from reaching your program.
Suspend
Upon receipt of a signal, the debugger can suspend your program as if it reached a breakpoint. Thus, you can step through your code and watch how your program handles the signal.
Description
A brief description of the signal.

To change how the debugger handles a signal:

  1. In the Signals view, select a signal (e.g. SIGINT) in the Name column.
  2. Right-click the signal's name, and then choose Signal Properties... from the menu.

    Signal properties

  3. In the signal's Properties dialog, check Pass this signal to the program to pass the selected signal to the program. Uncheck it to block this signal.

    Check Suspend the program when this signal happens to suspend the program when it receives this signal. Uncheck it to let the program handle the signal as it normally would.

To send a signal to a suspended program:

  1. If the program isn't suspended, click the Suspend button (Icon: Suspend) in the Debug view.
  2. In the Signals view, right-click your desired signal and select Resume With Signal. Your program resumes and the debugger immediately sends the signal to it.

Note: You can see a thread-by-thread summary of how your program handles signals using the Signal Information view. To learn more, see the "Mapping process signals" section in the Getting System Information chapter.

Viewing your output

The Console view shows you the output of the execution of your program and lets you supply input to your program:

Console view

The console shows three different kinds of text, each in a different default color:


Note: If you're connecting to your target via qconn, your process's output all appears in the same color because qconn combines standard output and standard error into one stream.

You can choose different colors for these kinds of text on the preferences pages.

To access the Console view's customization dialog:

  1. From the menu, select Window-->Preferences.
  2. In the left pane, select Run/Debug-->Console.

You can have more than one Console view, which is useful if you're working on more than one application at once:

Remember that you can copy data from the console and paste it elsewhere.

Interacting with GDB

The IDE lets you use a subset of the commands that the gdb utility offers:

Console view; GDB

To learn more about the gdb utility, see its entry in the Utilities Reference and the Using GDB appendix of the Neutrino Programmer's Guide.

Enabling the QNX GDB Console view

The QNX GDB Console view is part of the regular Console perspective. It appears as soon as the data is sent to it.

To switch to the QNX GDB Console view:

  1. In the Debug view, select a debug session.
  2. Click the arrow beside the Display selected console button (Display selected console).
  3. Choose the console whose name includes gdb. For example:

Choosing the GDB console

The Console view changes to the QNX GDB Console view.

Using the QNX GDB Console view

The QNX GDB Console view lets you bypass the IDE and talk directly to GDB; the IDE is unaware of anything done in the QNX GDB Console view. Items such as breakpoints that you set from the QNX GDB Console view don't appear in the C/C++ editor.


Note: You can't use the Tab key for line completion because the commands are sent to GDB only when you press Enter.

To use the QNX GDB Console view:
In the QNX GDB Console view, enter a command (e.g. nexti to step one instruction):

Console view; GDB; using


Note: To enter commands, you must be on the last line of the Console view.