gdb调试多线程演示
来源:互联网 发布:windows源代码谁写的 编辑:程序博客网 时间:2024/05/21 17:07
GDB多线程调试的基本命令
- info threads
显示当前可调式的所有线程,每个线程会有一个GDB为其分配的ID。后面调试时可能会使用。 - thread ID
切换当前调试的线程为指定ID的线程 - break thread_test.c:123 thread all
在所有线程中相应的行上设置断点 - thread apply ID1 ID2 command
让一个或者多个线程执行GDB命令command - thread apply all command
让所有被调试的线程执行GDB命令command - set scheduler-locking off|on|step
默认情况下,在使用step或者continue命令调试当前被调试线程的时,其他线程也是同时执行的。
off:不锁定任何线程,也就是所有的线程都执行。(默认值)
on:只有当前被调试的线程会执行。
step:在单步的时候,除了next过一个函数的情况以外,只有当前线程会执行。
GDB多线程调试的实现细节
- thread_list这个表存储了当前可调试的所有线程的信息。
- 函数add_thread_silent或者add_thread(不同版本GDB不同)用来向thread_list列表增加一个线程的信息。
- 函数delete_thread用来向thread_list列表删除一个线程的信息。
- 函数info_threads_command是被命令info threads调用的,就是显示thread_list列表的信息。
- 函数thread_command是被命令thread调用,切换当前线程最终调用的函数是switch_to_thread,这个函数会先将当前调试线程变量inferior_ptid,然后对寄存器和frame缓冲进行刷新。
- 函数thread_apply_command被命令thread apply调用,这个函数的实际实现其实很简单,就是先切换当前线为指定线程,然后调用函数execute_command调用指定函数。
GDB多线程调试示例
在pthread_example.c中:
#include <pthread.h>#include <stdio.h>#include <sys/time.h>#include <string.h>#define MAX 10pthread_t thread[2];pthread_mutex_t mut;int number=0;int i;void *thread1(){ printf ("thread1 : I'm thread 1\n"); for (i = 0; i < MAX; i++) { printf("thread1 : number = %d\n",number); pthread_mutex_lock(&mut); number++; pthread_mutex_unlock(&mut); sleep(2); } printf("thread1 :主函数在等我完成任务吗?\n"); pthread_exit(NULL);}void *thread2(){ printf("thread2 : I'm thread 2\n"); for (i = 0; i < MAX; i++) { printf("thread2 : number = %d\n",number); pthread_mutex_lock(&mut); number++;{ printf("thread2 : I'm thread 2\n"); for (i = 0; i < MAX; i++) { printf("thread2 : number = %d\n",number); pthread_mutex_lock(&mut); number++; pthread_mutex_unlock(&mut); sleep(3); } printf("thread2 :主函数在等我完成任务吗?\n"); pthread_exit(NULL);}void thread_create(void){ int temp; memset(&thread, 0, sizeof(thread)); /*创建线程*/ if ((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0) printf("线程1创建失败\n"); else printf("线程1创建成功\n"); if ((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0) printf("线程2创建失败\n"); else printf("线程2创建成功\n");}void thread_wait(void){ /*等待线程结束*/ if (thread[0] != 0) { pthread_join(thread[0], NULL); printf("线程1已经结束\n"); } if (thread[1] != 0) { pthread_join(thread[1], NULL); printf("线程2已经结束\n"); }}int main(){ /*用默认属性初始化互斥锁*/ pthread_mutex_init(&mut, NULL); printf("我是主函数,我正在创建线程。\n"); thread_create(); printf("我是主函数,我正在等待线程完成任务。\n"); thread_wait(); return 0;}
在Makefile中:(为了方便,将生成文件名改为test.o)
test.o : 多线程编程例子.c gcc 多线程编程例子.c -o test.o -g -pthread //注意添加 -g 和 -pthread.PHONY:cleanclean: rm -f *.o
输出结果:
[sjt@www 多线程编程]$ ./test.o 我是主函数,我正在创建线程。 线程1创建成功 线程2创建成功 我是主函数,我正在等待线程完成任务。 thread2 : I'm thread 2 thread2 : number = 0 thread1 : I'm thread 1 thread1 : number = 1 thread1 : number = 2 thread2 : number = 3 thread1 : number = 4 thread2 : number = 5 thread1 : number = 6 thread1 : number = 7 thread2 : number = 8 thread1 : number = 9 thread2 : number = 10 thread1 :主函数在等我完成任务吗? 线程1已经结束 thread2 :主函数在等我完成任务吗? 线程2已经结束
练练手,开始调试:
利用b main与b thread1与b thread2在三个函数入口建立断点(输入info b 可知三个断点编号分别是1, 2, 3)
输入r程序开始运行。
运行到编号为1的断点main处后,输入info threads命令,只会显示一个线程ID,那就是主线程main
(gdb) b mainBreakpoint 1 at 0x8048867: file 多线程编程例子.c, line 82.(gdb) b thread1Breakpoint 2 at 0x804862a: file 多线程编程例子.c, line 14.(gdb) b thread2Breakpoint 3 at 0x80486bf: file 多线程编程例子.c, line 32.(gdb) rStarting program: /home/sjt/code/多线程编程/test.o [Thread debugging using libthread_db enabled]Breakpoint 1, main () at 多线程编程例子.c:8282 pthread_mutex_init(&mut, NULL);(gdb) info threads* 1 Thread 0xb7ff16c0 (LWP 3468) main () at 多线程编程例子.c:82
输入c运行到下一断点处,再输入info threads查看线程信息发现,main没有了,变为另外三个线程,并且gdb还给他们分配了新的ID。我们可以看到熟悉的thread1。另外两个线程(个人推测,看名字类似于clone功能)我下来查阅。
(gdb) cContinuing.我是主函数,我正在创建线程。[New Thread 0xb7ff0b70 (LWP 3474)]线程1创建成功[Switching to Thread 0xb7ff0b70 (LWP 3474)]Breakpoint 2, thread1 () at 多线程编程例子.c:1414 printf ("thread1 : I'm thread 1\n");(gdb) info threads[New Thread 0xb75efb70 (LWP 3475)] 3 Thread 0xb75efb70 (LWP 3475) 0x00b34d58 in clone () from /lib/libc.so.6* 2 Thread 0xb7ff0b70 (LWP 3474) thread1 () at 多线程编程例子.c:14 1 Thread 0xb7ff16c0 (LWP 3468) 0x00b34d58 in clone () from /lib/libc.so.6
输入c继续运行到下一断点处,并且输入info threads继续查看线程信息。又发现了不同之处。发现换了三个线程的信息。并且其中之一就是thread2线程。另外两个下来继续查阅(发现LWP和上面的LWP一样,恐怕有什么规律?下来查查)。
Breakpoint 3, thread2 () at 多线程编程例子.c:3232 printf("thread2 : I'm thread 2\n");1: number = 1(gdb) info threads* 3 Thread 0xb75efb70 (LWP 3475) thread2 () at 多线程编程例子.c:32 2 Thread 0xb7ff0b70 (LWP 3474) 0x00110424 in __kernel_vsyscall () 1 Thread 0xb7ff16c0 (LWP 3468) 0x00110424 in __kernel_vsyscall ()
输入dispaly number来查看每步number的值。并且进行线程之间的切换,来试试互斥锁是否好用
先走到加锁的地方,记住此时number的值为3
(gdb)display number (gdb) n thread1 : number = 1 thread2 : I'm thread 2 34 for (i = 0; i < MAX; i++) 1: number = 2 (gdb) n 36 printf("thread2 : number = %d\n",number); 1: number = 2 (gdb) n thread1 : number = 2 //注意此时thread1与thread2的number都是2, thread2 : number = 2 //说明默认set scheduler-locking off 37 pthread_mutex_lock(&mut); 1: number = 3
就这样一直下去,next,发现线程thread1与线程thread2同时运行的,虽然有互斥锁的存在,但是由于我们只关注调试线程thread2,并且是单步导致,破坏了互斥锁的原子性。本来线程thread1修改number时,thread2不会修改,或者输出number内容。最终,失去了互斥锁的作用。number也不断增加,出现number>10的现象。
(gdb) nthread1 : number = 338 number++;1: number = 4(gdb) n39 pthread_mutex_unlock(&mut);1: number = 5(gdb) nthread1 : number = 540 sleep(3);1: number = 6(gdb) nthread1 : number = 6thread1 : number = 734 for (i = 0; i < MAX; i++)1: number = 8(gdb) n36 printf("thread2 : number = %d\n",number);1: number = 8(gdb) nthread1 : number = 8thread2 : number = 837 pthread_mutex_lock(&mut);1: number = 9(gdb) n38 number++;1: number = 9(gdb) n39 pthread_mutex_unlock(&mut);1: number = 10(gdb) nthread1 : number = 1040 sleep(3);1: number = 11(gdb) nthread1 : number = 11nthread1 :主函数在等我完成任务吗?线程1已经结束[Thread 0xb7ff0b70 (LWP 3474) exited]34 for (i = 0; i < MAX; i++)1: number = 12Missing separate debuginfos, use: debuginfo-install libgcc-4.4.7-18.el6.i686(gdb) n44 printf("thread2 :主函数在等我完成任务吗?\n");1: number = 12(gdb) info threads* 3 Thread 0xb75efb70 (LWP 3475) thread2 () at 多线程编程例子.c:44 1 Thread 0xb7ff16c0 (LWP 3468) 0x00110424 in __kernel_vsyscall ()(gdb) nthread2 :主函数在等我完成任务吗?45 pthread_exit(NULL);1: number = 12(gdb) info threads* 3 Thread 0xb75efb70 (LWP 3475) thread2 () at 多线程编程例子.c:45 1 Thread 0xb7ff16c0 (LWP 3468) 0x00110424 in __kernel_vsyscall ()(gdb) n线程2已经结束[Thread 0xb75efb70 (LWP 3475) exited]
总结
在测试GDB调试多线程的代码中,我认为最大的收获是,通过调试。发现了许多影藏在调试信息中的信息。找到了许多以前没有注意的地方。
比如:
- LWP每次有存在相同的ID。
- 另外在一个子线程中,另外两个名字相同的线程有什么作用。
- 单步调试竟然导致互斥锁失效。
还有许多需要去了解。
参考博客:
[1]: http://blog.csdn.net/liigo/article/details/582231/
[2]: http://www.oschina.net/question/565065_68060
[3]: http://www.cnblogs.com/armlinux/archive/2010/05/28/2396997.html
- gdb调试多线程演示
- GDB 调试演示
- GDB调试演示
- GDB 调试演示
- GDB调试演示
- GDB 调试演示
- GDB 调试演示
- 【Linux】GDB调试演示过程
- 使用gdb调试多线程
- GDB多线程调试
- gdb多线程调试1
- gdb多线程调试2
- gdb 调试多线程
- gdb调试多线程
- GDB多线程调试
- gdb调试多线程程序
- GDB 多线程调试
- linux GDB 调试多线程
- springBoot+druid+mybitis整合
- Plug-in 向导页
- 那些将来应该在博客中出现的东西
- Netty源码阅读
- js刷新页面的方式
- gdb调试多线程演示
- InnoDB O_DIRECT选项漫谈(一)
- 数据库索引原理及优化
- 来说说组件(Components)模式
- Tensorflow CIFAR-10训练例子报错解决
- PLC编程时三个注意事项
- 安装DPDK
- 垂直居中
- JavaScript深拷贝和浅拷贝数组