王 豐,印 釗
(武漢郵電科學研究院湖北武漢430074)
隨著信息技術的不斷發(fā)展,軟件開發(fā)周期越來越短,如何快速有效地檢驗軟件的可靠性成為一個重要問題。運行時驗證技術是一種軟件測試技術,其思想是通過持續(xù)監(jiān)控并采集程序運行狀態(tài)信息來驗證軟件運行是否符合規(guī)范。早期的運行時驗證技術受限于單核處理器(Central Processing Unit,CPU)的性能不足,使用其測試軟件會影響目標軟件的運行速度,導致測試效率降低、測試成本增加。近幾年多核CPU電腦已經(jīng)大規(guī)模普及,研究基于多核CPU的運行時驗證技術對于改進傳統(tǒng)的運行時驗證技術尤為重要。
本文在基于多核CPU的Ubuntu平臺上,對兩個不同的開源程序針對數(shù)組引用越界這種軟件錯誤進行運行時驗證技術的研究。通過實驗數(shù)據(jù)得出了基于多核CPU的運行時驗證技術能夠有效提高監(jiān)控軟件效率、增加軟件可靠性的結(jié)論。
驗證技術是一種檢驗系統(tǒng)的行為是否符合規(guī)范的技術,主要的驗證技術有定理證明、模型檢測和測試3種[1]。定理證明和模型檢測主要從宏觀層面通過公式推導和建模來驗證系統(tǒng)的正確性,但一個正確的系統(tǒng)運行之后仍可能出現(xiàn)一些無法預料的錯誤,這就需要通過測試的方法,從微觀層面檢驗系統(tǒng)的正確性。運行時驗證技術是一種測試方法[2]。該技術的思想是:運行程序前對軟件進行相關配置,設計一個監(jiān)控器,如果程序在運行過程中出現(xiàn)了不符合規(guī)范的異常,監(jiān)控器能夠?qū)崟r記錄和匯報軟件運行狀況。運行時驗證技術具有易于部署、可降低軟件測試的成本、可提高軟件測試效率等優(yōu)點。
運行時驗證技術一般通過插樁的方法來實現(xiàn)。插樁是在程序源代碼中插入一段自定義的代碼,這段自定義的代碼的作用是收集記錄源程序的運行信息,比如數(shù)組引用情況、指針引用情況、函數(shù)的調(diào)用情況等評估程序運行狀態(tài)的重要指標。插入的自定義代碼叫做樁代碼,樁代碼不能影響程序結(jié)果,也不能破壞程序執(zhí)行邏輯的完整性[3]。由于樁代碼需要實現(xiàn)信息獲取與傳遞功能,在運行程序時必然會占用系統(tǒng)資源、增加運行時間,這就是使用運行時驗證技術所帶來的性能損耗。
CPU是一臺計算機的運算核心和控制核心。它的功能主要是解釋計算機指令以及處理計算機軟件中的數(shù)據(jù)[4]。目前常用的多核CPU通常指的是單芯片多處理器(Chip Multiprocessors,CMP)。CMP的思想是將大規(guī)模并行處理器中的對稱多處理器集成到同一芯片內(nèi),各個處理器并行執(zhí)行不同的進程[5]。這種依靠多個CPU并行地運行程序的方式被稱為并行處理。并發(fā)和并行是兩個常用的計算機概念。并發(fā)是指當有多個線程在操作,而系統(tǒng)只有一個CPU時,宏觀上有多個程序在同時運行,但微觀上這些程序只是串行地交替執(zhí)行,在同一時刻只能有一個程序被執(zhí)行[6]。并行是指當系統(tǒng)有不止一個CPU時,不同的線程可以在不同的CPU上執(zhí)行,線程之間互不占用CPU資源,屬于真正的同時運行。
進程是系統(tǒng)進行資源分配和調(diào)度的基本單位,一個進程可以分為兩個部分:線程集合和資源集合。線程是進程中的一個動態(tài)對象,進程中的所有線程將共享進程里的資源。實現(xiàn)基于多核CPU的運行時驗證技術需要使用線程并行的編程技術。線程并行編程的主要內(nèi)容是解決共享內(nèi)存、消息傳遞和數(shù)據(jù)并行這3個方面的問題。本文使用POSIX threads線程庫,簡稱Pthreads線程庫[7],進行線程的相關操作。Pthreads線程庫是Linux系統(tǒng)中多線程編程的標準庫。與大多數(shù)線程庫的功能相似,Pthreads線程庫具有線程的創(chuàng)建和終止、線程同步、設置條件變量與信號量等功能[8]。
緩存溢出是一種輸入數(shù)據(jù)超出緩存區(qū)大小的程序錯誤,可導致程序運行出錯或者崩潰[9]。數(shù)組是程序中最常用的一種緩存區(qū),函數(shù)調(diào)用數(shù)組的數(shù)組名出錯、調(diào)用數(shù)組的下標超過了數(shù)組定義的大小等錯誤被稱為“數(shù)組越界”。數(shù)組越界是一種緩存溢出的表現(xiàn),是一種常見的程序錯誤。本文通過檢驗數(shù)組越界這一錯誤來證明運行時驗證技術的有效性。
本文的設計思想是:設置兩個線程,一個線程負責運行目標程序,另一個線程負責監(jiān)控目標程序運行狀況,在運行目標程序之前,修改目標程序的源代碼,在源代碼中插入負責監(jiān)控的樁代碼。為了模擬程序運行時出現(xiàn)錯誤,使用數(shù)組越界這種錯誤作為各種程序錯誤的代表。最后在其他條件相同的情況下使用單核CPU和多核CPU進行對比實驗,得到實驗結(jié)果和結(jié)論。下面是相關步驟的具體設計。
本文的實驗需要創(chuàng)建兩個線程:一個目標程序線程,一個監(jiān)控器線程。在目標程序運行的過程中,監(jiān)控器線程負責定時收集程序運行信息。一般的多核CPU系統(tǒng)能夠自動分配線程給不同的核心,但如果使用核綁定技術將目標程序線程和監(jiān)控器線程分別綁定到用戶自定義的處理器核心上可以使效率更高。監(jiān)控器線程如果一直處于運行狀態(tài),會占用系統(tǒng)資源,導致目標程序性能降低,因此需要根據(jù)目標程序使用數(shù)組的次數(shù)來決定是否喚醒監(jiān)控器線程,本次實驗把使用數(shù)組次數(shù)設為3000。每使用3000次數(shù)組,就喚醒監(jiān)控器線程來處理信息,判斷這3000次數(shù)組使用中是否出現(xiàn)了數(shù)組越界的錯誤。監(jiān)控器線程在對這些數(shù)據(jù)進行處理的過程中,會將每一個數(shù)據(jù)的處理結(jié)果寫入文件。故監(jiān)控器線程的工作流程為:
1)監(jiān)控器線程檢測目標程序進程是否使用數(shù)組,不是則休眠,使用數(shù)組則進入下一步;
2)記錄使用的數(shù)組信息,計數(shù)number(初始為0)加1;
3)number是否等于3000,等于則進入下一步,小于則返回第1步;
4)監(jiān)控器線程集中處理之前收集的3000次數(shù)組的數(shù)據(jù),處理完成后number清零。
5)結(jié)束。
兩個線程之間的通信使用一個結(jié)構(gòu)體數(shù)組來實現(xiàn)。下面是兩個線程用來通信的全局結(jié)構(gòu)體數(shù)組的定義:
該結(jié)構(gòu)體傳遞的信息是目標程序運行中用到的數(shù)組下標。這里定義一個全局整形變量number作為結(jié)構(gòu)體數(shù)組的下標。本次實驗希望通過運行時驗證技術來檢驗數(shù)組越界這一錯誤。檢驗數(shù)組是否越界的方法為:目標系統(tǒng)運行過程中,每次使用數(shù)組的信息都被會用結(jié)構(gòu)體數(shù)組communication[i]里保存。獲取結(jié)構(gòu)體數(shù)組下標的最大值、最小值和下標值subscript,如果使用的數(shù)組的下標subscript比min小或者比max大,那么可以認為目標程序運行過程中發(fā)生了數(shù)組越界的錯誤。每次目標程序調(diào)用一次數(shù)組就把結(jié)構(gòu)體數(shù)組的信息存入一個文件中,按順序分配行號和列號,如圖1所示。
圖1 保存使用過的數(shù)組信息
如果檢測到發(fā)生數(shù)組越界的錯誤,那么將出錯的數(shù)組的行號列號存入到“error.txt”文件里。如圖2所示。
運行時驗證技術對不同程序的驗證效率是不同的,為了得出更嚴謹?shù)慕Y(jié)論,本次實驗使用PPETS math functions和Qsort兩個開源目標程序進行對比實驗。PPETS math functions是一款是計算的軟件,具有計算量大,運行時間較長,大量使用數(shù)組的特點。
圖2 保存產(chǎn)生錯誤的數(shù)組信息
Qsort程序是編譯器自帶的快速排序程序,通過調(diào)用math.h頭文件中的qsort()函數(shù),程序會使用快速排序算法對數(shù)據(jù)進行排序,Qsort程序的代碼規(guī)模與復雜度遠小于PPETS math functions程序。
本次實驗把目標程序的執(zhí)行時間作為運行時驗證技術的性能判定的參考值,在其他條件相同的情況下,程序執(zhí)行時間越短表示性能越好。在Linux操作系統(tǒng)下,可以通過time命令來獲取一個程序的執(zhí)行時間。
為了證明多核CPU能夠加速實現(xiàn)運行時驗證技術,本次實驗設計5種場景:
1)單核平臺下直接運行目標程序;
2)單核平臺下使用運行時驗證技術,運行目標程序;
3)多核平臺下直接運行目標程序;
4)多核平臺下不進行核綁定,使用運行時驗證技術,運行目標程序;
5)多核平臺下進行核綁定,使用運行時驗證技術,運行目標程序。
本次實驗使用的處理器為Intel酷睿i3-5005U,CPU主頻2 GHz,雙核心,操作系統(tǒng)是Linux Ubuntu,編程語言為C語言。實驗通過編寫代碼并運行得到實驗結(jié)果,實現(xiàn)本次實驗的關鍵就是插樁代碼和線程控制的代碼。
本次實驗兩個目標程序PPETS math functions和Qsort都是可以直接運行的,因此需要在目標程序中插入作為記錄程序運行信息的樁代碼,如下所示:
以上代碼的作用是如果目標程序運行時使用了數(shù)組,通過調(diào)用aStake()樁函數(shù),將使用的數(shù)組的元素x[i]的下標最小值1、最大值2、下標i、存儲的文件名"test.txt"和存儲在文件中第3行第4列6個實參傳遞給結(jié)構(gòu)體數(shù)組。
由于目標程序PPETS math functions和Qsort都是非常成熟的程序,一般情況很難出現(xiàn)運行錯誤。為了模擬目標程序出現(xiàn)運行時錯誤,實驗需要隨機產(chǎn)生一些越界的數(shù)組下標。調(diào)用
實驗新建一個監(jiān)控器線程并將其與一個CPU進行核綁定,將目標程序與另一個CPU進行核綁定,這樣就能發(fā)揮多核CPU的優(yōu)勢,達到監(jiān)控程序與目標程序并行運行的效果。使用Pthreads線程庫提供的pthread_create()函數(shù)創(chuàng)建線程。Linux系統(tǒng)提供了調(diào)用函數(shù)來設置核親和性:sched_setaffinity(pid_t pid,unsigned int cpusetsize,cpu_set_t*mask),參數(shù)pid是目標線程或進程的id(若為0表示當前線程);參數(shù)cpusetsize指后一個參數(shù)mask所指向的內(nèi)存結(jié)構(gòu)對象的大??;指針參數(shù)mask指向類型為cpu_set_t的對象。模擬單核CPU運行程序只需把所有進程都放在一個核心上運行即可。
以下是監(jiān)控器線程功能實現(xiàn)代碼:
圖3是多核平臺下把兩個線程綁定到不同的CPU成功的結(jié)果。
圖3 不同線程核綁定成功
正如設計中提到的,本次實驗有5種場景:
1)單核平臺下直接運行目標程序;
2)單核平臺下使用運行時驗證技術,運行目標程序;
3)多核平臺下直接運行目標程序;
4)多核平臺下不進行核綁定,使用運行時驗證技術,運行目標程序;
5)多核平臺下進行核綁定,使用運行時驗證技術,運行目標程序。
實驗對每種場景都進行了多次實驗,最終結(jié)果取多次實驗結(jié)果的平均值[10-18]。
圖4是目標程序為Qsort,運行場景1的一次結(jié)果,其中real時間是程序運行時間,實驗結(jié)果以real時間為參考。
圖4 PPETS math functions單核直接運行結(jié)果
圖5顯示的是目標程序為PPETS math functions的5種實驗場景的平均結(jié)果,其中場景1的運行時間為40.7 s,場景2的運行時間為42.2 s,場景3的運行時間為33.3 s,場景4的運行時間為35.1 s,場景5的運行時間為31 s。
圖5 目標程序為PPETS math functions的實驗結(jié)果
對于Qsort軟件中也對上述5種情況分別進行了實驗,圖6為實驗結(jié)果。其中場景1的運行時間為18.9 s,場景2的運行時間為23.4 s,場景3的運行時間為17.1 s,場景4的運行時間為20.5 s,場景5的運行時間為17.9 s。
圖6 目標程序為Qsort的實驗結(jié)果
通過對比以上實驗結(jié)果,可以得出以下實驗結(jié)論:
1)運行同一個目標程序,無論是使用運行時驗證技術還是直接運行,多核運行速度都比單核運行速度快。目標程序為PPETS math functions時,多核運行速度(未綁定核)比單核運行速度平均快17.5%,核綁定的情況下多核運行速度比單核運行速度平均快26.5%。目標程序為Qsort時,多核運行速度(未綁定核)比單核運行速度平均快10.9%,核綁定的情況下多核運行速度比單核運行速度平均快23.5%;
2)運行同一個目標程序,無論是單核還是多核,使用運行時驗證技術都會增加程序運行時間。目標程序為PPETS math functions時,時間平均增加了4.5%,目標程序為Qsort時,時間平均增加了21.8%;
3)運行同一個目標程序,多核運行時進行核綁定能夠在多核運行原先的基礎上進一步提高性能。目標程序為PPETS math functions時,核綁定之后速度提高11.7%。目標程序為Qsort時,核綁定之后速度提高12.7%;
4)對于不同的目標程序,目標程序規(guī)模越大,使用運行時驗證技術占用資源的比例就越少,軟件測試效率越高。
本文通過對相關技術的研究與分析,實現(xiàn)了基于多核CPU的運行時驗證技術并進行了實驗。分析實驗結(jié)果可以得出結(jié)論:與基于單核CPU的運行時驗證技術相比,本文所實現(xiàn)的基于多核CPU的運行時驗證技術能夠有效提高軟件的測試效率和運行速度,具有一定的實用價值。當然本文的設計還存在需要改進和完善的地方,比如插樁代碼和插樁的位置還可以進行優(yōu)化,使得運行時驗證技術造成的損耗降低;監(jiān)控器線程的功能還可以增加,使其能夠檢驗多種軟件運行錯誤。這些是今后需要研究和關注的方向。