徐學(xué)政, 王 濤, 方 健, 張光達(dá)
(軍事科學(xué)院 國防科技創(chuàng)新研究院, 北京 100097)
RISC-V是一個(gè)基于精簡指令集(RISC)原則的開源指令集架構(gòu), 因其精簡、開放、模塊化的設(shè)計(jì)和高可定制的特點(diǎn)在工業(yè)界和教育界廣受歡迎.隨著面向RISC-V的處理器接連問世, 圍繞RISC-V建設(shè)完善的軟件生態(tài)系統(tǒng)將會大大提高系統(tǒng)和應(yīng)用軟件的設(shè)計(jì)開發(fā)效率, 并降低其維護(hù)成本.
在面向RISC-V的軟件開發(fā)過程中, 尤其是基于擴(kuò)展指令 (例如向量擴(kuò)展指令或用戶自定義指令) 進(jìn)行程序開發(fā)時(shí), 很難避免以手寫匯編的方式為程序編寫高效的匯編代碼.例如, 為標(biāo)準(zhǔn)的C函數(shù)庫編寫相應(yīng)的向量版本函數(shù).與編譯器自動(dòng)生成的代碼不同, 手工開發(fā)的匯編代碼雖然可以最大限度地提高程序的效率, 但繞過了編譯時(shí)對程序的約束 (如類型檢查[1]、寄存器分配[2]等), 因而對開發(fā)者提出了更高的要求.如果開發(fā)者經(jīng)驗(yàn)不足或?qū)χ噶畹睦斫庥姓`, 將會為代碼的正確性和安全性埋下隱患.以向量擴(kuò)展指令為例, 為常用函數(shù)(如strlen、memcpy等) 的標(biāo)準(zhǔn)版本開發(fā)相應(yīng)的向量版本后, 能否快速地、自動(dòng)化地測試向量版本與標(biāo)準(zhǔn)版本的函數(shù)在語義上是否等價(jià), 將大大影響代碼的正確性和軟件開發(fā)和調(diào)試的效率.
國內(nèi)外學(xué)者已提出多種程序測試方法[3-5], 主要分為靜態(tài)和動(dòng)態(tài)兩種測試思路: (1) 靜態(tài)測試?yán)眯问交姆椒╗6](如符號執(zhí)行[7]等), 通過對程序語句的語義建模, 模擬程序的執(zhí)行, 從而達(dá)到對程序正確性的驗(yàn)證.該方法不依賴指令的軟硬件運(yùn)行環(huán)境, 能夠?qū)Τ绦虻姆种нM(jìn)行全覆蓋從而達(dá)到嚴(yán)格的驗(yàn)證結(jié)果, 但隨著程序規(guī)模的擴(kuò)大, 靜態(tài)方法的運(yùn)算量呈現(xiàn)指數(shù)級增長.因別名、分支條件不可解等因素而被迫對程序采取保守的建模方式, 也會為靜態(tài)分析帶來大量誤報(bào)[8]; (2) 動(dòng)態(tài)測試 (如模糊測試[9]等) 給定程序的輸入值, 運(yùn)行測試程序以觸發(fā)程序中的錯(cuò)誤.雖有效避免了誤報(bào), 但在程序分支的覆蓋率上往往很難匹及靜態(tài)方法.此外, 學(xué)術(shù)界也利用靜態(tài)和動(dòng)態(tài)相結(jié)合的方式[10,11]對軟件測試提供新的思路.CASM-Verify[11]結(jié)合隨機(jī)測試和符號執(zhí)行驗(yàn)證密碼算法在X86_64和SSE的匯編實(shí)現(xiàn)上的等價(jià)性, 但缺乏針對RISC-V的支持.
目前, 面向RISC-V的指令或系統(tǒng)級軟件模擬器(如Spike、Qemu[12]、Ovpsim等) 可以為程序提供動(dòng)態(tài)測試環(huán)境.針對某段待測試的匯編程序, 并給定輸入值和期望的輸出值, 開發(fā)者能夠在模擬器上對匯編程序進(jìn)行功能級測試.例如, Ovpsim提供了以測試用例為主導(dǎo)的測試腳本, 開發(fā)者可以通過將程序的運(yùn)行結(jié)果 (某些寄存器或內(nèi)存的值) 與參考值進(jìn)行比對, 以達(dá)到測試的目的.然而, 這種方式并無法滿足語義等價(jià)性測試的需求, 因?yàn)? (1) 現(xiàn)有框架只針對單一測試程序,缺乏對多個(gè)程序語義等價(jià)性測試的框架; (2) 比對程序的實(shí)際輸出值與參考值, 并不足以支持語義等價(jià)性測試, 指令運(yùn)行過程中產(chǎn)生的副作用 (如是否更改了其他寄存器和內(nèi)存的值) 并未納入考慮, 將為程序的正確性和安全性埋下隱患.
本研究基于模擬器的動(dòng)態(tài)測試環(huán)境, 聚焦于設(shè)計(jì)并實(shí)現(xiàn)一套面向RISC-V的匯編程序語義等價(jià)性自動(dòng)化測試系統(tǒng), 旨在提高RISC-V匯編程序的開發(fā)與測試效率.針對已有框架的不足, 本文設(shè)計(jì)并實(shí)現(xiàn)了針對多個(gè)RISC-V匯編程序語義等價(jià)性的測試框架.系統(tǒng)可通過跟蹤機(jī)器狀態(tài), 捕獲程序執(zhí)行的副作用, 并結(jié)合用戶定義的測試目標(biāo)生成測試報(bào)告.實(shí)驗(yàn)表明, 本系統(tǒng)相比已有的測試系統(tǒng), 能夠有效地對RISC-V匯編程序的語義等價(jià)性進(jìn)行測試.
本文結(jié)構(gòu)如下: 第2節(jié)簡要介紹了RISC-V的應(yīng)用程序二進(jìn)制接口; 第3節(jié)對本文研究的問題進(jìn)行形式化描述; 第4節(jié)和第5節(jié)分別對系統(tǒng)設(shè)計(jì)和系統(tǒng)實(shí)現(xiàn)進(jìn)行詳細(xì)介紹; 第6節(jié)介紹了實(shí)驗(yàn)設(shè)計(jì)及結(jié)果并進(jìn)行了案例分析; 第7節(jié)進(jìn)行了總結(jié)和展望.
本節(jié)以RV64G默認(rèn)的LP64D規(guī)范為例, 簡要介紹RISC-V的應(yīng)用程序二進(jìn)制接口 (Application Binary Interface, ABI).ABI規(guī)定了RISC-V二進(jìn)制的寄存器規(guī)范、運(yùn)行時(shí)棧的空間分布、函數(shù)調(diào)用規(guī)范、C語言類型細(xì)節(jié)、ELF文件格式、尋址方式等, 影響了編譯工具鏈中指令生成、寄存器分配、ELF文件生成等諸多方面.了解ABI規(guī)范對于匯編程序的測試至關(guān)重要,例如返回值的傳遞規(guī)范將影響程序的觀測值的選定.本節(jié)簡要介紹其中寄存器規(guī)范、運(yùn)行時(shí)棧分布和函數(shù)調(diào)用規(guī)范3個(gè)方面.
依照LP64D規(guī)范, RISC-V的通用寄存器包含32個(gè)整型寄存器, 32個(gè)浮點(diǎn)寄存器和32個(gè)向量寄存器.
32 個(gè)整型寄存器中, 0號寄存器x0恒為全0; 16個(gè)為調(diào)用者負(fù)責(zé)保存 (caller-saved) , 寄存器值在函數(shù)調(diào)用之后的值可能被修改; 16個(gè)為被調(diào)用者負(fù)責(zé)保存 (calleesaved) , 寄存器值在函數(shù)調(diào)用之后不會更改.其中x10-x17共8個(gè)寄存器為參數(shù)寄存器 (即a0-a7寄存器);32個(gè)浮點(diǎn)寄存器中, 20個(gè)為caller-saved, 12個(gè)為callersaved, 同樣具有8個(gè)參數(shù)寄存器; 32個(gè)向量寄存器 (以及vl、vtype等) 均為caller-saved.
運(yùn)行時(shí)棧負(fù)責(zé)維護(hù)函數(shù)執(zhí)行時(shí)傳遞的參數(shù)、臨時(shí)變量、callee-saved寄存器等.依照LP64D規(guī)范, 運(yùn)行時(shí)??臻g從高地址向低地址生長, 以16字節(jié)對齊.具體的空間分布如圖1所示.
圖1 RISC-V運(yùn)行時(shí)棧的空間分布示意圖
函數(shù)調(diào)用規(guī)范 (calling convention) 主要規(guī)定函數(shù)調(diào)用時(shí)參數(shù)和返回值如何傳遞, 本節(jié)依照LP64D, 主要分為5部分進(jìn)行介紹: 整型參數(shù)、浮點(diǎn)參數(shù)、結(jié)構(gòu)體參數(shù)、變長參數(shù)和返回值.
整型參數(shù)傳遞的主要原則是: 對于大小不超過64 bit的參數(shù), 使用單個(gè)整型參數(shù)寄存器; 對于大小在64 bit和128 bit之間的參數(shù), 使用連續(xù)的兩個(gè)整型參數(shù)寄存器, 低64 bit和高64 bit分別放在寄存器x(n)和x(n+1)中, 若僅剩一個(gè)整型參數(shù)寄存器, 低位使用寄存器, 高位則使用棧傳遞; 若無可用的整型參數(shù)寄存器, 使用棧傳遞, 參數(shù)自右向左依次壓棧.
浮點(diǎn)參數(shù)傳遞的主要原則是: 對于大小不超過64 bit的參數(shù), 使用單個(gè)浮點(diǎn)參數(shù)寄存器, 不足64 bit的浮點(diǎn)數(shù)進(jìn)行1擴(kuò)展; 若無可用的浮點(diǎn)參數(shù)寄存器, 使用整型參數(shù)寄存器代替; 若無可用的整型參數(shù)寄存器, 將參數(shù)自右向左壓棧.
結(jié)構(gòu)體參數(shù)傳遞的主要原則是: 若結(jié)構(gòu)體無浮點(diǎn)數(shù)成員, 可看作1個(gè)整型參數(shù), 依照整型參數(shù)傳遞原則處理, 結(jié)構(gòu)體內(nèi)存分布不變; 若僅包含1個(gè)(或2個(gè))成員, 均為浮點(diǎn)數(shù)且均為超過64 bit, 則使用1個(gè)(或2個(gè))浮點(diǎn)參數(shù)寄存器傳遞; 若僅包含1個(gè)整型成員和1個(gè)浮點(diǎn)成員, 均不超過64 bit, 則分別使用1個(gè)整型和1個(gè)浮點(diǎn)參數(shù)寄存器傳遞; 其余情況均參照整型參數(shù)傳遞原則.
C語言中的變長參數(shù)分為有名和無名參數(shù), 無名參數(shù)以省略號代替, 其類型和個(gè)數(shù)編譯時(shí)未知, 由調(diào)用者通知被調(diào)用者本次調(diào)用的參數(shù)類型和個(gè)數(shù)(例如printf以格式化字符串的形式), 并默認(rèn)執(zhí)行參數(shù)的類型提升(例如將float提升為double).變長參數(shù)傳遞的不同之處在于: 使用連續(xù)的兩個(gè)寄存器傳遞64 bit至128 bit的參數(shù)時(shí), 第1個(gè)寄存器必須為偶數(shù)號; 被調(diào)用者先將所有使用寄存器傳遞的無名參數(shù)進(jìn)行壓棧, 并計(jì)算首個(gè)無名參數(shù)在棧中的地址.
返回值可看作被調(diào)用者向調(diào)用者傳遞的參數(shù), 以前兩個(gè)參數(shù)寄存器傳遞返回值.如果返回值超過128 bit(需要以指針的方式間接傳遞), 調(diào)用者負(fù)責(zé)為返回值在棧中分配空間, 并隱式地將其在棧中的地址作為第1個(gè)參數(shù)傳遞, 實(shí)際的參數(shù)依次后移.
一個(gè)簡單的機(jī)器狀態(tài)模型可將機(jī)器狀態(tài)m用一系列的“鍵-值”對 (k,v)來描述.即:
同時(shí), 值v可由下式表示:
其中, 鍵k可由寄存器名稱或內(nèi)存地址構(gòu)成, 值v可由十六進(jìn)制數(shù)字表示.例如, 可將值為0xffff的寄存器x1表示為 (x1, 0xffff), 值為0x17的內(nèi)存地址0x7f7e9b18表示為(0x7f7e9b18, 0x17).
給定程序p和初始 (輸入) 機(jī)器狀態(tài)mI, 機(jī)器可通過運(yùn)行p得到新的 (輸出) 機(jī)器狀態(tài)mO, 該過程可用p(mI)=mO表示.
我們將一般的基于測試用例的程序測試 (簡稱一般測試) 模型描述為: 對程序p輸入一組初始機(jī)器狀態(tài),并設(shè)置相對應(yīng)的期望輸出的機(jī)器狀態(tài), 通過判斷以下關(guān)系是否成立以測試程序p的正確性:
實(shí)踐中, 輸入的機(jī)器狀態(tài)可由測試的運(yùn)行環(huán)境 (初始化程序、輸入?yún)?shù)等) 給定, 而期望的輸出狀態(tài)卻難以完整描述, 一般只通過某個(gè) (或多個(gè)) 變量的值描述.例如, 針對某個(gè)輸出狀態(tài)mO, 期望的函數(shù)的返回值為1, 可記為mO={(a0, 0x1)}.顯然, 不完整的機(jī)器狀態(tài)描述為測試帶來諸多隱患.例如, 程序滿足期望的輸出值并通過測試, 但其間可能修改了預(yù)期之外的內(nèi)存值或callee-saved寄存器值從而引發(fā)安全問題.
對于兩段程序p0和p1和任意的機(jī)器狀態(tài)m, 嚴(yán)格的語義等價(jià)可描述為:
該定義需對比任意輸入狀態(tài)下的完整輸出機(jī)器狀態(tài)以確保語義的等價(jià), 相比一般測試模型具有更為嚴(yán)格的定義以確保捕獲程序的副作用.顯然, 我們無法負(fù)擔(dān)對任意機(jī)器狀態(tài)的窮舉.另外, 完整地描述機(jī)器狀態(tài)通常是沒有必要的.例如, 在第2節(jié)介紹的ABI中, RISC-V的32個(gè)整型寄存器中有16個(gè)屬于調(diào)用者維護(hù), 允許用戶程序進(jìn)行修改而無須恢復(fù)現(xiàn)場, 若兩個(gè)待測程序?qū)δ承┱{(diào)用者維護(hù)的寄存器進(jìn)行了不同的修改, 仍可認(rèn)為其語義等價(jià).實(shí)際上, 在調(diào)用者維護(hù)的寄存器中,除用于存放返回值的寄存器外, 大部分寄存器無須在測試中進(jìn)行描述.
我們將語義等價(jià)性測試模型描述為三元組(P,M,K), 包括一組待測程序P={p0,p1,p2,···}, 一組作為輸入的機(jī)器狀態(tài)M={m0,m1,m2,···}以及一組作為測試標(biāo)準(zhǔn)的鍵K={k0,k1,k2,···}.當(dāng)滿足以下條件時(shí), 程序通過語義等價(jià)性測試:
與一般的測試模型不同的是, 語義等價(jià)性測試模型: (1) 規(guī)定了一組鍵作為測試標(biāo)準(zhǔn), 通常包括返回值寄存器a0, 被調(diào)用者維護(hù)的寄存器、某些內(nèi)存值等, 遠(yuǎn)遠(yuǎn)多于一般測試的某幾個(gè)觀測值; (2) 無須給定輸入機(jī)器狀態(tài)對應(yīng)的輸出機(jī)器狀態(tài), 即無須由用戶為相應(yīng)鍵k計(jì)算值v.
基于第3.3節(jié)介紹的語義等價(jià)性測試模型, 我們設(shè)計(jì)了面向RISC-V的匯編程序語義等價(jià)性自動(dòng)化測試系統(tǒng).本節(jié)介紹總體框架(4.1節(jié))以及其中的系統(tǒng)配置(4.2節(jié))、測試運(yùn)行(4.3節(jié))和結(jié)果分析(4.4節(jié))3個(gè)環(huán)節(jié).
測試系統(tǒng)的整體流程如圖2所示.其中, 系統(tǒng)配置環(huán)節(jié)準(zhǔn)備了測試所需的測試用例和一組待測程序, 并由用戶配置測試目標(biāo).之后, 系統(tǒng)分別運(yùn)行各個(gè)待測程序并同時(shí)追蹤機(jī)器的狀態(tài)變化, 記錄下在運(yùn)行過程中被更改的機(jī)器狀態(tài).最后, 通過比對各個(gè)待測程序更改的機(jī)器狀態(tài), 結(jié)合測試目標(biāo), 生成語義等價(jià)性測試的測試報(bào)告.
圖2 語義等價(jià)性測試系統(tǒng)整體流程圖
4.2.1 生成測試用例
測試用例的生成[13,14]與本系統(tǒng)相對獨(dú)立, 可以采用手工編寫或在給定約束下自動(dòng)生成兩種方法.與傳統(tǒng)的“輸入-輸出”模式的測試用例不同, 語義等價(jià)性測試并不要求用戶給定期望的輸出值, 只需要比對待測程序的運(yùn)行結(jié)果是否一致即可, 這大大減少了生成測試用例的工作量.本系統(tǒng)采用測試用例的自動(dòng)生成, 以函數(shù)int add(inta, intb)為例, 在給定參數(shù)的數(shù)據(jù)類型int的前提下, 系統(tǒng)隨機(jī)生成一系列測試用例, 如a=1,b=2;a=99,b=-99等.
4.2.2 準(zhǔn)備待測程序
為保持待測程序運(yùn)行前機(jī)器狀態(tài)的一致性, 系統(tǒng)分別對每個(gè)待測程序進(jìn)行“匯編-鏈接”, 生成可執(zhí)行文件 (見圖3).其中測試用例負(fù)責(zé)準(zhǔn)備輸入?yún)?shù)并調(diào)用相應(yīng)的待測程序, 為提示模擬器待測程序運(yùn)行的開始和結(jié)束, 系統(tǒng)在待測程序的開始和結(jié)束插入兩條指示性的自定義指令test_start和test_end, 并同時(shí)在匯編器和模擬器中添加支持.另一個(gè)可行的實(shí)現(xiàn)思路是: 通過保存模擬器的狀態(tài), 實(shí)現(xiàn)在同一狀態(tài)下分別調(diào)用不同的待測程序.
圖3 待測程序生成可執(zhí)行文件流程圖
4.2.3 配置測試目標(biāo)
用戶的測試目標(biāo), 即第3.3節(jié)中的集合K, 在本系統(tǒng)中是可配置的, 覆蓋寄存器及內(nèi)存的值.具體地, 用戶需要配置: (1) 觀測目標(biāo), 包括反映程序功能正確性的寄存器或內(nèi)存的值 (如作為返回值的a0寄存器); (2) 約束條件, 包括是否允許待測程序修改被調(diào)用者維護(hù)的寄存器或其他內(nèi)存值.
單個(gè)待測程序的測試運(yùn)行流程如圖4所示.系統(tǒng)基于RISC-V軟件模擬器對輸入的可執(zhí)行文件進(jìn)行譯碼和執(zhí)行, 在識別自定義的test_start后, 記錄初始的機(jī)器狀態(tài), 運(yùn)行待測程序, 并同時(shí)跟蹤機(jī)器狀態(tài)的變化,直至運(yùn)行test_end后, 記錄最終所有被更改的機(jī)器狀態(tài).
圖4 單個(gè)待測程序測試運(yùn)行流程圖
圖5給出了系統(tǒng)針對一組待測程序的測試運(yùn)行和結(jié)果分析流程圖.系統(tǒng)通過將多個(gè)更改的機(jī)器狀態(tài)進(jìn)行比對, 結(jié)合用戶配置的測試目標(biāo), 形成最終的測試報(bào)告.若所有待測程序在觀測目標(biāo)上產(chǎn)生了相同的結(jié)果并滿足其他約束條件, 則測試通過.
圖5 一組待測程序測試運(yùn)行及結(jié)果分析流程圖
本系統(tǒng)基于Spike模擬器實(shí)現(xiàn).支持?jǐn)U充的指示性指令 (test_start和test_end) 所需的匯編器基于LLVM[15]后端實(shí)現(xiàn), 鏈接器使用RISC-V的gcc工具鏈(C庫使用newlib).向量擴(kuò)展指令的指令編碼及Spike模擬器功能實(shí)現(xiàn)遵循RVV-0.9規(guī)范.
系統(tǒng)利用偽隨機(jī)數(shù)實(shí)現(xiàn)了支持常用數(shù)據(jù)類型的測試用例隨機(jī)自動(dòng)生成的函數(shù)庫, 包括整型、浮點(diǎn)型、字符型、字符串型等.用戶通過提供參數(shù)類型, 取值范圍等信息可自動(dòng)生成隨機(jī)的測試用例.
系統(tǒng)支持通過JSON文件配置測試目標(biāo) (示例見圖6).其中, 決定程序正確性的觀測目標(biāo)可用戶根據(jù)ABI和程序的具體功能手動(dòng)設(shè)置, 默認(rèn)觀測目標(biāo)為a0寄存器.另外, 如圖6所示, 用戶可直接配置是否允許修改調(diào)用者維護(hù)的寄存器和其他內(nèi)存值.
圖6 測試目標(biāo)配置文件config.json示例
系統(tǒng)在運(yùn)行test_end指令時(shí)輸出寄存器文件以對比寄存器值.對于內(nèi)存值, 系統(tǒng)采取在模擬器插樁的方式監(jiān)測內(nèi)存變化.具體地, 在運(yùn)行test_start指令后, 所有store類指令會額外記錄目標(biāo)內(nèi)存地址, 并在執(zhí)行test_end是輸出記錄的內(nèi)存地址及內(nèi)存值.
圖7展示了針對某個(gè)測試用例的測試報(bào)告模板,分為測試結(jié)果、觀測目標(biāo)、內(nèi)存及被調(diào)用者維護(hù)的寄存器和其他.
圖7 測試報(bào)告模板
本實(shí)驗(yàn)旨在驗(yàn)證語義等價(jià)性測試系統(tǒng)的有效性,并以一般的測試系統(tǒng)為基準(zhǔn), 評估其測試效果和時(shí)間開銷.實(shí)驗(yàn)運(yùn)行于Intel Core i7-9700 CPU以及32 GB內(nèi)存的機(jī)器, 使用Ubuntu 20.04操作系統(tǒng), 實(shí)驗(yàn)結(jié)果均為運(yùn)行5次的平均值.
我們選取5個(gè)常用C函數(shù) (見表1) 作為系統(tǒng)的測試集, 每個(gè)函數(shù)具有一個(gè)標(biāo)準(zhǔn)版本、一個(gè)基于RVV的向量版本以及兩個(gè)變異版本.為充分驗(yàn)證系統(tǒng)的測試有效性, 我們基于每個(gè)向量版本, 為程序引入副作用:(1) 將程序中某個(gè)被調(diào)用者維護(hù)的寄存器更改為調(diào)用者維護(hù)的寄存器形成變異版1; (2) 在程序中插入一條store指令更改某個(gè)指針參數(shù)對應(yīng)的內(nèi)存中的值形成變異版 2.表1 中, “9、11”表示 memcpy 的兩個(gè)變異版本的代碼量分別是9和11.
表1 常用C函數(shù)組成的匯編程序測試集
為驗(yàn)證本系統(tǒng)的有效性, 實(shí)驗(yàn)采取隨機(jī)生成的1000個(gè)測試用例分別對 “標(biāo)準(zhǔn)-向量”“標(biāo)準(zhǔn)-變異1”和“標(biāo)準(zhǔn)-變異2”共3組程序進(jìn)行語義等價(jià)性測試 (簡稱等價(jià)測試), 并同時(shí)運(yùn)行一般測試 (僅比較函數(shù)觀測目標(biāo)) 作為對比.
表2給出了測試結(jié)果和時(shí)間開銷.語義等價(jià)的 “標(biāo)準(zhǔn)&向量”組通過了一般測試及等價(jià)測試.但面對具有副作用的兩個(gè)變異版本, 一般測試依然顯示通過, 而等價(jià)測試通過更加嚴(yán)格的機(jī)器狀態(tài)對比, 有效地捕獲到副作用, 并報(bào)告測試未通過.
表2 基于1000個(gè)測試用例的一般測試和等價(jià)測試的有效性和時(shí)間對比
時(shí)間開銷方面, 等價(jià)測試由于追蹤、記錄、對比機(jī)器狀態(tài)而相比一般測試平均帶來約350%的額外時(shí)間開銷.但由于等價(jià)測試通常針對函數(shù)級的程序, 時(shí)間開銷通常是可接受的.例如向量計(jì)算函數(shù)saxpy在1000個(gè)測試用例下僅用時(shí)0.14 s左右.
本節(jié)以函數(shù)strlen為例, 分析系統(tǒng)的測試有效性.表3給出了strlen的函數(shù)聲明、某測試用例、正確的返回值以及測試目標(biāo).該函數(shù)運(yùn)行結(jié)束后, 由標(biāo)準(zhǔn)版函數(shù)計(jì)算觀測得出, 觀測目標(biāo)a0應(yīng)為0x20, 即整數(shù)32.另外, 用戶規(guī)定被調(diào)用者維護(hù)的寄存器和運(yùn)行時(shí)棧以外的內(nèi)存值在函數(shù)運(yùn)行后不允許修改.
表3 函數(shù)strlen的測試用例及測試目標(biāo)
函數(shù)strlen的向量版本及相應(yīng)的兩個(gè)變異版本的生成方式見圖8.其中, 變異版1修改了被調(diào)用者維護(hù)的寄存器, 而變異版2修改了內(nèi)存值, 二者均不滿足測試目標(biāo).傳統(tǒng)的基于返回值對比的測試方法因無 法捕獲此類副作用而通過了測試.基于語義等價(jià)性測試系統(tǒng), 我們分別對“標(biāo)準(zhǔn)-向量”“標(biāo)準(zhǔn)-變異 1”和“標(biāo)準(zhǔn)-變異2”這3組程序進(jìn)行測試.三者的測試報(bào)告見圖9.其中, 正確的向量版函數(shù)產(chǎn)生了與標(biāo)準(zhǔn)版相同的觀測值(a0) , 并未引入任何違反測試目標(biāo)的副作用, 成功通過測試.變異版1修改了被調(diào)用者維護(hù)的寄存器s1, 變異版2修改了內(nèi)存地址0x23f00的值, 均違反了測試目標(biāo)而未通過測試.
圖8 函數(shù)strlen向量及變異版本
圖9 函數(shù)strlen的測試報(bào)告
此案例表明, 本系統(tǒng)能夠通過跟蹤機(jī)器狀態(tài), 結(jié)合測試目標(biāo), 有效地捕獲程序產(chǎn)生的副作用, 提供嚴(yán)格有效的語義等價(jià)性測試.
本研究基于Spike模擬器, 設(shè)計(jì)并實(shí)現(xiàn)一套面向RISC-V的匯編程序語義等價(jià)性自動(dòng)化測試系統(tǒng), 通過比對不同程序運(yùn)行后的全機(jī)器狀態(tài) (寄存器、內(nèi)存等) ,結(jié)合用戶配置的測試目標(biāo), 自動(dòng)完成測試并生成測試報(bào)告.實(shí)驗(yàn)表明, 本系統(tǒng)可成功捕獲程序運(yùn)行的副作用,為語義等價(jià)性有效地提供了更為嚴(yán)格的測試環(huán)境.
基于本系統(tǒng), 未來可通過以下3方面繼續(xù)提高語義等價(jià)性測試的有效性和易用性: (1) 結(jié)合模糊測試技術(shù), 更為有效地生成測試用例, 提高測試的分支覆蓋率和有效性; (2) 結(jié)合缺陷定位技術(shù)[16,17], 為語句進(jìn)行可疑性排序, 提高系統(tǒng)的易用性; (3) 采用動(dòng)靜結(jié)合的方式[11]對匯編語義等價(jià)性進(jìn)行更為嚴(yán)格的測試.