GDB的non-stop模式

线程调试必杀技 - GDB的non-stop模式

作者:破砂锅

开源的GDB被广泛使用在Linux、OSX、Unix和各种嵌入式系统(例如手机),这次它又带给我们一个惊喜。

多线程调试之痛

调试器(如VS2008和老版GDB)往往只支持all-stop模式,调试多线程程序时,如果某个线程断在一个断点上,你的调试器会让整个程序freeze,直到你continue这个线程,程序中的其他线程才会继续运行。这个限制使得被调试的程序不能够像真实环境中那样运行--当某个线程断在一个断点上,让其他线程并行运行。

GDBv7.0引入的non-stop模式使得这个问题迎刃而解。在这个模式下,

  • 当某个或多个线程断在一个断点上,其他线程仍会并行运行
  • 你可以选择某个被断的线程,并让它继续运行

让我们想象一下,有了这个功能后

  • 当其他线程断在断点上时,程序里的定时器线程可以正常的运行了,从而避免不必要得超时
  • 当其他线程断在断点上时,程序里的watchdog线程可以正常的运行了,从而避免嵌入式硬件以为系统崩溃而重启
  • 可以控制多个线程运行的顺序,从而重现deadlock场景了。由于GDB可以用python脚本驱动调试,理论上可以对程序在不同的线程运行顺序下进行自动化测试。

因此,non-stop模式理所当然成为多线程调试“必杀技”。这2009年下半年之后发布的Linux版本里都带有GDBv7.0之后的版本。很好奇,不知道VS2010里是不是也支持类似的调试模式了。

演示GDB的non-stop模式

让破砂锅用一个C++小程序在Ubuntu Linux 09.10下demo这个必杀技。虽然我的demo使用命令行版gdb,如果你喜欢图形化的调试器,Eclipse2009年5月之后的版本可以轻松的调 用这个功能,详情参见Eclipse参见http://live.eclipse.org/node/723

1. 编译以下程序nonstop

1 // gdb non-stop mode demo
 2 // build instruction: g++ -g -o nonstop nonstop.cpp -lboost_thread
 3 
 4 #include <iostream>
 5 #include <boost/thread/thread.hpp>
 6 
 7 struct op
 8 {
 9         op(int id): m_id(id) {}
10 
11         void operator()()
12         {
13                 std::cout << m_id << " begin" << std::endl;
14                 std::cout << m_id << " end" << std::endl;
15         }
16 
17         int m_id;
18 };
19 
20 int main(int argc, char ** argv)
21 {
22         boost::thread t1(op(1)), t2(op(2)), t3(op(3));
23         t1.join(); t2.join(); t3.join();
24         return 0;
25 }
26

2. 把一下3行添加到~/.gdbinit来打开non-stop模式

set target-async 1
set pagination off
set non-stop on

3. 启动gdb,设断点,运行.可以看到主线程1是running,3个子线程都断在断点上,而不是只有一个子线程断在断点上.

~/devroot/nonstop$ gdb ./nonstop
GNU gdb (GDB) 7.0-ubuntu
Reading symbols from /home/frankwu/devroot/nonstop/nonstop...done.
(gdb) break 14
Breakpoint 1 at 0x402058: file nonstop.cpp, line 14.
(gdb) break 24
Breakpoint 3 at 0x401805: file nonstop.cpp, line 24.
(gdb) run
Starting program: /home/frankwu/devroot/nonstop/nonstop
[Thread debugging using libthread_db enabled]
[New Thread 0x7ffff6c89910 (LWP 2762)]
[New Thread 0x7ffff6488910 (LWP 2763)]
1 begin
Breakpoint 1, op::operator() (this=0x605118) at nonstop.cpp:14
14                  std::cout << m_id << " end" << std::endl;
2 begin
Breakpoint 1, op::operator() (this=0x605388) at nonstop.cpp:14
14                  std::cout << m_id << " end" << std::endl;
[New Thread 0x7ffff5c87910 (LWP 2764)]
3 begin
Breakpoint 1, op::operator() (this=0x605618) at nonstop.cpp:14
14                  std::cout << m_id << " end" << std::endl;
(gdb) info threads
  4 Thread 0x7ffff5c87910 (LWP 2764)  op::operator() (this=0x605618) at nonstop.cpp:14
  3 Thread 0x7ffff6488910 (LWP 2763)  op::operator() (this=0x605388) at nonstop.cpp:14
  2 Thread 0x7ffff6c89910 (LWP 2762)  op::operator() (this=0x605118) at nonstop.cpp:14
* 1 Thread 0x7ffff7fe3710 (LWP 2759)  (running)

4. 让线程3继续运行,注意我顾意把主线程1也continue,这是我发现的workaround,否则gdb不能切回thread 1.

(gdb) thread apply 3 1 continue

Thread 3 (Thread 0x7ffff6488910 (LWP 2763)):
Continuing.

Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
Continuing.
Cannot execute this command while the selected thread is running.
2 end
[Thread 0x7ffff6488910 (LWP 2763) exited]

warning: Unknown thread 3.

Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
Continuing.
Cannot execute this command while the selected thread is running.
(gdb) info threads
  4 Thread 0x7ffff5c87910 (LWP 2764)  op::operator() (this=0x605618) at nonstop.cpp:14
  2 Thread 0x7ffff6c89910 (LWP 2762)  op::operator() (this=0x605118) at nonstop.cpp:14
