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

jackxiang 2015-4-24 17:30 | |
背景:多进程有set follow-fork-mode=child,多线程怎么办呢?尤其是有那个时钟啥的,共享变量,这儿提供了一方法,我也没看懂,先放这儿。应该是gdb新的版本有这个功能。

作者:破砂锅

开源的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

摘自:http://www.cnblogs.com/dongzhiquan/archive/2011/07/09/2101578.html
一个 pthread_cancel 引起的线程死锁【整理转载】:
http://www.cnblogs.com/mydomain/archive/2011/08/15/2139830.html

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:http://jackxiang.com/post/7982/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2015-4-24 17:55
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]