基于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 调试接口下载代码,同时对微处理器进行控制,设置软件断点,单步运行等功能查看程序的运行情况。

嵌入式

这里假设 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

嵌入式

其中 –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 工具引入到嵌入式软件调试中,和常用的嵌入式软件调试设备有机地结合成一体,能够迅速准确的查出各种致命的软件错误,提高调试效率,加快产品上市,节省了开发成本,带来实在的经济效益。

责任编辑:gt

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分