* 1 Thread 0x7ffff7fe3710 (LWP 2759)  (running)

5. 让另外两个线程继续运行而结束,主线程断在第24行,最后结束.

(gdb) thread apply 4 2 1 continue

Thread 4 (Thread 0x7ffff5c87910 (LWP 2764)):
Continuing.

Thread 2 (Thread 0x7ffff6c89910 (LWP 2762)):
Continuing.

Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
Continuing.
Cannot execute this command while the selected thread is running.
3 end
1 end
[Thread 0x7ffff5c87910 (LWP 2764) exited]
[Thread 0x7ffff6c89910 (LWP 2762) exited]

Breakpoint 3, main (argc=1, argv=0x7fffffffe348) at nonstop.cpp:24
24          return 0;

(gdb) continue
Thread 1 (Thread 0x7ffff7fe3710 (LWP 2759)):
Continuing.

Program exited normally.

参考资料

Debugging with GDB

Reverse Debugging, Multi-Process and Non-Stop Debugging Come to the CDT

时间: 2024-10-03 21:41:16

GDB的non-stop模式的相关文章

gdb 调试程序

要调试生成的可执行程序,必须在生成的时候加入-g选项,生成可以调试的代码 例如:gcc -o test a.c b.c -g 这样gcc就会在链接的时候加入一些用于调试的符号 在生成可以调试的可执行程序后,使用gdb命令进入调试模式 1 [email protected]:/home/leo/test/project/classes# gdb test 2 GNU gdb (Ubuntu 7.10-1ubuntu2) 7.10 3 Copyright (C) 2015 Free Software

[skill][gdb] gdb 多线程调试

中文快速入门: http://coolshell.cn/articles/3643.html 进阶: 多线程怎么调试: 分 all-stop 和 non-stop 两个模式. all-stop 模式下,一个断点.所以线程全部终止运行. 使用 set non-stop on命令可以进入non-stop模式.其他线程不会受到一个线程停止的影响. 例如:non-stop模式下设置了一个中断: 其他线程正常运行. (gdb) info thread Id Target Id Frame 5 Thread

GCC编译器和GDB调试器常用选项

http://blog.csdn.net/u014328976/article/details/46745349 GCC编译器 gcc hello.c -o hello                   #将hello.c编译成hello可执行文件 gcc -E hello.c -o hello.i              #将hello.c 转换成预处理后的文件hello.igcc -S hello.c -o hello.S              #将hello.c 转换成汇编文件 h

GDB调试命令小结

1.启动调试 前置条件:编译生成执行码时带上 -g,如果使用Makefile,通过给CFLAGS指定-g选项,否则调试时没有符号信息.gdb program //最常用的用gdb启动程序,开始调试的方式gdb program core //用gdb查看core dump文件,跟踪程序core的原因gdb program pid //用gdb调试已经开始运行的程序,指定pid即可gdb attach pid //用gdb调试已经开始运行的程序,指定pid即可 2.调试命令(1)执行命令模式-bat

Linux下gdb调试(tui)

1 处于TUI模式的GDB 为了以TUI模式运行GDB,可以在调用GDB时在命令行上指定-tui选项,或者处于非TUI模式时在GDB中使用Ctrl+X+A组合键.如果当前处于TUI模式,后一种命令方式就会使你离开TUI模式. 在TUI模式中,GDB窗口划分为两个子窗口——一个用于输入GDB命令,而另一个用于查看源代码. 例如: 源代码为ins.c #include <stdio.h> int x[10], y[10], num_inputs, num_y = 0; void get_args(

Linux多进程和多线程的一次gdb调试实例

转载https://typecodes.com/cseries/multilprocessthreadgdb.html 1 原文整理 默认设置下,在调试多进程程序时gdb只会调试主进程.gdb7以上的版本(gdb --version)支持多进程调试,只需要设置好follow-fork-mode(fork追踪模式)以及detach-on-fork(指示GDB在fork之后是否断开某个进程的调试)即可. 这两个参数的设置命令分别是:set follow-fork-mode [parent|child

C++ gdb调试

cmake和gdb调试程序 由于出发点是想要在cmake后使用gdb,因此先写一下cmake和gdb的简单的一个流程,此部分转自:WELEN 1.  cmake支持gdb的实现,首先在CMakeLists.txt下加入SET(CMAKE_BUILD_TYPE "Debug") 在下面加入:SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")SET(CMAKE_CXX_FLAGS_RELEASE

小代码 linux小操作 待添加

ctags 查看代码 makefile make 维护项目 i  开始写 esc  u 撤销 vim 默认打开的模式为命令行模式 插入模式为正常的编辑模式 底行模式  :wq GG           TO FIRST LINE HEAD shift+G    TO LAST DD           TO CTRL+X P               TO pate something P               TO s+4 $        TO LAST s+6 ^        T

gdb7.10.1的交叉编译以及远程调试

引子:    因为程序使用了第三方提供的openssl库,该openssl库是在官方源码的基础上进行定制,使用3G AT命令来收发数据,现在需要升级openssl的库,但是没有该库的定制后的源码.那么要搞清楚定制支持3G AT命令的机制,一个思路是研究openssl的收发数据机制,另一个思路就是用GDB来调试现有程序,去跟踪openssl如何和3G AT进行交互.第一个思路,要么是openssl提供架构设计文档,openssl只提供API和对应数据结构的文档,大多数开源都是如此,要么就是走读op