13号星期五steam dns:使用 CDT 调试器,第 1 部分: 了解 C/C++ 调试器接口

来源:百度文库 编辑:偶看新闻 时间:2024/04/20 10:05:33

简介: Eclipse C/C++ 开发工具(C/C++ Development Tooling,CDT)是面向 C/C++ 开发的最著名的开源环境之一,它提供了功能丰富的调试器,这是它非常流行的主要原因。但是,很少人知道可以扩展 CDT 框架使它支持定制调试器。通过使用合适的插件,定制调试器可以访问完整的 CDT 图形调试环境:代码单步调试、检查点(watchpoint)、断点(breakpoint)、注册表内容、内存内容和变量视图。通过本文了解如何构建这种插件,同时关注 C/C++ 调试接口(C/C++ Debugging Interface,CDI)。


命令行接口是一种用于调试的工具,而设计良好的图形环境则非常专业、非常出色。因此,要从头构建功能丰富的调试环境需要花费大量时间并且非常困难。但是,可以使用另外一种选择:Eclipse C/C++ 开发工具(CDT)。CDT 的可扩展性允许您将它提供的图形化调试功能连接到您的定制调试器。您不需要编写太多代码,但是要理解 CDT 的扩展点和 CDI。

CDI 是基于 Java? 的应用程序编程接口(Application Programming Interface,API),它的类和接口使它能够访问 CDT 的调试框架。Eclipse 插件使用 CDI 可以将新的调试器添加到 CDT 操作中并在 Eclipse/CDT 调试透视图中显示调试结果。本文将详细介绍 CDI。“使用 CDT 调试器” 系列的第 2 部分将展示 CDI 如何通过专门化它的 Machine Interface(MI)来与 GNU Debugger(gdb)交互。

示例 CDI 插件

要了解 CDT 调试器的工作方式,最好的办法是查看并尝试实际的代码。本文将解释如何构建一个特性很少的插件,使用它扩展 CDT 从而提供基本的调试功能。没有提供实际可执行的调试器,但可以以这些代码为基础将您自己的定制调试器添加到 CDT。

这个示例插件包含有三个 CDT 扩展和 Eclipse Debug Framework:

org.eclipse.debug.core.launchConfigurationTypes
创建一个单独的启动程序来调试 C/C++ 应用程序
org.eclipse.debug.ui.launchConfigurationTabGroups
从用户端接收调试配置参数
org.eclipse.cdt.debug.core.CDebugger
为启动的 C/C++ 应用程序创建调试会话

本文将解释以上内容并通过示例代码展示其工作原理。然后将详细研究 CDI 的操作,介绍 CDI 模型并研究 CDI 如何实现断点和检查点。

创建定制的启动配置类型

在 Eclipse 中,应用程序的启动过程被称为在Run 模式下启动。调试会话被称为在Debug 模式下启动。选择了一种启动模式后,下一步是选择启动配置类型。这将明确地告诉应用程序应当如何执行或调试。例如,Debug 模式下的启动配置类型定义调试器可执行文件、默认调试选项,以及应该如何在 Eclipse 中呈现调试输出。图 1 展示了 CDT Debug Configurations 窗口中呈现的配置类型。


图 1. CDT Launch Configuration 窗口


要在 Eclipse 中使用新的调试器,第一步是创建一个新的启动配置类型。这需要使用一个插件扩展 org.eclipse.debug.core.launchConfigurationTypes 扩展点。在图 1 中,可以查看窗口左侧的 Example Configuration Type。清单 1 给出了定义这种新类型的扩展。


清单 1. 示例 LaunchConfigurationType 扩展
                                  

Example Configuration Type 位于 Debug 窗口,因为 modes 字段被设置为 debug,而 public 字段被设置为 true。最重要的字段是 delegate,它将识别管理启动过程的类。该类必须实现 ILaunchConfigurationDelegate 接口,并且从头编写委托非常复杂。幸运的是,CDT 简化了这项工作,它提供了 4 个预构建的 delegate 类:

