Interfacing with the CDT debugger, Part 1: Understand the C/C++ debugger interface

Add custom tools to the CDT debugger framework

The Eclipse C/C++ Development Tooling (CDT) is one of the most well-known open source environments for C/C++ development, and its full-featured debugger plays a large role in its popularity. What isn't as well known is that the CDT framework can be extended to support custom debuggers. With the right plug-in, a custom debugger can access the full spectrum of the CDT's graphical debugging environment: code-stepping, watchpoints, breakpoints, register contents, memory contents, and variable views. Learn how to construct this plug-in, with a focus on the C/C++ Debugging Interface (CDI).

Share:

Matthew Scarpino, Java Developer, Eclipse Engineering, LLC

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).



10 June 2008

Also available in Japanese Vietnamese

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:

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
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
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.


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 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.


Conclusion

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).


Download

DescriptionNameSize
Sample codeos-eclipse-cdt-debug-ex-debugger-plugin.zip15KB

Resources

Learn

Get products and technologies

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.

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 Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=312452
ArticleTitle=Interfacing with the CDT debugger, Part 1: Understand the C/C++ debugger interface
publish-date=06102008