天天看點

GDB多線程調試和死鎖break if指令ps -aux / -ef | grep xxx 檢視目前運作的程序ps -aL | grep xxx 檢視目前運作的輕量級程序(線程)pstree -p 主線程id 檢視主子線程的關系pstack 4360(主線程id)gdb attach 主線程id 将程序附加到gdb中,可以看到建立了兩個子線程檢視線程的一些資訊設定斷點 break 行号/函數名檢視斷點 info b繼續使某線程運作 thread apply 1-n(第幾個線程) n

set schedular-locking on / off

條件斷點檢視循環中的某些變量

break if指令

示例:break test.c:34 if (x & y) == 1

預設情況下我們執行到斷點處繼續執行時,所有線程都會運作。想要控制隻有目前線程運作可用上面指令的on實作。

#include <iostream>
#include <thread>
#include <chrono>

void thread1func()
{
    while(1)
    {
        std::cout << "this is thread1, ID: " << std::this_thread::get_id() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

void thread2func()
{
    while(1)
    {
        std::cout << "this is thread2, ID: " << std::this_thread::get_id() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}


int main()
{
    std::thread thd1(thread1func);
    std::thread thd2(thread2func);

    std::cout << "this is main thread" << std::endl;

    thd1.join();
    thd2.join();

    return 0;
}
           

// 編譯要添加-g選項

運作程式,指令行檢視:

ps -aux / -ef | grep xxx 檢視目前運作的程序

[[email protected] ~]# ps -aux | grep a.out

root 4360 0.0 0.1 31240 1108 pts/0 Sl+ 17:20 0:00 ./a.out

root 4366 0.0 0.1 112728 1004 pts/1 S+ 17:21 0:00 grep --color=auto a.out

ps -aL | grep xxx 檢視目前運作的輕量級程序(線程)

[[email protected] ~]# ps -aL

PID LWP TTY TIME CMD

4360 4360 pts/0 00:00:00 a.out

4360 4361 pts/0 00:00:00 a.out

4360 4362 pts/0 00:00:00 a.out

4367 4367 pts/1 00:00:00 ps

[[email protected] ~]# ps -aL | grep a.out

4360 4360 pts/0 00:00:00 a.out

4360 4361 pts/0 00:00:00 a.out

4360 4362 pts/0 00:00:00 a.out

pstree -p 主線程id 檢視主子線程的關系

[[email protected] ~]# pstree -p 4360

a.out(4360)─┬─{a.out}(4361)

└─{a.out}(4362)

[[email protected] ~]#

線程棧結構的檢視

pstack 4360(主線程id)

gdb檢視線程資訊

gdb attach 主線程id 将程序附加到gdb中,可以看到建立了兩個子線程

[[email protected] ~]# gdb attach 4360

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-119.el7

Copyright © 2013 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law. Type “show copying”

and “show warranty” for details.

This GDB was configured as “x86_64-redhat-linux-gnu”.

For bug reporting instructions, please see:

http://www.gnu.org/software/gdb/bugs/…

attach: 沒有那個檔案或目錄.

Attaching to process 4360

Reading symbols from /home/code/cpp考察點/gdb多線程/a.out…(no debugging symbols found)…done.

Reading symbols from /lib64/libpthread.so.0…(no debugging symbols found)…done.

[New LWP 4362]

[New LWP 4361]

[Thread debugging using libthread_db enabled]

Using host libthread_db library “/lib64/libthread_db.so.1”.

Loaded symbols for /lib64/libpthread.so.0

Reading symbols from /lib64/libstdc++.so.6…(no debugging symbols found)…done.

Loaded symbols for /lib64/libstdc++.so.6

檢視線程的一些資訊

(gdb) info inferiors # 檢視程序

Num Description Executable

  • 1 process 4360 /home/code/cpp考察點/gdb多線程/a.out

    (gdb) info threads # 檢視線程

    Id Target Id Frame

    3 Thread 0x7fc93ccbb700 (LWP 4361) “a.out” 0x00007fc93cd80e2d in nanosleep () from /lib64/libc.so.6

    2 Thread 0x7fc93c4ba700 (LWP 4362) “a.out” 0x00007fc93cd80e2d in nanosleep () from /lib64/libc.so.6

  • 1 Thread 0x7fc93dcd3740 (LWP 4360) “a.out” 0x00007fc93d8b0f47 in pthread_join () from /lib64/libpthread.so.0

    (gdb) bt # 檢視線程棧結構

    #0 0x00007fc93d8b0f47 in pthread_join () from /lib64/libpthread.so.0

    #1 0x00007fc93d655e37 in std::thread::join() () from /lib64/libstdc++.so.6

    #2 0x000000000040120b in main ()

    (gdb) thread 2 # 切換線程

    [Switching to thread 2 (Thread 0x7fc93c4bathread apply all700 (LWP 4362))]

    #0 0x00007fc93cd80e2d in nanosleep () from /lib64/libc.so.6

    (gdb) thread 3

    [Switching to thread 3 (Thread 0x7fc93ccbb700 (LWP 4361))]

    #0 0x00007fc93cd80e2d in nanosleep () from /lib64/libc.so.6

    (gdb)

    gdb調試多線程

設定斷點

設定斷點 break 行号/函數名

檢視斷點 info b

執行線程2的函數,執行完畢繼續運作到斷點處

繼續使某線程運作 thread apply 1-n(第幾個線程) n

(gdb) break thread1func()

Breakpoint 1 at 0x401101: file main.cc, line 9.

(gdb) thread apply 2 n

隻運作目前線程

(gdb) set scheduler-locking on

(gdb) c

所有線程并發執行

(gdb) set scheduler-locking off

(gdb) c

gdb定位死鎖

  1. thread apply all bt 列印所有線程的堆棧資訊,觀察哪些線程棧卡在lock_wait或者類似調用上,說明這幾個線程極有可能産生死鎖
  2. 利用thread ID指令切換到懷疑的線程,列印鎖的資訊,比如檢視線程3,并列印堆棧資訊
  3. 可以看到堆棧最終卡在源碼中的第53行,堆棧的第7層,我們使用frame指令檢視第7層具體資訊

    (gdb) frame 7

    #7 0x000000000040183f in DataPool::pushToPool (this=0xeab058, inputData=std::shared_ptr (count 2, weak 0) 0x7fdc480008c0) at deadlock.cpp:53

    53 std::unique_lockstd::mutex lckTwo(mutexTwo_);

    (gdb)

  4. 檢視一下lckTwo這把鎖和mutexTwo_的資訊,Owner字段表示哪個線程持有這把鎖,它是線程的lwp号,可以通過info threads檢視。

    (gdb) p lckTwo

    $1 = {_M_device = 0xeab098, M_owns = false}

    (gdb) p mutexTwo

    $2 = {std::__mutex_base = {_M_mutex = {__data = {__lock = 2, __count = 0, __owner = 18736, __nusers = 1, __kind = 0, __spins = 0, __elision = 0, __list = {__prev = 0x0, __next = 0x0}},

    __size = “\002\000\000\000\000\000\000\000\060I\000\000\001”, ‘\000’ <repeats 26 times>, __align = 2}}, }

    (gdb)

    #這裡我們可以看到線程3目前沒有擁有lckTwo這把鎖,互斥量mutexTwo_目前被lwp号為18736的線程占有,即線程2,那麼這個時候我們就找到了死鎖的具體點,通過審查線程2執行的代碼,死鎖很快就能定位出來。我們這段代碼是有意地構造死鎖條件,進行逆推。不過真實開發中出現的死鎖問題,也可以通過gdb調試結合core dump分析來解決。

繼續閱讀