【Linux】gdb調試core文件


編寫服務器端程序,很容易遇到Crash問題,比較幸運的是Linux提供了core file,保留了Crash的現場。有時候,根據當前的調用棧,並且打印出當前棧的變量就可以分析出crash的原因,但是,有時候看到調用棧卻束手無策。下面就介紹自己通過GDB的幾個命令的結合,發現一個crash的原因的過程。  

下面讓我們一起進入現場,來逐步發現其中的原因。  

首先,還是運行gdb 命令,gdb wbxgs core.5797,來看看現場。  

[root@hfgs126 bin]# gdb wbxgs_crash core.5797  

GNU gdb Red Hat Linux (6.3.0.0-1.132.EL4rh)  

……  

#0 0x00000038e8d70540 in strlen () from /lib64/tls/libc.so.6  

(gdb) bt  

#0 0x00000038e8d70540 in strlen () from /lib64/tls/libc.so.6  

#1 0x000000000057cfc0 in T120_Trace::Text_Formator::advance (this=0x7e800a70, lpsz=0x1 <Address 0x1 out of bounds>)  

    at ./t120trace.cpp:1464  

#2 0x000000000057ceb1 in T120_Trace::Text_Formator::operator<< (this=0x7e800a70, lpsz=0x1 <Address 0x1 out of bounds>)  

    at ./t120trace.cpp:1411  

#3 0x0000000000407927 in ~func_tracer (this=0x7e804bd0) at ../h/t120trace.h:381  

#4 0x00000000004432fd in CGSSocketServer::readHeader (this=0x8e4130, socketfd=1088,  

    buf=0x7e806cc0 "GET /detectService?cmd=selfcheck HTTP/1.1/r/nConnection: Close/r/nHost: 10.224.122.94/r/n/r/n", bufsize=1024)  

    at mgr/gssocketserver.cpp:337  

#5 0x0000000000443981 in CGSSocketServer::handle (this=0x8e4130, socketfd=1088, strRet=@0x7e807190) at mgr/gssocketserver.cpp:424  

#6 0x0000000000442f5e in CGSSocketServer::readThread (pArg=0x9ae9c0) at mgr/gssocketserver.cpp:304  

#7 0x00000038e980610a in start_thread () from /lib64/tls/libpthread.so.0  

#8 0x00000038e8dc68b3 in clone () from /lib64/tls/libc.so.6  

#9 0x0000000000000000 in ?? ()  

通過這個調用棧,可以看出,程序crash在打log的時候。雖然遇到過類似的crash,但是,當時的原因是有死循環,通過review code,沒有發現死循環。但是當前的調用棧對於分析Crash的原因是一點用也沒有,如果分析具體的原因呢?會不會是其他得線程出現錯誤導致程序Crash在這個線程呢?為了找到深一層的原因,嘗試着通過GDB的一些關於線程的命令,來看看其他的線程是否有問題。於是,使用info threads,查看了一下當時線程的情況。  

(gdb) info threads  

21 process 5797 0x00000038e8d7186d in memset () from /lib64/tls/libc.so.6  

20 process 5839 0x00000038e8dc6c8c in epoll_wait () from /lib64/tls/libc.so.6  

19 process 5842 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6  

18 process 5845 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6  

17 process 5846 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

16 process 5847 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

15 process 5848 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

14 process 5849 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

13 process 5850 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

12 process 5852 0x00000038e8dbf946 in __select_nocancel () from /lib64/tls/libc.so.6  

11 process 5854 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

10 process 5856 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

9 process 5857 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

8 process 5858 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

7 process 5859 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6  

6 process 5861 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

5 process 5862 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0  

4 process 5863 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6  

3 process 5864 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6  

2 process 5883 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6  

* 1 process 5853 0x00000038e8d70540 in strlen () from /lib64/tls/libc.so.6  

對於線程如果停止在sleep或者wait的情況,都是正常的,但是我們看到thread 21有些異常,程序停止在memset,不管是否有問題,都需要看看這樣的線程具體有沒有出錯。  

於是通過命令thread 21,進入到thread 21的調用棧。  

(gdb) thread 21  

[Switching to thread 21 (process 5797)]#0 0x00000038e8d7186d in memset () from /lib64/tls/libc.so.6  

(gdb) bt  

#0 0x00000038e8d7186d in memset () from /lib64/tls/libc.so.6  

