9.1. Introduction to Debugging with GNATbench¶
Debugging Ada applications is supported via the GDB debugger, tailored for Ada by AdaCore, and the CDT interactive debugging perspective.
The screenshots in this section use the project “sdc.gpr” from the GNAT Studio tutorial. That project and its associated code can be found under the GNAT installation in the directory share/examples/gnatstudio/tutorial.
9.1.1. Preparing to Debug¶
Before you begin to debug your project, make sure your project has built properly and you can see the executable in the GNAT Project Explorer. In particular, make sure to apply the “-g” switch when building.
9.1.2. Other Resources¶
We provide an overview of some of the material, delving into the details only when they pertain to debugging Ada applications. You can find details about the CDT debugger interface in the C/C++ Developer User Guide. All the User Guides are available via the Eclipse Help menu, under “Help Contents”.
The following figure shows the result of selecting Run and allowing the debugger to halt at a breakpoint.
To the right, a tabbed dialog presents various views for inspecting local variables, breakpoints, and registers. These tabs provide a variety of different tools for exploring information about the state of your halted application.
Eclipse provides additional optional views through the Window -> Show View menu item. In the screenshot, the “Expressions” view has been added by clicking Window -> Show View -> Expressions. In this example, a watchpoint expression has been added by double-clicking inside the expressions window and adding an entry for the local variable “Last.”
A tabbed dialog in the middle of the perspective provides a number of editor windows, in which the point at which the debugger is currently halted is shown by an arrow in the left gray border.
At the bottom of the screen, a tabbed dialog provides access to the console for the process being debugged. This window allows text input and allows the user to provide input to a process that expects to read text input for the console. Additional entries provide interfaces for working with tasks and inspecting the state of memory.
9.2. Creating A Debug Configuration¶
Eclipse allows you to maintain configuration options and preferences from debug session to session via the “debug configuration” mechanism. The first step in debugging under Eclipse is to create such a configuration. You can then reuse the configuration indefinitely unless/until some aspect changes.
Although not mandatory, compiling your project beforehand will facilitate creating the corresponding debug launch configuration. In particular, it will make it possible to browse and search for some of the required entries instead of manually entering the values. Also, it is most convenient to first select a project before creating a new launch configuration for that project because much of the required information will be filled in automatically.
See Quick Configuration Creation below for a convenient way to create a new debugger launch configuration for an Ada application.
First, open the Debug launch dialog. Select “Debug Configurations…” from the Run menu, or click on the down-arrow next to the Debug button on the toolbar and make the same selection there. The following dialog box will appear. (In this figure there are no existing user-defined launch configurations; yours may have some already.)
In the left-hand pane of the dialog, double-click on “Ada Application.”
If you had a project actively selected in the GNAT Project Explorer when you started, Eclipse (really, the CDT launch facility) will have automatically filled in the name of the new configuration and the information for the “Main” tab on the right of the dialog. Otherwise, you should see an empty new configuration, like the following:
Using the tabbed dialog on the right, you will specify various different configuration options. For example, you can specify the name of the configuration, the executable you wish to debug, the arguments you wish to pass to it when it’s launched, if any, the environment under which it will run, the debugger to use, and so forth.
Enter the name for the new configuration at the top of the page. Then, in the “Main” tab, first enter the project name and then the location of the executable. You can manually enter the project name and executable location or you can browse for them. For example, clicking on the “Search Project…” button will invoke this dialog box:
Once filled in, the “Main” tab content will appear something like this:
Next, you will likely want to change some settings in the “Debugger” tab. Initially the page will appear as follows:
If you want to debug library unit elaboration, leave the entry point name in the text pane labeled “Stop on startup at:” set to “main”. You can then step through the elaboration of all the library units.
Typically, however, you will want to stop at (the elaboration of) the main subprogram, in which case you will replace “main” with the name of the Ada main subprogram unit. In this example the name would be “sdc”.
Once filled in, the “Debugger” page will appear something like this:
Generally you can leave the other tabs with their contents unchanged.
Press the Apply button to save the changes, and then press either Close or Debug, depending upon whether or not you want to use the configuration immediately.
9.2.1. Quick Configuration Creation¶
You can quickly create a new Ada debug configuration in several convenient ways. Note, however, that not all the settings described above are automatically specified. For example, the entry point will remain set to “main” rather than the name of your actual Ada main procedure.
In all cases, you first select the node for an Ada project or an Ada executable in the GNAT Project Explorer. This step is essential. Next, either:
right-click to get the contextual menu and select the “Debug As” menu entry;
use the “Debug” toolbar icon;
use the “Run -> Debug” menu entry.
For example, in the following figure we have right-clicked on the executable:
If instead you use the “Run -> Debug” menu entry, you must then specify how to debug the application. Choose “Local Ada Application” and press OK, as shown below:
Finally, select the “gdb/mi” debug configuration to finish the launch configuration process. Press OK to launch the debugger.
9.3. Launching A Debug Session¶
Creating A Debug Configuration for the details of the creating new debugger launch configurations.
Once you have created a debug configuration, you can launch it from the Debug dialog (assuming it is still open) by clicking the “Debug” button. Alternatively, if you have an existing configuration you can re-apply it in various ways to launch the debugger. If you have only one such configuration you can simply press the Debug toolbar icon. Usually you will have more than one launch configuration so perhaps the easiest way is by clicking on the down-arrow next to the Debug button on the toolbar and selecting the desired configuration from the list.
If you are not in the Debug perspective when launching a debug configuration, Eclipse will ask whether you to want to switch to that perspective. (Eclipse will remember your answer if you tell it to do so, and in that case will not ask again.) Once in the Debug perspective you will see your application in the debugger.
The upper left pane contains the debugger controls and stacks view. At the upper right are the variables and breakpoints views, among others. The source files appear in the middle on the left, with the Outline view to the right. The debugger Console view is on the bottom.
The source view will initially show the source file containing the next line to execute, which in this case will be the elaboration of the main subprogram (if that is the entry point specified in the launch configuration). The green line and blue arrow highlight that line, as shown in the figure below.
9.3.1. Potential Trouble¶
When the debugger first comes up you might see an error indicating that no source is available for the first code entered (on Windows XP, in particular, due to Windows code that is initially required). In that situation the source file and debugger pane will look something like the following:
In that case, pressing the Resume button (or F8) a few times will eventually get to the elaboration of the main subprogram.
9.3.2. Build Prior to Launch¶
When a debug configuration is launched, Eclipse will first check to see if a new build is required. You can control that behavior with a preference, located under the “General Options” section of the “Run/Debug/Launching” preference page. A number of useful options are controlled on that page.
9.4. Setting Breakpoints¶
You can create breakpoints in the source editor by double clicking in the gray column on the left-hand side of the editor window, to the left of the line numbers. A blue dot will appear there.
In the following figure two breakpoints have been set: one on line 15 and one on line 30:
Note that you may need to switch back and forth between the Debug and Ada perspectives because the Ada perspective displays the source files for editing. Setting breakpoints in an open file is the easiest method of setting breakpoints.
You can enable line numbers in the source view by a preference. Use the Window menu entry and select “Preferences…”, then expand the “General/Editors/Text Editors” nodes and select “Show line numbers.”
Also note that you can create breakpoints in this fashion before beginning your debug session. You may also switch back and forth between the Debug and Ada perspectives at any time by clicking on the “Ada” and “Debug” perspective icons in the top right-hand corner of the display.
When a breakpoint has been hit the source view will highlight it:
9.5. Breaking On Exceptions¶
If you are running Eclipse Helios (3.6) or later, you can stop execution when an exception becomes active. Specifically, you can stop execution whenever any of the following take place:
Any exception raised
Any unhandled exception raised
A specific exception raised
An assertion in pragma Assert evaluating to False
To set a breakpoint on an Ada exception, first use the View Menu control to activate that menu. The View menu control is the downward-pointing triangle next to the view’s Minimize control at the far upper right of the view:
Clicking on the View Menu control invokes the corresponding menu. In the following figure we have highlighted the entry for Ada events:
Selecting the entry for Ada exception events brings up the dialog box in which you can select the specific event intended. In the following figure we have used the pull-down list to show the choices:
If you specify that the breakpoint should occur whenever a specific exception is raised, you will then be asked to specify the name of that exception. As soon as an Ada debug session is started, the exception name combo box is automatically filled with the names of the exceptions your programs can raise.
Once you have specified the event, the breakpoint will appear in the list of known breakpoints, along with any others:
9.6. Controlling Execution¶
In the upper left hand corner you will find the “Debug” view.
From this view you may control overall execution, navigate between debugging sessions, navigate between threads in a given session, and traverse up and down the call stack, among other actions.
For example, the figure below shows two debugger sessions active. You can control either session by clicking on the corresponding entries in the Debug view. The other views will automatically reflect the selected application and thread.
The tool bar in the Debug view includes buttons for running, halting, and stepping your application. In the figure below the mouse cursor is over the “Step Over” button so the tooltip is displayed. Note the key binding.
The “Step Over” button will go to the next Ada statement in the same call frame. In other words, if the next statement is a subprogram call, the debugger will not step into the code for that subprogram.
To the left of the “Step Over” button is the “Step Into” button. The “Step Into” button will step into the code for a subprogram call (when a call is the next statement).
Moving further left, the red box is the “Terminate” button that terminates execution of the debugging session:
Next to the “Terminate” button is the “Suspend” button, which in this case is disabled because the debugger is already suspended (on a breakpoint).
To the left of the “Suspend” button is the “Resume” button, a green-tipped arrow.
These are the primary buttons for controlling the program’s execution. See the C/C++ Developer Guide for further details of the Debug view controls. Note the call stack in the Debug view, and the corresponding source code displayed in the source window:
If we select a different call stack frame in the Debug view we see the displayed source code change to the source corresponding to that call:
Traversing the call stack can go in both directions, for any thread, from the current frame all the way back to the first frame for the thread.
9.7. Examining Data¶
Note that application execution must be suspended to examine data.
Perhaps the simplest way to examine the value of a variable (strictly, an expression) while debugging is to hover the mouse cursor over the variable in the source editor. A tooltip will then appear showing the value. For example, as indicated in the following figure, the mouse cursor has been hovered over the variable “Commanded_Deflection”:
You can see that the earlier value of 0.0 has been replaced by subsequent execution.
As usual with Eclipse, moving the mouse over the tooltip will make it persist so that you can resize and reposition it. In addition, when made persistent, you can traverse to the corresponding declaration and body (if extant) using icons presented at the bottom left of the tooltip pane. For example, the following figure shows the persistent tooltip resulting from hovering the mouse over the call to procedure Send:
Note the two Ada “file” icons at the bottom left. Clicking on the leftmost icon will open the declaration for procedure Send, whereas clicking on the icon to the right will open the corresponding body.
More sophisticated facilities are also available. In the Debug perspective, a tabbed dialog presents various views for inspecting local variables, breakpoints, and registers. These tabs provide a variety of different tools for exploring information about the state of your application.
The tabbed dialogs can be repositioned within the perspective. In the figure below the Variables view has been moved down and then to the right of the source view. The perspective provides additional, optional debugger views available via the Window -> Show View menu item. The “Expressions” view has been added in the same figure. The Variables and Expressions views are the two primary views for examining Ada objects.
Note that the CDT debugger interface is responsible for displaying the values of variables, but it does not understand how to display values of every possible Ada type. As a result, some variables may not be displayed. The common types are fully supported.
9.7.1. Examining Variables Declared in Subprograms and Tasks¶
Variables declared within the declarative regions of subprograms and tasks are most easily examined using the Variables view. These variables will appear automatically when the execution frame is currently selected. In the figure above, the call frame for the call to Stack.Push is currently selected so the formal parameter “V” appears in the Variables view.
Note that in the current version of the CDT debugger perspective, names are mapped to lower case.
The variable V is an object of an access type so the value displayed in the Variables view is an address literal. We can dereference the address by clicking on the expansion control next to the variable name in the Variables view. In this case we see that V designates a record with a component named “E” having a value of two.
9.7.2. Examining Variables Declared in Packages¶
Variables declared within the declarative regions of packages are examined using the Expressions view.
In the figure above, a “watchpoint” expression has been added for the variable named “Last” that is declared within package Stack. We have also added an expression for the variable “Tab”, a table of 200 pointers, and expanded it so the individual components are displayed.
Expressions can be added and removed via the contextual menu for the view:
When the name of the variable to be used in the expression is not unique, you can use the full name, including the package name, to qualify it. However, in place of the dot between the package name and the variable name, the CDT debugger requires you to use two consecutive underscores. Thus, for example, in the following figure we have specified the full name for the variable Stack.Last as “stack__last”:
Note that the Variables and Expressions view, like all views, can be maximized to make it easier for large amounts of data to be visible in the view:
9.8. Debugging Tasks¶
It is possible to debug a multitasking application with GNATbench and the CDT debugger GUI. The version of GDB that comes with the GNAT compiler is Ada-aware and is therefore strongly suggested. Note also that a recent version of GDB is required for the task information enhancements shown in the Debug view.
To debug individual tasks set breakpoints in those tasks using the source view.
After hitting a breakpoint you should see something similar to the figure below.
Instead of seeing one task and one stack in the Debug view, we now have a number of them displayed in a tree. You can navigate from thread to thread and up and down the threads’ individual stacks. This allows you to view local variables or evaluate expressions in the context of the thread and stack frame of your choosing. In the figure above we have clicked in the stack frame of thread 3, so the corresponding source for that task at that line is displayed.
Note that the call stack frames for the underlying run-time library and operating systems calls are displayed but are not traversable (unless you make the corresponding sources available to the debugger).
In addition to the lower-level thread-oriented information, the thread data also include higher-level Ada-oriented task information. For example, the following figure shows three threads in the application. Task 1 is the main procedure called by the environment task and is thus named “main_task” automatically. It is at priority 15 and is runnable. Tasks 2 and 3 are both suspended in either an accept statement (which is actually the case) or select-or-terminate alternative. Both have priority 15 as well. Both tasks are indicated as “computer” because that is the actual task object name in the source code. (Each is declared in a separate package so the same name could be used, such as “Flight.Computer” and “Actuator.Computer” but only the object name is shown). Note that the thread number and the task number need not be identical, but are the same in this example.
9.9. Ending A Debug Session¶
To finish your debug session, either allow your application to run to completion or halt it using the square red button in the debug tool bar.
If you had more than one session active, the other sessions will remain active and the terminated session will indicate the terminated status:
You can go back to the Ada perspective (or any other) by clicking on the Ada perspective icon at the upper right of the Eclipse window. You can close the Debug perspective or leave it open, as you wish. Leaving it open does not make it visible. To close it, right-click on the “Debug” perspective icon and then click on “Close” to close the perspective.
9.10. Using the GNAT Studio Debugger¶
As an alternative to using the GNATbench debugger, you have the option of launching the GNAT Studio debugger from Eclipse. This option may be attractive for users who are more familiar with or prefer the GNAT Studio debugger.
To debug your application under GNAT Studio, you need to create a new debug configuration, just as if you were going to use the built-in debugger. Select “Debug Configurations…” from the Run menu, or click on the down-arrow next to the Debug button on the toolbar and make the same selection there.
Next, in the left hand pane of the resulting Debug dialog, double click on “GNAT Studio debugger.” You will then need to configure the name of the project to use and executable to run. You should see something like the following after pressing the Apply button:
You can now launch a GNAT Studio debug session by pressing “Apply” (if you have not already done so) and then “Debug”.
If your GNAT Studio debugger session does not open the file in the source editor and instead indicates a strange file name in the GNAT Studio debugger console for the “file” command, it may be that you have one or more blanks in the path to the file. This is an easy situation to get into on Windows because of the blanks in the actual pathnames for the Desktop, “My Documents”, and so on. For example, the typical pathname used for items located on the Windows desktop is “C:\Documents and Settings\*user-name*\Desktop\*item-name*”.
For that same reason you may instead see a simple error dialog box pop up, titled “Launching”, when you attempt to launch a GNAT Studio configuration. In this case GNAT Studio is not even started.
Assuming that removing these blanks is difficult, the alternative is to invoke GNAT Studio externally and select (browse to) the GNAT project file, then enter the debug mode from within GNAT Studio. (See the GNAT Studio User’s Guide for details regarding the GNAT Studio debugger.)