线程调试必杀技 - 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

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

  1. GDB技巧:使用终端界面模式

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 简 ...

  2. GDB调试命令小结

    1.启动调试 前置条件:编译生成执行码时带上 -g,如果使用Makefile,通过给CFLAGS指定-g选项,否则调试时没有符号信息.gdb program //最常用的用gdb启动程序,开始调试的方 ...

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

    中文快速入门: http://coolshell.cn/articles/3643.html (关于多线程的部署说的并不太对) 进阶: 多进程相关概念: inferiors 是什么? http://m ...

  4. GDB 调试 一些命令

    1. gdb查看指定地址的内存地址的值:examine 简写 x-----使用gdb> help x 来查看使用方式     x/ (n,f,u为可选参数)n: 需要显示的内存单元个数,也就是从 ...

  5. gdb 调试程序

    要调试生成的可执行程序,必须在生成的时候加入-g选项,生成可以调试的代码 例如:gcc -o test a.c b.c -g 这样gcc就会在链接的时候加入一些用于调试的符号 在生成可以调试的可执行程 ...

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

    http://blog.csdn.net/u014328976/article/details/46745349 GCC编译器 gcc hello.c -o hello                 ...

  7. Gdb远程调试Linux内核遇到的Bug

    知识共享许可协议本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/gdb-bug 本博客同步在http://www.cn ...

  8. 【疑难杂症】gdb调试多线程程序报错:interrupted system call

    一. cmake生成可调试版本的程序,该内容参考自https://www.linuxidc.com/Linux/2014-03/98622.htm 具体内容如下: 1, 使用CMAKE编译确实很方便. ...

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

    转载https://typecodes.com/cseries/multilprocessthreadgdb.html 1 原文整理 默认设置下,在调试多进程程序时gdb只会调试主进程.gdb7以上的 ...

  10. Linux下gdb调试(tui)

    1 处于TUI模式的GDB 为了以TUI模式运行GDB,可以在调用GDB时在命令行上指定-tui选项,或者处于非TUI模式时在GDB中使用Ctrl+X+A组合键.如果当前处于TUI模式,后一种命令方式 ...

随机推荐

  1. 有很多功能交给IIS实现就好了啊,之前干嘛要自己写?

    比方说设置默认主页啊,错误页伪静态之类的

  2. 使用libsvm对MNIST数据集进行实验

    使用libsvm对MNIST数据集进行实验 在学SVM中的实验环节,老师介绍了libsvm的使用.当时看完之后感觉简单的说不出话来. 1. libsvm介绍 虽然原理要求很高的数学知识等,但是libs ...

  3. ArrayList与Vector、HashMap与HashTable

    摘自api: 1.ArrayList与Vector: 原文:This class(ArrayList) is roughly equivalent to Vector, except that it ...

  4. UIKIT网页基本结构学习

    没办法,哈哈,以后一段时间,如果公司没有招到合适的运维研发, 啥啥都要我一个人先顶上了~~~:) 也好,可以让人成长. UIKIT,BOOTSTRAP之类的前端,搞一个是有好处的,我们以前即然是用了U ...

  5. 【网络流24题】No.4 魔术球问题 (二分+最小路径覆盖)

    [题意] 假设有 n 根柱子, 现要按下述规则在这 n 根柱子中依次放入编号为 1, 2, 3, ¼的球.( 1)每次只能在某根柱子的最上面放球.( 2)在同一根柱子中,任何 2 个相邻球的编号之和为 ...

  6. CAS单点登录配置[5]:测试与总结

    终于要结束了... 测试 1 我们同时打开Tomcat6和Tomcat7,如果报错请修改. 打 开浏览器,输入http://fighting.com/Client1,进入CAS登录界面,这里我们先输入 ...

  7. ANDROID_MARS学习笔记_S02重置版_001_Hander\Looper\Message\Thread\ThreadLocal

    一. * class LooperThread extends Thread { * public Handler mHandler; * * public void run() { * Looper ...

  8. PCB快速打样规范

    基本情况 板材为FR-4,板厚1.6mm    板材铜厚为1/2oz,成品铜厚为1oz(加工过程中的沉铜工艺会让铜层增加厚度) 绿油白字    喷锡工艺    最小孔内铜厚1.27um    电铜18 ...

  9. 【Xamarin开发 Android 系列 6】 Android 结构基础(上)

    原文:[Xamarin开发 Android 系列 6] Android 结构基础(上) 前面大家已经熟悉了什么是Android,而且在 [Xamarin开发 Android 系列 4] Android ...

  10. Jquery 获取表单值如input,select等方法

    1 if($("input[name=item][value='val']").attr('checked')==true) //判断是否已经打勾 name即控件name属性,va ...