LocalRunLaunchDelegate
运行一个本地 C/C++ 应用程序
LocalAttachLaunchDelegate
调试本地应用程序
CoreFileLaunchDelegate
执行后分析核心文件
LocalCDILaunchDelegate
使用一个本地 CDI 调试器

CDT 使用前三个类实现本地软件启动。最后一个 delegate 是为 CDT 外部的工具创建,并且本文将重点讨论这个类。当调用 LocalCDILaunchDelegate 以在调试模式下启动时,它将获得有需要调试的可执行文件的信息以及将发送给调试器的参数。这些信息必须被打包到 ILaunchConfiguration 对象,并需要使用一个合适的 UI 从用户端获得信息。

为启动配置添加一个选项卡组

在调试 Eclipse 应用程序之前,必须选择一个启动配置类型,并通过双击鼠标或右键单击创建一个新的启动配置。此时,一个图形化面板将出现在右侧并请求与启动有关的信息。这个面板称为 launch configuration tab group;图 1 右侧显示了一个示例选项卡组。这个选项卡组包含有多个选项卡,而图 1 所示的可见选项卡接受将要调试的可执行文件和项目的名称。

每个启动必须有自己的选项卡组,并且必须在 plug-in.xml 中标识为 org.eclipse.debug.ui.launchConfigurationTabGroups 的扩展。清单 2 显示了这个示例中的扩展。


清单 2. 声明示例选项卡组
                                  

这个扩展很容易理解。type 字段标识启动配置,class 标识创建选项卡组的类,而 id 为选项卡组提供一个惟一的名字。class 标识的类必须实现 ILaunchConfigurationTabGroup 接口,它负责创建一个或多个 ILaunchConfigurationTab。这些选项卡为调试器提供了需要处理的信息,包括调试标记、源代码的位置,以及内存地址。要配置 ILaunchConfigurationTab 以完成上述成工作,需要实现两个重要的方法:

  • createControl(Composite parent) — 为调试器选项卡创建用户接口
  • performApply(ILaunchConfigurationWorkingCopyconfiguration) — 配置调试器参数

第二个方法尤其重要。它的作用是为 LaunchConfigurationWorkingCopy 中的属性分配值。这个数据对象持有调试所需的信息,并且在启动调试会话时发送给调试器。属性名在 ICDTLaunchConfigurationConstants 接口中列出。重要的属性包括:

ATTR_DEBUGGER_ID
将要启动的调试器的 ID
ATTR_DEBUGGER_SPECIFIC_ATTRS_MAP
提供给调试器会话的属性
ATTR_PROJECT_NAME
进行调试的项目的名称
ATTR_PROGRAM_NAME
进行调试的程序的名称
ATTR_PROGRAM_ARGUMENTS
为运行中程序提供的参数
ATTR_PLATFORM
运行程序的操作系统

ExampleConfigurationTabGroup 代码创建一个单独的配置选项卡:ExampleTab。这个选项卡显示 6 个预初始化的文本框 — 每个文本框分别与前面介绍的各个属性对应。图 2 显示了 ExampleTab。通常,应该使用各种控件创建多个选项卡。CDT 提供配置 C/C++ 调试器的 CDebuggerTab 类。


图 2. 示例启动配置选项卡


ExampleTab 中的代码使用 setDefaults() 方法初始化调试属性。这个方法将示例调试器标识为要使用的调试器并搜索已选的 CDT 资源,从而查找要进行调试的项目和程序。执行这种搜索不需要新的代码,因为 ExampleConfigurationTab 扩展了 CDT 的 CLaunchConfigurationTab 类。这个抽象类提供了两个重要方法:getContext()(返回一个 CDT 资源)和 initializeCProject()(使用项目名初始化 ATTR_PROJECT_NAME 属性)。

创建定制调试器

如果单击 Debug,Eclipse 将搜索由 ATTR_DEBUGGER_ID 属性标识的调试器。在示例项目中,该属性被设置为 org.dworks.debugexample.ExampleDebugger,该值对应于示例项目的 org.eclipse.cdt.debug.core.CDebugger 扩展的 id 字段。清单 3 给出了完整的扩展。


清单 3. 示例调试器扩展点
                                  

当启动调试器时,Eclipse 将检查调试器的 platformcpu 元素是否支持处理环境。如果支持,它将检查以确保当前的启动模式受调试器的 mode 元素支持。如果通过所有检查,Eclipse 将查找 class 字段中标识的类。这个类必须实现 ICDIDebugger2 接口,因此必须提供 createSession() 方法的实现。

createSession() 的目标是构建一个可以实现 ICDISession 接口的对象。这个对象使 CDT 能够在运行时访问调试器。这种访问通过一些方法提供,其中最重要的三个方法如下所示:

  • getSessionProcess() — 返回运行调试器的进程
  • getTargets() — 返回调试目标数组
  • getEventManager() — 返回添加和删除事件侦听器的管理器对象

第一种方法十分简单,它将返回与调试器可执行文件相对应的 Process 对象。接下来的两种方法 getTargets()getEventManager() 则较为复杂;本文稍后将介绍这些内容。由于这些方法依赖于调试器可执行文件,而示例插件没有提供这些文件,因此 ExampleDebugger 类将这些方法留空。

目标、事件和 CDI 模型

当 CDT 调用 getTargets() 时,会话必须为每个要调试的进程提供一个 ICDITargetICDITarget 的任务是接收调试命令,为调试器转换这些命令,然后发送给调试器。例如,当单击 CDT 调试透视图中的 StepResume 按钮时,CDT 将分别调用目标的 stepOver()resume() 方法。CDI 对调试器目标接口如何工作没有具体要求,但是第 2 部分将解释 gdb 如何使用 MI 协议与目标交互。

除了向目标发送命令外,ICDITarget 还向任何希望接收调试器输出的对象传输调试器输出。这将通过 CDI 事件完成,这里将使用会话的 getEventManager() 方法。当需要向任何 CDI 对象(例如一个 RegisterView)通知调试器事件时,它将调用 getEventManager() 以访问会话的 ICDIEventManager。然后调用管理器的 addEventListener() 方法,以将该方法添加为侦听器。当调试器生成输出时,目标调用管理器以通知所有侦听器。管理器通过调用每个侦听器的 handleDebugEvents() 方法来完成这个目的,而这个方法必须通过所有 ICDIEventListener 来实现。

CDI 提供了一组标准的 ICDIEvent。每个调试器在调试环境中的响应或修改必须被打包到以下内容中:

ICDIBreakpointMovedEvent
当用户选择新断点时触发
ICDIChangedEvent
当目标的某个元素(例如变量、注册表或部分内存)的值发生改变时触发
ICDICreationEvent/ICDIDestroyedEvent
当创建或删除某个对象(例如断点)时触发
ICDIExitedEvent
当程序终止时触发
ICDIRestartedEvent/ICDIDisconnectedEvent
当目标重启或断开连接时触发
ICDIExecutableReloadedEvent
当重新加载程序(例如在新的构建之后)时触发
ICDISuspendedEvent/ICDIRestartedEvent/ICDIResumedEvent
当可执行文件停止、重启或重新执行时触发

本系列第 2 部分将详细介绍 gdb 输出如何转换为 MIEvent 以及 MIEvent 如何成为 ICDIEvent

ICDIChangedEvent 非常重要,因为它在目标每次改变时都触发。目标的每个可修改的方面都使用一个 ICDIObject 表示,并且这些对象组成了一个称为CDI Model 的层次结构。它们包括 ICDIRegisterICDIVariableICDIMemoryBlockICDIThread 等等。ICDITarget 是这些 CDI Model 对象的根和容器。每个 ICDIObject 必须实现的惟一方法是 getTarget()

CDI 断点和检查点

