時(shí)間:2006-04-25 10:13:00來(lái)源:0
圖1. 遠(yuǎn)程調(diào)試系統(tǒng)
2、RSP協(xié)議
GDB RSP(Remote Serial Protocol)定義了GDB宿主機(jī)與被調(diào)試目標(biāo)機(jī)進(jìn)行通信時(shí)數(shù)據(jù)包的格式。信息的格式是:$數(shù)據(jù)#校驗(yàn)碼。多數(shù)的信息都使用ASCII碼,數(shù)據(jù)由一系列的ASCII碼組成,校驗(yàn)碼是由兩個(gè)16進(jìn)制數(shù)組成的單字節(jié)校驗(yàn)碼。接受方接受數(shù)據(jù)并校驗(yàn),若正確則回應(yīng)“+”,錯(cuò)誤則回應(yīng)“-”。通信的內(nèi)容包括讀寫數(shù)據(jù)、控制程序運(yùn)行、報(bào)告程序狀態(tài)等命令。RSP的基本命令從通信對(duì)話角度可以分為兩種:
1) 請(qǐng)求
?:讀當(dāng)前系統(tǒng)狀態(tài)
g:讀所有寄存器
G:寫所有寄存器
m:讀內(nèi)存
M:寫內(nèi)存
c:繼續(xù)執(zhí)行
s: 單步執(zhí)行
k:終止進(jìn)程
2) 答復(fù)
“”:告訴GDB上次請(qǐng)求命令不支持。
E:告訴GDB出錯(cuò)
OK:上次請(qǐng)求正確
W:系統(tǒng)在exit_status狀態(tài)下退出。
X:系統(tǒng)在signal信號(hào)下終止。
S:系統(tǒng)在signal信號(hào)下停止。
O:告訴GDB控制臺(tái)輸出,這也是唯一向GDB發(fā)出的命令
3、GDB遠(yuǎn)程調(diào)試功能
調(diào)試內(nèi)核時(shí)通常還沒(méi)有文件系統(tǒng),而且多數(shù)嵌入式由于自身資源的限制不具備文件系統(tǒng),因此將與文件系統(tǒng)有關(guān)的源文件、目標(biāo)文件及符號(hào)表都存放在主機(jī)上,由主機(jī)上的調(diào)試器處理。同樣,調(diào)試用的輸入輸出設(shè)備也是由主機(jī)提供。主機(jī)上的調(diào)試器接受用戶輸入的調(diào)試命令并進(jìn)行預(yù)處理,對(duì)于有些命令(如breakpoint)的處理就在主機(jī)GDB上實(shí)現(xiàn),不需要同目標(biāo)機(jī)進(jìn)行通信。當(dāng)然,更多的指令需要在目標(biāo)機(jī)上調(diào)試代理上實(shí)現(xiàn)的。主機(jī)將預(yù)處理完之后的命令根據(jù)RSP進(jìn)行封裝,發(fā)送給目標(biāo)機(jī)上的調(diào)試代理,調(diào)試代理接受命令后作相應(yīng)的處理,并返回信息給主機(jī)上的調(diào)試器。
4、目標(biāo)機(jī)上stub的實(shí)現(xiàn)
目標(biāo)機(jī)上stub的基本功能是與主機(jī)GDB進(jìn)行通信,實(shí)現(xiàn)讀寫內(nèi)存、寄存器,stop,continue。主機(jī)GDB同目標(biāo)機(jī)上stub進(jìn)行通信的通用模型如圖2:
圖2. GDB同目標(biāo)機(jī)上stub通信的通用模型
目標(biāo)機(jī)與主機(jī)通過(guò)硬件連接,被調(diào)試部分插入stub,GDB與被調(diào)試部分通過(guò)RSP進(jìn)行通信。根據(jù)stub所處層的不同來(lái)實(shí)現(xiàn)不同層的調(diào)試,包括內(nèi)核層、應(yīng)用層的調(diào)試。
4.1 內(nèi)核層調(diào)試模型
圖3. 使用stub對(duì)內(nèi)核進(jìn)行調(diào)試
如圖3,將stub插入到內(nèi)核里就可以實(shí)現(xiàn)內(nèi)核的調(diào)試了。Linux內(nèi)核調(diào)試機(jī)制KGDB就是使用這種模式。KGDB可以分為初始化模塊和控制模塊。
4.1.1初始化模塊
修改異常處理函數(shù),使得在異常發(fā)生時(shí)都進(jìn)入函數(shù)handle_exception(),這樣GDB就能夠捕獲這些異常。初始化之后使用breakpoint()函數(shù)將系統(tǒng)控制權(quán)直接交給GDB。KGDB對(duì)異常處理函數(shù)的修改基本上可以分為二種。
定義宏CHK_REMOTE_DEBUG
#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) { if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs)) { (*linux_debug_hook)(trapnr, signr, error_code, regs) ; after; } }
改變程序的流程,以int3的處理函數(shù)為例
#define DO_VM86_ERROR(trapnr, signr, str, name) asmlinkage void do_##name(struct pt_regs * regs, long error_code) { CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,goto skip_trap) do_trap(trapnr, signr, str, 1, regs, error_code, NULL); skip_trap: return; }
展開(kāi)DO_VM86_ERROR (3,SIGTRAP,"int3",int3)
asmlinkage void do_int3(struct pt_regs *regs, long error_code)
{ if (linux_debug_hook != ( gdb_debug_hook *)NULL&&! user_mode(regs))
{ (*linux_debug_hook)(3, SIGTRAP, errorcode, regs);
goto skip_trap;
}
do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL);
skip_trap:
return;
}
從以上代碼可見(jiàn),進(jìn)入內(nèi)核調(diào)試狀態(tài)之后,異常處理函數(shù)就是handle_exception(),程序流程跳過(guò)了非調(diào)試狀態(tài)時(shí)的處理函數(shù)do_trap。
不改變程序的流程,以異常divide_error 的處理函數(shù)為例
#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) asmlinkage void do_##name(struct pt_regs * regs, long error_code) { …… do_trap(trapnr, signr, str, 1, regs, error_code, &info); }
展開(kāi)DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip)
asmlinkage void do_divide_error (struct pt_regs *regs, long error_code)
{ if (linux_debug_hook != ( gdb_debug_hook *)NULL&&! user_mode(regs))
{ (*linux_debug_hook)(3, SIGTRAP, errorcode, regs);
}
do_trap(0, SIGTRAP, "divide erro", 1, regs, error_code, &info);
}
從以上代碼中看不出調(diào)試狀態(tài)跟非調(diào)試狀態(tài)的區(qū)別,然而我們看一下do_trap函數(shù)中可能會(huì)調(diào)用的函數(shù)die()。
void die(const char * str, struct pt_regs * regs, long err)
{
CHK_REMOTE_DEBUG(1,SIGTRAP,err,regs,)
do_exit(SIGSEGV);
}
由此可見(jiàn),調(diào)試狀態(tài)下的異常處理函數(shù)還是進(jìn)入了handle_exception函數(shù)。不過(guò)與上面一種異常不同之處在于:異常處理函數(shù)在調(diào)試與非調(diào)試狀態(tài)下的程序流程是相同的,handle_exception只提供獲取系統(tǒng)當(dāng)時(shí)的狀態(tài),繼續(xù)運(yùn)行的結(jié)果還是do_exit。
雖然不是所有異常函數(shù)都是按上述兩種方法定義的,但本質(zhì)上都可以歸劃為其一,顯然絕大多數(shù)處理函數(shù)的修改屬于第二種,因?yàn)榈谝环N異常就是為調(diào)試準(zhǔn)備的。因此在目標(biāo)機(jī)具有調(diào)試用的輸出設(shè)備的情況下,完全可以不修改第二種異常處理函數(shù),因?yàn)閘inux內(nèi)核在非調(diào)試狀態(tài)下的異常處理函數(shù)已經(jīng)輸出必要的狀態(tài)信息、出錯(cuò)信息。
4.1.2控制模塊
在控制模塊完成與主機(jī)GDB的通信,具體流程如圖4,handle_exception函數(shù)首先判斷CPU是否處于VM86模式或用戶態(tài),若是則返回,可見(jiàn)KGDB只調(diào)試內(nèi)核態(tài)程序。然后接受GDB發(fā)來(lái)的信息,根據(jù)接受的信息作出相應(yīng)的操作和回復(fù)。流程圖的虛線框內(nèi)是所有GDBstub中handle_exception函數(shù)的通用流程。
4.2 應(yīng)用程序調(diào)試模型
在嵌入式Linux開(kāi)發(fā)領(lǐng)域里調(diào)試應(yīng)用程序常用調(diào)試代理工具GDBserver,其工作原理并不是將stub編譯在被調(diào)試應(yīng)用程序內(nèi),而是把被調(diào)試程序作為GDBserver的子進(jìn)程,這樣GDBserver就可以利用內(nèi)核提供的代碼跟蹤機(jī)制(ptrace)監(jiān)控被調(diào)試進(jìn)程的運(yùn)行,從而來(lái)完成調(diào)試任務(wù)。此工作原理同GDB本地調(diào)試相似。其調(diào)試模型如圖5。GDBserver的工作流程是:GDBserver創(chuàng)建子進(jìn)程->綁定跟蹤ptrace(ptrace_traceme,,)->從主機(jī)傳來(lái)的各種調(diào)試命令通過(guò)GDBserver轉(zhuǎn)化為各種操作需求的ptrace。顯然,如果要用GDBserver來(lái)進(jìn)行遠(yuǎn)程調(diào)試的話,就需要內(nèi)核操作系統(tǒng)的支持,包括子進(jìn)程、代碼跟蹤機(jī)制,這樣對(duì)于其他嵌入式系統(tǒng)內(nèi)核工作量會(huì)比較大。而且ptrace也有其局限性,比如只能跟蹤它的子進(jìn)程,在調(diào)試進(jìn)程和被調(diào)試進(jìn)程之間傳送一個(gè)長(zhǎng)字的數(shù)據(jù)。使用通用的調(diào)試模式工作量會(huì)更小。如圖6,將stub編譯在應(yīng)用程序中,并在應(yīng)用程序入口處就插入斷點(diǎn),程序開(kāi)始就上控制權(quán)交給GDB,之后的流程跟內(nèi)核層調(diào)試類似。
圖4. GDBKGDB中handle_exception函數(shù)流程
圖5. 使用GDBserver對(duì)應(yīng)用程序進(jìn)行調(diào)試
圖6. 使用stub對(duì)應(yīng)用程序進(jìn)行調(diào)試
5. 不修改內(nèi)核前提下調(diào)試應(yīng)用程序
GDB實(shí)現(xiàn)設(shè)置斷點(diǎn)的方式是使用內(nèi)存的讀寫,即將原指令用一個(gè)trap指令代替,使得程序執(zhí)行到該指令時(shí)產(chǎn)生單步調(diào)試中斷,然后就進(jìn)入異常處理函數(shù),針對(duì)調(diào)試器的各種操作處理函數(shù)需要作出相應(yīng)的操作。不同的系統(tǒng)提供不同的調(diào)試異常指令,如int3,trap2等,顯然對(duì)于使用這些硬件平臺(tái)提供的斷點(diǎn)指令為了實(shí)現(xiàn)GDBstub調(diào)試功能需要改寫這些指令異常處理函數(shù)。因此一般的調(diào)試系統(tǒng)器或調(diào)試代理都需要涉及單步調(diào)試指令的處理函數(shù),需要系統(tǒng)內(nèi)核的支持。上面提到的KGDB修改了異常處理函數(shù),GDBserver需要系統(tǒng)內(nèi)核提供ptrace函數(shù)。這種方法存在一些不足之處:修改內(nèi)核工作量大,移植性差。針對(duì)這些情況我們可以采用另一種斷點(diǎn)實(shí)現(xiàn)方案:在stub中定義一個(gè)設(shè)置斷點(diǎn)函數(shù)。
斷點(diǎn)函數(shù)模擬調(diào)試異常指令,實(shí)現(xiàn)保護(hù)現(xiàn)場(chǎng)、調(diào)用異常處理函數(shù)、恢復(fù)現(xiàn)場(chǎng)并將控制權(quán)交給被調(diào)試程序。斷點(diǎn)函數(shù)的基本流程如下。
#define BREAKPOINT __asm__ __volatile__(" bl ent_exception\n)
void debug_trap()
{ __asm__ __volatile__(
" ent_exception: \n"
保存現(xiàn)場(chǎng)
" bl handle_exception \n"
" out_exception: \n"
恢復(fù)現(xiàn)場(chǎng)
);
}
handle_exception()函數(shù)流程類似圖4中的虛線框部分。其中有一點(diǎn),也是這種方法實(shí)現(xiàn)的關(guān)鍵部分是:斷點(diǎn)指令的替換。斷點(diǎn)設(shè)置時(shí)從GDB傳過(guò)來(lái)的硬件平臺(tái)提供的斷點(diǎn)異常指令的二進(jìn)制碼,必須將此二進(jìn)制碼替換成在stub中新定義的BREAKPOINT二進(jìn)制碼,這樣才能進(jìn)入調(diào)試異常處理函數(shù)。因此在handle_exception()函數(shù)中,如果收到的請(qǐng)求是“M”,則需要作些處理,流稱如圖7:
圖7. 替換指令
這種方法理論上在內(nèi)核調(diào)試和應(yīng)用程序調(diào)試中都可以使用,但在應(yīng)用程序的調(diào)試中其優(yōu)點(diǎn)更明顯。這種方法在寫stub時(shí)候不涉及內(nèi)核,在調(diào)試應(yīng)用程序時(shí)不需要切換到內(nèi)核模式下,直接在用戶模式中就可以完成。這種方法也存在些不足之處。為了實(shí)現(xiàn)現(xiàn)場(chǎng)保護(hù),要求用戶了解系統(tǒng)內(nèi)的寄存器。隨著stub本身復(fù)雜度的增加,它的正確性需要更多的檢驗(yàn)。
6、結(jié)束語(yǔ)
加stub的遠(yuǎn)程調(diào)試方法方便而有效,而且可以降低項(xiàng)目成本,在實(shí)際工作中得到廣泛的研究和應(yīng)用。本文提到在不修改內(nèi)核前提下調(diào)試應(yīng)用程序的方法已成功應(yīng)用于我們自己開(kāi)發(fā)的微內(nèi)核結(jié)構(gòu)的操作系統(tǒng)里,為該系統(tǒng)的開(kāi)發(fā)應(yīng)用提供良好的調(diào)試手段。當(dāng)然加stub的遠(yuǎn)程調(diào)試方法也存在一些不足。顯然stub的應(yīng)用是在串口通信的基礎(chǔ)上,因此串口處理函數(shù)以及stub自身處理函數(shù)的正確性是確保stub安全調(diào)試的前提。
參考文獻(xiàn)
[1].李紅衛(wèi)李翠萍,kgdb調(diào)試Linux內(nèi)核肋剖析與改進(jìn),微型機(jī)與應(yīng)用,2004年第10期
[2].郭勝超,GDB遠(yuǎn)程調(diào)試及其在嵌入式Linux系統(tǒng)中的應(yīng)用,計(jì)算機(jī)工程與應(yīng)用,2004年第26卷第10期.
[3].彭進(jìn)展,GRDBS:一種針對(duì)嵌入式系統(tǒng)的通用遠(yuǎn)程調(diào)試系統(tǒng),計(jì)算機(jī)工程,2003年2月第29卷第2期.
[4] .Gatliff, Bill, Embedding with GNU: the gdb Remote Serial Protocol, Embedded Systems Programming, September 1999, p. 109.
[5].Gilmore J, Shebs S, GDB Internals: A Guild to the Internals of the GNU Debugger, Free Software Foundation Inc.,1999.
作者單位:浙江大學(xué)計(jì)算機(jī)系
地址:浙江大學(xué)玉泉校區(qū)4舍230 310027
Email:liulin@zju.edu.cn
標(biāo)簽:
傳動(dòng)網(wǎng)版權(quán)與免責(zé)聲明:凡本網(wǎng)注明[來(lái)源:傳動(dòng)網(wǎng)]的所有文字、圖片、音視和視頻文件,版權(quán)均為傳動(dòng)網(wǎng)(surachana.com)獨(dú)家所有。如需轉(zhuǎn)載請(qǐng)與0755-82949061聯(lián)系。任何媒體、網(wǎng)站或個(gè)人轉(zhuǎn)載使用時(shí)須注明來(lái)源“傳動(dòng)網(wǎng)”,違反者本網(wǎng)將追究其法律責(zé)任。
本網(wǎng)轉(zhuǎn)載并注明其他來(lái)源的稿件,均來(lái)自互聯(lián)網(wǎng)或業(yè)內(nèi)投稿人士,版權(quán)屬于原版權(quán)人。轉(zhuǎn)載請(qǐng)保留稿件來(lái)源及作者,禁止擅自篡改,違者自負(fù)版權(quán)法律責(zé)任。
產(chǎn)品新聞
更多>2025-10-31
勇梅機(jī)械液壓閘門給煤機(jī)的優(yōu)點(diǎn)
2025-10-22
2025-10-17
2025-10-11
「一體機(jī)性價(jià)比王者」NK290M普及型數(shù)控...
2025-10-09
耐磨管道機(jī)器人檢測(cè)電纜CCTV,水下管道...
2025-09-23