摘 要:軟件內建自測試是軟件測試和可測性設計研究領域中的一個新概念,其思想來源于硬件內建自測試BIST(BuildinSelfTest)。軟件內建自測試為程序員提供一套預先設計好的模板,由模板對所編寫的程序植入測試信息,實現軟件內建自測試以解決軟件測試難的問題。模板是軟件內建自測試系統的基石,其內容關系到整個系統的性能和效果。具體討論了模板的實現,根據軟件故障模型對代碼進行改裝,從而減少程序出錯的概率,同時為軟件內建自測試系統中測試用例的生成提供了更豐富的信息。
關鍵詞:軟件內建自測試; 可測性; 模板函數; 程序插裝; 故障模型
中圖法分類號:TP393 文獻標識碼:A 文章編號:1001-3695(2006)10-0078-04
Research and Implementation of Template Content in
BuildinSelfTest for Software
PENG Chenghan, XU Shiyi
(College of Computer Engineering Science, Shanghai University, Shanghai 200072, China)
Abstract:The BIST (BuildInSelfTest) for software is a new concept in software testing and design for testability, which comes from the BIST for hardware testing. The main idea is Template can be designed in advance, which can be used to embed in the program under test. The test complexity can be reduced when this method applied to the software testing. Template is a cornerstone in the BIST for software, which affect the effectiveness and efficiency in software testing. This paper discusses the implementation of Template in detail. According to the fault model for software, the program can be optimized by program instrumentation. Furthermore, there are more useful information can be generated for the test case.
Key words:BuildInSelfTest for Software; Testability; Template Function; Program Instrumentation; Fault Model
1 引言
在信息科技高速發展的今天,軟件已廣泛應用于社會的各個領域中。但是伴隨著軟件工程規模的迅速膨脹,人們在開發軟件的過程中難免會引入錯誤。軟件測試是保證軟件質量和可靠性的重要手段,它能夠發現軟件中隱藏的許多錯誤。因此如何盡可能地通過軟件測試來檢測和預防錯誤,成了目前軟件工程領域中的一個重要研究課題。
BIST技術在硬件的可測性設計上是一個比較成熟的技術。在硬件設計時,為了方便測試,根據電路自檢測試的原理,增加一些額外的自檢測試電路。在進行測試時根據電路內部的某些功能,使電路可以由正常模式切換到測試模式,通過附加電路進行自測試。軟件內建自測試正是根據這種思想,在編寫軟件的過程中插入一些自檢測程序的函數,從而達到軟件內建自測試的目的。
軟件內建自測試系統的基本結構主要包括兩大部分,即模板(Template)部分和自治測試部分。其基本思想是:首先為程序設計員提供一套預先設計好的模板,這套模板要求程序員在編寫程序時必須滿足模板中提出的相關條件,并在模板中輸入所要求的有關數據[1],由模板對所編寫的程序進行包裝(包括設置Checkpoint策略[2]、測試用例生成策略[3]、預期結果輸入、代碼改裝等);然后由自治測試部分根據模板中信息生成測試用例,并在Checkpoint上進行比較和測試,以完成基本的測試功能。其基本框架如圖1所示。
在軟件內建自測試中,主要圍繞故障模型為主進行設計研究[4]。軟件故障模型(Software Fault Model)就是一些基本故障的組合,它一般有兩個方面的用途:①它能夠提供測試所必須的信息;②它可以用于開發預測。軟件故障模型應該是軟件物理錯誤的抽象,并能反映其本質的一定程度的組合。建立故障模型的好處是使用模型中給出的信息就可以設計和編制算法,用來產生可以運行的測試用例[5]。
本文主要研究根據故障類型對代碼進行改裝,從而減少程序出錯的概率,同時為生成測試用例提供豐富的信息。
2 相關概念
BIST系統運行過程如下:①在BIST系統中編寫程序或者打開源程序文件,當執行BIST系統后,BIST系統的預編譯器對代碼進行相應的詞法分析、語法分析和簡單的語義分析;②BIST系統的內建部分根據掃描到的信息結合模板中的信息插入相應的自測函數,同時生成測試用例;③運行測試程序驗證程序并生成測試報告。在此給出幾個相關的定義:
定義1 模板是為軟件自測試項目而生成的一個信息庫,其中存有軟件自測試所需的軟件配置、測試配置、測試工具配置等信息。
定義2 模板函數是針對故障模型(如空指針、數組越界等)而插入的函數,模板函數可分為保護函數和檢測函數。
定義3 保護函數是為了保證程序能夠正確執行而插入的函數。
定義4 檢測函數是為了檢測程序的某些特定故障所插入的函數。
模板函數的實現是對原來的程序重新進行改造、重組,在系統中主要采用程序插裝技術來實現的[6]。
3 模板內容的研究與實現
由模板函數的定義和分類可知需要根據故障模型來產生相應的插入函數,所以要分析不同的故障模型,從而實現不同類型的插入函數。這些模板函數是模板在實現內建自測試時的主要內容。
在BIST系統中模板有測試模態和正常模態兩種模態。在測試模態下模板會進行相應的工作(如插入模板函數、與程序員進行回話等),而在正常模態下不進行操作。進一步來說,正常模態是在測試模態之后,用于程序發布,以便提高運行效率。在BIST系統中主要通過預編譯命令來實現這兩種模態的轉換。模板函數的插入僅在測試模態下進行。
3.1 保護函數的研究與實現
根據上述對保護函數的定義結合編譯器的特點對保護函數分成強保護函數和弱保護函數兩類。強保護函數是針對編譯器本身的不足而插入的函數。例如,在一般的C語言編譯器中對數組越界不加以檢測,由程序員對程序負責,這樣大大加重了程序員的負擔,但由于程序員的失誤極易引起程序的崩潰,因此BIST系統需要對程序插入此類函數。弱保護函數是針對編譯器能夠檢測的故障,但編譯器并不處理,如果用戶在一定情況下激活故障同樣會引起程序的崩潰。下面分別對這兩類保護函數進行闡述。
3.1.1 強保護函數示例
以數組(僅以字符數組說明)越界為例,如果程序中出現下面的代碼strcpy(str1,str2)。對于這樣一個函數,若str2的長度超過了str1的長度,str2超過str1部分的字符串很可能將串str1的結束標志擦除,這就有可能引起程序的崩潰。對于這種情況,應插入強保護函數:
if (strlen(str2) { strcpy(str1,str2); } else { 字符串截斷處理; 提示信息 } 上述代碼可以保證程序的正確執行,同時改裝后的代碼還有一些其他的優點,提高了可移植性,具體參見文中模板函數的實驗和結果這一部分。 3.1.2 弱保護函數示例 以scanf[7]函數為例,對于這樣的一個程序段: int x,y; scanf(\"%d,%d\",x,y); 該代碼段是用戶輸入一串數字,數字以逗號隔開,但是假設用戶鍵入“10 20”(沒有逗號),則x被賦值為10,而y被賦值為某個不可見的數。因此在鍵盤輸入時,最好能夠接受字符串執行的語法檢查以獲得正確的輸入。另外,初學者很容易忽略x和y前面的地址符號,所以在輸入后應該將程序輸入與用戶的輸入進行驗證。BIST系統插裝后的代碼如下: #include char str[128]; fgets(str,128,stdin); if ((char * s=strchr(str,′,′))==NULL) /*strchr(str,′,′)找到第一個出現在串中的\",\",否則返回NULL。參見MSDN*/ scanf(str,\"%d %d\", x,y); else scanf ( str,\"%d , %d\", x,y); printf (\"x=%d,y=%d\",x,y);//最后的輸出驗證 示例演示了這樣的功能:首先判斷輸入序列是否存在字符“,”,然后選擇不同的scanf函數(本示例僅僅是對非字符輸入的改裝),在最后輸出初試輸入的結果供測試人員驗證。插入這樣的弱保護函數不僅可以減少程序出錯的幾率;而且可以根據分支覆蓋為生成測試用例提供更有利的信息。 3.2 檢測函數實現的研究 由于程序員編寫代碼是一種創造性的工作,為此需要對于一些容易出錯的代碼進行檢測。首先需要對這些易出錯的代碼進行統計,其次需要對這些代碼根據實際情況插入相應的檢測代碼。BIST系統對被測代碼插入檢測函數以檢測故障,主要通過Checkpoint[2]來實現。例如對于以下代碼: if(a>b)x=a; else x=b; 我們可以通過插入檢測函數完成許多功能,如可以知道分支覆蓋的情況或者程序運行到此處的運行結果;可以統計分支覆蓋;根據分支覆蓋生成測試用例等。故可插裝成如下的代碼,具體方法以及插裝策略參見文獻[2]。 if(a>b){x=a;printf(\"if路徑運行\");} else{x=b;printf(\"else路徑運行\");} 4 軟件BIST系統中針對特定故障的處理 BIST系統的目標當然是將盡可能多的故障檢測到,但是由于軟件故障的錯綜復雜,要實現這一目標幾乎是不可能的。其中一個原因是大多數供應商提供的集成開發環境是根據ANSI C的標準的,而ANSI C標準的一部分內容是建立在對程序員信任的基礎上。這就導致了集成開發環境提供的編譯器忽略了一些很重要的信息。在BIST系統中針對未初始化變量、空指針等部分故障的檢測,實現了預編譯處理器,對編譯器功能的補充,根據預編譯處理器提供的信息可以輕松地檢測這些故障。 以未初始化變量這類故障為例,BIST系統中提取了變量的信息,可以檢測這一故障。在BIST系統預編譯器中變量的數據結構如下: struct var_init{ char [20] namespace; //變量作用范圍 int line;//行號 char [20] type;//類型 char [20] name;//變量名 char lvalue;//左值標志:#表示有操作,-表示無操作 char rvalue;//右值標志:#表示有操作,-表示無操作 char state;//變量狀態 struct var_init *next;//變量鏈表 } 根據程序的書寫格式,基本上可以將變量的操作方式歸結為以下幾種情況,而變量未初始化就是下面的D標記狀態,如表1所示。 表1 預編譯處理器掃描源程序對數據結構填充的例子 變量作用 范圍行號類型變量名左值右值范 例標記下一個 變量intx--int x;Afloatx#-float x=0;Bx-#F(x=3,y=x*4);*Dx##x=x+1;C/Dx#-x=1;B表1中*的位置主要是為了說明逗號運算符的一種實例。C/D表示狀態是C或者D,具體取決于前一個狀態。 根據以上情況的總結,可以根據自動機的理論來實現變量未初始化的檢測,如圖2所示。值得一提的是這種方法也可以檢測空指針的部分故障,在實現的過程中可以一起對這些故障進行檢測,在此為了簡明而不考慮其他故障。 BIST系統對每一行代碼進行掃描,為每一個變量都生成上述數據結構,同時根據掃描的代碼對變量的數據結構進行填充。當變量狀態轉移到狀態B則不需要再對變量進行跟蹤說明變量已經初始化;如果變量狀態轉移到狀態D則說明變量未初始化;如果變量狀態轉移到狀態C則說明使用了未定義的變量;如果變量狀態轉移到狀態A則說明變量定義未使用(在BIST系統中不處理后兩類故障,編譯器可以檢測這兩類故障),算法如下: //變量狀態掃描分析 While(源代碼未掃描結束) { var=getscanvar();//var是接收掃描到的變量 if(var不在變量狀態鏈表中)產生新節點,插入到變量狀態鏈表中; else if(var.state==B)var.state=B; else if(getVarPorperity()==lvalue) //變量只具有左值操作或賦值操作等 var.state=B; else if(getVarPorperity()==rvalue )//變量具有右值操作 switch(var.state ) { S:{var.state=C;報錯;//此錯誤編譯器可發覺,可以不處理} break; A:{var.state=D;報錯;//變量未初始化} break; B:var.state=B; break; C:var.state=C; break; default: break; } else var.state = A; } //處理變量狀態報錯信息,并回收資源 pnext=varList->next; while(pnext!=NULL) { if(pnext.state==B)銷毀節點; else (pnext.state==C)變量未初始化,根據變量的數據結構顯示出錯位置; /*其他不用處理,編譯器會報警告錯誤:變量未使用或使用未定義的變量*/ pnext++; } BIST系統的預編譯器主要完成詞法、簡單的語法和語義分析,我們不考慮編譯器能夠檢測的常規錯誤,主要是針對編譯器不能處理的故障實現預編譯器。 5 模板函數的組織 在BIST系統中的模板會根據代碼的情況插入相應的保護函數和檢測函數。所以模板函數應該按照不同的類型(如字符串、輸入/輸出、網絡通信等)、不同的使用頻率(使用頻率高的放在高層,可以讓不同的類型繼承)等進行組織[8,9]。這樣既有利于模板的管理也減少了運行的代價。在這一部分中,我們需要將不同的插入函數放在不同的頭函數(或“包”)中,在使用時可以根據程序員的輸入方便地插入函數。借鑒其他集成環境中庫函數的組織,在BIST系統中我們根據以下規則來組織模板函數: (1)大眾化錯誤(即常見錯誤,如未初始化變量、空指針等)靠前; (2)針對編譯器的增強、改進的模板函數靠前; (3)模板函數的組織按照分類進行; (4)當規則沖突或插入函數符合多個規則時,以存儲方便作為優先考慮。 目前,BIST系統中已經開發的類別有:①BIST_stdio,針對輸入/輸出部分的模板函數;②BIST_string,針對字符串的模板函數;③BIST_compiler,BIST系統的預編譯器。我們還計劃進一步完善針對內存(BIST_memory)、指針(BIST_point)、數學函數(BIST_math)、文件(BIST_file)等的其他類別的模板函數[10]。 6 模板應用實驗及結果 限于篇幅的關系,在本文中僅給出一個示例來演示模板的使用。對下面的例子[11]在BIST系統上進行測試和分析。為了便于比較和分析結果,對原來的代碼和通過BIST系統模板改裝后的代碼分別在三種不同的編譯器(Turbo C 2.0,Visual C++ 6.0和Linux gcc)上進行測試。 #include #include main() { char*cp1,*cp2; cp1=\"12345\"; cp2=\"abcdefghij\"; strcpy(cp1,cp2); printf(\"cp1= %s\ cp2 = %s \\",cp1,cp2); } 該程序在TC2.0下的運行結果如下: cp1=abcdefghij cp2=ghij 該程序在VC6.0下的運行結果是程序中止,引發程序異常,且無任何提示;該程序在Linux下的運行結果是程序可以運行,無輸出,且無任何提示。 不管在以上任何編譯器下,用戶得到的結果均是錯誤且不可理解。在TC下cp2所指的串被修改了,在VC6.0和Linux下程序運行無結果,且無提示。由此說明在程序中存在錯誤,仔細閱讀和理解程序后,發覺這一錯誤正是由于字符串數組的越界引起的。對于這樣的程序在BIST系統中模板會插入強保護函數。考慮到本例相對來說比較簡單,因此對這一小程序沒有進行測試點的設置,相應部分可參考文獻[2]。 #include #include main() { char*cp1,*cp2; cp1=\"12345\"; cp2=\"abcdefghij\"; BIST_strcpy(cp1,cp2); printf(\"cp1= %s\ cp2 = %s \\",cp1,cp2); } 其中BIST_strcpy(cp1,cp2)是模板插入的函數,被組織在BIST_string中。其基本結構如3.1.1中所示。具體對于這個程序來說代碼如下: BIST_strcpy(char *des,char *src) { /*strlen(src)和strlen(des)分別是BIST系統通過對源代碼的掃描得到的常量,此處這樣寫是為便于理解*/ if(strlen(src)<=strlen(des)) { strcpy(des,src); } else { while(*des) *des++=*src++; printf(\"警告:源串沒有完全拷貝到目的串中,發生了截斷\"); } } 插裝后的程序在TC2.0,VC6.0以及Linux下的運行結果相同,如下: 警告:源串沒有完全拷貝到目的串中,發生了截斷 cp1=abcde cp2=abcdeghij 由此可以看出,使用BIST系統不僅可以保證程序的正確性,而且有較好的可移植性,消除了不同編譯器的細微差別。通過這個簡單的例子,演示了模板的使用,同時驗證了BIST思想的可行性。 7 小結 在軟件內建自測試中,模板是系統的基石,其內容關系到整個系統的性能和效果。本文主要探討了根據故障模型的類型,插入強保護函數、弱保護函數和檢測函數;還研究了針對特定故障模板的處理方式。借鑒了其他集成系統的特點以及結合軟件BIST的特色對模板函數進行了組織。最后針對字符數組越界故障給出了一個示例并通過BIST系統進行測試和驗證。 參考文獻: [1]凌良合,徐拾義.基于軟件內建自測試的模板和基準程序設計[J].同濟大學學報(自然科學版),2002,30(10):11591163. [2]徐拾義,李文.軟件內建自測試中測試點的設計及實現[J].同濟大學學報(自然科學版),2004,32(8):10571060. [3]李文鋒,徐拾義.軟件內建自測試中的測試數據生成方法[C].北京:第十屆全國容錯計算學術會議論文集,2003.263-266. [4]朱榮,徐拾義.軟件測試中故障模型的建立[J].計算機工程與應用,2003,39(17):69-71. [5]宮云戰.軟件測試的故障模型[J].裝甲兵工程學院學報,2004,18(2):15. [6]孫昌愛,金茂忠.基于程序插裝的動態測試技術實現[J].小型微型計算機系統,2001,22(12):14751479. [7][美]Charles W, et al. Visual C++程序員實用大全[M].鄧勁生,張曉明.北京:中國水利水電出版社,2001.85. [8]Wang Y, King G, Fatad M, et al. On Builtin Test Reuse in ObjectOriented Framework Design[J]. ACM Journal on Computing Survey, 2000,32(1):712. [9]Wang Y,King G,Court I, et al. On Builtin Tests in ObjectOriented Reengineering[C]. Proceedings of the 5th ACM Symposium on FSE/The 6th European Conference on Software Engineering/Workshop on ObjectOriented Reengineering(FSE/ESEC/WOOR’97), 1997. [10]Erich Gamma, et al.設計模式可復用面向對象軟件的基礎[M].李英軍,馬曉星,蔡敏.北京:機械工業出版社, 2000.91146. [11]陳意云.編譯原理和技術(第2版)[M].合肥:中國科學技術大學出版社,2002.195196. 作者簡介: 彭成寒(1980-),男,河南南陽人,碩士研究生,主要研究方向為軟件測試;徐拾義(1941-),男,上海人,教授,主要研究方向為計算機容錯、測試。 注:本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文