Linux調試 call trace dump_stuck__Linux

來源:互聯網
上載者:User


call trace能把當前的函數調用棧列印出來。 核心態call trace

核心態有三種出錯情況,分別是bug, oops和panic。

bug屬於輕微錯誤,比如在spin_lock期間調用了sleep,導致潛在的死結問題,等等。

oops代表某一使用者進程出現錯誤,需要殺死使用者進程。這時如果使用者進程佔用了某些訊號鎖,所以這些訊號鎖將永遠不會得到釋放,這會導致系統潛在的不穩定性。

panic是嚴重錯誤,代表整個系統崩潰。

 

OOPS

先介紹下oops情況的處理。Linux oops時,會進入traps.c中的die函數。

int die(const char *str, struct pt_regs *regs, long err)

       。。。

       show_regs(regs);

 

void show_regs(struct pt_regs * regs)函數中,會調用show_stack函數,這個函數會列印系統的核心態堆棧。

具體原理為:

       從寄存器裡找到當前棧,在棧指標裡會有上一級調用函數的棧指標,根據這個指標回溯到上一級的棧,依次類推。

       在powerpc的EABI標準中,當前棧的棧底(注意是棧底,不是棧頂,即Frame Header的地址)指標儲存在寄存器GPR1中。在GPR1指向的棧空間,第一個DWORD為上一級調用函數的Frame Header指標(Back Chain Word),第二個DWORD是當前函數在上一級函數中的返回地址(LR Save Word)。通過此種方式一級級向上回溯,完成整個call dump。除了這種方法,內建函數__builtin_frame_address函數理論上也應該能用,雖然在核心中沒有見到。(2.6.29的ftrace模組用到了__builtin_return_address函數)。

 

show_regs函數在call trace的時候,只是用printk列印了一下棧中的資訊。如果當前系統沒有終端,那就需要修改核心,把這些棧資訊根據需求儲存到其它地方。

例如,可以在系統的flash中開出一塊空間專門用於列印資訊的儲存。然後,寫一個核心模組,再在die函數中加一個回呼函數。這樣,每當回呼函數被調用,就通知自訂的核心模組,在模組中可以把調用棧還有其它感興趣的資訊儲存到那塊專用flash空間中去。這裡有一點需要注意的是,oops時核心可能不穩定,所以為了確保資訊能被正確寫入flash,在寫flash的函數中盡量不要用中斷,而用輪循的方式。另外訊號量、sleep等可能導致阻塞的函數也不要使用。

此外,由於oops時系統還在運行,所以可以發一個訊息(訊號,netlink等)到使用者空間,通知使用者空間做一些資訊收集工作。

 

Panic

Panic時,Linux處於更最嚴重的錯誤狀態,標誌著整個系統不可用,即中斷、進程調度等都已經停止,但棧還沒被破壞。所以,oops中的棧回溯理論上還是能用。printk函數中因為沒有阻塞,也還是能夠使用。 使用者態call trace

使用者程式可以在以下情形call trace,以方便調試:

l         程式崩潰時,都會收到一個訊號。Linux系統接收到某些訊號時會自動列印call trace。

l         在使用者程式中添加檢查點,類似於assert機制,如果檢查點的條件不滿足,就執行call trace。

使用者態的call trace與核心態相同,同樣滿足EABI標準,原理如下:

在GNU標準中,有一個內建函數__builtin_frame_address。這個函數可以返回當前執行內容的棧底(Frame Header)指標(同時也是指向Back Chain Word的指標),通過這個指標得到當前調用棧。而這個調用棧中,會有上一級調用函數的棧底指標,通過這個指標再回溯到上一級的調用棧。以此類推完成整個call dump過程。

得到函數的地址後,可以通過符號表得到函數名字。如果是動態庫中定義的函數,還可以通過擴充函數dladdr得到這個函數的動態庫資訊。


Linux Kernel BUG:soft lockup CPU#1 stuck分析 1.線上核心bug日誌

 kernel:Modules linked in: fuse ipv6 power_meter bnx2 sg microcode serio_raw iTCO_wdtiTCO_vendor_support hpilo hpwdt i7core_edac edac_core shpchp ext4 mbcache jbd2sd_mod crc_t10dif hpsa radeon ttm drm_kms_helper drm i2c_algo_bit i2c_coredm_mirror dm_region_hash dm_log dm_mod [last unloaded: scsi_wait_scan]

 kernel: Pid:5483, comm: master Not tainted 2.6.32-220.el6.x86_64 #1

 kernel: CallTrace:

 kernel:[<ffffffff81069b77>] ? warn_slowpath_common+0x87/0xc0

 kernel:[<ffffffff81069bca>] ? warn_slowpath_null+0x1a/0x20

 kernel:[<ffffffff810ea8ae>] ? rb_reserve_next_event+0x2ce/0x370

 kernel:[<ffffffff810eab02>] ? ring_buffer_lock_reserve+0xa2/0x160

 kernel:[<ffffffff810ec97c>] ? trace_buffer_lock_reserve+0x2c/0x70

 kernel:[<ffffffff810ecb16>] ? trace_current_buffer_lock_reserve+0x16/0x20

kernel:[<ffffffff8107ae1e>] ? ftrace_raw_event_hrtimer_cancel+0x4e/0xb0

 kernel:[<ffffffff81095e7a>] ? hrtimer_try_to_cancel+0xba/0xd0

kernel:[<ffffffff8106f634>] ? do_setitimer+0xd4/0x220

 kernel:[<ffffffff8106f88a>] ? alarm_setitimer+0x3a/0x60

 kernel:[<ffffffff8107c27e>] ? sys_alarm+0xe/0x20

kernel:[<ffffffff8100b308>] ? tracesys+0xd9/0xde

 kernel: ---[end trace 4d0a1ef2e62cb1a2 ]---

 

2.核心軟死結(soft lockup)bug原因分析

         Soft lockup名稱解釋:所謂,soft lockup就是說,這個bug沒有讓系統徹底死機,但是若干個進程(或者kernel thread)被鎖死在了某個狀態(一般在核心地區),很多情況下這個是由於核心鎖的使用的問題。

         Linux核心對於每一個cpu都有一個監控進程,在技術界這個叫做watchdog(看門狗)。通過ps –ef | grep watchdog能夠看見,進程名稱大概是watchdog/X(數字:cpu邏輯編號1/2/3/4之類的)。這個進程或者線程每一秒鐘運行一次,否則會睡眠和待機。這個進程運行會收集每一個cpu運行時使用資料的時間並且存放到屬於每個cpu自己的核心資料結構。在核心中有很多特定的中斷函數。這些中斷函數會調用soft lockup計數,他會使用當前的時間戳記與特定(對應的)cpu的核心資料結構中儲存的時間對比,如果發現當前的時間戳記比對應cpu儲存的時間大於設定的閥值,他就假設監測進程或看門狗線程在一個相當可觀的時間還沒有執。Cpu軟鎖為什麼會產生,是怎麼產生的。如果linux核心是經過精心設計安排的CPU調度訪問,那麼怎麼會產生cpu軟死結。那麼只能說由於使用者開發的或者第三方軟體引入,看我們伺服器核心panic的原因就是qmgr進程引起。因為每一個無限的迴圈都會一直有一個cpu的執行流程(qmgr進程示一個後台郵件的訊息佇列服務進程),並且擁有一定的優先順序。Cpu調度器調度一個驅動程式來運行,如果這個驅動程式有問題並且沒有被檢測到,那麼這個驅動程式將會暫用cpu的很長時間。根據前面的描述,看門狗進程會抓住(catch)這一點並且拋出一個軟死結(soft lockup)錯誤。軟死結會掛起cpu使你的系統不可用。

         如果是使用者空間的進程或線程引起的問題backtrace是不會有內容的,如果核心線程那麼在soft lockup訊息中會顯示出backtrace資訊。 3.根據linux核心源碼分析錯誤

         根據我們第一部分核心拋出的錯誤資訊和call trace(linux核心的跟蹤子系統)來分析產生的具體原因。

首先根據我們的centos版本安裝相應的linux核心源碼,具體步驟如下:

(1)下載源碼的rpm包kernel-2.6.32-220.17.1.el6.src.rpm

(2)安裝相應的依賴庫,命令:yuminstall rpm-build redhat-rpm-config asciidoc newt-devel

(3)安裝源碼包:rpm -ikernel-2.6.32-220.17.1.el6.src.rpm

(4)進入建立源碼的目錄:cd~/rpmbuild/SPECS

(5)建立產生源碼目錄:rpmbuild-bp --target=`uname -m` kernel.spec

 

下面開始真正的根據核心bug日誌分析源碼:

(1)第一階段核心錯誤日誌分析(時間在Dec 4 14:03:34這個階段的日誌輸出程式碼分析,其實這部分代碼不會導致cpu軟死結,主要是第二階段錯誤記錄檔顯示導致cpu軟死結)

我們首先通過日誌定位到相關原始碼:看下面日誌:Dec 4 14:03:34 BP-YZH-1-xxxx kernel: WARNING: atkernel/trace/ring_buffer.c:1988 rb_reserve_next_event+0x2ce/0x370() (Not tainted)

根據日誌內容我們可以很容易的定位到kernel/trace/ring_buffer.c這個檔案的1988行代碼如下:WARN_ON(1)。

         先簡單解釋一下WARN_ON的作用:WARN_ON只是列印出當前棧資訊,不會panic。所以會看到後面有一大堆的棧資訊。這個宏定義如下:

#ifndef WARN_ON

#defineWARN_ON(condition) ({                                            \

         int __ret_warn_on = !!(condition);                             \

         if (unlikely(__ret_warn_on))                                         \

                   __WARN();                                                               \

         unlikely(__ret_warn_on);                                              \

})

#endif

這個宏很簡單保證傳遞進來的條件值為0或者1(兩次邏輯非操作的結果),然後使用分支預測技術(保證執行機率大的分支緊鄰上面的指令)判斷是否需要調用__WARN()宏定義。如果滿足條件執行了__WARN()宏定義也接著執行一條空指令;。上面調用WARN_ON宏是傳遞的1,所以會執行__WARN()。下面繼續看一下__WARN()宏定義如下:

#define __WARN()            warn_slowpath_null(__FILE__,__LINE__)

從接下來的call trace資訊中我們也確實發現調用了warn_slowpath_null這個函數。通過在linux核心原始碼中搜尋這個函數的實現,發現在panic.c(核心恐慌時的相關功能實現)中實現如下:

voidwarn_slowpath_null(const char *file, int line)

{

         warn_slowpath_common(file, line,__builtin_return_address(0),

                                TAINT_WARN, NULL);

}

EXPORT_SYMBOL(warn_slowpath_null);//都出這個符號,讓其他模組可以使用這個函數

同樣的我們看到了warn_slowpath_common這個函數,而在call trace當中這個函數在warn_slowpath_null函數之前列印出來,再次印證了這個流程是正確的。同樣在panic.c這個檔案中我發現了warn_slowpath_common這個函數的實現如下:

static voidwarn_slowpath_common(const char *file, int line, void *caller,

                                      unsigned taint, struct slowpath_args *args)

{

         const char *board;

 

         printk(KERN_WARNING "------------[ cut here]------------\n");

         printk(KERN_WARNING "WARNING: at %s:%d %pS()(%s)\n",

                   file, line, caller, print_tainted());

         board = dmi_get_system_info(DMI_PRODUCT_NAME);//得到dmi系統資訊

         if (board)

                   printk(KERN_WARNING "Hardware name:%s\n", board);//通過我們的日誌資訊可以發現我們硬體名稱是ProLiant DL360 G7

 

         if (args)

                   vprintk(args->fmt, args->args);

 

         print_modules();//列印系統模組資訊

         dump_stack();//dump資訊輸出(call trace開始)

         print_oops_end_marker();//列印oops結束

         add_taint(taint);

}

分析這個函數的實現不難發現我們的很多日誌資訊從這裡開始輸出,包括列印一些系統資訊,就不繼續深入分析了(請看代碼注釋,裡面調用相關函數列印對應資訊,通過我分析這些函數的實現和我們的日誌資訊完全能夠對應,其中dump_stack是與cpu體繫結構相關的,我們的伺服器應該是屬於x86體系)。這裡在繼續分析一下dump_stack函數的實現,因為這個是與cpu體繫結構相關的,而且這個函數直接反應出導致核心panic的相關進程。這個函數實現如下:

/*

 * The architecture-independent dump_stackgenerator

 */

void dump_stack(void)

{

         unsigned long stack;

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.