线程调试必杀技 - 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. 工作总结:文件对话框的分类(C++)

    原文地址:http://www.jizhuomi.com/software/173.html 文件对话框分为打开文件对话框和保存文件对话框,相信大家在Windows系统中经常见到这两种文件对话框.例如 ...

  2. 构建简单的 C++ 服务组件,第 1 部分: 服务组件体系结构 C++ API 简介

    构建简单的 C++ 服务组件,第 1 部分: 服务组件体系结构 C++ API 简介 熟悉将用于 Apache Tuscany SCA for C++ 的 API.您将通过本文了解该 API 的主要组 ...

  3. 【UVA 11354】 Bond (最小瓶颈生成树、树上倍增)

    [题意] n个点m条边的图 q次询问 找到一条从s到t的一条边 使所有边的最大危险系数最小 InputThere will be at most 5 cases in the input file.T ...

  4. Rewriting History with Git Rebase

    http://code.tutsplus.com/tutorials/rewriting-history-with-git-rebase--cms-23191 1. Rebasing for a Li ...

  5. Eclipse中添加android sdk javadoc和source

    在 javadoc location path中添加file:/D:/Android_SDK/sdk/docs/reference/   在 source attachment中添加为 Externa ...

  6. WordPress Tweet Blender插件跨站脚本漏洞

    漏洞名称: WordPress Tweet Blender插件跨站脚本漏洞 CNNVD编号: CNNVD-201310-645 发布时间: 2013-10-30 更新时间: 2013-10-30 危害 ...

  7. ♫【CSS】命名颜色

    color: aqua; 每个浏览器都支持一套它能识别且能正确渲染的命名颜色.这些颜色包括: aqua 浅绿色 #00ffffblack 黑色 #000000blue 蓝色 #0000fffuchsi ...

  8. 【转】EditText获取焦点不自动弹出键盘设置--失去焦点的方法,不错

    原文网址:http://zouhuajian01.blog.163.com/blog/static/1176987720121128115813176/ 当我们在activity中加入EditText ...

  9. 30个最常用css选择器解析

    转自:http://www.cnblogs.com/yiyuanke/archive/2011/10/22/CSS.html 你也许已经掌握了id.class.后台选择器这些基本的css选择器.但这远 ...

  10. Qt学习之路(1)------Qt常用类用法说明

    Qt常用类 向控制台输出文本 第一个例子,我们采用STL的方式: console.cpp #include <iostream> int main() { std::cout <&l ...