Linux 使用gcore、gdb、pstack、strace命令查死鎖問題

gdb 

thread apply all bt

如果你發現有那麼幾個棧停在 pthread_wait 或者類似調用上,大致就可以得出結論:就是它們幾個兒女情長,耽誤了整個進程。

 

注意gdb的版本要高於7.0,之前使用過gdb6.3調試多線程是不行的。

 

 

從上圖可以看出兩個線程都阻塞在wait上,而且還給出了在哪一行代碼中,很容易就定位到產生死鎖的位置。

 

有時候線程太多,想寫到文件裏,可以:

(gdb)set logging file <文件名>

(gdb)set logging on

(gdb)thread apply all bt

(gdb)set logging off

(gdb)quit

 

繼續gdb + 多線程

http://www.cnblogs.com/aixingfou/archive/2011/07/28/2119875.html

 

先介紹一下GDB多線程調試的基本命令。 

info threads 顯示當前可調試的所有線程,每個線程會有一個GDB爲其分配的ID,後面操作線程的時候會用到這個ID。 前面有*的是當前調試的線程。 

thread ID 切換當前調試的線程爲指定ID的線程。 

break thread_test.c:123 thread all 在所有線程中相應的行上設置斷點(watch也可以指定thread

thread apply ID1 ID2 command 讓一個或者多個線程執行GDB命令command。 

thread apply all command 讓所有被調試線程執行GDB命令command。 

 

set scheduler-locking off|on|step 估計是實際使用過多線程調試的人都可以發現,在使用step或者continue命令調試當前被調試線程的時候,其他線程也是同時執行的,怎麼只讓被調試 程序執行呢?

 

(注:step是進入內部,next是外部過一下)

通過這個命令就可以實現這個需求。off 不鎖定任何線程,也就是所有線程都執行,這是默認值。 on 只有當前被調試程序會執行。 step 在單步的時候,除了next過一個函數的情況(熟悉情況的人可能知道,這其實是一個設置斷點然後continue的行爲)以外,只有當前線程會執行

 

gdb對於多線程程序的調試有如下的支持:

  • 線程產生通知:在產生新的線程時, gdb會給出提示信息

(gdb) r
Starting program: /root/thread 
[New Thread 1073951360 (LWP 12900)] 
[New Thread 1082342592 (LWP 12907)]---以下三個爲新產生的線程
[New Thread 1090731072 (LWP 12908)]
[New Thread 1099119552 (LWP 12909)]

 

  • 查看線程:使用可以查看運行的線程。info threads

(gdb) info threads
  4 Thread 1099119552 (LWP 12940)   0xffffe002 in ?? ()
  3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
  2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931)   main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)

 

注意,行首的藍色文字爲gdb分配的線程號,對線程進行切換時,使用該該號碼,而不是上文標出的綠色數字

另外,行首的紅色星號標識了當前活動的線程

 

  • 切換線程:使用 thread THREADNUMBER 進行切換,THREADNUMBER 爲上文提到的線程號。下例顯示將活動線程從 1 切換至 4。

(gdb) info threads
   4 Thread 1099119552 (LWP 12940)   0xffffe002 in ?? ()
   3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
   2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931)   main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb) thread 4
[Switching to thread 4 (Thread 1099119552 (LWP 12940))]#0   0xffffe002 in ?? ()
(gdb) info threads
* 4 Thread 1099119552 (LWP 12940)   0xffffe002 in ?? ()
   3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
   2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
   1 Thread 1073951360 (LWP 12931)   main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)

 

以上即爲使用gdb提供的對多線程進行調試的一些基本命令。另外,gdb也提供對線程的斷點設置以及對指定或所有線程發佈命令的命令。

 

初次接觸gdb下多線程的調試,往往會忽視gdb中活動線程的概念。一般來講,在使用gdb調試的時候,只有一個線程爲活動線程,如果希望得到其他的線程的輸出結果,必須使用thread命令切換至指定的線程,才能對該線程進行調試或觀察輸出結果。

 

多線程如果dump,多爲段錯誤,一般都涉及內存非法讀寫。可以這樣處理,使用下面的命令打開系統開關,讓其可以在死掉的時候生成core文件。 
ulimit -c unlimited

 

這樣的話死掉的時候就可以在當前目錄看到core.pid(pid爲進程號)的文件。接着使用gdb:
gdb ./bin ./core.pid 
進去後,使用bt查看死掉時棧的情況,在使用frame命令。frame命令是切換到bt棧的各個層級。

gcore命令(好像也可以gdb attach到進程上,quit就是detach,不影響原進程)

遇到某個進程挺住,可以用gcore命令:
gcore pid (調試進程的pid號)
注意:不會退出的,親自實驗,有的文章說主動出Core並退出,是錯的!程序正常運行。

$ gcore 34272

$ gdb -c core.34272 ./errno_demo 
GNU gdb Red Hat Linux (6.3.0.0-1.96rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu"...Using host libthread_db library "/lib64/tls/libthread_db.so.1".

Failed to read a valid object file image from memory.
Core was generated by `/home/work/data/code/errno_demo/errno_demo'.
Reading symbols from /usr/lib64/libstdc++.so.6...done.
Loaded symbols for /usr/lib64/libstdc++.so.6
Reading symbols from /lib64/tls/libm.so.6...done.
Loaded symbols for /lib64/tls/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/tls/libc.so.6...done.
Loaded symbols for /lib64/tls/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
#0  0x0000003f0b08f072 in __nanosleep_nocancel () from /lib64/tls/libc.so.6
(gdb) bt
#0  0x0000003f0b08f072 in __nanosleep_nocancel () from /lib64/tls/libc.so.6
#1  0x0000003f0b08ef10 in sleep () from /lib64/tls/libc.so.6
#2  0x0000000000400603 in main () at errno_demo.cpp:27
(gdb) info thread
* 1 process 34272  0x0000003f0b08f072 in __nanosleep_nocancel () from /lib64/tls/libc.so.6
(gdb) 

而這個時候,原來sleep的程序正常在sleep的。在sleep再加一些語句,也是會正常運行的

手動生成core文件,在使用pstack(linux下好像不好使)查看堆棧的情況。如果都看不出來,就仔細查看代碼,看看是不是在if,return,break,continue這種語句操作是忘記解鎖,還有嵌套鎖的問題,都需要分析清楚了。
  

gdb attach

gdb 調試運行的進程

gdb -p <進程號>

或者:

gdb attach <進程號> 

gdb裏面quit之後,
Detaching from program: /home/work/data/code/errno_demo/errno_demo, process 33386
原進程照樣正常運行。

 

調試常用命令

 

gdb

gcore

pstack

$ pstack 5737 
#0  0x0000003f0b08f072 in __nanosleep_nocancel () from /lib64/tls/libc.so.6
#1  0x0000003f0b08ef10 in sleep () from /lib64/tls/libc.so.6
#2  0x0000000000400603 in main ()

還能跟蹤到線程級別:

pstack顯示每個進程的棧跟蹤: pstack 4551 Thread 7 (Thread 1084229984 (LWP 4552)): #0 0x000000302afc63dc in epoll_wait () from /lib64/tls/libc.so.6 #1 0x00000000006f0730 in ub::EPollEx::poll () #2 0x00000000006f172a in ub::NetReactor::callback () #3 0x00000000006fbbbb in ub::UBTask::CALLBACK () #4 0x000000302b80610a in start_thread () from /lib64/tls/libpthread.so.0 #5 0x000000302afc6003 in clone () from /lib64/tls/libc.so.6 #6 0x0000000000000000 in ?? () Thread 6 (Thread 1094719840 (LWP 4553)): #0 0x000000302afc63dc in epoll_wait () from /lib64/tls/libc.so.6 #1 0x00000000006f0730 in ub::EPollEx::poll () #2 0x00000000006f172a in ub::NetReactor::callback () #3 0x00000000006fbbbb in ub::UBTask::CALLBACK () #4 0x000000302b80610a in start_thread () from /lib64/tls/libpthread.so.0 #5 0x000000302afc6003 in clone () from /lib64/tls/libc.so.6 #6 0x0000000000000000 in ?? ()

 

strace

# strace -o server.strace -Ttt -p 16739
常用的參數, -T是時間,tt是usecs

sleep函數裏面的系統調用是這樣的:
$ strace -Ttt -p 28699 
Process 28699 attached - interrupt to quit
16:05:45.894785 restart_syscall(0x7fff19ca31f0, 0x7fff19ca31f0, 0, 0x8, 0x7fff19ca3050

過了一會兒,出來更多結果:

$ strace -Ttt -p 28699 
Process 28699 attached - interrupt to quit
16:05:45.894785 restart_syscall(0x7fff19ca31f0, 0x7fff19ca31f0, 0, 0x8, 0x7fff19ca3050) = 0 <39.413698>
16:06:25.308576 write(1, "bye\n", 4)    = 4 <0.000014>
16:06:25.308665 munmap(0x7f55029a8000, 4096) = 0 <0.000019>
16:06:25.308707 exit_group(0)           = ?
Process 28699 detached

 

更多gdb選項

http://blog.csdn.net/hejinjing_tom_com/article/details/9472041

默認編譯的時候,調試過程是看不見宏的值的。編譯時候需要給選項。-g3 (沒實驗過)

 

tbreak 臨時設置一次

break,tbreak可以根據行號、函數、條件生成斷點。tbreak設置方法與break相同,只不過tbreak只在斷點停一次,過後會自動將斷點刪除

 

附錄:gdb  常用調試命令
-------------------------------------------------
(gdb) l :(字母l)列出源碼
(gdb) b n :在第n行處設置斷點
(gdb) b func:在函數func()的入口處設置斷點

(gdb) 條件斷點:條件可以是任何合法的c 表達式。 例如 b n if val1==val2

          當已經設置了斷點,可以用condition 命令對斷點號添加條件, 例: condition 2 val1==val2 , 注意,沒有if 單詞

          當對變量的改變更感興趣時,可以用watch 命令

(gdb) info break: 查看斷點信息  (更多斷點類,見下)
(gdb) r:運行程序
(gdb) n:單步執行
(gdb) s:單步調試如果有函數調用,則進入函數;與命令n不同,n是不進入調用的函數的
(gdb) c:繼續運行
(gdb) p 變量 :打印變量的值     也能夠修改變量的值(用 = 賦值) // 打印寄存器值 p $eax

(gdb) x /nfu <addr> 顯示內存  // n爲個數,f 爲格式,u爲每單元長度

命令:x /3uh 0x54320 表示,從內存地址0x54320讀取內容,h表示以雙字節爲一個單位,3表示輸出三個單位,u表示按十六進制顯示。


(gdb) bt:查看函數堆棧
(gdb) finish:退出函數

(gdb) display <var> 每次中斷或單步都顯示你關心的變量

(gdb)undisplay <編號>
(gdb) shell 命令行:執行shell命令行
(gdb) set args 參數:指定運行時的參數
(gdb) show args:查看設置好的參數
(gdb)info program: 來查看程序的是否在運行,進程號,被暫停的原因。 // 打印寄存器數組, info reg,  簡寫 i reg; info threads(注意有s)
(gdb)clear 行號n:清除第n行的斷點
(gdb)delete 斷點號n:刪除第n個斷點
(gdb)disable 斷點號n:暫停第n個斷點
(gdb)enable 斷點號n:開啓第n個斷點

 

 

----------------------------------------
gdb 條件調試
----------------------------------------
“break ... if cond" 命令
例: b fun() if i==20

 

watch var    // 此時監測var的變化

 

發出 'info locals' 命令時,gdb 會打印出當前幀中的局部變量,缺省情況下,這個幀中的函數就是被中斷的函數(0 號幀)。

 

要獲取有關幀的進一步信息,如它的地址和程序語言,可以使用命令 'info frame'

 backtrace <n>, bt <n> n是一個正整數,表示只打印棧頂上n層的棧信息。
 backtrace <-n> ,bt <-n> -n表一個負整數,表示只打印棧底下n層的棧信息。     

up <n>         表示向棧的上面移動n層,可以不打n,表示向上移動一層。
down <n> 表示向棧的下面移動n層,可以不打n,表示向下移動一層。

 

info frame,info f 

(gdb) info f
       Stack level 0, frame at 0xbffff5d4:
         eip = 0x804845d in func (tst.c:6); saved eip 0x8048524
         called by frame at 0xbffff60c
         source language c.
         Arglist at 0xbffff5d4, args: n=250
         Locals at 0xbffff5d4, Previous frame's sp is 0x0
         Saved registers:
         ebp at 0xbffff5d4, eip at 0xbffff5d8              
     info args      打印出當前函數的參數名及其值。     
     info locals     打印出當前函數中所有局部變量及其值。        
     info catch      打印出當前的函數中的異常處理信息

 

遠程調試

沒用過,是不是嵌入式用的?

 

gdb 內存斷點設置。
rwatch, watch, awatch 分別代表讀,寫,讀寫內存斷點,用的是硬件斷點。

 

與調試控制相關的命令

 

  • continue    繼續運行程序直到下一個斷點(類似於VS裏的F5)
  • next        逐過程步進,不會進入子函數(類似VS裏的F10)
  • step        逐語句步進,會進入子函數(類似VS裏的F11)
  • until        運行至當前語句塊結束
  • finish  運行至函數結束並跳出,並打印函數的返回值(類似VS的Shift+F11)

 

s : step in

fin: step out, 跳出函數

until 行號。 可用於跳出循環,加快了調試速度。

 

 

----------------------------------------
gdb 調試跟蹤多進程程序
----------------------------------------
gdb只能跟蹤一個進程(默認是跟蹤父進程),而不能同時跟蹤多個進程,
可以設置gdb跟蹤父進程還是子進程, 命令如下:
set follow-fork-mode parent 跟蹤父進程, 默認
set follow-fork-mode child  跟蹤子進程

 

 

gdb斷點

http://blog.csdn.net/yangzhongxuan/article/details/6897968

gdb斷點分類:

以設置斷點的命令分類:

breakpoint

可以根據行號、函數、條件生成斷點。

watchpoint

監測變量或者表達式的值發生變化時產生斷點。

catchpoint

監測信號的產生。例如c++的throw,或者加載庫的時候。

gdb中的變量從1開始標號,不同的斷點採用變量標號同一管理,可以 用enable、disable等命令管理,同時支持斷點範圍的操作,比如有些命令接受斷點範圍作爲參數。

例如:disable 5-8

 

1、break及break變種詳解:

相關命令有break,tbreak,rbreak,hbreak,thbreak,後兩種是基於硬件的,先不介紹。

>>break 與 tbeak

break,tbreak可以根據行號、函數、條件生成斷點。tbreak設置方法與break相同,只不過tbreak只在斷點停一次,過後會自動將斷點刪除,break需要手動控制斷點的刪除和使能。

break 可帶如下參數:

linenum                 本地行號,即list命令可見的行號

filename:linenum  制定個文件的行號

function                函數,可以是自定義函數也可是庫函數,如open

filename:function  制定文件中的函數

condtion                條件

*address      地址,可是函數,變量的地址,此地址可以通過info add命令得到

 

例如:

break 10    

break test.c:10

break main

break test.c:main

break system

break open

如果想在指定的地址設置斷點,比如在main函數的地址出設斷點。

可用info add main 獲得main的地址如0x80484624,然後用break *0x80484624.

 

條件斷點就是在如上述指定斷點的同時指定進入斷點的條件。(要同時滿足)

例如:(假如有int 類型變量 index)

break 10 if index == 3

tbreak 12 if index == 5

 

>>rbreak

rbreak 可以跟一個規則表達式。rbreak + 表達式的用法與grep + 表達式相似。即在所有與表達式匹配的函數入口都設置斷點。

 

rbreak list_* 即在所有以 list_ 爲開頭字符的函數地方都設置斷點。

rbreak ^list_ 功能與上同。

 

>>查看斷點信息

info break [break num ]

info break 可列出所有斷點信息,info break 後也可設置要查看的break num如:

info break 1 列出斷點號是1的斷點信息

 

watch

watch [-l|-location] expr [thread threadnum] [mask maskvalue]

     -l 與 mask沒有仔細研究,thread threadnum 是在多線程的程序中限定只有被線程號是threadnum的線程修改值後進入斷點。

經常用到的如下命令:

     watch <expr>

     爲表達式(變量)expr設置一個觀察點。變量量表達式值有變化時,馬上停住程序。

 

     表達式可以是一個變量

     例如:watch value_a

 

     表達式可以是一個地址:

     例如:watch *(int *)0x12345678 可以檢測4個字節的內存是否變化。

 

     表達式可以是一個複雜的語句表達式:

     例如:watch a*b + c/d

 

watch 在有些操作系統支持硬件觀測點,硬件觀測點的運行速度比軟件觀測點的快。如果系統支持硬件觀測的話,當設置觀測點是會打印如下信息:

     Hardware watchpoint num: expr

watch兩個變種 rwatch,awatch,這兩個命令只支持硬件觀測點如果系統不支持硬件觀測點會答應出不支持這兩個命令的信息

 

rwatch <expr>

    當表達式(變量)expr被讀時,停住程序。

       

    awatch <expr>

    當表達式(變量)的值被讀或被寫時,停住程序。

   

    info watchpoints

    列出當前所設置了的所有觀察點。

 

     watch 所設置的斷點也可以用控制斷點的命令來控制。如 disable、enable、delete等。 

 

爲停止點設定運行命令

爲斷點號bnum指寫一個命令列表。當程序被該斷點停住時,gdb會依次運行命令列表中的命令。

    例如:

 

        break foo if x>0

        commands

        printf "x is %d/n",x

        continue

        end

 

 

gdb 斷點(三)catch

在調試的時候通常用catchpoints來捕獲事件,如c++的異常等

捕獲點的設置通過catch與tcatch兩個命令。
    tcatch所設置的斷點停止一次後自動刪除,設置斷點的方法與catch相同。

用法:catch event
    這些event事件如下:
    throw
        The throwing of a C++ exception.
    catch
        The catching of a C++ exception.
    exception
    
    exception unhandled
        An exception that was raised but is not handled by the program.
    assert
        Ada 語言 assert斷言失敗時,斷點被踩到。
    exec
        調用exec時斷點被踩到。
    syscall
    syscall [name | number] ...
        通過系統函數的名稱和系統號,來設置捕獲點,當所設定的系統調用時,斷點被踩到。

 

因爲經常在linux用c語言,所以主要用到的event是最後四個,其他的沒有仔細研究。
     例如:
     catch syscall open
     catch syscall 5
     這兩個捕獲斷點一樣。

 

gdb斷點(四)刪除

斷點的刪除與斷點的設置同樣的重要。刪除斷點的命令有兩個: delete , clear

 

delete
用法:delete [breakpoints num] [range...]
delete可刪除單個斷點,也可刪除一個斷點的集合,這個集合用連續的斷點號來描述
例如:
delete 5
delete 1-10

 

clear
用法:clear 
    刪除所在行的多有斷點。
    clear location
clear 刪除所選定的環境中所有的斷點
clear location location描述具體的斷點。
例如:
clear list_insert         //刪除函數的所有斷點
clear list.c:list_delet   //刪除文件:函數的所有斷點
clear 12                  //刪除行號的所有斷點
clear list.c:12           //刪除文件:行號的所有斷點

clear 刪除斷點是基於行的,不是把所有的斷點都刪除。

 

 

gdb斷點(五)激活與禁止

 

對斷點的控制除了建立和刪除外,還可以通過使能和禁止來控制,後一種方法更靈活。

 

enable [breakpoints] [range...] 完全使能
enable                //激活所有斷點
enable 4            //激活4斷點
enable 5-6            //激活5~6斷點
disable [breakpoints] [range...] 禁止


用法舉例:
diable                //禁止所有斷點
disble 2            //禁止第二個斷點
disable 1-5            //禁止第1到第5個斷點

enable once [breakpoints] [range...] 使能一次,觸發後禁止
enable delete [breakpoints] [range...]使能一次,觸發後刪除

 

 

gdb斷點(六)condition 與ignore

同一個斷點,因不同的條件(不同的地方調用)而停止程序,同時你也可以自定義命令行,來

打印所需要的信息。

 

 

設置斷點的條件方式如下:

 

1、設置斷點的時候加入條件
      break foo if value_a > value_b

2、用condition命令

      condition bnum expression

 

      例如: condition 6 if value_a == 10
      如果你設置的斷點條件,無效會提示:(這於斷點的上下文有關,關於斷點的上下文會子專門章節闡述)
      No symbol "foo" in current context

3、取消斷點條件

     condition bnum

 

4、斷點條件特殊用法
      斷點條件的一個特殊用法是,程序只有在到達斷點一定次數之後纔會停止。這用一個特殊的命令可以實現。
      ignore bnum count

      ignore 設置的觸發條件在重新加載程序之後自動刪除。

 ignore 2 10  //觸發斷點10次後,纔會停止,每次觸發斷點count自減1

        如果一個斷點及設置了條件,又設置了觸發次數,在觸發次數count爲0之前,是不會判斷斷點的條件
      ignore 命令對breakpoint watchpoint catchpoint都有效。



轉自: https://www.cnblogs.com/charlesblc/p/6256912.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章