基于Valgrind和在线调试器实现Linux应用程序调试系统的设计

1.引言
随着嵌入式系统处理能力的显著提高,系统设计越来越复杂,对软件调试提出了新的挑战 。传统的调试技术及工具已经难以满足嵌入式软件系统的调试要求 。本文介绍了利用工具软件Valgrind 调试嵌入式Linux 应用程序的技术,嵌入式Linux 继承了传统Linux 功能强大、高效稳定等特点,近年来在嵌入式领域取得了飞速的发展,并且成为主流嵌入式操作系统之一 。研究嵌入式Linux 应用程序的调试技术,不仅能有效提升系统的稳定性,而且从技术和应用角度而言也具有一定的价值 。
嵌入式 Linux 应用程序主要使用c++/c++开发,多数语法和语义错误可以由编译器发现,但有些错误编译器无法发现,它们往往在程序运行后表现出来 。这些错误主要包括:内存泄露、引用未初始化的指针、不恰当释放内存空间、内存越界访问以及使用已经释放的内存空间等等 。内存操作在嵌入式编程中大量使用,并且有关内存操作的错误经常出现并且难以被发现 。这些错误都会影响系统性能和稳定性,甚至造成瘫痪,所以必须避免它们在程序中出现 。通常采用人工方法查找这些错误,当程序规模较大时,工作量将十分巨大,并且效率低下 。Valgrind 能够监视应用程序的运行并且发现上述问题,利用Valgrind 对应用程序进行调试能够显著降低人工工作量,提高效率 。
2. Valgrind 的工作原理
Valgrind 是运行在Linux 上的多用途代码剖析和内存调试软件 。主要包括Memcheck、Callgrind、Cachegrind 等工具,每个工具都能完成一项任务调试、检测或分析 。可以检测内存泄露、线程违例和Cache 的使用等 。
Valgrind 基于仿真方式对程序进行调试,它先于应用程序获取实际处理器的控制权,并在实际处理器的基础上仿真一个虚拟处理器,并使应用程序运行于这个虚拟处理器之上,从而对应用程序的运行进行监视 。应用程序并不知道该处理器是虚拟的还是实际的,已经编译成二进制代码的应用程序并不用重新进行编译,Valgrind 直接解释二进制代码使得应用程序基于它运行,从而能够滴水不漏地检查内存操作时可能出现的错误 。在嵌入式应用程序开发中,c 或c++是最为常用的语言,由于这两种语言非常灵活的特性,使得在编程时很容易出现上述错误 。因此,为了提高嵌入式系统的可靠性,可以将Valgrind 引入到嵌入式程序的开发过程中,利用它对应用程序进行调试,从而达到高效、准确去除错误的目的 。
3.Valgrind 在程序排错中的应用
3.1 嵌入式软件基本开发流程
嵌入式系统是一个资源受限的系统,直接在嵌入式硬件平台上进行软件的开发与调试是不合适的,开发与调试通常需要在PC 机上进行,然后通过交叉编译,将程序编译成可以运行在目标平台上的二进制代码,最后将代码下载到目标硬件平台运行 。由于嵌入式Linux 的内核和系统调用与运行在PC 上的Linux 几乎完全一样,因此嵌入式Linux应用程序的调试可以在一台装有Linux 的PC 机上进行 。如果能在PC 机的Linux 系统上正常运行,则在目标平台上基本也能正常运行 。
3.2 调试过程
本文提出了一种利用Valgrind和在线调试器配合使用的嵌入式程序调试方法,利用它可以结合两种不同软件调试工具的特点,对复杂的嵌入式软件系统进行调试 。图1 是嵌入式软件开发与调试的过程 。软件设计人员利用已有的开发工具编写源代码,通过编译器、汇编器、链接实时运行库文件,生成目标代码 。Valgrind 对目标代码进行仿真执行,调用相关工具进行调试、分析和监测;调试器通过调试器的JTAG 调试接口下载代码,同时对微处理器进行控制,设置软件断点,单步运行等功能查看程序的运行情况 。

基于Valgrind和在线调试器实现Linux应用程序调试系统的设计
文章插图
这里假设 PC 机所用的处理器是x86 指令系统,目标平台处理器是ARM 指令系统 。基于Valgrind 的嵌式程序调试过程包括以下几个步骤:
(1)在一台装有Linux 操作系统的PC 机上安装ValgrindValgrind
是一个遵循GPL 条例的开源项目,用户获取到它的源程序后自行编译安装 。
获取到源程序包后,使用“cd”命令进入包含源代码的目录;然后输入“ 。/configure”进行配置;配置完毕后输入“make”对源程序进行编译;编译完成后执行“make install”将编译好的程序和一些数据文件安装到系统的相应位置 。至此,Valgrind 安装完成 。
(2)将嵌入式程序的源代码编译成运行于x86 处理器的代码格式
使用当前 PC 机Linux 系统中附带的gcc 或g++编译器对源程序进行编译,如果程序较大,拥有较多源代码文件,可以通过编写makefile 文件的方式来对文件进行组织,使用make命令对源程序进行编译 。编译时给编译器加上-g 参数,使得编译器添加调试信息到代码中 。
(3)启动Valgrind

基于Valgrind和在线调试器实现Linux应用程序调试系统的设计
文章插图
其中 –leak-check 参数表示让Valgrind 检查内存泄露错误,其他更多的参数项可以参考Valgrind 的相关文档 。
(4)根据Valgrind 的检查结果修改源程序
(5)调试完毕后用ARM 编译器编译成ARM 处理器的代码格式
以下几个小节将通过具体的应用技术来具体说明Valgrind 在嵌入式应用程序调试技术中的应用 。
3.3 起重机安全监控系统应用程序的调试
起重机安全监控系统使用 Linux2.6 内核作为操作系统,应用程序使用C++语言开发,通过图形界面向用户提供起重机的各种工作情况,如吊臂姿态,吊载重量等等 。图形界面使用Qt 图形系统开发 。程序源代码*有超过200 处的地方进行了动态内存分配 。
将应用程序编译成可以运行在 x86 平台上的二进制代码后,启动Valgrind 执行应用程序,在执行过程中如果发现错误,Valgrind 会将有关信息打印在屏幕上,信息的格式如下所示:
==13380== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==13380== at 0x40046EE: malloc (vg_replace_malloc.c:149)
==13380== by 0x804838B: f1 (aaa.c:4)
==13380== by 0x80483FB: main (aaa.c:20)
“==13380==”表示进程号,一般不用考虑;第一行(“Invalid write. 。.”)给出了错误的种
类,这里指发现内存泄露错误 。第二行给出了错误发生的地址,以及相关函数 。第三行给出了发生错误所在的函数的名称 。第四行指出发生错误的函数被调用的地方在main()函数中 。
应用程序使用 Valgrind 进行调试发现了所有内存泄露的故障,根据Valgrind 所打印的提示信息到源代码中相应位置进行修改,排除故障,使得应用程序的可靠性得到了增强,使得整个起重机安全监控系统能够非常稳定、可靠的运转 。
4.结语
本文介绍了一个功能强大的工具软件 Valgrind,它可以检测多种内存方面的错误 。内存错误是嵌入式程序设计中最常见的错误,使用Valgrind 对基于嵌入式Linux 的应用程序进行错误检测,可以迅速准确的检查出各种致命错误,确保及时得到改正,避免在投入运行后出现故障,极大的提高了调试效率 。调试工作在PC 机上进行,调试完毕后使用目标平台处理器的编译器对源代码进行交叉编译,从而生成可以运行在目标硬件平台上的二进制代码,整个调试过程非常方便 。在资源消耗方面,由于Valgrind 采用虚拟处理器方式运行应用程序,并且使用了V-bit 对每一位数据进行监视,因此使用Valgrind 调试程序时会大量占用内存,并且程序的运行速度要比在实际处理器上运行时的速度慢20 到30 倍 。
本文作者的创新点:将Valgrind 工具引入到嵌入式软件调试中,和常用的嵌入式软件调试设备有机地结合成一体,能够迅速准确的查出各种致命的软件错误,提高调试效率,加快产品上市,节省了开发成本,带来实在的经济效益 。
【基于Valgrind和在线调试器实现Linux应用程序调试系统的设计】 责任编辑:gt

    推荐阅读