#1 0x000000000049da0d in CGSPduFactory::streamStringFrom (is=@0x7fff9b436360, strFrom=@0x2aaaec979760) at common/pdu/gspdu.cpp:422  

#2 0x00000000004d1f25 in CGSOthShardUserRspPdu::streamFrom (this=0x2aaaec951650, is=@0x7fff9b436360) at common/pdu/pdugs.cpp:2707  

#3 0x000000000049cb2d in CGSPduFactory::derivePdu (is=@0x7fff9b436360, ulPDULen=30506) at common/pdu/gspdu.cpp:79  

#4 0x000000000049c78e in CGSPduFactory::streamPduFrom (pDataPacket=0x2aaaeca31d70) at common/pdu/gspdu.cpp:35  

#5 0x0000000000449681 in CGSWDMSManager::on_wdms_message_indication (this=0x8e3680, msg=0x2aaae9894360)  

    at mgr/gswdmsmanager.cpp:344  

……  

#18 0x0000000000407733 in main (argc=1, argv=0x7fff9b44ac98) at gsmain.cpp:118  

(gdb) f 3  

#3 0x000000000049cb2d in CGSPduFactory::derivePdu (is=@0x7fff9b436360, ulPDULen=30506) at common/pdu/gspdu.cpp:79  

79      common/pdu/gspdu.cpp: No such file or directory.  

        in common/pdu/gspdu.cpp  

使用命令 i locals,打印所有的變量的值。  

(gdb) i locals  

pPdu = (CBasePdu *) 0x2aaaec951650  

pPduHeader = (CPduHeader *) 0x2aaaea1c4190  

ulPduType = 50  

到現在還沒有看出有什么明顯的異常,然后再把PDU的頭打印出來如下:  

(gdb) p *pPduHeader  

$1 = {m_ulHeadLen = 61, m_ulVersion = 2080000, m_ulPduType = 50, m_ulSrcSvrType = WEBEX_CONNECT_GS, m_strSrcSvrAddr = {  

    static npos = 18446744073709551615,  

    _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>},  

      _M_p = 0x2aaaeca52a68 "10.224.95.109:9900"}}, m_strSubject = {static npos = 18446744073709551615,  

    _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>},  

      _M_p = 0x2aaaec929b28 "qawin.qazone.GS"}}, m_ulSequence = 0}  

從藍色的字的部分可以看出,這個PDU是從10.224.95.109這台server上發過來的。  

當時QA測試的環境,都是10.224.122開頭的IP的server,怎么會有這個IP的PDU,於是,詢問QA,發現10.224.95.109這個server是其他DataCenter的Server,而且還是老的版本,由於當前測試環境的版本刪除了兩個PDU,同時又增加了四個PDU,導致了老的PDU發來的時候,新的版本的把它當作新的PUD解析,從而導致不能正確解析,最終導致了解析出來的長度不對。可以通過f 1命令進入第一級調用棧查看所有的局部變量。  

(gdb) f 1  

#1 0x000000000049da0d in CGSPduFactory::streamStringFrom (is=@0x7fff9b436360, strFrom=@0x2aaaec979760) at common/pdu/gspdu.cpp:422  

422                  in common/pdu/gspdu.cpp  

(gdb) i locals  

strTmp = 0x2aaaf1c00010 ""  

iRet = 0  

ulLen = 1179995975  

可以看出解析出來的長度是一個很大的值1179995975,而線程21正式停止在分配內存之后,使用memset時,停止在那里。從Log中也可以看到,thread 21也一致阻塞在這里,而且沒有再繼續運行。  

由於當時有兩台server crash,通過查看另外一台server的core file,發現另外一台server也是和本台server一樣的調用棧。在QA更新了10.224.95.109的版本后,crash沒有再出現。  

通過這個實例,可以看出,當server出現crash的時候,雖然當前的調用棧可能沒有什么價值,但是,通過分析所有線程的調用棧,還是可能分析出蛛絲馬跡的,從而對於解決Crash的問題帶來幫助。  

通過這個問題可以得到一個教訓,在修改Server之間的接口時,一定要考慮到和老版本的兼容問題,即使這個PDU可能永遠也不會使用,仍然需要保留,因為Production上,是先上GSB,然后再上Primary,肯定會存在兩個版本同時運行的情況。如果出現刪除或者改變PDU順序的情況,可能會導致整個系統不能工作。  

希望本文章,對解決Crash問題和避免類似的Crash問題有一定的借鑒作用。  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM