调试前准备
获取进程的内核转储(core dump)
why:最大好处是,其保存了问题发生时的状态。记录进程当前状态。
步入正题:
启动内核转储,查看当前内核转储功能是否有效,结果为0表示内核转储无效
$ ulimit -c
那么,则需要开启内核转储,该命令不限制转储文件大小。亦可设置为有限上限1G,ulimit -c 1073741842。
$ ulimit -c unlimited
$ ./a.out 生成转储文件
查看生成的转储文件
$ file core*
GDB启动,使用转储文件
$ gdb -c core.xxx ./a.out
一、GDB调试前问题汇总
1.编译出现undefined reference to 'pthread_create'
编译加上参数 -lpthread
2.apue.h找不到
导入src.3e.tar.gz 中apue/include/apue.h 到 /usr/include
3.err_sys未定义
二、GDB例子(切入)
一个调试示例
——————
源程序:tst.c
#include <stdio.h>
int func(int n){
int sum = 0,i;
for(i=0; i<7;i++){
sum += i; } return sum;}
main(){
int i;
long result = 0;
for(i=1;i<=100;i++){
result += i;}
printf("result[1-100]= %d/n",result);
printf("result[1-250]= %d/n",func(250));
}
编译生成执行文件:(Linux下)
hchen/test> cc -g tst.c -o tst
使用GDB调试:
hchen/test> gdb tst <---------- 启动GDB
GNU gdb 5.1.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-suse-linux"...
(gdb) l <-------------------- l命令相当于list,从第一行开始例出原码。
1 #include
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i
7 {
8 sum+=i;
9 }
10 return sum;
(gdb) <-------------------- 直接回车表示,重复上一次命令
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
(gdb) break 16 <-------------------- 设置断点,在源程序第16行处。
Breakpoint 1 at 0x8048496: file tst.c, line 16.
(gdb) break func <-------------------- 设置断点,在函数func()入口处。
Breakpoint 2 at 0x8048456: file tst.c, line 5.
(gdb) info break <-------------------- 查看断点信息。
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048496 in main at tst.c:16
2 breakpoint keep y 0x08048456 in func at tst.c:5
(gdb) r <--------------------- 运行程序,run命令简写
Starting program: /home/hchen/test/tst
Breakpoint 1, main () at tst.c:17 <---------- 在断点处停住。
17 long result = 0;
(gdb) n <--------------------- 单条语句执行,next命令简写。
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) n
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) c <--------------------- 继续运行程序,continue命令简写。
Continuing.
result[1-100] = 5050 <----------程序输出。
Breakpoint 2, func (n=250) at tst.c:5
5 int sum=0,i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p i <--------------------- 打印变量i的值,print命令简写。
$1 = 134513808
(gdb) n
8 sum+=i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$2 = 1
(gdb) n
8 sum+=i;
(gdb) p i
$3 = 2
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$4 = 3
(gdb) bt <--------------------- 查看函数堆栈。
#0 func (n=250) at tst.c:5
#1 0x080484e4 in main () at tst.c:24
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
(gdb) finish <--------------------- 退出函数。
Run till exit from #0 func (n=250) at tst.c:5
0x080484e4 in main () at tst.c:24
24 printf("result[1-250] = %d /n", func(250) );
Value returned is $6 = 31375
(gdb) c <--------------------- 继续运行。
Continuing.
result[1-250] = 31375 <----------程序输出。
Program exited with code 027. <--------程序退出,调试结束。
(gdb) q <--------------------- 退出gdb。
hchen/test>
好了,有了以上的感性认识,还是让我们来系统地认识一下gdb吧。
三、adb + GDB用法
3.1 静态调试
(1)通过命令ulimit -c unlimited设置系统生成Core
Dump文件
gdb+ 带符号表的可执行文件+Core Dump文件
该方式可以直接定位到错误发生的地方。
(2)直接gdb+ 带符号表的可执行文件
该方式需要启动之后运行run命令才能执行调试。
3.2 动态调试
就是用attach的方式连接到正在运行中的进程上面去,进行调试,下面是利用PC机动态调试手机上面的进程的操作步骤。
編譯小程序:
arm-none-linux-gnueabi-gcc -static HelloWorld.c -o HelloWorld
在android運行軟件-小程序
<1>adb shell ps -elf 查看所需要调试程序的进程号
<2>adb shell gdbserver :5039 --attach PID attach到对应进程号上面
<3>打开另一个终端,adb forward tcp:5039 tcp:5039
<4>運行arm-eabi-gdb
路径:
/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-gdb
<5>set solib-absolute-prefix + 符号表文件路径
<6>set solib-search-path + 带符号表可执行文件路径:动态共享库文件路径:....
<7>鏈接遠端程序target remote:5039 進行調試
四、GDB 命令(一)
gcc -g main.c -o xxxxx //在目标文件加入源代码的信息
gdb a.out
(gdb) start //开始调试
(gdb) n //一条一条执行
(gdb) step/s //执行下一条,如果函数进入函数
(gdb) backtrace/bt //查看函数调用栈帧
(gdb) info/i locals //查看当前栈帧局部变量
(gdb) frame/f //选择栈帧,再查看局部变量
(gdb) print/p //打印变量的值
(gdb) finish //运行到当前函数返回
(gdb) set var sum=0 //修改变量值
(gdb) list/l 行号或函数名 //列出源码
(gdb) display/undisplay sum //每次停下显示变量的值/取消跟踪
(gdb) break/b 行号或函数名 //设置断点
(gdb) continue/c //连续运行
(gdb) info/i breakpoints //查看已经设置的断点
(gdb) delete breakpoints 2 //删除某个断点
(gdb) disable/enable breakpoints 3 //禁用/启用某个断点
(gdb) break 9 if sum != 0 //满足条件才激活断点
(gdb) run/r //重新从程序开头连续执行
(gdb) watch input[4] //设置观察点
(gdb) info/i watchpoints //查看设置的观察点
(gdb) x/7b input //打印存储器内容,b--每个字节一组,7--7组
(gdb) disassemble //反汇编当前函数或指定函数
(gdb) si // 一条指令一条指令调试 而 s 是一行一行代码
(gdb) info registers // 显示所有寄存器的当前值
(gdb) x/20 $esp //查看内存中开始的20个数
(gdb) i threads //查看threads
(gdb) t x //切换至x线程下调试
(gdb) i r //查看寄存器状态
(gdb) i proc m //(info proc mappings 的简写)核查Mapped address
(gdb) i signals //查看所有信号量的设置情况。
(gdb) p $eax //查看eax寄存器的内容。
(gdb) generate-core-file //生成转储文件
(gdb) i p //info proc 查看进程信息
(未完待续)
四、GDB 命令(二)
attach 到进程
调试守护进程等已经启动的进程,或是调试陷入死循环而无法返回的进程时使用。
attach pid
以sleep命令为举例:
ps aux| grep sleep 查看sleep进程ID 若为17606
(gdb) attach 17606