Linux应用程序调试技巧

段错误调试      

        Linux程序在运行时出现段错误,对于程序员来说是非常头疼的。这里列举了几种能够快速定位到段错误的方法。

       一 .printf大法输出日志

   1 .在应用程序中使用printf,直接打印出当前数据的值,观测值是否正确
    2.将日志打印到文件中,且增加打印级别,可在编译或者运行程序时根据需要选择打印级别。该方法需要实现自己的my_printf函数
    3.查看打印日志文件的方法:  taif -f /path/to/log 

        二.GDB及远程GDB 

        本地调试使用gdb,远程调试使用gdbserver。

    1.在编译时,需要选择-g参数,以使用gdb
    2.在本地运行程序,可直接使用gdb运行程序,常用命令如下:
        r  #运行程序
        b  file:line #file文件的line行处设置断点,程序运行到这里会暂停
        b  line  #在当前文件的line行设置断点
        c  #继续运行程序,直到下一个断点,
        n  #单步运行程序,只执行一行代码
        l  #查看程序源码,只显示10行
        p /x val  #查看内存变量val的值,/x表示以16进制显示
        bt #栈帧回溯,可查看当前栈的调用过程
    3.运行在嵌入式设备端的程序,无法直接使用gdb,可使用gdb+gdbserver,
        a)首先需要交叉编译gdbserver工具,以在目标板上运行
        b)确保主机能够和目标板正常通信
        c)在目标板上执行  gdbserver hostIp:port a.out
        d)在主机上执行 arm-linux-gdb a.out  #注意,此处需要用交叉编译工具链中的gdb执行程序
            进入gdb环境后,执行
            (gdb) target remote boardIp:port  a.out #建立通信
            若出现无法读取库文件的警告,使用如下命令进行设置库文件路径
            (gdb) set solib-search-path /my/path/to/compile/gcc/lib 

        e) 经过以上步骤,可以执行目标板上的程序了,同样在主机上执行
            (gdb) c 
            即可启动程序,其余指令可按照本地调试的方法执行

        参考链接:  https://blog.csdn.net/zhaoxd200808501/article/details/77838933?spm=1001.2014.3001.5506

gdb调试的示例代码如下,core.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char * argv [ ])
{	
	int *p = NULL;

	p = malloc(4);
	if (p == NULL)
	{
		perror("malloc failed");
	}
    printf("address [0x%p]\r\n", p);
	
	free(p);	
	free(p);	/* 重复释放*/
	
    return 0;
}

编译命令为: gcc  core.c -g  #gdb加入-g参数表示保留调试信息

gdb启动命令为: gdb ./a.out

该指令的执行结果如下,进入gdb环境后,执行r,可以看到程序发生段错误,可以看到释放了两次,但是无法知道具体发生在哪个函数的哪一行。使用bt回溯栈信息,根据最后一行可知段错误发生在core.c的第17行,对照源码,就能够找到段错误的地方。

        三.栈帧回溯

        使用backstrace进行栈帧回溯,在 发生段错误后,程序能够把当前的栈帧信息打印出来,便于我们查找错误。需要注意的是, 在编译二进制文件时,可以使用-g参数,保留调试信息,在生成可执行文件时,使用stip去掉调试信息;在编译二进制文件为可执行文件时,使用链接选项-rdynamic参数,用于动态加载动态符号表,在打印栈信息时才能打印出函数名。

        在发生段错误后,需要用到命令objdump和addr2line来查找发生错误的地方。常见 的命令有:

        objdump -S a.out    #反汇编可执行程序
        objdump -S main.o   #反汇编二进制文件
        addr2line -e backtrace 0x400a3e #读取行号

         演示demo文件backstrace.c源代码如下:

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

 
/* Obtain a backtrace and print it to stdout. */
void
print_trace (void)
{
  void *array[10];
  char **strings;
  int size, i;
 
  size = backtrace (array, 10);
  strings = backtrace_symbols (array, size);
  if (strings != NULL)
  {
    //打印栈帧信息
    printf ("Obtained %d stack frames.\n", size);
    for (i = 0; i < size; i++)
      printf ("%s\n", strings[i]);
  }
 
  free (strings);
}

void sigHandler(int sig, siginfo_t *info, void *arg)
{
	print_trace ();
	exit(0);
}
 
int main (void)
{
	struct sigaction sa={0};
  //用于注册段错误回调
	sa.sa_handler = sigHandler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_ONSTACK;
	sigaction(11, &sa, NULL);
	*((int*)0x0) = 123456;
  while(1){
    sleep(1);
  }
  return 0;
}

        对于以上文件,我们需要编译为可执行文件。编译命令如下:

gcc backstrace.c -c -g #输出二进制文件backstrace.o
gcc backstrace.o -rdynamic #使用多台加载符号表的方式输出可执行文件a.out
stip a.out  #缩减调试信息

·        执行结果如下图所示。

 

        根据以上信息,我们可以看到,段错误发生在main函数中。在main函数入口偏移0x7b的位置处。现在我们使用objdump来找到这一行代码。使用命令objdump -S backstrace.o可查看反汇编代码,main函数的反汇编代码如下:

 

        main函数的入口地址为0xde,根据前面的错误偏移地址为0x7b,将入口地址加上错误偏移地址,0xde+0x7b =  0x159,正好对应发生段错误的位置:*((int*)0x0) = 123456; 

        四.coredump分析

                开启coredump后,应用程序在发生段错误后,会生成coredump文件,我们可以根据coredump进行分析,找出错误的地方。步骤如下:

    1.打开coredump
        ulimit -c 0         ## 不产生core文件. 如果结果为0,说明当程序崩溃时,系统并不能生成coredump。
        ulimit -c 1024      ## 设置core文件最大为1024k  
        ulimit -c unlimited ## 不限制core文件大小  
    2.设置coredump生成路径
        # 例如:将所有的core文件生成到/corefile目录下,文件名的格式为core-命令名-pid-时间戳. 
        echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
    3.生成coredump文件后,使用gdb调试coredump文件,命令
        gdb exe-file core-file
        进入gdb环境后,使用bt命令,查看栈帧
        (gdb) bt
        栈帧回溯可参考gdb调试小节。 

网络程序调试 

        对应网络应用程序,可以使用网络抓包工具抓包进行分析查看网络数据通信情况。嵌入式环境下,没有可视化的界面进行抓包,可使用命令行抓包工具tcpdump进行抓包,然后将抓包文件放到windows下分析。步骤如下:

    1.交叉编译tcpdump工具,将生成的库文件和可执行文件拷贝到目标板中
    2.在目标板执行 
        tcpdump -n -i eth0 -w /path/to/test.cap 
        该命令表示抓取eth0网口的网络数据并保存到test.cap文件中
        tcpdump更多命令参考
            https://blog.csdn.net/ljbcharles/article/details/122256796
    3.将生成的test.cap文件,拷贝到windows中,可用wireshark进行分析,能够更详细的展示数据帧的信息

     备注:熟悉网络通信协议对于调试网络应用程序很有帮助。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值