代码调试篇:gdb调试快速入门指南
Posted csstormq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码调试篇:gdb调试快速入门指南相关的知识,希望对你有一定的参考价值。
代码调试篇(1):gdb调试快速入门指南
Author:StormQ
Monday, 25. February 2019 10:31PM
启动 gdb
启动一个不带参数的进程
命令:
# executable-file 为可执行文件的路径
$ gdb <executable-file>
示例:
# 启动一个不带参数的可执行程序 main,位于./samples目录下
$ gdb ./samples/main
启动一个带参数的进程
命令:
# executable-file 为可执行文件的路径
# arg1 为可执行文件的第一个参数
# argn 为可执行文件的第n个参数
$ gdb --args <executable-file> <arg1> <argn>
示例:
# 启动一个带参数的可执行程序 main,位于./samples目录下
# 假设程序参数只有一个,为配置文件 ./config.txt
$ gdb --args ./samples/main ./config.txt
调试一个正在运行的进程
命令:
$ gdb
# process-id为要调试进程的进程ID
(gdb) attach <process-id>
示例:
$ gdb
# 假设要调试进程的进程ID为8888
(gdb) attach 8888
使用命令文件调试进程
命令:
# command-file为存放gdb命令的文件
# 为了便于区分,该文件使用.gdb结尾(当然也可以使用其他格式结尾,比如:.txt)
$ gdb -x <command-file>
示例:
$ gdb -x main.gdb
假设 main.gdb 文件的内容为:
# 指定可执行文件的路径
file ./samples/main
# 设置可执行文件的参数(如果有的话)
set args ./config.txt
# 启动进程
start
# 继续执行
c
调试进程
查看进程信息
- 查看进程ID、可执行文件路径、当前目录
命令:
# info命名可缩写为i
(gdb) info proc
示例:
(gdb) i proc
process 11356
cmdline = '/home/tmp/b_Og'
cwd = '/home/tmp'
exe = '/home/tmp/b_Og'
从输出结果中可以看出,进程ID为 11356,进程启动项为 /home/tmp/b_Og,当前目录(即启动gdb时所在的目录)为 /home/tmp,可执行程序为 /home/tmp/b_Og。
- 查看所有线程
命令:
(gdb) i threads
示例:
(gdb) i threads
Id Target Id Frame
* 1 Thread 0x7ffff7fce740 (LWP 1549) "main" 0x00007ffff7626c1d in nanosleep ()
at ../sysdeps/unix/syscall-template.S:84
2 Thread 0x7ffff6f42700 (LWP 1553) "main" 0x00007ffff7626c1d in nanosleep ()
at ../sysdeps/unix/syscall-template.S:84
打印结果中Id
列左侧带*的为当前线程,ID
列为 gdb 自定义的线程ID,Target Id
为真实的线程ID(这里有两个线程,线程ID分别为:1549、1553),Frame
为线程的当前帧(包含:线程运行到什么位置了等信息)。
- 查看断点当前所在的线程
命令:
(gdb) p $_thread
示例:
(gdb) p $_thread
$1 = 1
注意: 打印结果所显示的线程ID(这里是1)是 gdb 内部自定义的(即i threads
命令输出结果中最左侧的Id
列的值),而不是真实的线程ID。
查看堆栈信息
- 查看当前线程的堆栈信息
命令:
# 只打印堆栈信息的调用层次
(gdb) bt
# 打印堆栈信息的调用层次,并打印函数参数和局部变量的值
(gdb) bt full
示例:
(gdb) bt
#0 0x00007ffff7626c1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
#1 0x0000000000401633 in std::this_thread::sleep_for<double, std::ratio<1l, 1000l> > (
__rtime=...) at /usr/include/c++/6/thread:323
#2 0x00000000004010ad in main () at main.cpp:11
(gdb) bt full
#0 0x00007ffff7626c1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
No locals.
#1 0x0000000000401633 in std::this_thread::sleep_for<double, std::ratio<1l, 1000l> > (
__rtime=...) at /usr/include/c++/6/thread:323
__s = __r = 0
__ns = __r = 10000000
__ts = tv_sec = 0, tv_nsec = 7543688
#2 0x00000000004010ad in main () at main.cpp:11
No locals.
- 查看所有线程的堆栈信息
命令:
# 只打印堆栈信息的调用层次
(gdb) thread apply all bt
或
# 只打印堆栈信息的调用层次
(gdb) thread apply all where
示例:
(gdb) thread apply all bt
Thread 2 (Thread 0x7ffff6f42700 (LWP 1553)):
#0 0x00007ffff7626c1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
#1 0x0000000000401633 in std::this_thread::sleep_for<double, std::ratio<1l, 1000l> > (
__rtime=...) at /usr/include/c++/6/thread:323
#2 0x00000000004019d7 in ShmManager::threadFunc (this=0x61fc20) at shm_manager_sim.cpp:49
#3 0x0000000000401841 in ShmManager::<lambda()>::operator()(void) const (
__closure=0x61fc68) at shm_manager_sim.cpp:28
#4 0x0000000000401e74 in std::_Bind_simple<ShmManager::start()::<lambda()>()>::_M_invoke<>(std::_Index_tuple<>) (this=0x61fc68) at /usr/include/c++/6/functional:1391
#5 0x0000000000401dfe in std::_Bind_simple<ShmManager::start()::<lambda()>()>::operator()(void) (this=0x61fc68) at /usr/include/c++/6/functional:1380
#6 0x0000000000401dce in std::thread::_State_impl<std::_Bind_simple<ShmManager::start()::<lambda()>()> >::_M_run(void) (this=0x61fc60) at /usr/include/c++/6/thread:197
#7 0x00007ffff7b0857f in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#8 0x00007ffff761d6ba in start_thread (arg=0x7ffff6f42700) at pthread_create.c:333
#9 0x00007ffff735341d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
Thread 1 (Thread 0x7ffff7fce740 (LWP 1549)):
#0 0x00007ffff7626c1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
#1 0x0000000000401633 in std::this_thread::sleep_for<double, std::ratio<1l, 1000l> > (
__rtime=...) at /usr/include/c++/6/thread:323
#2 0x00000000004010ad in main () at main.cpp:11
(gdb) thread apply all where
Thread 2 (Thread 0x7ffff6f42700 (LWP 1553)):
#0 0x00007ffff7626c1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
#1 0x0000000000401633 in std::this_thread::sleep_for<double, std::ratio<1l, 1000l> > (
__rtime=...) at /usr/include/c++/6/thread:323
#2 0x00000000004019d7 in ShmManager::threadFunc (this=0x61fc20) at shm_manager_sim.cpp:49
#3 0x0000000000401841 in ShmManager::<lambda()>::operator()(void) const (
__closure=0x61fc68) at shm_manager_sim.cpp:28
#4 0x0000000000401e74 in std::_Bind_simple<ShmManager::start()::<lambda()>()>::_M_invoke<>(std::_Index_tuple<>) (this=0x61fc68) at /usr/include/c++/6/functional:1391
#5 0x0000000000401dfe in std::_Bind_simple<ShmManager::start()::<lambda()>()>::operator()(void) (this=0x61fc68) at /usr/include/c++/6/functional:1380
#6 0x0000000000401dce in std::thread::_State_impl<std::_Bind_simple<ShmManager::start()::<lambda()>()> >::_M_run(void) (this=0x61fc60) at /usr/include/c++/6/thread:197
#7 0x00007ffff7b0857f in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#8 0x00007ffff761d6ba in start_thread (arg=0x7ffff6f42700) at pthread_create.c:333
#9 0x00007ffff735341d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
Thread 1 (Thread 0x7ffff7fce740 (LWP 1549)):
#0 0x00007ffff7626c1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
#1 0x0000000000401633 in std::this_thread::sleep_for<double, std::ratio<1l, 1000l> > (
__rtime=...) at /usr/include/c++/6/thread:323
#2 0x00000000004010ad in main () at main.cpp:11
- 查看指定线程的堆栈信息
命令:
# 先切换到指定线程,线程ID为gdb自定义的
(gdb) thread <thread-id-by-gdb>
# 只打印堆栈信息的调用层次
(gdb) bt
示例:
(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff6f42700 (LWP 1553))]
#0 0x00007ffff7626c1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
84 in ../sysdeps/unix/syscall-template.S
(gdb) bt
#0 0x00007ffff7626c1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
#1 0x0000000000401633 in std::this_thread::sleep_for<double, std::ratio<1l, 1000l> > (
__rtime=...) at /usr/include/c++/6/thread:323
#2 0x00000000004019d7 in ShmManager::threadFunc (this=0x61fc20) at shm_manager_sim.cpp:49
#3 0x0000000000401841 in ShmManager::<lambda()>::operator()(void) const (
__closure=0x61fc68) at shm_manager_sim.cpp:28
#4 0x0000000000401e74 in std::_Bind_simple<ShmManager::start()::<lambda()>()>::_M_invoke<>(std::_Index_tuple<>) (this=0x61fc68) at /usr/include/c++/6/functional:1391
#5 0x0000000000401dfe in std::_Bind_simple<ShmManager::start()::<lambda()>()>::operator()(void) (this=0x61fc68) at /usr/include/c++/6/functional:1380
#6 0x0000000000401dce in std::thread::_State_impl<std::_Bind_simple<ShmManager::start()::<lambda()>()> >::_M_run(void) (this=0x61fc60) at /usr/include/c++/6/thread:197
#7 0x00007ffff7b0857f in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#8 0x00007ffff761d6ba in start_thread (arg=0x7ffff6f42700) at pthread_create.c:333
#9 0x00007ffff735341d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
查看/设置源码的搜索路径
- 查看源码搜索路径
命令:
(gdb) show directories
示例:
(gdb) show directories
Source directories searched: $cdir:$cwd
- 添加源码搜索路径
命令:
# source-code-path 为要添加的源码搜索路径
(gdb) directory <source-code-path>
示例:
# 添加源码搜索路径为 /usr/include/boost/
(gdb) directory /usr/include/boost/
Source directories searched: /usr/include/boost:$cdir:$cwd
- 重置为默认的源码搜索路径
命令:
(gdb) directory
示例:
(gdb) directory
Reinitialize source path to empty? (y or n) y
Source directories searched: $cdir:$cwd
查看源码/汇编
注意: 要查看源码,需要在编译时带-g
选项。
- 查看当前文件的源码
命令:
(gdb) list <line-number>
示例:
(gdb) list 1
warning: Source file is more recent than executable.
1 #include "shm_manager_sim.h"
2 #include "topic_manager_sim.h"
3
4 int main()
5
6 TopicManager::instance();
7
8 ShmManager::instance()->start();
9
10 while(true)
- 查看指定文件的源码
命令:
(gdb) list <file-name>:<line-number>
示例:
(gdb) list shm_manager_sim.h:10
5 #include <thread>
6
7 class ShmManager;
8 typedef std::shared_ptr<ShmManager> ShmManagerPtr;
9
10 class ShmManager
11
12 public:
13 static const ShmManagerPtr& instance();
14 void start();
- 上翻/下翻源码
命令:
# 上翻源码
(gdb) list -
# 下翻源码
(gdb) list
示例:
(gdb) list shm_manager_sim.h:10
5 #include <thread>
6
7 class ShmManager;
8 typedef std::shared_ptr<ShmManager> ShmManagerPtr;
9
10 class ShmManager
11
12 public:
13 static const ShmManagerPtr& instance();
14 void start();
(gdb) list -
1 #ifndef ROS_SHM_MANAGER_H
2 #define ROS_SHM_MANAGER_H
3
4 #include <memory>
(gdb) list
5 #include <thread>
6
7 class ShmManager;
8 typedef std::shared_ptr<ShmManager> ShmManagerPtr;
9
10 class ShmManager
11
12 public:
13 static const ShmManagerPtr& instance();
14 void start();
- 查看指定函数的汇编代码
命令:
# 只打印汇编代码
(gdb) disas <function-name>
# 打印汇编代码和对应的源码
(gdb) disas /m <function-name>
示例:
(gdb) disas main
Dump of assembler code for function main():
0x0000000000401046 <+0>: push %rbp
0x0000000000401047 <+1>: mov %rsp,%rbp
0x000000000040104a <+4>: sub $0x20,%rsp
0x000000000040104e <+8>: mov %fs:0x28,%rax
0x0000000000401057 <+17>: mov %rax,-0x8(%rbp)
0x000000000040105b <+21>: xor %eax,%eax
0x000000000040105d <+23>: callq 0x405248 <TopicManager::instance()>
0x0000000000401062 <+28>: callq 0x401776 <ShmManager::instance()>
0x0000000000401067 <+33>: mov %rax,%rdi
0x000000000040106a <+36>: callq 0x401172 <std::__shared_ptr<ShmManager, (__gnu_cxx::_Lock_policy)2>::operator->()
const>
0x000000000040106f <+41>: mov %rax,%rdi
0x0000000000401072 <+44>: callq 0x40184a <ShmManager::start()>
...
(gdb) disas /m main
Dump of assembler code for function main():
5
0x0000000000401046 <+0>: push %rbp
0x0000000000401047 <+1>: mov %rsp,%rbp
0x000000000040104a <+4>: sub $0x20,%rsp
0x000000000040104e <+8>: mov %fs:0x28,%rax
0x0000000000401057 <+17>: mov %rax,-0x8(%rbp)
0x000000000040105b <+21>: xor %eax,%eax
6 TopicManager::instance();
0x000000000040105d <+23>: callq 0x405248 <TopicManager::instance()>
7
8 ShmManager::instance()->start();
0x0000000000401062 <+28>: callq 0x401776 <ShmManager::instance()>
0x0000000000401067 <+33>: mov %rax,%rdi
0x000000000040106a <+36>: callq 0x401172 <std::__shared_ptr<ShmManager, (__gnu_cxx::_Lock_policy)2>::operator->() const>
0x000000000040106f <+41>: mov %rax,%rdi
0x0000000000401072 <+44>: callq 0x40184a <ShmManager::start()>
...
查看/设置/调试断点
- 添加断点(所有线程)
命令:
(gdb) b <file-name>:<line-number>
示例:
(gdb) b main.cpp:13
Breakpoint 2 at 0x40108c: file main.cpp, line 13.
- 添加断点(指定线程)
命令:
(gdb) b <file-name>:<line-number> thread <thread-id-by-gdb>
示例:
(gdb) b main.cpp:13 thread 1
Breakpoint 5 at 0x40108c: file main.cpp, line 13.
- 在指定的内存地址添加断点
命令:
(gdb) b *<address>
示例:
(gdb) b *0x00000000004010a1
Breakpoint 7 at 0x4010a1: file main.cpp, line 13.
- 添加条件断点
命令:
(gdb) b <file-name>:<line-number> if <expresion>
示例:
(gdb) b main.cpp:14 if TopicManager::instance()->getNumSubscriptions() > 1000
Breakpoint 6 at 0x4010b2: file main.cpp, line 14.
(gdb) i breakpoints
Num Type Disp Enb Address What
5 breakpoint keep y 0x000000000040108c in main() at main.cpp:13 thread 1
stop only in thread 1
6 breakpoint keep y 0x00000000004010b2 in main() at main.cpp:14
stop only if TopicManager::instance()->getNumSubscriptions() > 1000
- 查看所有断点
命令:
(gdb) i breakpoints
示例:
(gdb) i breakpoints
Num Type Disp Enb Address What
2 breakpoint keep y 0x000000000040108c in main() at main.cpp:13
3 breakpoint keep y 0x000000000040105d in main() at main.cpp:6
- 查看指定断点
命令:
(gdb) i breakpoints <breakpoint-number>
示例:
(gdb) i breakpoints 2
Num Type Disp Enb Address What
2 breakpoint keep y 0x000000000040108c in main() at main.cpp:13
- 查看指定范围的断点
命令:
(gdb) i breakpoints <breakpoint-number>-<breakpoint-number>
示例:
(gdb) i breakpoints 2-3
Num Type Disp Enb Address What
2 breakpoint keep y 0x000000000040108c in main() at main.cpp:13
3 breakpoint keep y 0x000000000040105d in main() at main.cpp:6
- 删除所有断点
命令:
(gdb) d
示例:
(gdb) d
Delete all breakpoints? (y or n) y
(gdb) i breakpoints
No breakpoints or watchpoints.
- 删除指定断点
命令:
(gdb) d breakpoints <breakpoint-number>
示例:
(gdb) i breakpoints
Num Type Disp Enb Address What
4 breakpoint keep y 0x000000000040108c in main() at main.cpp:16
5 breakpoint keep y 0x000000000040105d in main() at main.cpp:8
(gdb) d breakpoints 4
(gdb) i breakpoints
Num Type Disp Enb Address What
5 breakpoint keep y 0x000000000040105d in main() at main.cpp:8
- 删除指定范围的断点
命令:
(gdb) d breakpoints <breakpoint-number>-<breakpoint-number>
示例:
(gdb) i breakpoints
Num Type Disp Enb Address What
5 breakpoint keep y 0x000000000040108c in main() at main.cpp:13 thread 1
stop only in thread 1
6 breakpoint keep y 0x00000000004010b2 in main() at main.cpp:14
stop only if TopicManager::instance()->getNumSubscriptions() > 1000
(gdb) d 5-6
(gdb) i breakpoints
No breakpoints or watchpoints.
- 单步调试(语句级别)
命令:
# next命令可缩写为n
(gdb) next
示例:
(gdb) l
4 L_Subscription getAllSubscription();
5
6 TopicManagerPtr g_topic_manager;
7 std::mutex g_topic_manager_mutex;
8 const TopicManagerPtr& TopicManager::instance()
9
10 if (!g_topic_manager)
11
12 std::lock_guard<std::mutex> lock(g_topic_manager_mutex);
13 if (!g_topic_manager)
(gdb) n
10 if (!g_topic_manager)
(gdb) n
19 return g_topic_manager;
- 单步调试(指令级别)
命令:
# nexti命令可缩写为ni
(gdb) nexti
示例:
(gdb) ni
0x0000000000405265 10 if (!g_topic_manager)
(gdb) ni
0x000000000040526a 10 if (!g_topic_manager)
- 跳入函数(语句级别)
命令:
# step命令可缩写为s
(gdb) step
示例:
(gdb) c
Continuing.
Thread 1 "main" hit Breakpoint 6, main () at main.cpp:14
14 if (TopicManager::instance()->getNumSubscriptions() > 1000)
(gdb) s
TopicManager::instance () at topic_manager_sim.cpp:9
9
(gdb) l
4 L_Subscription getAllSubscription();
5
6 TopicManagerPtr g_topic_manager;
7 std::mutex g_topic_manager_mutex;
8 const TopicManagerPtr& TopicManager::instance()
9
10 if (!g_topic_manager)
11
12 std::lock_guard<std::mutex> lock(g_topic_manager_mutex);
13 if (!g_topic_manager)
- 跳入函数(指令级别)
命令:
# stepi命令可缩写为si
(gdb) stepi
示例:
(gdb) si
TopicManager::instance () at topic_manager_sim.cpp:9
- 跳出函数
命令:
(gdb) finish
示例:
(gdb) s
TopicManager::instance () at topic_manager_sim.cpp:9
9
(gdb) finish
Run till exit from #0 TopicManager::instance () at topic_manager_sim.cpp:9
0x00000000004010b7 in main () at main.cpp:14
14 if (TopicManager::instance()->getNumSubscriptions() > 1000)
Value returned is $2 =
std::shared_ptr<TopicManager> (use count 1, weak count 0) = get() = 0x620c30
- 继续执行
命令:
# continue命令可缩写为c
(gdb) continue
示例:
(gdb) c
Continuing.
Thread 1 "main" hit Breakpoint 6, main () at main.cpp:14
14 if (TopicManager::instance()->getNumSubscriptions() > 1000)
打印/更改变量的值
- 打印变量的值(只打印一次)
命令:
# print命令可缩写为p
(gdb) print <variable-name>
示例:
(gdb) p g_shm_manager
$3 = std::shared_ptr<ShmManager> (use count 1, weak count 0) = get() = 0x620c80
- 打印变量的值(自动打印)
命令:
(gdb) display <variable-name>
示例:
(gdb) display g_shm_manager
1: g_shm_manager = std::shared_ptr<ShmManager> (use count 1, weak count 0) = get() = 0x620c80
(gdb) n
13 std::this_thread::sleep_for(std::chrono::duration<double, std::milli>(10));
1: g_shm_manager = std::shared_ptr<ShmManager> (use count 1, weak count 0) = get() = 0x620c80
- 更改变量的值
命令:
(gdb) p <variable-name>=<new-value>
示例:
(gdb) p g_shm_manager._M_ptr
$5 = (ShmManager *) 0x620c80
(gdb) p g_shm_manager._M_ptr=0
$6 = (ShmManager *) 0x0
打印函数参数/局部变量的值
- 打印函数参数的值
命令:
(gdb) i args
- 打印局部变量的值
命令:
(gdb) i locals
查看/更改寄存器的值
- 查看常见寄存器的值
命令:
(gdb) i registers
示例:
(gdb) i registers
rax 0x0 0
rbx 0x0 0
rcx 0x7ffff7626c1d 140737343810589
rdx 0x0 0
rsi 0x7fffffffd8e0 140737488345312
rdi 0x0 0
rbp 0x7fffffffd930 0x7fffffffd930
rsp 0x7fffffffd910 0x7fffffffd910
r8 0x0 0
r9 0x7ffff6f42700 140737336583936
r10 0x1381 4993
r11 0x0 0
r12 0x400f50 4198224
r13 0x7fffffffda10 140737488345616
r14 0x0 0
r15 0x0 0
rip 0x4010b2 0x4010b2 <main()+108>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
- 查看所有寄存器的值
命令:
(gdb) i registers all
- 查看指定寄存器的值
命令:
(gdb) p $<register-name>
示例:
(gdb) p $rcx
$9 = 140737343810589
- 更改指定寄存器的值
命令:
(gdb) p $<register-name>=<new-value>
示例:
(gdb) p $rcx
$9 = 140737343810589
(gdb) p $rcx=1
$10 = 1
(gdb) p $rcx
$11 = 1
查看内存的值
- 查看内存的值
命令:
(gdb) x/<n><f><u> <address>
注:
-
n 表示要打印多少个单元(1个单元是指即参数u所指定的字节数),默认值为1。如果为正数,那么打印从地址addr后面的;否则打印从地址addr前面的。
-
f 表示输出格式,默认值为
x
(十六进制)。其他可选值:i
,机器指令,忽略输出单元的大小;d
,(有符号的)十进制;u
,(无符号的)十进制;o
,八进制;t
,二进制;a
,十六进制,省略最前面的0;s
,字符串,且默认输出单元大小为b
(bytes)。 -
u 表示输出单元大小,默认值为
w
(Words,4 bytes)。其他可选值:b
,bytes;h
,Halfwords,2 bytes;g
,Giant words,8 bytes。**注意:**执行命令x
时,如果没有显示指定输出单元大小,会默认使用上次所指定的值。
示例:
# 查看起始地址为0x7fb00008f0后面的4字节的内容,并以十六进制的格式输出
(gdb) x/wx 0x7fb00008f0
调用C/C++函数
- 调用C/C++函数
命令:
(gdb) call <function-name>
示例:
(gdb) call TopicManager::instance()->subscribe()
$13 = true
如果你觉得本文对你有所帮助,欢迎关注公众号,支持一下!
以上是关于代码调试篇:gdb调试快速入门指南的主要内容,如果未能解决你的问题,请参考以下文章