Understand the C/C++ debugger interface
Add custom tools to the CDT debugger framework
This content is part # of # in the series: Interfacing with the CDT debugger, Part 1
This content is part of the series:Interfacing with the CDT debugger, Part 1
Stay tuned for additional content in this series.
A command-line interface is a serviceable tool for debugging, but nothing exudes professionalism and polish like a well-crafted graphical environment. It takes considerable time and pain to build a full-featured debugging environment from scratch, but there's an alternative: the Eclipse C/C++ Development Tooling (CDT). The CDT's extensibility allows you to connect its graphical debugging capabilities to your custom debugger. This doesn't require a lot of code, but you need to understand the CDT's extension points and the CDI.
The CDI is a Java™-based Application Programming Interface (API) whose classes and interfaces make it possible to access the CDT's debugging framework. An Eclipse plug-in that uses the CDI can add new debuggers to the CDT's operation and display the debugging results in the Eclipse/CDT debug perspective. This article covers the CDI in detail. Part 2 of this "Interfacing with the CDT debugger" series shows how the CDI is specialized to interface the GNU Debugger (gdb) through its Machine Interface (MI)..
Example CDI plug-in
The best way to learn how CDT debuggers work is to see and experiment with actual code. This article explains how to build a feature-poor plug-in that extends the CDT to provide a basic debugging capability. There is no actual debugger executable, but you can use this code as a basis for adding your own custom debugger to the CDT.
This example plug-in consists of three extensions to the CDT and the Eclipse Debug Framework:
- Creates a separate launcher to debug C/C++ applications
- Receives debug configuration parameters from the user
- Creates the debug session for the launched C/C++ application
This article explains each of these and provides example code showing how they work. Then it delves into the operation of the CDI, covering the CDI model and investigating how the CDI makes breakpoints and watchpoints possible.
Create a custom launch-configuration type
In Eclipse, the process of starting an application is called launching the application in Run mode. A debug session is referred to as a launch in Debug mode. Once you choose a launch mode, the next step is to choose a launch-configuration type. This tells Eclipse precisely how the application should be executed or debugged. For example, a launch-configuration type for Debug mode defines the debugger executable, default debug options, and how the debug output should be presented in Eclipse. Figure 1 shows configuration types presented in the CDT Debug Configurations window.
Figure 1. CDT Launch Configuration window
To interface a new debugger with Eclipse, the first step is to create a new
launch-configuration type. This requires a plug-in that extends the
org.eclipse.debug.core.launchConfigurationTypes extension point. In
Figure 1, you can see Example Configuration Type at left in the window. Listing 1
presents the extension that defines this new type.
Listing 1. Example
<extension point="org.eclipse.debug.core.launchConfigurationTypes"> <launchConfigurationType name="Example Configuration Type" delegate="org.dworks.debugexample.ExampleConfigurationDelegate" modes="debug" public="true" sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator" sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer" id="org.dworks.debug.example.ExampleLaunch"> </launchConfigurationType> </extension>
The Example Configuration Type is placed in the Debug window because the
modes field is set to
debug, and the
public field is set to
true. The most
important field is
delegate, which identifies the class that
manages the launch process. This class must implement the
ILaunchConfigurationDelegate interface, and coding a delegate from
scratch can be complex. Thankfully, the CDT makes your life easier by
providing four pre-built delegate classes:
- Runs a local C/C++ application
- Debugs local applications
- Analyzes core files after execution
- Interfaces a local CDI debugger
The CDT uses the first three for local software launches. The last delegate was created
for tools outside the CDT and serves as the focus of this discussion. When
LocalCDILaunchDelegate is called upon to launch in debug
mode, it obtains information about the executable to be debugged and the parameters to
be sent to the debugger. This information must be packaged in an
ILaunchConfiguration object, and a suitable UI is
required to obtain this information from the user.
Adding a tab group for the launch configuration
Before an Eclipse application can be debugged, you must select a launch-configuration type and, by double- or right-clicking, create a new launch configuration. At this point, a graphical panel appears on the right and requests information related to the launch. This panel is called a launch configuration tab group; an example tab group is shown at right in Figure 1. This tab group consists of multiple tabs, and the visible tab in Figure 1 accepts the names of the executable to be debugged and its project.
Each launch must have its own tab group, and this must be identified in plug-in.xml as
an extension of
org.eclipse.debug.ui.launchConfigurationTabGroups. Listing 2 shows
what this extension looks like in this example.
Listing 2. Declaring the example tab group
<extension point="org.eclipse.debug.ui.launchConfigurationTabGroups"> <launchConfigurationTabGroup type="org.dworks.debug.example.ExampleLaunch" class="org.dworks.debug.example.ExampleTabGroup" id="org.dworks.debug.example.ExampleTabGroup"> </launchConfigurationTabGroup> </extension>
This extension is easy to understand. The
identifies the launch configuration, the
the class that creates the tab group, and
id gives the tab
group a unique name. The class identified by
ILaunchConfigurationTabGroup interface, and
its job is to create one or more
These tabs provide the debugger with the information it needs to operate, including
debugging flags, the location of the source code, and memory addresses. To configure an
ILaunchConfigurationTab to do its job, you have to implement two important methods:
createControl(Composite parent)— Creates the user interface for the debugger tab
performApply(ILaunchConfigurationWorkingCopy configuration)— Configures the debugger parameters
The second method is particularly important. It functions by assigning values to
attributes in a
LaunchConfigurationWorkingCopy. This data
object holds the information required for debugging, and it's sent to the debugger
when the debug session starts. Attribute names are listed in the
ICDTLaunchConfigurationConstants interface. Important attributes
- ID of the debugger to be launched
- Attributes supplied to the debugger session
- Name of the project being debugged
- Name of the program being debugged
- Parameters supplied to the running program
- Operating system running the program
ExampleConfigurationTabGroup code creates a single
ExampleTab. This tab displays six
pre-initialized text boxes — one for each of the attributes detailed previously. Figure
2 shows what the
ExampleTab looks like. Normally, you should
create multiple tabs with a variety of controls. The CDT provides the
CDebuggerTab class for configuring a C/C++ debugger.
Figure 2. Example launch-configuration tab
The code in
ExampleTab initializes debug attributes with the
setDefaults() method. This method identifies the example
debugger as the debugger to use and searches selected CDT resources to find the
project and program to debug. Performing this search doesn't require new code because
ExampleConfigurationTab extends the CDT's
CLaunchConfigurationTab class. This abstract class provides two
getContext(), which returns a CDT
initializeCProject(), which initializes the
ATTR_PROJECT_NAME attribute with the name of the project.
Creating the custom debugger
When you click Debug, Eclipse searches for the debugger identified by the
ATTR_DEBUGGER_ID attribute. In the example project, this is set to
org.dworks.debugexample.ExampleDebugger, which corresponds to
id field in the example project's extension of
org.eclipse.cdt.debug.core.CDebugger. Listing 3 presents the full extension.
Listing 3. The example debugger extension point
<extension point="org.eclipse.cdt.debug.core.CDebugger"> <debugger platform="*" name="Example Debugger" modes="run" cpu="*" class="org.dworks.debugexample.ExampleDebugger" id="org.dworks.debugexample.ExampleDebugger"> </debugger> </extension>
When the debugger starts, Eclipse checks to see if the processing environment is supported
by the debugger's
cpu elements. If so, it checks to make sure the current launch mode
is supported by the debugger's
mode element. If all the
checks pass, Eclipse finds the class identified in the
field. This class must implement the
and must, therefore, provide an implementation of the
The goal of
createSession() is to construct an object that
ICDISession interface. This object enables
the CDT to access the debugger as it operates. This access is provided with a number of
methods, and three of the most important are as follows:
getSessionProcess()— Returns the process of the running debugger
getTargets()— Returns an array of targets being debugged
getEventManager()— Returns a manager object that adds and removes event listeners
The first of these methods is straightforward and returns a
Process object corresponding to the debugger executable. The next
getEventManager(), are more involved; the rest of this article
explains them. Because these methods depend on the debugger executable, and because the
example plug-in doesn't have one, the
leaves these methods empty.
Targets, events, and the CDI Model
When the CDT calls
getTargets(), the session must provide an
ICDITarget for each process being debugged. The
ICDITarget's job is to receive debugging commands, translate them
for the debugger, and send them to the debugger. For example, when you click the
Step or Resume button in the CDT debug perspective, the CDT calls the
method, respectively. The CDI doesn't make any requirements as to how the
debugger target interface should work, but Part 2 explains how gdb interfaces a target using the MI protocol.
In addition to sending commands to the debugger, the
ICDITarget also transmits the debugger's output to any object
interested in receiving it. This is accomplished through CDI events, which is where the
getEventManager() method comes in. When any CDI
object, such as a
RegisterView, wants to be informed of
debugger events, it calls
getEventManager() to access the
ICDIEventManager. Then it calls the manager's
addEventListener() method to add itself as a listener. When
the debugger produces output, the target calls on the manager to alert all of its
listeners. The manager does this by calling each listener's
handleDebugEvents() method, which must be implemented by all
The CDI provides a standard set of
debugger's response or change in the debug environment must be packaged into one of the following:
- Fired when the user chooses a new breakpoint
- Fired when an aspect of the target, such as a variable, register, or portion of memory, changes value
- Fired when an object, such as a breakpoint, is created or removed
- Fired when the program has finished
- Fired when the target restarts or disconnects
- Fired when a program is reloaded, such as after a new build
- Fired when the executable is stopped, restarted, or resumed
Part 2 details how gdb output is converted into
MIEvents and how
ICDIChangedEvent is particularly important because it's
fired every time the target changes. Each changeable aspect of
the target is represented by an
ICDIObject, and these
objects form a hierarchy called the CDI Model. They include
ICDIThreads, and many
ICDITarget is the root and container of these CDI
Model objects. The only method that each
CDI breakpoints and watchpoints
Two of the most important ways of controlling the debugger use breakpoints and
watchpoints. A breakpoint halts the debugger once it reaches a specific location or
once a condition is met. A watchpoint halts the debugger when a specific variable is
read from or written to. The CDI provides two interfaces to represent these:
ICDIBreakpoint and its subinterface,
ICDIBreakpoint can be
regular, temporary, or hardware-assisted; and an
ICDIWatchpoint can be read-type, write-type, or both.
You create and delete breakpoints and watchpoints through an instance of
ICDIBreakpointManagement. This presents many related methods, such
But it's important to remember that the plug-in class of the
org.eclipse.debug.core plug-in provides its own breakpoint manager,
IBreakpointManager, which you can access with
DebugPlugin.getDefault().getBreakpointManager(). In many
cases, you may need to access this and convert
Building a graphical debugging environment from scratch is a Herculean task: Not only do you have to know the target processor inside and out but you also have to communicate with the debugger and deliver its output to a graphical user environment. For those who aren't Hercules, Eclipse provides an API for adding custom debuggers to the CDT framework. This API is called the C/C++ Debugger Interface (CDI), and this article has covered the basics of its operation and use. Part 2 of this "Interfacing with the CDT debugger" series explains how the CDT uses the CDI to interface the finest open source debugger of them all: the GNU Debugger (gdb).
- Visit CDT at Eclipse.org to get started with C/C++ Development Tooling.
- Read the CDT project leader's blog to get the inside scoop on CDT.
- Download the Eclipse C/C++ Development Tooling (CDT), a fully functional C and C++ IDE for the Eclipse platform.
- Check out the latest Eclipse technology downloads at IBM alphaWorks.
- Check out the "Recommended Eclipse reading list."
- Download Eclipse Platform and other projects from the Eclipse Foundation.
- Browse all the Eclipse content on developerWorks.
- New to Eclipse? Read the developerWorks article "Get started with Eclipse Platform" to learn its origin and architecture, and how to extend Eclipse with plug-ins.
- Expand your Eclipse skills by checking out IBM developerWorks' Eclipse project resources.