最近需要调试多线程程序,就试着在网上搜了些资料。正好整理了一个例子方便自己理解和记忆
static void* WorkThread1(void *threadId)
{
int64 tid = (int64)threadId;
int i = 0;
while(i <= 60)
{
i++;
sleep(1);
printf("Hello [%d]\n",i); //b2
}
printf("After Sleep, [%lld]\n", tid); //b3
Myfunc1();
return NULL; //建议在此设置一个断点
}
static void* WorkThread2(void *threadId)
{
int64 tid = (int64)threadId;
int i = 0;
while(i <= 60)
{
i++;
sleep(1);
printf("Hello [%d]\n",i); //b4
}
printf("After Sleep, [%lld]\n", tid); //b5
Myfunc2();
return NULL; //建议在此设置一个断点
}
int32 Task15()
{
pthread_t threadId1, threadId2;
int32 ret = 0;
if ((ret = pthread_create(&threadId1, NULL, WorkThread1, (void*)1)))
{
perror("Thread 1 create");
return 0;
}
if ((ret = pthread_create(&threadId2, NULL, WorkThread2, (void*)2)))
{
perror("Thread 2 create");
}
ret = pthread_join(threadId1, NULL); //主线程在此等待
if(ret != 0)
{
printf("tid:[%lld]\n",(uint64)threadId1);
perror("Thread Join");
}
ret = pthread_join(threadId2, NULL);
if(ret != 0)
{
printf("tid:[%lld]\n",(uint64)threadId1);
perror("Thread Join");
}
return 0;
}
则通过gdb进行上述程序的多线程调试如下
>gdb mytest
>r 15 //假设程序在执行时需要带参数15 才会运行Task15(./mytest 15), 则需要在gdb调试时,使用r 15命令
[New Thread0x7ffff63a8710 (LWP 13315)]
[New Thread0x7ffff5ba7710 (LWP 13316)]
Hello [1]
Hello [1]
Hello [2]
Hello [2]
Hello [3]
Hello [3]^C//从运行中退出来,我认为这个时候所有线程都被suspend住
>
Program receivedsignal SIGINT, Interrupt.
0x00007ffff7bc8da5 inpthread_join () from /lib64/libpthread.so.0>info threads //查看线程运行状态,带*号的为当前线程
3 Thread 0x7ffff5ba7710 (LWP 13316) 0x00007ffff68a607d in nanosleep () from/lib64/libc.so.6
2 Thread 0x7ffff63a8710 (LWP 13315) 0x00007ffff68a607d in nanosleep () from/lib64/libc.so.6
* 1 Thread0x7ffff7fbc320 (LWP 13312) 0x00007ffff7bc8da5 in pthread_join () from /lib64/libpthread.so.0
>thread //查看当前所在线程
[Current thread is 1(Thread 0x7ffff7fbc320 (LWP 13312))]
>bt //查看当前线程的执行位置
#0 0x00007ffff7bc8da5 in pthread_join () from/lib64/libpthread.so.0
#1 0x00000000004053e9 in Task15 () atmytest.c:837 //上述代码是从我的测试代码中抽出来的部分,行号请忽略
#2 0x00000000004087d1 in main (argc=3,argv=0x7fffffffe2f8) at ipc1_demo.c:126
>thread 2 //切换到线程2
[Switching to thread 2(Thread 0x7ffff63a8710 (LWP13315))]#0 0x00007ffff68a607d in nanosleep () from /lib64/libc.so.6
>bt //查看线程2的当前执行位置
#0 0x00007ffff68a607d in nanosleep () from /lib64/libc.so.6
#1 0x00007ffff68a5e9cin sleep () from /lib64/libc.so.6
#2 0x00000000004055fd in WorkThread1(threadId=0x1) at mytest.c:780 -----------> 从这里看出他是工作线程1
#3 0x00007ffff7bc85f0 instart_thread () from /lib64/libpthread.so.0
#4 0x00007ffff68d784d in clone () from/lib64/libc.so.6
#5 0x0000000000000000 in ?? ()
>thread 3 //切换到线程3
[Switching to thread 3(Thread 0x7ffff5ba7710 (LWP 13316))]#0 0x00007ffff68a607d innanosleep () from /lib64/libc.so.6
>bt //查看线程3的当前执行位置
#0 0x00007ffff68a607d in nanosleep () from /lib64/libc.so.6
#1 0x00007ffff68a5e9cin sleep () from /lib64/libc.so.6
#2 0x00000000004054e7 in WorkThread2 (threadId=0x2) at mytest.c:805 --------------->他是工作线程2
#3 0x00007ffff7bc85f0 instart_thread () from /lib64/libpthread.so.0
#4 0x00007ffff68d784d in clone () from/lib64/libc.so.6
#5 0x0000000000000000 in ?? ()
>b 806 thread 3 //在线程3上设置断点
>b 808 thread 3
>b 781 thread 2 //在线程2上设置断点
>b 783 thread 2 //建议在线程退出return的时候加上断点,因为如果不加,当前线程退出后,gdb就挂住了...
>info b //确认断点情况,(断点号与上面代码标注的有点偏差,且线程退出的断点未显示出来)
Num Type Disp Enb Address What
2 breakpoint keep y 0x00000000004054e7 inWorkThread2(void*) at mytest.c:806 thread 3
stop only in thread 3
3 breakpoint keep y 0x00000000004054ff in WorkThread2(void*) at mytest.c:808 thread 3
stop only in thread 3
4 breakpoint keep y 0x00000000004055fd in WorkThread1(void*) at mytest.c:781 thread 2
stop only in thread 2
5 breakpoint keep y 0x0000000000405615 inWorkThread1(void*) at mytest.c:783 thread 2
stop only in thread 2
>continue //如果直接continue,则会所有线程同时执行,2,4,两个断点,任何一个都有可能被打断
>set scheduler-locking on//通过该条命令使得:仅当前线程执行
>thread //确认当前线程
[Current thread is 3(Thread 0x7ffff5ba7710 (LWP 13316))]
>c //继续执行
>n //单步执行
>info b //查看breakpoint信息,将循环中的断点disable
Num Type Disp Enb Address What
2 breakpoint keep y 0x00000000004054e7 inWorkThread2(void*) at mytest.c:806 thread 3
stop only in thread 3
breakpoint already hit 6 times
3 breakpoint keep y 0x00000000004054ff in WorkThread2(void*) at mytest.c:808 thread 3
stop only in thread 3
4 breakpoint keep y 0x00000000004055fd in WorkThread1(void*) at mytest.c:781 thread 2
stop only in thread 2
breakpoint already hit 5 times
5 breakpoint keep y 0x0000000000405615 inWorkThread1(void*) at mytest.c:783 thread 2
stop only in thread 2
> disable 2 将b2禁止掉>c
Continuing.
Hello [9]
Hello [10]
Hello [11]
Hello [12]
Hello [13]
Hello [14]
Hello [15]
…Breakpoint 3,WorkThread2 (threadId=0x2) at mytest.c:808
808 printf("After Sleep,[%lld]\n", tid);
>until 814 //执行至814 b5
>s //进入函数myfunc
>finish //完成函数,至函数返回语句
>n //从函数返回
>thread 2//切换到thread 2
>c//执行thread 2
>
Hello [7]
Hello [8]
Hello [9]
Hello [10]
Hello [11]
Hello [12]
Hello [13]
Hello [14]
Hello [15]
Hello [16]
Hello [17]
Hello [18]
Hello [19]
Hello [20]
Hello[21]
Hello[22]
Hello[23]
Hello[24]
Hello[25]
Hello[26]
Hello[27]
Hello[28]
Hello[29]
Hello[30]
Hello[31]
Hello[32]
Hello[33]
…>thread 1 //切回到主线程
>set scheduler-lockingoff //线程并行执行
>c
>c
>c
Program exited with code 01.
进程结束,调试结束
另外:
调试多线程时,由于可能还没来的及调试线程,线程就已经完成工作退出了, 因此,可以使用上述例子中的方法,在每个线程中,一起来先让他sleep一段时间,待gdb进入,将所有线程中断,可以调试时,再跳过sleep进行真正的调试。
在对多线程的守护进程进行调试时,可以直接gdb进入调试模式,之后attach <pid>,其中pid为待调试进程,一旦attach到该进程,该进程的所有线程都将进入suspend状态,此时就可如上所述一样,设置断点进行调试。