目录
前段时间换了一个工作,也是做嵌入式开发的,但工作后发现其开发环境实在是太简陋了,一是没有统一的服务器进行编译,二是没有gdb调试。在做项目过程中如果出现segmentation fault 错误,就很难排查,如果是添加/修改了少量代码,那查起来也相对容易些,但若是几个人同时开发,在最后合并代码编译出来的程序出现这种问题,那就很麻烦了。因为没有gdb调试,所以在出现问题时,只能靠加打印来进行排查(时间花费就多了很多,真不知道他们一直以来是怎么开发的)。终于在做完一个项目后,空出了一段时间来搞这个arm下的gdb调试环境(最后写出了一篇doc文档,但他们好像也没有拿去分享,尴尬了^_^)。
1)安装gdb
因为编译程序用的是虚拟机,然而虚拟机上没有安装对应的gdb(如果有安装的,这一步可以忽略),开发板程序用的交叉编译器为:arm-linux-gnueabihf,要调试程序则需要安装对应的arm-linux-gnueabihf-gdb,步骤如下:
- 下载源码:wget http://ftp.gnu.org/gnu/gdb/gdb-7.8.1.tar.gz
- tar xzvf gdb-7.8.1.tar.gz
- cd gdb-7.8.1; ./configure --target=arm-linux-gnueabihf --prefix=/usr/bin/arm-linux-gnueabihf-gdb
- make; sudo make install
- sudo vim /etc/profile, 添加:export PATH=$PATH:/usr/bin/arm-linux-gnueabihf-gdb/bin
备注:--target=arm-linux-gnueabihf 指定平台(需要跟当前已经安装gcc,g++一样,如虚拟机已经安装的arm-linux-gnueabihf-g++);--prefix 指定生成的目录,可自行创建,最后将gdb添加到系统搜索路径下:PATH=$PATH:/usr/bin/arm-linux-gnueabihf-gdb/bin
2)编译选项-g
因为编译用的是虚拟机里的Eclipse集成开发环境,所以需要添加-g编译选项,如下图所示:
1)配置core
若要系统在程序发生Segmentation fault时生成core 文件,需要配置一下,设置生成core文件的大小,若如下图所示,在登录开发板后,输入命令:ulimit –c 进行查看,系统默认不生成core文件。
配置成:ulimit –c unlimited,设置生成的core文件大小不限制。
2)拷贝core
在程序出现Segmentation fault时,生成了core文件,如下图:
可看到/bin 目录下,已经有了core文件。将core拷贝出来即可进行调试,拷贝方法:自行实现一个TCP客户端和服务端,将服务端放到开发板上运行,用客户端去请求下载文件。具体操作在文末介绍。
3)调试core
在下载了core文件后,就可以结合debugt程序进行调试了,命令:arm-linux-gnueabihf-gdb xxx core,案例1:数组越界导致的死机
从打印的调用栈可以看到程序死在HiLink_TopicNameManager.cpp:122行,初始化函数publishTopicInit,查看代码后发现,新定义的枚举类型加在了最后(代码做了简化处理),如:
而定义数组的大小只有DOWNLOAD_MAX_COUNT,如果访问array[DOWNLOAD_XXX]必将导致数组越界造成死机。
1)挂载问题
若是能将一个目录挂载到IPBOX上,再将生成的core文件指定到挂载目录里,这样就不用从开发板上拷贝core文件。
命令: mount -t nfs 192.168.2.115:/home/daikin/nfsv4 /home -o rw,nolock,tcp
将虚拟机上的nfsv4目录挂载到开发板的home目录下,mount命令列出所有的挂载分区,可以看到nfsv4已经成功挂载到/home目录下。但出现了一个问题:
挂载后的分区没有写权限。如果有写权限就可以指定core文件的生成路径,可以用 echo "/home/core-%e-%p" > /proc/sys/kernel/core_pattern,指定在/home目录下生成带程序名(%e)和进程号(%p)的core文件。试了网上搜索的方法,没有解决,有兴趣的同学可研究一下,若解决了请告知我一下。
2)拷贝core问题
因挂载方法行不通,就只能自行实现一个简单的TCP客户端和服务端。注意:编译服务端程序时要用: arm-linux-gnueabihf-g++,用wget 方法,获取server:
server即为tcp服务端,用./server ip port,即IPBOX ip地址和端口号。
在虚拟机上用客户端连接服务端进行文件下载即可,如:
./client_get 192.168.2.111:9898/core,命令固定格式为:./client_get 服务端ip:端口号/文件名
但是遇到了一个问题:
在下载core文件过程中,在下载到100M或200M左右,server程序就被kill掉,打印如下:
打印显示没有可用缓存,系统把server程序kill掉了。这样下载的core文件是不完整的,但如果用gdb调试时也能打印出完整的堆栈信息也可以不用再关心。
解决办法:下载过程中,手动进行缓存释放。即在服务端添加缓存释放操作,添加代码如下
// param[in] : drop
// 1: 释放页面缓存
// 2: 释放目录文件和inodes
// 3: 释放所有缓存(页面缓存,目录文件和inodes)
void dropCache(int drop)
{
int ret = 0;
int fd = open("/proc/sys/vm/drop_caches", O_RDWR);
char dropData[4] = {0};
int dropSize = snprintf(dropData, sizeof(dropData), "%d", drop);
ret = write(fd, dropData, dropSize);
close(fd);
}
我在服务端添加如下,每发送100M数据后进行一次释放缓存动作,调用如下:
这样就可以成功下载core了。
后记:在这个公司呆了三个多月我就跳到其他公司去了,这个文档也分享给了部门的几个人,不知他们会不会去用^_^。