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)..
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:
-
org.eclipse.debug.core.launchConfigurationTypes - Creates a separate launcher to debug C/C++ applications
-
org.eclipse.debug.ui.launchConfigurationTabGroups - Receives debug configuration parameters from the user
-
org.eclipse.cdt.debug.core.CDebugger - 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
LaunchConfigurationType extension
<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:
-
LocalRunLaunchDelegate - Runs a local C/C++ application
-
LocalAttachLaunchDelegate - Debugs local applications
-
CoreFileLaunchDelegate - Analyzes core files after execution
-
LocalCDILaunchDelegate - 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
the 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 type field
identifies the launch configuration, the class identifies
the class that creates the tab group, and id gives the tab
group a unique name. The class identified by class must
implement the ILaunchConfigurationTabGroup interface, and
its job is to create one or more ILaunchConfigurationTabs.
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
include:
-
ATTR_DEBUGGER_ID - ID of the debugger to be launched
-
ATTR_DEBUGGER_SPECIFIC_ATTRS_MAP - Attributes supplied to the debugger session
-
ATTR_PROJECT_NAME - Name of the project being debugged
-
ATTR_PROGRAM_NAME - Name of the program being debugged
-
ATTR_PROGRAM_ARGUMENTS - Parameters supplied to the running program
-
ATTR_PLATFORM - Operating system running the program
The ExampleConfigurationTabGroup code creates a single
configuration tab: 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
important methods: getContext(), which returns a CDT
resource, and initializeCProject(), which initializes the
ATTR_PROJECT_NAME attribute with the name of the project.
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
the 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 platform and 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 class
field. This class must implement the ICDIDebugger2 interface
and must, therefore, provide an implementation of the createSession() method.
The goal of createSession() is to construct an object that
implements the 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
two methods, getTargets() and 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 ExampleDebugger class
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
target's stepOver() or resume()
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
session's 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
session's 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 ICDIEventListeners.
The CDI provides a standard set of ICDIEvents. Every
debugger's response or change in the debug environment must be packaged into one of the following:
-
ICDIBreakpointMovedEvent - Fired when the user chooses a new breakpoint
-
ICDIChangedEvent - Fired when an aspect of the target, such as a variable, register, or portion of memory, changes value
-
ICDICreationEvent/ICDIDestroyedEvent - Fired when an object, such as a breakpoint, is created or removed
-
ICDIExitedEvent - Fired when the program has finished
-
ICDIRestartedEvent/ICDIDisconnectedEvent - Fired when the target restarts or disconnects
-
ICDIExecutableReloadedEvent - Fired when a program is reloaded, such as after a new build
-
ICDISuspendedEvent/ICDIRestartedEvent/ICDIResumedEvent - Fired when the executable is stopped, restarted, or resumed
Part 2 details how gdb output is converted into MIEvents and how MIEvents become ICDIEvents.
The 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 ICDIRegisters, ICDIVariables, ICDIMemoryBlocks, ICDIThreads, and many
more. The ICDITarget is the root and container of these CDI
Model objects. The only method that each ICDIObject must
implement is getTarget().
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, ICDIWatchpoint. The 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
as setLineBreakpoint(), deleteBreakpoints(), and setWatchpoint().
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 IBreakpoints into ICDIBreakpoints.
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).
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | os-eclipse-cdt-debug-ex-debugger-plugin.zip | 15KB | HTTP |
Information about download methods
Learn
-
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.
-
Check out the "Recommended Eclipse reading list."
-
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.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Stay current with developerWorks' Technical events and webcasts.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
Get products and technologies
-
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.
-
Download Eclipse Platform and other projects from the Eclipse Foundation.
-
Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
Discuss
-
The Eclipse Platform newsgroups should be your first stop to discuss questions regarding Eclipse. (Selecting this will launch your default Usenet news reader application and open eclipse.platform.)
-
The Eclipse newsgroups has many resources for people interested in using and extending Eclipse.
-
Participate in developerWorks blogs and get involved in the developerWorks community.
Matthew Scarpino is a project manager and Java developer at Eclipse Engineering LLC. He is the lead author of SWT/JFace in Action and made a minor but important contribution to the Standard Widget Toolkit (SWT). He enjoys Irish folk music, marathon running, the poetry of William Blake, and the Graphical Editing Framework (GEF).
Comments (Undergoing maintenance)





