摘 要:调试功能是软件集成开发环境中重要也是复杂的功能之一,调试功能的完善与否很大程度上决定了一个集成开发环境的优劣。作为一个大型软件集成开发环境的一部分,为了实现其调试功能,这里通过简要分析Eclipse CDT的调试机制,设计并实现了一个应用于基于Eclipse CDT的嵌入式开发环境的调试器,其中实现了一系列调试功能如断点设置、单步执行、源代码搜索以及变量、内存和寄存器查看等,为整个集成开发环境的实现打下了基础。
O 引 言
在软件开发过程中,程序出现错误在所难免。无论是普通软件还是嵌入式软件,调试器都是开发过程中不可缺少的工具。
“调试器”是用于跟踪程序执行情况,快速有效地定位错误产生的位置,从而找到引起错误的原因,并将其消除的工具。调试功能是开发环境中重要、也是复杂的功能之一,其好坏在很大程度上决定了一个开发环境的优劣。调试是一个从错误现象出发,通过某种手段寻找错误代码的过程。调试的基本原理是在发现程序运行的错误后,设法再现程序的执行过程,并获取程序执行中的有关信息,通过对这些信息的分析,逐步找到引起错误的原因。循环调试是基本的调试模型;循环调试的意思是反复运行程序,利用打印输出、断点、单步等技术,观察程序状态,控制程序运行,逐渐逼近错误代码;循环调试的前提是程序运行是确定的,即对同样的输入,程序的执行过程是相同的。串行程序具有确定性。
调试的基本行为有观察、控制与修改。观察是对程序的执行状态进行察看,包括处理机状态、进程状态和数据状态等;控制即控制程序的执行过程,例如设断点、单步执行等;修改就是改变程序的运行状态,如修改变量的值、修改指令计数器、修改状态寄存器等。调试器在不同程度上支持这3种行为的综合使用。
经典的调试手段有断点(Break Pointing)、跟踪(Tracing)、断言(Assertion)、分析(Analysis)、打印输出等。在源程序级支持图形化的用户界面使得调试工作更加容易进行。对于实时程序或专用机上的程序还常采用模拟手段进行调试。
1 CDT的调试机制解析
CDT提供了4个插件用于实现调试功能。这几个插件对于GDB调试器进行操作,并且应用GDB提供的MI接口进行通信。这四个插件分别为org.eclipse.cdt.debug.mi.cote插件,用以实现GDB的MI接口功能及GDB调用功能;org.eclipse.cdt.debug.mi.ui插件,主要用于实现GDB的相关设置选项界面;org.eclipse.cdt.debug.core插件,用于实现CDT的调试模型,包括用串口调试,用网卡调试,设置波特率等功能;org.eclipse.cdt.debug.ui插件,用于实现界面显示功能,包括调试视图,调试动作等,比如单步调试,设置断点,,运行程序等调试功能。它们之间的关系如图1所示。
标准的调试模型并不能代表所有的体系结构,像嵌入式硬件结构是不同的,有可能是多个处理器的不同应用,也可能包括DSP处理器的应用。CDT平台支持将非标准的调试器集成到平台的视图和动作中。调试器围绕着活动调试上下文进行操作,活动调试上下文是在调试视图中选择的一帧或者一个线程,这个上下文驱动了源代码搜索,变量和寄存器显示,以及动作使能等操作。它们之间的关系如图2所示。
2 调试器的详细设计
2.1 断点设置
调试器提供的断点依赖于调试系统所具有的能力,以及应用这些能力可以构建的集合功能。例如,用行断点实现运行到行,实现条件断点等。
断点属性保存在标记中,平台提供接口IMarker,作为文件中的一般目的标记。标记是原始数据类型键值对的存储。平台提供接口IBreakpoint实现断点行为。为了实现复杂的断点行为,调试器需要提供IBreakpoint的实现。所有的断点都有一个相关的标记保存属性和在编辑器中显示,提供和断点类型相关的标记扩展点。如果需要断点被保存,需要指定persistent为true。其内容如下:
断点管理器(IBreakpoint Manager)是在工作空间中的断点集合。当断点创建时,会在管理器中注册。当断点被删除,它也会在管理器中移除,当断点添加,移除,以及断点属性改变时,提供更改通知。客户端需要实现IBreakpointsListener以及在断点管理器中注册。例如:调试目标侦听更改通知,因此当它们改变时,可以安装、移除、更新断点信息。客户端也需要实现IBreak-pointManaagerListener,以便实现当断点管理器被使能或禁止时的通知侦听,也可以允许所有的断点被暂时禁止,而不用改变单个断点的状态。
当建立了调试目标之后,应该搜索断点管理器,查找存在的断点,并安装这些断点。调试目标应该在其生命周期中侦听断点的添加、移除、改变等信息,并且能够更新这些信息。
在编辑器中显示了断点和观察点的位置。编辑器在竖直条中显示标记,当标记改变时更新。继承AbstractDecoratedTextEditor的编辑器会有一个竖直标记条,用于显示和正在编辑的文件有关的标记。在竖直标记条中双击,可能会产生多于一种的操作,可以设置行断点或者设置观察点。在编辑器竖直标记条中添加双击动作,可以应用工作台扩展点来添加编辑器操作。参考AbstractRulerActionDelegate给期望的工作添加操作,可通过扩展点editorActions予以添加。断点的模型如图3所示。
2.2 命令处理
命令处理包括单步进入、单步跳过、返回、悬挂、恢复、、运行、继续运行、终止等。平台提供标准模式的命令处理的实现。
所有的命令处理需要实现接口IDeblugCom-mandHandler,并且实现如下函数:
canExecute(IEnabledStateRequest request)
execute(IDebugCommandRequest request)
标准模式可以重载平台的实现,定制模式需要自己提供一个实现,包括提供作为适配器的处理类,可以直接实现处理接口。
平台所定义的处理者进程为如下几个:
IDisconnectHandler
IDroDToFrameHandler
IResumeHandler
IStepFiltersHandler
IStepIntoHandler
IStepOvetHandler
IStepReturnHandler
ISuspendHandler
ITerminateHandler
处理者和命令处理之间的交互是异步的。需要先更新使能状态,然后开始执行。传递到命令处理的是一个IRequest对象,实现接收状态,允许取消,当请求结束时通知等功能。
命令执行的过程如图4所示。
平台提供的命令处理应用JOB来实现异步交互。命令处理为每一个请求和返回维护一个JOB的进度表,并且JOB是异步运行的。
2.3 视图内容
利用平台提供的tree视图的实现变量、寄存器的查看,并用内容提供者和标签提供者来支持定制异步内容。这个tree视图的实现基于JFace的tree视图,并实现了接口ILazyTreePathContentProvider。这个视图不能被继承,但是可以被初始化。
一些调试视图定义了很多列。像变量视图,寄存器视图,都分别有名字列,值列等。调试模型提供要显示的列,以及元编辑器对某一个值进行编辑,还为每一个列提供标签。视图、列、元编辑器之间的关系如图5所示。列表述定义了要显示的列,包括列头、列图像、列标识。允许提供多个列,在初始化时显示其中的几列,用户可以选择显示哪些列。
元编辑器允许用户在线编辑。为每一个元素和列提供元编辑器,应用JFace的CellEditor进行编辑,提供了对文本选择框、组合框的标准编辑。为每一个元素提供元修改器,决定哪一个列可以被修改,在修改之后更新相应的值,可以用实现接口IcellModifier来达到目标。
2.4 内存视图
内存视图显示一段内存中的内容。内存显示由内存块、内存块内容重新获取、内存块管理器、内存块描述类型、内存块描述、内存块描述绑定、内存块描述管理器等几部分组成。
读取并显示内存块的过程如下:用户启动内存查看命令,从当前调试上下文中,平台获取适配器IMetm-ryBlockRetrieval,平台为内存块请求内存块描述适配器,内存块添加到内存块管理器中,平台查找内存描述类型列表,找到和内存块符合的内存描述,然后创建它,将内存描述添加到内存视图中。
用扩展点
需要在描述之间提供同步机制来使得用户不用手工进行同步。在此提供同步服务器,以解决当前显示描述之间的同步问题。当某一个描述内容改变后,会发送一个描述改变的事件,同步服务器接收到这个事件,同步服务器通知对这个事件感兴趣的描述,然后其他描述接收这个事件并进行响应,其过程如图6所示。
内存描述绑定定义可以应用于某种类型中内存块的内存描述。可以用扩展点memoryRenderings进行创建。其内容如下:
终的内存视图如图7所示。
3 结 语
作为大型项目的一个部分,在分析CDT调试机制的基础上,设计并实现一个适用于嵌入式软件集成开发环境的调试器,具备断点设置、单步执行、源代码搜索以及变量、内存和寄存器查看等常用调试功能。
免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。