林廣棟,黃光紅,耿銳
(中國電子科技集團公司 第三十八研究所,合肥230088)
本文提出的方案充分考慮了C語言單步調試需要面臨的問題,可以實現任意復雜程序的C語言代碼行的單步調試功能。方案原理簡單,容易理解,并經過大量BWDSP芯片調試過程的檢驗,已經證明了其正確性。
單步調試是指以源代碼行為單位對被調試程序的運行進行控制的調試功能。單步調試不需要用戶顯式地設置斷點,可以控制程序運行一個或若干個代碼行。通過這種方式,用戶可以結合變量查看、堆棧查看、觀察點等調試功能,把潛在的程序錯誤定位至某個代碼行。
單步調試功能一般分為兩大類:匯編級單步調試功能和源代碼級單步調試功能。匯編級單步調試功能指單步調試控制運行的單位是匯編語言的代碼行。源代碼級單步調試指單步調試控制運行的單位是高級語言源代碼中的代碼行。每類單步調試功能一般又包括三種:跳入、跳過、跳出。
對于匯編級單步調試,3種單步調試功能的實現較為簡單,其功能概述如下:
①跳入調試功能控制被調試程序運行至當前代碼行中調用的函數內部。若當前代碼行中無函數調用,則控制被調試程序運行完當前代碼行。
②跳過調試功能控制被調試程序運行完當前代碼行。若當前代碼行中有函數調用,則控制被調試程序執(zhí)行完該函數調用并繼續(xù)運行完當前代碼行。
③跳出調試功能控制被調試程序運行至當前函數的返回地址處。
對于高級語言(如C語言),其3種單步調試功能從總體概念上與匯編級單步調試功能類似。但由于C語言一行代碼經編譯后生成一段匯編指令,且一行代碼中的語句可能非常復雜,其單步調試功能相對于匯編級單步調試更為復雜。C語言單步調試功能的實現要面對的問題略——編者注。
目前,單步調試功能的實現方案分為兩大類:一類通過不斷控制程序流按匯編指令行逐行執(zhí)行,進行匯編級單步執(zhí)行,每執(zhí)行一行匯編代碼就控制被調試程序停下來,分析程序地址,根據當前程序地址判斷單步調試功能是否完成;另一類通過反匯編當前源代碼行生成的匯編程序段,尋找其中的函數調用、出口地址,在這些位置設置臨時斷點。
第一種方案的優(yōu)點是實現簡單,容易理解。但是,若源代碼行中的代碼很復雜,生成的匯編程序段內容很多,使用第一種方案將使程序反復處于運行和停止狀態(tài),調試效率不高。本文介紹的單步調試功能實現方法屬于第二類方案。
國內已經有少數研究機構開展單步調試功能的研究,但這些研究并未能完全解決C語言單步調試功能面臨的所有問題。參考文獻[4-5]按第一種方案實現了一款調試器的單步調試功能,顯然這種實現方式具有不可避免的通信負擔。參考文獻[6]實現了一款基于串口通信的嵌入式調試系統(tǒng),其單步調試功能也是基于第一種方案。參考文獻[7]較為完整地介紹了一款調試器中單步調試功能的實現,其實現方案屬于第二類方案,基本完成了C語言調試的主要功能。但該項工作并未考慮到C語言中單步調試功能的復雜性,若被調試代碼中包含較為復雜的代碼,該款調試器的單步調試功能將不能正確完成。
BWDSP芯片調試系統(tǒng)軟件在充分調研了國內外前人工作的基礎上,參考已經成功實現的方案,自主實現了C語言單步調試功能。BWDSP芯片配套調試系統(tǒng)軟件的單步調試功能基于在代碼行出口處設置臨時斷點的方案實現。與國內外已有工作相比,該方案充分考慮了C語言代碼行中的代碼復雜性,可以解決很多問題。
BWDSP芯片配套調試系統(tǒng)把一行C語言代碼行的出口分為如下幾類:
①通過for、while循環(huán)、break、continue語句跳轉至其他代碼行,該類出口稱為L(Label)類出口,其出口地址集記為L。C語言中的這些跳轉指令最終編碼為BWDSP芯片指令集中的跳轉指令。BWDSP芯片指令集中的跳轉指令分為4類:
(a)條件跳轉。跳轉有可能發(fā)生,也可能不發(fā)生,根據運行時的計算結果或寄存器值確定。
(b)絕對跳轉。跳轉一定會發(fā)生,跳轉目的地址在指令機器碼中編碼。
(c)相對跳轉。跳轉一定會發(fā)生,跳轉目的地址是當前地址加上一個偏移。
(d)寄存器跳轉。跳轉一定會發(fā)生,跳轉目的地址是某個寄存器中的值,不能從可執(zhí)行文件程序段獲取。
對于前面3類跳轉,其目的地址可以從程序段反匯編得到。而對第4類跳轉,其目的地址不能從程序段得到,需要在運行時從某個寄存器中讀取。
②通過函數調用跳轉至其他行,這類出口稱為F(Function)類出口,這些函數的入口地址集記為F。C語言中的函數調用最終被編碼為BWDSP芯片指令集中的函數調用指令。BWDSP指令集中的函數調用指令分為兩類:
(a)絕對地址函數調用。函數調用的入口地址編碼在指令中,可以從程序段得到。
(b)寄存器函數調用。函數調用的入口地址是調用時某個寄存器中的值。
一般,直接調用C語言中的函數會編譯為第一類函數調用指令;通過函數指針調用函數會編譯為第二類函數調用指令。同樣,第一類函數調用的入口地址可以通過反匯編程序段得到,而第二類函數調用的入口地址只能在運行時讀取某個寄存器中的值獲得。
③通過return語句跳轉至本幀的返回地址,這類出口稱為R(Return)類出口,返回地址記為R。一個函數可能有多個retrun語句,一行C語言代碼中也可能有多處函數返回。但由于程序流一定處于函數調用棧中的棧頂,該函數返回后肯定返回至上一幀的現場,所以當前PC所處的函數中的多處return語句其實返回到同一個地址。而且,由于函數可能在多處被調用,所以該返回地址不能通過反匯編程序段獲得,只能通過運行時函數調用棧獲得。
④直接運行完本行代碼來到的程序地址,這類出口稱為N(Next),其出口地址記為N。程序運行完當前行后,會自然地運行下一行的指令,因此下一行指令的開始地址也是本行代碼的一個出口地址。對于部分C語言代碼行,并沒有下一行出口地址。例如,若一個代碼行是一函數的最后一行,若執(zhí)行該行代碼時,或者跳轉至其他行,或返回調用函數,肯定不會進入下一行代碼執(zhí)行。所以,本類出口可能存在,也可能不存在。
在將一行C語言代碼行的出口分為以上4類的基礎上,BWDSP調試系統(tǒng)通過在這些出口上設置臨時斷點實現單步調試功能。設置的臨時斷點位置不同,實現的單步調試功能也不同。對有些特殊的出口,除了設置臨時斷點,還要做一些特殊處理。
跳出是實現方式最簡單的單步調試功能,其功能是跳出當前函數,來到其返回地址處。由于一個函數在運行時返回地址只有一個,即R,所以只需要在R處設置一個臨時斷點,然后讓被調試程序正常運行即可。當程序遇到斷點停下時,若該斷點為用戶設置的斷點,說明單步調試過程中用戶斷點被觸發(fā),跳出單步調試結束。若該斷點為臨時斷點R,說明跳出功能正常實現,單步調試結束。若當前函數是main函數,則函數棧中只有一幀,無需設置臨時斷點,直接運行即可。
跳出單步調試功能的實現方案流程如圖1所示。
圖1 跳出單步調試功能實現流程圖
跳過功能使程序執(zhí)行完當前代碼行。若本代碼行中有函數調用,則執(zhí)行完該函數,并返回本代碼行繼續(xù)運行,直至程序自然地跳出該代碼行為止。所以,跳過單步調試功能應在L、N類出口設置臨時斷點。
一般,C語言程序在進入函數和跳出函數時會有一小段代碼用來進行函數棧入棧和出棧的操作。這一小段代碼在調試信息中一般與源代碼中函數開始和結束時的正反大括號對應。單步調試時,若代碼行中有return語句,該語句實際上被編譯為一個跳轉語句,跳轉至函數結尾的大括號處,并不會真正返回上一層調用函數。若代碼行中有函數結束時的大括號,則程序在該代碼行中真正有可能返回調用函數。返回上一層調用函數的指令在BWDSP指令集中被編碼為RET指令,因此可以通過檢查代碼行中是否有RET指令判斷該代碼行是否有可能返回調用函數。若代碼行中有RET指令,還要在R類出口處設置斷點。
程序PC即使在一行代碼中間某個位置,也有可能跳回到該行代碼的開始位置繼續(xù)執(zhí)行。設置臨時斷點時,需要在該行代碼中的全部L類出口處都設置斷點,不管該跳轉指令是在當前PC之前,還是之后。
對于L類出口中的第一類跳轉(條件跳轉)出口,不論該跳轉是否會發(fā)生,都在該出口處設置臨時斷點。若程序執(zhí)行了該條件跳轉,本方案可以使跳過單步調試功能正常結束。若程序沒有執(zhí)行該條件跳轉,設置的該臨時斷點也不會對單步調試功能產生影響。
對于L類出口中的第4類跳轉(寄存器跳轉),其出口地址不能在運行之前獲得,因此也不能在出口處設置臨時斷點。對于這類跳轉,需要在其跳轉指令處設置臨時斷點,若該臨時斷點觸發(fā),則讀取其跳轉目的地址寄存器的值。若讀到的跳轉目的地址在本行代碼中,則保留所有已經設置的臨時斷點,繼續(xù)運行程序。若該跳轉的目的地址不在本行代碼中,則控制程序進行一次匯編級指令行單步以執(zhí)行該跳轉,然后結束本次單步調試過程。
由于本方案沒有在函數調用出口處設置斷點,若源代碼行中有多個函數調用,則這些函數調用會依次執(zhí)行,不會觸發(fā)斷點。
被調試程序觸發(fā)以上設置的臨時斷點中的任意一個,都意味著單步跳過調試過程的結束。所以,只要臨時斷點位置是代碼行的合法可能出口,設置多個臨時斷點就不會對單步調試功能的正確性產生影響。
本方案解決了第1節(jié)中介紹的第1、2、3、4、5、7、8、9問題,而第6個問題不會對跳過單步調試功能產生影響。跳過單步調試的實現方案流程如圖2所示。
跳入調試功能使程序可以進入代碼行中調用的函數內部。若程序在當前代碼行中的執(zhí)行過程中沒有遇到函數調用,則執(zhí)行完本代碼行即完成單步調試功能,此時其調試功能與跳過單步調試類似。顯然,實現單步跳入調試功能需要在L、F、R、N處都設置臨時斷點。
對于F類出口中的第二類函數調用(寄存器函數調用),同樣需要在函數調用指令處設置臨時斷點。當該臨時斷點觸發(fā)時,控制程序進行一次匯編級指令行單步,使程序進入調用函數。
由于本方案在所有函數調用處都設置了斷點,所以程序會停止在執(zhí)行過程中的第一個函數調用出口。調試行為表現為程序會跳入執(zhí)行過程中遇到的第一個函數。
第1節(jié)中介紹的所有問題都對跳入單步調試功能有影響,而本方案可以解決所有9個問題。跳入調試功能的實現流程如圖3所示。
圖2 跳過單步調試功能實現流程圖
BWDSP芯片配套調試系統(tǒng)的C語言單步調試功能按照本文介紹的方案實施。開發(fā)人員以任意復雜的C語言代碼行在各種復雜的調試場景下對單步調試功能進行測試,調試系統(tǒng)均可按單步調試預定義的功能完成調試過程。目前,在BWDSP芯片的模擬器、編譯器、操作系統(tǒng)開發(fā)過程中,均已經在項目組內部試用本調試器進行C語言級調試。
經過反復測試、試用、修改,本方案已經證實是一個理想的實施方案,可以實現任意復雜C語言代碼行的單步調試功能。
本文介紹了BWDSP芯片調試系統(tǒng)中C語言單步調試功能的實現方案。該方案充分研究了C語言一行源代碼中可能對單步調試功能產生影響的各種情況,充分考慮了C語言代碼行的復雜性,可以實現任意復雜C語言代碼行的單步調試功能。經BWDSP芯片調試過程的驗證,證實了本方案的有效性。
圖3 跳入單步調試功能實現流程圖
編者注:本文為期刊縮略版,全文見本刊網站www.mesnet.com.cn。
[1]黃光紅,劉冠南.可配置多核處理器的調試器模塊化分層設計[J].單片機與嵌入式系統(tǒng)應用,2014,14(7):13-15.
[2]向征.基于C30的嵌入式計算機自主調試環(huán)境的建立和軟件設計[J].微電子學與計算機,1999(3):18-20,56.
[3]文躍榮.基于UART的電能芯片在線調試設計[D].長沙:湖南大學,2012.
[4]劉曉升,王宜懷.HC08系列微控制器在線調試的關鍵技術分析[J].計算機工程與設計,2009,30(3):532-535.
[5]龔蘭蘭,劉曉升,朱巧明.遠程調試系統(tǒng)的關鍵技術分析[J].計算機應用與軟件,2010,27(10):258-261.
[6]梁泉.嵌入式系統(tǒng)交叉調試器的設計與實現[D].成都:電子科技大學,2008.
[7]趙民棟.嵌入式軟件集成開發(fā)環(huán)境中調試器的設計與實現[D].西安:西北工業(yè)大學,2004.