linux下GDB调试

16次阅读

共计 10443 个字符,预计需要花费 27 分钟才能阅读完成。

  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—

正文完
 0