引言
传统上,调试嵌入式Linux产品需要将硬件和软件工具结合起来,如用JTAG工具进行硬件bring-up,用基于代理(agent- based)的解决方案进行软件开发。这些JTAG和基于代理的工具相结合的方法通常可以解决单点问题,但它们最初并不是专门针对集成化的Linux开发而设计的。因而,在当今集成化的产品开发中,这些传统方法常常是不可行的。
但是,我们可以在Linux内核的配置、补丁管理以及在基于Eclipse的IDE环境中的用户空间应用开发、调试和分析之中,将传统JTAG 硬件调试融入其中得到一种全新的方法,从而完全改变开发人员使用JTAG连接进行Linux设备软件调试的方法,这就是Wind RiverWorkbench。
Linux设备调试的复杂性
在嵌入式设备领域,Linux的应用正在迅速增加。根据技术市场研究机构VDC的报告,在新的设备研发项目中,有23%会采用Linux。由于开发工作跨越Boot Loader、Linux内核、内核模块和应用,调试工作很可能极为复杂。Linux开发人员必须面对的问题包括为Boot Loader建立目标配置文件,在用户模式和内核模式之间双向对硬Linux虚拟地址、映射内核符号信息以及排除遍布于用户和内核空间之中的差错。包括内核GNU调试器和GNU调试器在内,在基于代理的调试方案中,要想解决上述任何问题都会遇到极大困难。
Boot Loader调试
如果浪费太多的时间在BootLoader的开发与调试上,将会严重影响开发人员对于系统稳定性、设备软件与应用开发的精力投入。因此,开发人员应当借助于先进的工具,尽快逾越这个阶段。
Linux需要依靠BootLoader来启动操作系统。这段代码存放在Flash或者其他非易失性存储器之中,在系统开机或者复位之后立即运行。Boot Loader的调试可能会非常复杂。这段代码与硬件密切相关,在系统启动之后开发人员必须把它从Flash存储重新定位到RAM之中。在今天的SoC处理器中可能包括了数百个配置寄存器,都需要在此时进行初始化,这项工作需要熟悉数千页的特殊设定文档。如果设定寄存器错误,可能导致随后Linux内核或者应用调试的异常。并且手工编辑寄存器设定是一项极为繁琐易错的工作。
Boot Loader开发的另一项常见挑战出现在Boot Loader把Linux装入RAM并启动操作系统的时候。基于代理的调试解决方案不支持BootLoader调试,因为在此过程中还没有开始发挥作用。因此,开发人员只能寄希望于JTAG工具。
JTAG调试解决方案提供了很强的能力来帮助开发人员快速有效地完成Boot Loader的测试与故障排除工作。它使寄存器设置工作大大简化,通过设置硬件断点以及单步执行Flash中的代码,可以快速发现原代码中的错误。IDE 可以支持反汇编,还可以让你混合查阅源代码和汇编代码,符号管理功能比较便于代码从Flash向RAM的重新定位,使整个调试工作得到很大帮助。
JTAG调试解决方案不需要通过Boot Loader即可装载Linux内核。对于那些在Boot Loader尚未完成之前就希望开始系统开发的项目管理人员来说,这项功能具有特殊的重要意义。提供了引导行能力的JTAG调试解决方案可以支持Boot Loader和操作系统稳定化的并行开发,从而加速软件开发项目的整体进程。
Linux内核及内核模块调试
Linux内核及内核模块是Linux操作系统的核心构建。在系统被Boot Loader初始化之后,首先装载的就是Linux内核。Linux模块则根据需要进行装载。在进行操作系统bring-up时,开发人员必须专注于 Linux操作系统的优化或剪裁以及内核模块的开发,需要必须密切监控硬件与软件之间的互动。Linux内核调试要求具备观察寄存器、数据缓存器及其它底层数据。LinuxKGDB要求具备稳定的Linux内核,并且确保诸如设备驱动之类的客户硬件接口处于就绪状态,其中的代理才能工作。基于代理的调试不具备底层硬件的可视化能力,也不能提供完全的诊断功能,因而无法让开发者了解硬件与Linux内核之间的互动。
如果采用代理来调试Linux内核和内核模块,在进入调试断点时可能会涉及系统暂停或者冻结方面的问题。例如,KGDB无法暂停CPU(特别是在多核或者多处理器环境中)来让开发人员检查CPU的现行状态,它也不能帮助开发人员对崩溃的系统进行调试,因为崩溃的操作系统显然已经不能再运行代理。而且,KGDB还需要以太网等通信接口实现主机系统与目标之间的沟通。总之,采用代理来实现Linux内核模式调试,需要具备由IP栈、稳定的Linux 内核和处于运行状态的设备驱动。在上述条件尚未具备,或者上述软件本身还需要调试的时候,基于代理的调试显然就无能为力。
为了实现并验证一个目标系统中的Linux内核,必须拥有可以监控和管理Linux内核和内核模块的全面调试解决方案。基于JTAG的调试解决方案功能特性包括查看局部/全局符号和寄存器以及指令和数据缓存器。已经有商业化的JTAG调试解决方案可以把物理内存和虚拟内存顺畅地映射过来,从而帮助开发人员正确地观察内存地址和内容,也具有对Linux内核模块进行调试的能力,以及在不必反复连接和切断目标系统的前提下多次装载和卸载。
JTAG调试解决方案的另一个重要能力是把系统完全置于暂停状态并且全面观察操作系统和应用的状态。这种能力又被称为“系统模式调试 (system mode debug)”,对于Linux内核和Linux内核模块的调试是极为有用的。有了系统模式调试能力,开发人员就可以把整个系统完全暂停下来,包括处理器、操作系统和所有的线程以及中断处理程序。以这种方式暂停系统,就有可能获得系统硬件和软件的完整细节视图,当然也可以让系统继续执行或者分步骤执行某些代码。
因此,在一些KGDB无法使用的情况下,JTAG解决方案就可大派用场,特别是在Linux内核出错或者目标崩溃的情况下更是如此。因此JTAG解决方案在提升操作系统和设备驱动稳定性方面特别有用。
Linux应用调试
Linux应用是在Linux内核控制之下运行的用户程序,它通过系统调用来访问系统资源。Linux内核负责处理系统调用并且决定如何来提供硬件和内存访问。
对于用户模式下的应用调试,开发人员需要通过启动和停止线程、查看变量和堆栈来直接访问应用线程。由于一个应用可能由多个进程或者线程组成,所以有可能需要停止与正在调试的应用线程相关的所有线程。开发人员也常常需要跨越不同的进程和CPU来查看外设寄存器。然而,GDB只能在线程的级别上操作,而且只能启动和停止单个线程,根本不具备启动和停止整个系统或者同时启动/停止多个线程的能力。
另外,在用户模式下进行调试的时候,可能会因为单步运行系统调用而从用户模式进入Linux内核模式,然后又单步执行回到用户模式。在 Linux的虚拟内存结构下,在两种模式之间来回转换的同时还要保持内存地址跟踪,仅仅依赖于物理地址将会效果有限。于是,基于代理的解决方案需要同时采用GDB和KGDB来跟踪系统调用进入Linux内核和内核模块。同时,这个过程将会非常复杂。
Linux应用开发人员也常常遇到没有配备以太网或者串行通信接口的设备开发项目。即便是配有这些通信接口,相应的软件也不一定能很快就开发完成。而基于代理的调试方法必须依赖这些通信接口才能工作。如果目标设备没有通信接口,或者这些通信接口本身还需要开发与调试,或者内存对于IP栈或代理软件不可用,在这些情况下,基于代理的方法也不能使用。
基于JTAG的调试方法对于运行在Linux用户模式下的应用则具有深度可视化能力。对于提出系统调用的应用,双模式的JTAG调试解决方案还将可视化的深度延伸到Linux内核,所有的应用线程、运行环境以及在线程变量中用到的参数都一览无遗,而且这些功能的实现不会对Linux内核本身造成任何影响。对于没有通信接口的目标设备,例如移动电话、医疗设备、车载设备等,基于JTAG的调试解决方案都显示出远高于基于代理调试的优越性。一个 Linux的软件与硬件连接如图1所示。
采用系统模式调试可以让多线程应用调试大大简化,这是因为开发人员获得了暂停处理器并观察操作系统和全部线程运行状态的能力。如前所述,许多问题的发生是因为多个线程之间的交互。而基于代理的调试根本不具备停止全部线程的能力,自然也就难以发现问题所在,这就意味着工程进度将会因为调试工作的低效率而被大大延迟。
基于JTAG的调试解决方案与目标硬件的连接是非干扰性的,可以连接到一个已经运行并且发生错误的目标系统之中,也可以不改变处理器寄存器的状态,即可观察到Linux内核和应用的运行状况并进行调试。例如,如果一个Linux的目标设备进入死锁状态,利用JTAG调试工具,开发人员就可以在不会破坏现场状态的前提下与目标系统连接,进而观察其中的Linux内核对象、应用运行情况,找到引发错误的线程、系统调用以及调用参数。在有些情况下,基于代理的调试工具根本无法使用,而基于JTAG的调试工具却可以简化调试工作,加快调试进度。
Wind River基于JTAG的Linux调试工具
Wind Rivet推出的基于JTAG的OCD((On-Chip Debugging,片上调试)解决方案运行在符合工业标准的Eclipse平台上,为嵌入式Linux设备提供了先进的基于JTAG的调试功能特性。
Workbench OCD主要支持Wind River Linux。不过,对于开发者自行获得的Linux或者半导体厂商提供的Linux版本,Wind River也可提供片上调试功能,也为kernel.org Linux提供基于JTAG的内核及用户模式调试。Wind River调试解决方案还支持广泛的目标硬件,包括先进的多核处理器。它还支持移动终端设备市场上所有最新的主流处理器,实现各种量身定制的增强功能特性,使设备软件和硬件的开发调试变得更加简单、更加直观。
Workbench实现了与Eclipse全整合,并且通过在Workbench基于Eclipse的环境中加入新的视图和功能模块,进一步加强了IDE的支持。
目前,大多数移动终端设备都采用了多处理内核,对很多片上调试解决方案都造成很大的挑战。JTAG Server和JTAG Accelerator技术提供了面向多内核处理器的高速JTAG调试能力。JTAG Accelerator技术支持当前最复杂的32位和64位处理器,实现了JTAG带宽利用率的最大化。JTAG Servet技术则使开发人员能够同时连接多达128个处理器,并且在单个IDE实例中动态地调试多个处理器。
这样,Linux开发人员可以灵活地采用JTAG连接来进行硬件bring-up、Linux内核、中间件和用户模式应用的调试,并且在适当的时候转移到基于代理的调试方式之下,而且不论是JTAG或是代理模式都在同一个IDE架构之中,这种功能可以显着改善多个开发团队之间的协同效率,节省排除差错的时间。
结语
由于嵌入式系统已经变得越来越复杂,软件开发人员在调试工作上所面临的挑战也越来越多。传统基于代理的Linux调试解决方案在用户模式和内核模式的调试上的有效性都面临尴尬。基于JTAG的调试可以在Linux开发中扮演极有价值的角色,当它与Eclipse之类的标准化开发环境相结合,就可以显着改善编辑-编译-调试过程。在产品上市速度已经成为关键因素的今天,拥有高效率的Wind RiverWorkbench调试工具无疑是明智的选择。