郭 峰 徐 建
(南京理工大學計算機科學與工程學院 南京 210094)
在軟件開發(fā)中,調試是在程序中找到并修復故障的過程。通常,這個過程需要調試人員大量的主觀參與(如插入打印語句和設置斷點等),而人工調試尋找故障的效率取決于調試人員對程序的理解、調試人員的邏輯判斷能力、調試經驗以及調試方式[1]。因此,調試工作是軟件開發(fā)過程中成本昂貴、耗時最長的步驟之一。為了降低開發(fā)成本和縮短開發(fā)周期,研究人員提出了許多故障定位技術,通過自動提供程序語句的可疑性信息來提高調試效率[2~4]。
近年來,深度學習得到了快速發(fā)展,在魯棒性和準確性方面都有了很大的提高,已經成功應用于圖像分類、目標檢測、分割[5]等領域。這意味著深度學習或許可以為故障定位提供一個新的解決方案,構建故障定位模型利用其對數據的學習能力,對程序語句的可疑性進行評估。實際上,研究人員已經使用具有多個隱藏層的神經網絡來探索和評估深度學習在故障定位中的潛力[6]。
然而,當前的一些故障定位方法還不足以證明深度學習在該領域的潛力,需要進行進一步的研究。例如,僅使用含有錯誤的小型程序進行實驗論證[7]。此外,神經網絡中隱藏層的參數數量也會對故障定位的效率有所限制。隨著應用程序的規(guī)模越來越大,待排查語句的數目也越來越多,這導致隱藏層的數目非常大,伴隨而來的是待訓練參數數量的飛速增長,這將大大限制深度學習在故障定位領域的潛力。
因此,本文將更多地探討如何通過深度學習來提高故障定位效率,我們的目標是提出一種新的故障定位方法,并通過實驗對故障定位模型進行評估,從而證明其有效性。
傳統(tǒng)的基于程序譜的故障定位技術由于其形式簡單、易于操作實現、存儲方便,而且可以很清晰地表示程序執(zhí)行信息,為評估語句的可疑性提供了便利的條件。基于程序譜的故障定位技術[8]通常將語句作為程序實體,根據每條語句的運行特征,計算可疑度。但是由于程序譜僅考慮了每條語句的執(zhí)行情況,并沒有對上下文語句之間包含的依賴關系進行分析,因此有時會出現定位到的結果并不是真正的錯誤語句,而是由于錯誤傳遞而產生非預期運行結果的相關語句。
Hinton等[9]提出了深度神經網絡模型(DNNs),多層感知機(Multi-Layer Perceptron,MLP)是一種含有多個隱含層的深度神經網絡,MLP具有結構靈活的特點,能夠擬合出輸入和輸出之間高度復雜的非線性函數關系。但隨著輸入數據集規(guī)模的增長,待訓練的隱藏層參數也會驟增,同時也可能帶來局部極小值等問題。Wong和Qi[10]提出了一種基于反向傳播(BP)神經網絡的故障定位技術,BP 神經網絡不僅具有結構簡單,容易實現的特性,還具有近似復雜非線性函數的能力。Ascari 等[11]將基于BP神經網絡的定位技術拓展到面向對象程序。然而,BP 神經網絡仍存在局部極小化以及收斂速度慢等問題,隨著卷積神經網絡CNN 的問世,這些問題被很好地解決。卷積神經網絡是一種具有極強的數據處理能力的神經網絡,可以通過學習到的高度抽象的數據特征來有效地表示復雜對象[12]。
因此,本文提出了一種基于深度卷積神經網絡的軟件故障定位方法(DCFL),通過權值共享可以明顯減少神經網絡模型中的參數數量。與標準的多層感知器相比,卷積神經網絡具有更少的連接和參數,有助于減輕局部極小值問題。首先,DCFL構建一個深度卷積神經網絡模型,然后使用測試用例數據集對神經網絡模型進行訓練,最后將虛擬測試集輸入到訓練好的神經網絡模型中,對程序語句的可疑性進行評估。在實驗部分,我們對7 個現實中的大型程序進行了驗證分析,結果表明DCFL 可以有效地降低代碼的檢查成本。
DCFL 的模型框架如圖1 所示,基本思想是以卷積神經網絡架構為基礎,構建一個可以用于對程序語句可疑性進行評估的模型。數據準備工作包括:需要將輸入程序在測試用例集上運行,并在程序的執(zhí)行過程中采集語句覆蓋信息以及執(zhí)行結果信息,然后將采集到的信息轉換成矩陣形式,作為神經網絡模型的訓練集數據。再根據輸入程序的規(guī)模生成虛擬測試用例集,作為神經網絡模型的測試集數據。
圖1 DCFL模型框架
1)構造深度卷積神經網絡模型,根據程序的規(guī)模估算每個神經層中的節(jié)點數。
2)使用程序覆蓋信息和對應的執(zhí)行結果作為訓練集數據來訓練網絡模型。DCFL將每個覆蓋信息矩陣輸入到神經網絡模型中,以獲得覆蓋數據和執(zhí)行結果之間的復雜非線性映射關系。
3)將生成的虛擬測試用例集輸入到經過訓練的神經網絡模型中,并獲得輸出。輸出值ti反映了xi包含錯誤的可能性,輸出值ti越大,則程序語句i為錯誤語句的可能性就越大。
4)按照語句的可疑性進行降序排名,以文本形式輸出到本地文件。文本提供了程序語句的可疑性信息,可幫助調試人員或自動程序修復工具快速查找故障。
1)訓練集中程序表示
對于訓練集,我們這里采用了在軟件故障定位領域中有效且被廣泛使用的數據模型[13],該模型由程序執(zhí)行過程中的覆蓋信息和程序執(zhí)行結果組成。如圖2 所示,給定一個含有n語句的程序P,以及包含m個測試用例的測試集合T(集合T中至少包含一個失敗測試用例),T在程序P上執(zhí)行。矩陣元素xij=1表示語句j在測試用例i上被執(zhí)行,xij=0表示不被執(zhí)行。大小為m*n的矩陣中記錄了每條程序語句在不同測試用例中的執(zhí)行情況,誤差向量e記錄了測試用例的執(zhí)行結果,ei的值為1 表示測試用例i執(zhí)行失敗,值為0 則表示執(zhí)行成功。通過覆蓋信息矩陣與誤差向量e可以將可執(zhí)行語句與執(zhí)行結果相關聯(lián)。
圖2 測試用例覆蓋信息模型
在此基礎上,我們使用小批量隨機梯度下降算法來更新網絡參數,批量大小固定為h,每次將一個大小為h*n的矩陣作為網絡的輸入,并將與其對應的誤差向量作為標簽,迭代訓練卷積神經網絡。
2)測試集中程序表示
對于測試集,我們同樣采用了矩陣形式的數據模型。與訓練集有所不同,測試集是根據待檢測程序的規(guī)模由系統(tǒng)自動生成的虛擬覆蓋信息集合,在這里我們稱之為虛擬用例集。如圖3 所示,給定一個含有n條語句的程序P,則生成大小為n*n的虛擬用例集,其中包含n個虛擬測試用例,假設每個虛擬用例在程序上執(zhí)行時只覆蓋到一條程序語句。
圖3 虛擬測試用例模型
經過訓練得到的神經網絡模型,可以反映程序語句覆蓋信息與執(zhí)行結果之間的復雜非線性關系。最后,將虛擬測試用例輸入得到模型中,以評估每條可執(zhí)行語句與執(zhí)行結果之間的關聯(lián)性。
卷積神經網絡能夠在輸入數據集上學習到大量高度抽象的數據特征,我們認為它強大的學習能力有助于評估程序語句與程序運行失敗之間的關聯(lián)性。DCFL 網絡模型由一個輸入層、多組卷積池化層、多個全連接層以及一個輸出層構成。
1)數據輸入層:在輸入層,訓練集數據是大小為M×N的覆蓋信息矩陣和誤差向量,每次在其中選擇h行作為輸入數據,即當前選定的h個測試用例對應的覆蓋信息和執(zhí)行結果。
2)卷積計算層:卷積運算就是使用卷積核在輸入數據上進行滑動并計算的過程。卷積層通過使用多個卷積核來提取輸入數據中的特征,從而識別出輸入數據中包含的重要屬性。進行卷積運算時,可以通過strides 參數調整滑動步長,合適的滑動步長可以在確保數據完整的前提下,有效地降低輸出數據的維度。在我們的網絡模型中有3 個卷積層,每個卷積層也包含多個卷積核。
3)激勵層:將卷積層的輸出結果進行非線性映射。常用的激活函數有三種:Sigmoid 函數(式(1))、Tanh 函數(式(2))和ReLU 函數(式(3))。Sigmoid 函數的輸出值為[0,1]之間的實數,但存在飽和使梯度消失、輸出值不是“零為中心”以及計算消耗資源等問題。Tanh 函數,也稱雙曲正切函數,與Sigmoid 函數非常相似,解決了輸出非“零為中心”的問題,但仍無法解決另外兩個問題。ReLU(The Rectified Linear Unit,修正線性單元),它的特點是收斂速度快,求梯度簡單。對于所有正數,輸出均為輸入本身,對于所有負數輸出值均為零。ReLU 函數可以解決梯度消失問題,并且由于其線性、非飽和的形式,在SGD 優(yōu)化器中能夠快速收斂。在我們的網絡模型中,將使用ReLU 函數作為激活函數。
4)池化層:該層主要用于壓縮數據和降低參數數量,減小過擬合同時可以提高模型的容錯性。池化層使用的子采樣有兩種形式,分別是均值子采樣(avg-pooling)和最大值子采樣(max-pooling)。最大值子采樣在當前窗口范圍內選取最大值,目標是在張量中保持最大值。與最大值子采樣不同,均值子采樣是在當前窗口范圍內計算平均值。實驗表明向量中的最大值對故障定位是有益的,因此在我們的模型中采用最大值子采樣。
5)全連接層:相鄰層網絡之間的所有神經元均有權重連接。卷積神經網絡將全連接層排列成鏈狀結構,通過對特征進行重新擬合,減少特征信息的丟失。在訓練過程中,迭代更新權重和偏置參數。
6)輸出層:用于輸出結果。根據實際輸出與預期輸出之間的差異構建損失函數,通過反向傳播算法訓練網絡模型。最終,擬合出輸入與輸出之間高度復雜的非線性關系。這里,我們使用Sigmoid函數作為模型的輸出函數,Sigmoid 函數可以確保輸出值的范圍為0~1。Sigmoid 函數結果向量中的每個元素與目標向量對應的元素間存在差異,這個差異是輸出層和目標之間的損失。
我們使用反向傳播算法對模型的參數進行更新調整,旨在得到最優(yōu)的全局參數矩陣。前向傳遞輸入信號直至輸出產生誤差,反向傳播誤差信息更新權重矩陣。由于學習率會影響收斂速度,我們的模型采用了動態(tài)調整學習率。在式(4)中,一個Ep?och 表示一次完成所有訓練數據,LR 表示學習率,DropRate是每次修改學習率的量,而EpochDrop是更改學習率的頻率。我們將初始學習率設置為0.01,將DropRate 設置為0.95。根據測試用例的數量設置EpochDrop。
圖4 展示了一個帶有15 條語句的錯誤程序P,其中包含一條錯誤語句S2。表1 中每個語句下面的單元格表示該語句是否被測試用例覆蓋,最右邊的單元格表示測試用例的執(zhí)行結果。
表1 測試用例信息
圖4 示例程序
我們可以觀察到有6 個測試用例,其中有兩個失敗。 第一步構造一個卷積網絡模型,其中輸入層的節(jié)點數為15,全連接層中節(jié)點數設置為8,輸出層的節(jié)點數為1,使用Sigmoid函數作為輸出。第二步使用測試數據集訓練網絡,在這個例子中我們將h設置為3,首先輸入的矩陣是t1、t2、t3,目標輸出向量為(1,0,0),然后我們再輸入矩陣t4、t5、t6,及其執(zhí)行結果向量(0,0,1)。重復使用這些數據訓練網絡,直到損失足夠小并達到收斂條件為止。
經過訓練后,第三步是構建一個包含15 個測試用例的虛擬測試集,將每個虛擬測試用例輸入到經過訓練的網絡中。模型輸出值就是對應語句的錯誤可疑性,結果如表2 所示,我們可以檢查列表中的語句并首先找到錯誤的語句S2。
表2 程序語句可疑性評估結果
Zheng 等[6]提出使用深度神經網絡進行軟件故障定位方法,獲得了比PPDG[14]和Tarantula[15]等方法更好的實驗結果,因此我們在研究中選擇了將其(簡稱MLPs)作為對比方法。與此同時,我們還選擇了另外兩種技術(Dstar[13]和Barinel[16])進行對比分析。最終,我們將DCFL 與這三種方法進行了比較,以證明其在軟件故障定位過程中的有效性。
為了確保實驗結果的可靠性,我們選擇了7 個基準程序集,程序規(guī)模為5K 行到491K 行。表3 中記錄了程序的概括信息,包括程序功能的簡單描述、程序的錯誤版本數目、程序規(guī)模、測試用例數目。
表3 程序集相關信息
這里我們采用兩個被廣泛使用的指標來評估DCFL 模型的有效性,分別是Exam 和RImp。Exam值表示在找到錯誤語句之前要檢查的程序語句占比,Exam 值越低表示軟件故障定位模型的效果越好。RImp 表示分別使用兩種不同的軟件故障定位方法時,在找到錯誤程序語句之前需要檢查的程序語句數量的比值,RImp的值越小表示DCFL相對其它定位技術具有更高的有效性。
表4展示了DCFL 和其它三種軟件故障定位方法在各個程序集上的Exam 指標結果,對于每種方法,我們給出了它在每個程序集上的最優(yōu)Exam 結果和平均Exam 結果。最優(yōu)Exam 結果是指實驗中在程序的不同版本中取得的最小Exam 值,表示當前方法在該程序集上可以達到的最佳效率。平均Exam 結果是指實驗中在程序的所有版本上取得的Exam 的平均值,表示當前方法在該程序集上的平均效率。從表4 中我們可以看到,在七個程序集上無論是最優(yōu)Exam 還是平均Exam,DCFL 都取得了較好的實驗結果。在圖5展示4種軟件故障定位方法在不同程序集上的Exam 方差情況,通過Exam 方差我們可以了解每個定位方法的穩(wěn)定性。從圖中不難看出DCFL 在大部分程序集上的Exam 方差均取得了最小值,說明DCFL 在這4 種方法中性能更穩(wěn)定,同時也可以看出在不同程序集上,DCFL的性能波動較小,不會產生隨著程序集的變化導致方法性能出現較大波動的問題。
表4 不同方法的Exam實驗結果
圖5 Exam方差波動情況
為了更詳細比較DCFL 與其他故障定位方法的效率,我們使用RImp 指標來評估不同方法之間的性能差異。圖6、7、8中展示了DCFL分別與Bari?nel、Dstar 和MLPs 在RImp 指標上的實驗結果。以MLPs 為例說明,DCFL 在RImp 指標上的得分從nanoxml_v3程序集上的30.87%到space程序集上的82.83%,這表示相對MLPs 方法需要檢查的語句數目,DCFL需要檢查其30.87%到82.83%即可定位出軟件故障位置。也就是說相對于MLPs 方法,我們最多可以減少69.13%的代碼檢查量,最少可以減少17.17%的代碼檢查量。實驗結果表明,DCFL 與MLPs 相比,在軟件故障定位的過程中平均可以減少43.15%的代碼檢測量。如圖7 所示,與Dstar 方法的代碼檢查量相比,DCFL在libtiff程序集上的代碼檢查量最多減少59.66%,在nanoxml_v2 程序集上最少減少5.69%,平均減少32.68%。與Barinel方法的代碼檢測量相比,DCFL 在gzip 程序集上最多可以減少60.7%,在nanoxml_v1 程序集上最少可以減少22.46%,平均減少41.58%。因此,DCFL 方法在軟件故障定位中更為有效。
圖6 基于Barinel的RImp
圖7 基于Dstar的RImp
軟件故障定位是軟件調試過程中最重要的任務之一,本文提出了一種利用深度卷積神經網絡進行故障定位的有效方法(DCFL)。首先構建一個深度卷積網絡模型,通過程序集測試用例對應的語句覆蓋信息及執(zhí)行結果來訓練神經網絡,然后將一組虛擬測試用例作為測試數據集輸入到訓練完成的網絡模型中,最后通過模型輸出的結果來評估程序語句的可疑性,進而根據可疑性進行軟件故障定位。進一步,通過將DCFL 方法與當前先進的三種軟件故障定位方法進行對比發(fā)現,在進行軟件故障定位時,DCFL 方法的總體效率優(yōu)于其他三種方法。但該方法仍存在改進之處,比如將程序集的控制流信息應用到軟件故障定位過程中,進一步提高DCFL 的準確性。此外,未來我們還計劃將目前的工作擴展到多故障軟件定位的情景中去。
圖8 基于MLPs的RImp