首页课程动态就业师资
新闻栏目背景新闻栏目背景

武汉粤嵌教你怎么学习嵌入式开发调试的方法

武汉嵌入式培训2018-10-15 14:27:12

    在嵌入式项目的开发中,受限于资源和工具,对程序的调试往往没有PC环境下的那么容易和轻松。而在嵌入式开发中,因为设备和环境的多样性、复杂性更易遇到各种奇怪的问题,这就产生了一个矛盾。为了定位和解决程序的问题,往往需要花费很大的精力,这就是最近一个礼拜我的经历。排查一个偶然才会出现的段错误,最后也才只是定位到线程栈被改写的位置,而没有定位到改写它的代码段,所以还是一筹莫展。这里总结一下用过和可用的调试方法与经验。
    最简单:printf
    在合理关键的路径位置添加打印信息是最简单,有时也是最有效的方法。如果程序的问题总是在固定的代码位置复现,那么通过在路径上添加打印信息就是最快速的定位问题出现在哪一代码行的方法。除了直接使用printf函数外,可以借鉴日志文件的记录格式来获取更多的信息,下面是我自己常用来替代日志记录的宏封装,可以指定打印信息的级别以便于分析和筛选:
武汉嵌入式培训
    宏中的level在使用时直接填写想要的信息级别就可以,比如常用的:DBG,ERR,WRN等,使用示例:
武汉嵌入式培训
    最实用:日志文件
    printf是输出到标准输出或者终端的,需要人为地实时进行跟踪或者保存,否则就会失去这些信息。对于中大型的项目来说,大量地使用printf来进行跟踪和调试是不现实的,也不可能用眼睛去扫描那不断刷新的打印屏幕。程序运行过程的日志文件记录是最实用最规范的用来跟踪程序运行路径的方法。
    实现一套日志文件记录接口没有什么难度,最简单的只需要将上面宏中的printf改成fprintf就可以,因此在嵌入式开发中,往往不同的项目常常会实现自己的日志记录接口。这只是一种轻量的重复劳动,倒问题不大,除了不利于程序的移植外。此外日志记录还是有不少细节需要考虑的,比如:循环覆盖写、不同线程或进程的同步写等问题,有时实现很容易会疏漏。这里推荐一个用C语言实现开源日志库:zlog,它提供了线程安全的日志记录接口和独立的日志接口行为配置文件,因此在不修改项目代码的情况下,你可以通过修改外部的zlog日志格式和行为配置文件来改变日志记录的格式和是否记录到日志文件中。
获取线程ID和名称
    在多线程的程序中,获取线程的ID有现成的接口,但是获取线程的名称没有。在Linux2.6及更低版本的linux中,线程是使用clone系统调用创建进程来实现的,此时pthread的线程ID和线程本身的进程ID是不一致的。这里提供一个参考的实现来获取线程ID和名称:
武汉嵌入式培训
    关于gettid参考linux中的man手册,注意其中的NOTES:
武汉嵌入式培训
    最直接:gdb+gdbserver+core
    在linux上最常用的调试工具就是gdb,它也绝对是最给力的调试利器。因此嵌入式开发中遇到棘手的问题是,首先想到的就是gdb工具。使用gdb进行嵌入式程序的调试有三种途径:
    交叉编译gdb
    如果开发板上的资源(内存和flash)充足的话,可以考虑下载gdb的源代码直接交叉编译得到可在开发板上运行的gdb工具,然后在编译嵌入式程序时加-g参数以保留调试符号信息,就可以直接在开发板上进行gdb的调试了。
    gdbserver远程调试
    交叉编译出的gdb一般会比较大,我这边使用arm-none-linux-gnueabi工具链编出的stripped后的都有3.3M。考虑到资源情况就需要使用gdbserver远程调试的方法,详细的步骤参考前文:使用gdbserver远程调试。这里使用时的说明几个注意点:
    gdb和gdbserver的版本必须是一致的,PC上运行的应该是编译工具链提供的gdb。
    PC上加载的程序和库应该是debug版本的,即not stripped的,以提供符号和代码信息。
    PC上使用的库的版本应该和开发板上使用的库版本一致,否则会出现库不匹配的warning。
    PC上使用gdb时首先应该设置好相应的环境变量及信号处理方式,以有效控制程序行为。
    PC上可以通过写gdb的配置文件来实现环境和信号处理方式的设置,使用示例,下面是ak-gdbinit配置文件:
武汉嵌入式培训
    使用时的命令如下,假设配置文件在/tftpboot目录下:
武汉嵌入式培训
    分析core文件
    像我在项目中遇到的问题:程序运行10几个小时或者几天后偶尔才出现段错误,这让我使用gdbserver时很郁闷,开着gdb+gdbserver等了两天都不出现段错误,而一不小心把gdb给关了就前功尽弃了。这种场景就是使用gdb+core文件调试的最佳选择了。
    首先你得确定你的问题会不会生成core文件,SIGSEGV信号产生时,linux系统会生成core文件,所以我可以使用这种方法来调试。参考《APUE》P235可以知道还有以下信号的默认动作是终止+core:
    SIGABRT
    SIGBUS
    SIGEMT
    SIGFPE
    SIGILL
    SIGIOT
    SIGQUIT
    SIGSEGV
    SIGSYS
    SIGTRAP
    SIGXCPU
    SIGXFSZ
    然后就是设置保存core文件的环境和使用,参考之前的:Linux系统中core文件调试方法。
    gdb加载core文件时如果出现下面的警告:
武汉嵌入式培训
    也许只是因为你使用的是带调试信息的程序,而开发板上生成core文件的是stripped后的程序,所以不用担心。
    使用gdb调试时,对于多线程,以下的一些gdb命令是比较有用的:
武汉嵌入式培训

免费预约试听课

x

长按复制微信号
13352868059
点击确定到微信添加老师

确定