linux下GDB调试
linux下GDB是一个非常强大的调试工具,但是他不像vs一样具有强大的图形界面,基本都靠命令来进行调试,对于新手来说也算是个坎。下面就跟大家一起探究一下gdb这个强大的调试工具。
1.开启core
1.1 查看core是否开启
$ulimit -c 0
0:表示关闭,不会生成core文件;否则此值说明core文件的最大限制;
1.2 打开开启core
打开/etc/profile
$sudo gedit /etc/profile
在文件末尾加上:
ulimit -S -c unlimited > /dev/null 2>&1
wq!保存退出。然后让设置立即生效:
$source /etc/profile
然后输入命令:
$ulimit -c unlimited
core文件已经开启。unlimited为不限制core文件大小。
1.3 设置保存core文件的路径和命名方式
$sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t
/tmp目录为core文件的保存路径,core文件命名格式为:
%%:相当于% %p:相当于<pid> %u:相当于<uid> %g:相当于<gid> %s:相当于导致dump的信号的数字 %t:相当于dump的时间 %e:相当于执行文件的名称 %h:相当于hostname
2.编译
2.1 先准备一份简单的源码:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include<pthread.h> void printinfo(const char* pstr) { printf("%s\n", pstr); } void* threadproc1(void* arg) { int i = 0; while(i < 20)// { printf("I am threadproc1, tid:%u, i:%d\n", pthread_self(), i++); sleep(1); } return NULL; } void* threadproc2(void* arg) { int i = 0; while(i < 20)// { printf("I am threadproc2, tid:%u, i:%d\n", pthread_self(), i++); sleep(1); } return NULL; } int main(int narg, const char** args) { const char* pstr = args[1]; printinfo(pstr); pthread_t tid1,tid2; pthread_create(&tid1,NULL,threadproc1,NULL);//创建线程1 pthread_create(&tid2,NULL,threadproc2,NULL);//创建线程2 pthread_join(tid1,NULL);//等待线程1 printf("thread1 joined\n"); pthread_join(tid2,NULL);//等待线程2 printf("thread2 joined\n"); free((void*)pstr); char* pend = "main end\n"; printf(pend); free(pend); return 0; }
2.2 编译源码
将主程序命名为gdbtest.cpp,使用g++进行编译:
$g++ -g -c gdbtest.cpp $g++ -o gdbtest gdbtest.o -lpthread
注意,编译时需要添加-g选项,通知编译器生成gdb调试信息。代码编译完成后,将在当前目录下生成gdbtest可执行程序。
3 gdb调试
3.1 gdb调试常用命令说明
1.gdb $(filename)
启动gdb调试,$(filename)也可以使用file命令指定;
2.file $(filename)
设置gdb调试的文件名
3. set args $(arg1) $(arg2) $(arg3) ...
设置main函数输入参数
4.b/break linenum
设置断点,linenum为行号
5.r/run $(arg1) $(arg2) $(arg3) ...
开始运行gdb调试,参数如果没有或者已经设置,可以不输入
6.p/print $(var)
打印变量
7.display $(var)
显示变量
8.n/next
单步执行
9.bt
显示栈帧以及局部变量
10.s/step
进入子函数
11.up
退出当前函数,返回上一级堆栈
12.c/continue
继续执行
13.list
输出从上次调用list命令开始往后的10行程序代码
14.info threads
线程信息
15.thread id
切换调试线程
16.tbreak/tb
设置临时断点,只生效一次。
16.info break/i b
显示所有断点信息
3.2 gdb调试
使用GDB运行gdbtest
$gdb gdbtest
在37(const char* pstr = args[1];)行设置断点
(gdb)b 37 Breakpoint 1 at 0x40090c: file gdbtest.cpp, line 37.
gdb调试运行,"hello, welcome to gdb debug world!"为运行参数
(gdb) run "hello, welcome to gdb debug world!" Starting program: /home/zwu/Desktop/work/coding/transcode/gdbtest/gdbtest "hello, welcome to gdb debug world!" [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Breakpoint 1, main (narg=2, args=0x7fffffffdf08) at gdbtest.cpp:37 37 const char* pstr = args[1];
程序运行后,停止在37行断点处。
p/print:打印args[0]变量:
(gdb) p args[0] $3 = 0x7fffffffe278 "/home/zwu/Desktop/work/coding/transcode/gdbtest/gdbtest"
display:打印变量
(gdb) display args[1] 1: args[1] = 0x7fffffffe2b0 "hello, welcome to gdb debug world!"
n/next:执行单步调试
(gdb) n 38 printinfo(pstr);
s/step:进入子函数
(gdb) s printinfo (pstr=0x7fffffffe2b0 "hello, welcome to gdb debug world!") at gdbtest.cpp:9 9 printf(pstr);
up:退出当前函数,返回上一级堆栈:
(gdb) up #1 0x0000000000400924 in main (narg=2, args=0x7fffffffdf08) at gdbtest.cpp:38 38 printinfo(pstr); (gdb) n main (narg=2, args=0x7fffffffdf08) at gdbtest.cpp:41 41 pthread_create(&tid1,NULL,threadproc1,NULL);//创建线程1 1: args[1] = 0x7fffffffe2b0 "hello, welcome to gdb debug world!"
list:输出从上次调用list命令开始往后的10行程序代码
(gdb) list 36 { 37 const char* pstr = args[1]; 38 printinfo(pstr); 39 40 pthread_t tid1,tid2; 41 pthread_create(&tid1,NULL,threadproc1,NULL);//创建线程1 42 pthread_create(&tid2,NULL,threadproc2,NULL);//创建线程2 43 pthread_join(tid1,NULL);//等待线程1 44 printf("thread1 joined\n"); 45 pthread_join(tid2,NULL);//等待线程2
输出线程信息:
(gdb) info threads Id Target Id Frame * 1 Thread 0x7ffff7fdd700 (LWP 5045) "gdbtest" main (narg=2, args=0x7fffffffdf08) at gdbtest.cpp:42 2 Thread 0x7ffff77ef700 (LWP 5047) "gdbtest" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:81
bt full输出调用栈信息
(gdb) bt full
0 main (narg=2, args=0x7fffffffdf08) at gdbtest.cpp:42
pstr = 0x7fffffffe2b0 "hello, welcome to gdb debug world!" tid1 = 140737345681152 tid2 = 4196832 pend = 0x7fffffffdf00 "\002"
c/continue:继续执行
(gdb) c Continuing. [New Thread 0x7ffff77ef700 (LWP 4517)] hello, welcome to gdb debug world! I am threadproc1, tid:4152293120, i:0 [New Thread 0x7ffff6fee700 (LWP 4518)] I am threadproc2, tid:4143900416, i:0 ... I am threadproc1, tid:4152293120, i:19 I am threadproc2, tid:4143900416, i:19 [Thread 0x7ffff6fee700 (LWP 4518) exited] thread1 joined thread2 joined
程序在50行free(pend)时崩溃,以下为core dump信息:
*** Error in `/home/zwu/Desktop/work/coding/transcode/gdbtest/gdbtest': munmap_chunk(): invalid pointer: 0x00007fffffffe2b0 *** [Thread 0x7ffff77ef700 (LWP 4517) exited] ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7ffff78677e5] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7ffff7874698] /home/zwu/Desktop/work/coding/transcode/gdbtest/gdbtest[0x40099c] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7ffff7810830] /home/zwu/Desktop/work/coding/transcode/gdbtest/gdbtest[0x400759] ======= Memory map: ======== 00400000-00401000 r-xp 00000000 08:01 950010 /home/zwu/Desktop/work/coding/transcode/gdbtest/gdbtest 00600000-00601000 r--p 00000000 08:01 950010 /home/zwu/Desktop/work/coding/transcode/gdbtest/gdbtest 00601000-00602000 rw-p 00001000 08:01 950010 /home/zwu/Desktop/work/coding/transcode/gdbtest/gdbtest 00602000-00623000 rw-p 00000000 00:00 0 [heap] 7ffff65d8000-7ffff65ee000 r-xp 00000000 08:01 1054507 /lib/x86_64-linux-gnu/libgcc_s.so.1 7ffff65ee000-7ffff67ed000 ---p 00016000 08:01 1054507 /lib/x86_64-linux-gnu/libgcc_s.so.1 7ffff67ed000-7ffff67ee000 rw-p 00015000 08:01 1054507 /lib/x86_64-linux-gnu/libgcc_s.so.1 7ffff67ee000-7ffff67ef000 ---p 00000000 00:00 0 7ffff67ef000-7ffff6fef000 rw-p 00000000 00:00 0 7ffff6fef000-7ffff6ff0000 ---p 00000000 00:00 0 7ffff6ff0000-7ffff77f0000 rw-p 00000000 00:00 0 7ffff77f0000-7ffff79b0000 r-xp 00000000 08:01 1054469 /lib/x86_64-linux-gnu/libc-2.23.so 7ffff79b0000-7ffff7bb0000 ---p 001c0000 08:01 1054469 /lib/x86_64-linux-gnu/libc-2.23.so 7ffff7bb0000-7ffff7bb4000 r--p 001c0000 08:01 1054469 /lib/x86_64-linux-gnu/libc-2.23.so 7ffff7bb4000-7ffff7bb6000 rw-p 001c4000 08:01 1054469 /lib/x86_64-linux-gnu/libc-2.23.so 7ffff7bb6000-7ffff7bba000 rw-p 00000000 00:00 0 7ffff7bba000-7ffff7bd2000 r-xp 00000000 08:01 1054615 /lib/x86_64-linux-gnu/libpthread-2.23.so 7ffff7bd2000-7ffff7dd1000 ---p 00018000 08:01 1054615 /lib/x86_64-linux-gnu/libpthread-2.23.so 7ffff7dd1000-7ffff7dd2000 r--p 00017000 08:01 1054615 /lib/x86_64-linux-gnu/libpthread-2.23.so 7ffff7dd2000-7ffff7dd3000 rw-p 00018000 08:01 1054615 /lib/x86_64-linux-gnu/libpthread-2.23.so 7ffff7dd3000-7ffff7dd7000 rw-p 00000000 00:00 0 7ffff7dd7000-7ffff7dfd000 r-xp 00000000 08:01 1054441 /lib/x86_64-linux-gnu/ld-2.23.so 7ffff7fdc000-7ffff7fe0000 rw-p 00000000 00:00 0 7ffff7ff6000-7ffff7ff7000 rw-p 00000000 00:00 0 7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0 [vvar] 7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso] 7ffff7ffc000-7ffff7ffd000 r--p 00025000 08:01 1054441 /lib/x86_64-linux-gnu/ld-2.23.so 7ffff7ffd000-7ffff7ffe000 rw-p 00026000 08:01 1054441 /lib/x86_64-linux-gnu/ld-2.23.so 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] Thread 1 "gdbtest" received signal SIGABRT, Aborted. 0x00007ffff7825428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
注:gdb调试时,遇到coredump将直接输出coredump信息,不会生成core文件。
4.coredump文件调试
退出gdb,在terminal中直接运行:
$./gdbtest *** Error in `./gdbtest': free(): invalid pointer: 0x0000000000400ac6 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f83f713e7e5] /lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f83f714737a] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f83f714b53c] ./gdbtest[0x4009c1] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f83f70e7830] ./gdbtest[0x400759]
程序崩溃,并在/tmp目录下产生core-gdbtest..ubuntu.的core dump文件,如果想要解析该文件,只需输入gdb $(program) $(coredump)
$gdb gdbtest core-gdbtest.*.ubuntu.* GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from gdbtest...done. [New LWP 5183] [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Core was generated by `./gdbtest'. Program terminated with signal SIGABRT, Aborted. #0 0x00007f83f70fc428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
输入bt,查看崩溃程序的调用栈
(gdb) bt #0 0x00007f83f70fc428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54 #1 0x00007f83f70fe02a in __GI_abort () at abort.c:89 #2 0x00007f83f713e7ea in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7f83f7257ed8 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175 #3 0x00007f83f714737a in malloc_printerr (ar_ptr=<optimized out>, ptr=<optimized out>, str=0x7f83f7254caf "free(): invalid pointer", action=3) at malloc.c:5006 #4 _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:3867 #5 0x00007f83f714b53c in __GI___libc_free (mem=<optimized out>) at malloc.c:2968 #6 0x00000000004009c1 in main (narg=1, args=0x7fff510a3d48) at gdbtest.cpp:50
在没有调试信息的情况下,打开coredump堆栈,并不会直接显示core的代码行,这时可以使用disassemble打开该帧函数的反汇编代码,然后使用汇编代码来判断程序崩溃的地方:
(gdb) disassemble
Dump of assembler code for function __GI_raise:
0x00007f83f70fc3f0 <+0>: mov %fs:0x2d4,%ecx
0x00007f83f70fc3f8 <+8>: mov %fs:0x2d0,%eax
0x00007f83f70fc400 <+16>: movslq %eax,%rsi
0x00007f83f70fc403 <+19>: test %esi,%esi
0x00007f83f70fc405 <+21>: jne 0x7f83f70fc438 <__GI_raise+72>
0x00007f83f70fc407 <+23>: mov $0xba,%eax
0x00007f83f70fc40c <+28>: syscall
0x00007f83f70fc40e <+30>: mov %eax,%ecx
0x00007f83f70fc410 <+32>: mov %eax,%fs:0x2d0
0x00007f83f70fc418 <+40>: movslq %eax,%rsi
0x00007f83f70fc41b <+43>: movslq %edi,%rdx
0x00007f83f70fc41e <+46>: mov $0xea,%eax
0x00007f83f70fc423 <+51>: movslq %ecx,%rdi
0x00007f83f70fc426 <+54>: syscall
=> 0x00007f83f70fc428 <+56>: cmp $0xfffffffffffff000,%rax
0x00007f83f70fc42e <+62>: ja 0x7f83f70fc450 <__GI_raise+96>
0x00007f83f70fc430 <+64>: repz retq
0x00007f83f70fc432 <+66>: nopw 0x0(%rax,%rax,1)
0x00007f83f70fc438 <+72>: test %ecx,%ecx
0x00007f83f70fc43a <+74>: jg 0x7f83f70fc41b <__GI_raise+43>
0x00007f83f70fc43c <+76>: mov %ecx,%edx
0x00007f83f70fc43e <+78>: neg %edx
---Type <return> to continue, or q <return> to quit---