控制调试器的两种最重要的方法是使用断点和检查点。当达到某个特定位置或满足某个条件时,断点将暂停调试器。当读取或写入某个特定变量时,检查点将暂停调试器。CDI 提供两个接口来呈现这些方法:ICDIBreakpoint 和其子接口 ICDIWatchpointICDIBreakpoint 可以是长期的、临时的或硬件支持的;而 ICDIWatchpoint 可以是读类型、写类型,或者同时具备两种类型。

您可以通过 ICDIBreakpointManagement 的实例创建或删除断点和检查点。这将涉及很多相关的方法,例如 setLineBreakpoint()deleteBreakpoints()setWatchpoint()。但是需要注意,org.eclipse.debug.core 插件的插件类提供了它自己的断点管理器 IBreakpointManager,您可以通过 DebugPlugin.getDefault().getBreakpointManager() 访问。在很多情况下,您可能需要访问并将 IBreakpoint 转换为 ICDIBreakpoint

结束语

从头构建图形化调试环境是一项非常浩大的工程:您不仅需要详细了解目标处理程序,还需要与调试器通信并将其输出传递给一个图形化用户环境。对于那些非专业人员来说,Eclipse 提供了一个可以将定制调试器添加到 CDT 框架的 API,这个 API 称为 C/C++ Debugger Interface(CDI),本文介绍了它的基本操作和使用。“使用 CDT 调试器” 系列文章的第 2 部分将解释 CDT 如何使用 CDI 与最优秀的开源调试器进行交互:GNU Debugger(gdb)。


参考资料

学习

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文。

  • 访问 Eclipse.org 中的 CDT,获得 C/C++ Development Tooling 入门。

  • 阅读 CDT 项目领导的博客,获得有关 CDT 的独家解读。

  • 查阅 “Eclipse 推荐读物列表”。

  • 浏览 developerWorks 中所有 Eclipse 内容。

  • 您是 Eclipse 新用户吗?请阅读 developerWorks 文章 “Eclipse 平台入门” 以了解它的起源和架构,以及如何用插件扩展 Eclipse。

  • 查看 IBM developerWorks 的 Eclipse 项目资源 以扩展 Eclipse 技巧。

  • 收听针对软件开发人员的有趣访谈和讨论,一定要访问 developerWorks podcasts。

  • 随时关注 developerWorks 的 技术活动和网络广播。

  • 查看免费的 developerWorks On demand demos 观看并了解 IBM 及开源技术和产品功能。

  • 查阅最近将在全球举办的面向 IBM 开放源码开发人员的研讨会、交易展览、网络广播和其他 活动。

  • 访问 developerWorks 开放源码专区,获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。

获得产品和技术

  • 在 IBM alphaWorks 中查看最新的 Eclipse 技术下载。

  • 从 Eclipse Foundation 下载 Eclipse Platform 和其他项目。

  • 下载 IBM 产品评估版 并开始使用来自 DB2?、Lotus?、Rational?、Tivoli? 和 WebSphere? 的应用程序开发工具和中间件产品。

  • 使用 IBM 试用软件 改进您的下一个开源开发项目,可以下载或从 DVD 获得。

讨论

  • Eclipse Platform 新闻组 是讨论关于 Eclipse 的问题的第一站(选择此链接将启动默认的 Usenet 新闻阅读器应用程序并打开 eclipse.platform)。

  • Eclipse 新闻组 中有很多参考资料适用于对使用和扩展 Eclipse 感兴趣的人员。

  • 参与 developerWorks blogs 并加入 developerWorks 社区。

关于作者

Matthew Scarpino 是 Eclipse Engineering LLC 的一名项目经理兼 Java 开发人员。他是 SWT/JFace in Action 的首席作家,并对标准部件工具包(Standard Widget Toolkit,SWT)做出过一次较小的但却非常重要的贡献。他喜爱爱尔兰民间音乐、马拉松赛跑、William Blake 的诗歌以及图形化编辑框架(Graphical Editing Framework,GEF)。