林鍇,陶傳奇,2,3,4+,黃志球,2,4
1.南京航空航天大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,南京211106
2.南京航空航天大學(xué) 高安全系統(tǒng)的軟件開發(fā)與驗(yàn)證技術(shù)工信部重點(diǎn)實(shí)驗(yàn)室,南京211106
3.南京大學(xué) 計(jì)算機(jī)軟件新技術(shù)國家重點(diǎn)實(shí)驗(yàn)室,南京210023
4.軟件新技術(shù)與產(chǎn)業(yè)化協(xié)同創(chuàng)新中心,南京210093
異常是指程序運(yùn)行過程中出現(xiàn)的不可預(yù)期的錯(cuò)誤。不適當(dāng)?shù)漠惓L幚矸椒赡軙?huì)無意中增加程序故障的風(fēng)險(xiǎn),例如系統(tǒng)崩潰或者內(nèi)存泄漏,因?yàn)楫惓T谲浖到y(tǒng)中很難被測(cè)試到。因此,有效的異常處理對(duì)于軟件開發(fā)來說至關(guān)重要。Sawadpong 等人[1]的研究表明,異常處理產(chǎn)生的缺陷數(shù)量大約是總體缺陷數(shù)量的三倍。
目前大多數(shù)的編程語言,例如Java、C++、Python,都內(nèi)置了異常處理機(jī)制來指導(dǎo)開發(fā)人員考慮程序中的異常路徑,防止程序崩潰的發(fā)生。然而,如今的軟件開發(fā)對(duì)API(application programming interface)庫的依賴程度很高。一個(gè)API 庫中包含了大量的異常類型以及異常處理的規(guī)則,例如Android 框架中就包含了超過260 個(gè)異常類型[2]。另外,熱門的API 庫的升級(jí)頻率很高并且版本間改變較大。因此,每個(gè)方法所產(chǎn)生的異常類型及其處理方式,是軟件開發(fā)過程中的重要活動(dòng)。研究表明即使是經(jīng)驗(yàn)豐富的開發(fā)者也經(jīng)常會(huì)編寫出錯(cuò)誤的異常處理代碼[3-4]。
異常處理代碼推薦是指將try 語句塊中捕捉的異常代碼作為上下文信息,并針對(duì)上下文信息推薦合適的異常處理代碼。推薦的異常處理代碼可以是具體的語句塊也可以是API 調(diào)用序列。現(xiàn)有的異常處理相關(guān)的推薦方法大多是利用啟發(fā)式的規(guī)則或者統(tǒng)計(jì)學(xué)習(xí)的方法進(jìn)行推薦。CAR-miner[5]通過挖掘try語句中的方法調(diào)用與catch 語句塊中異常處理代碼之間的關(guān)聯(lián)規(guī)則為上下文代碼推薦強(qiáng)相關(guān)的異常處理代碼。FuzzyCatch[6]則是利用模糊邏輯理論分析每個(gè)API 調(diào)用可能產(chǎn)生的各種異常的概率,并根據(jù)異常發(fā)生概率的高低來檢測(cè)代碼可能存在的缺陷。除此之外,F(xiàn)uzzyCatch 還會(huì)為存在缺陷的代碼推薦具體的異常處理API。以上方法大多從詞法層面挖掘代碼中的特征信息,忽略了語義信息。隨著深度學(xué)習(xí)的發(fā)展,軟件開發(fā)也逐漸走向智能化。一些研究將長短期記憶網(wǎng)絡(luò)(long short-term memory,LSTM)應(yīng)用于源代碼中[7],LSTM 處理過長輸入序列時(shí),存在梯度爆炸以及梯度消失的問題[8]。自注意力網(wǎng)絡(luò)[9]很好地解決了這一問題。目前,自注意力網(wǎng)絡(luò)已廣泛應(yīng)用于自然語言處理領(lǐng)域。本文將自注意力網(wǎng)絡(luò)應(yīng)用于源代碼中,提出了一種基于自注意力網(wǎng)絡(luò)的異常處理代碼推薦方法,即DeepEHCR。該方法不同于現(xiàn)有方法的地方在于它利用深度學(xué)習(xí)技術(shù)挖掘異常代碼中各API 調(diào)用之間的深度特征信息進(jìn)行異常處理的推薦。首先,DeepEHCR 利用API 調(diào)用樹結(jié)構(gòu)對(duì)上下文代碼進(jìn)行建模,并采用基于結(jié)構(gòu)遍歷算法將樹轉(zhuǎn)化為序列的同時(shí)充分保留樹中的結(jié)構(gòu)信息。其次,借鑒Li 等人[10]的方法,將異常處理的策略分為三類:處理異常、記錄日志以及重新拋出。DeepEHCR 利用自注意力網(wǎng)絡(luò)學(xué)習(xí)API 調(diào)用樹中的特征信息,并根據(jù)這些信息推薦異常處理的策略。最后,針對(duì)處理異常這一策略太過抽象、缺乏針對(duì)性的問題,DeepEHCR利用Transformer 模型進(jìn)一步推薦具體的異常處理API 序列。為了驗(yàn)證DeepEHCR 模型的有效性,分別對(duì)其異常處理策略推薦功能以及異常處理API 序列推薦功能進(jìn)行大規(guī)模實(shí)驗(yàn)。本文從GitHub 中收集了1 524 個(gè)Android 項(xiàng)目,其中包含了55 763 個(gè)含有異常處理的代碼塊。這些代碼按照9∶1 的比例劃分為訓(xùn)練集與測(cè)試集。實(shí)驗(yàn)結(jié)果表明DeepEHCR 在異常處理策略推薦方面,Accuracy、Precision、Recall 以及F1-score 的值分別達(dá)到了89.78%、89.98%、89.34%以及89.59%。在API 序列推薦方面,Hit@1/3/5 的值分別達(dá)到了57.83%、69.73%、74.79%。最后,本文還將DeepEHCR 的整體性能和當(dāng)前現(xiàn)有工作進(jìn)行對(duì)比,DeepEHCR 在修復(fù)真實(shí)的異常漏洞方面明顯地優(yōu)于CARMiner[5]以及FuzzyCatch[6]。
本文的主要貢獻(xiàn)包括:
(1)提出了一種新型的API 調(diào)用模型,該API 調(diào)用模型根據(jù)代碼中的嵌套關(guān)系構(gòu)建API 調(diào)用樹以充分保留代碼的結(jié)構(gòu)信息。
(2)提出了一種基于自注意力網(wǎng)絡(luò)的智能化異常處理代碼推薦方法,該方法通過挖掘上下文代碼中的深層次特征信息來推薦異常處理策略以及異常處理代碼。
程序異常在軟件工程領(lǐng)域被廣泛地關(guān)注。許多研究人員對(duì)異常處理的代碼與軟件的魯棒性的關(guān)系進(jìn)行了研究[11-13]。Osman 等人[12]研究了異常代碼的不同修改方式會(huì)對(duì)軟件系統(tǒng)的魯棒性產(chǎn)生怎樣的影響。Marinescu[13]驗(yàn)證了使用到異常的類要比未使用異常的類更復(fù)雜,并且未正確處理異常的類要比正確處理異常的類出現(xiàn)漏洞的可能性高很多。
異常處理的復(fù)雜性導(dǎo)致開發(fā)者們?cè)诋惓L幚磉^程中產(chǎn)生了大量漏洞[10]。因此,近些年異常處理[5-6,14-16]引起了研究人員的較大關(guān)注。他們通過分析異常處理代碼來幫助開發(fā)者們理解程序中的異常鏈以及編寫異常處理的方式。
Li等人[10]將異常處理的策略共分為了四類:處理異常、記錄日志、拋出和包裹并重新拋出。他們通過異常代碼來推薦相關(guān)的異常處理策略。由于Li等人[10]是將整個(gè)方法塊作為上下文代碼,而本文默認(rèn)捕獲異常并且將try 語句塊內(nèi)代碼作為上下文信息,Deep-EHCR 沒有考慮拋出這一種情況。Zhong 等人[14]提出了一種方法Mono,用于修復(fù)異常相關(guān)的漏洞。它首先挖掘異常和修復(fù)方式之間的關(guān)聯(lián),然后建立每種異常的修復(fù)模型。CARMiner[5]利用代碼搜索引擎從現(xiàn)有開源項(xiàng)目中收集相關(guān)的代碼示例以增加數(shù)據(jù)量,并且結(jié)合一種新的挖掘算法,以序列關(guān)聯(lián)的形式檢測(cè)異常處理規(guī)則。DeepEHCR不同于CARMiner的地方在于它不僅考慮了上下文代碼中的API 調(diào)用信息,而且捕捉了其中的結(jié)構(gòu)信息。DeepEHCR 通過API 調(diào)用樹挖掘異常代碼中API 調(diào)用的潛在關(guān)聯(lián)來推薦相關(guān)的異常處理代碼。FuzzyCatch[6]利用模糊邏輯的算法預(yù)測(cè)代碼中可能會(huì)發(fā)生的運(yùn)行時(shí)異常,并且推薦相關(guān)的異常處理代碼來修復(fù)異常。Fuzzy-Catch 通過分析每個(gè)API 調(diào)用產(chǎn)生異常的概率以及每種API 調(diào)用產(chǎn)生異常后對(duì)應(yīng)的異常處理API 調(diào)用概率來預(yù)測(cè)異常以及推薦異常處理的API。它將上下文中的API 調(diào)用相互獨(dú)立開進(jìn)行分析,而DeepEHCR通過API 調(diào)用樹結(jié)構(gòu)充分考慮了上下文中各API 之間的潛在關(guān)聯(lián)。考慮到異常處理中可能存在多個(gè)API 調(diào)用,DeepEHCR 推薦的異常處理代碼為API 序列而不僅僅是單個(gè)API。
還有一些研究分析了異常對(duì)于程序全局的影響。Robillard 等人[17]針對(duì)當(dāng)前Java 的異常處理機(jī)制,認(rèn)為異常是全局問題并且很難提前定位異常源。Cacho等人[18]針對(duì)異常處理提出了一種面向切面的模型。該模型能夠顯式描述異常控制流的全局視圖。DeepEHCR不同于這些方法的地方在于它是從局部出發(fā),以try語句塊作為上下文信息來推薦異常處理代碼。
本章主要對(duì)異常處理代碼推薦模型DeepEHCR進(jìn)行介紹。圖1 展示了DeepEHCR 的整體結(jié)構(gòu)。該模型可以分為三個(gè)核心階段:上下文代碼表示、異常處理策略推薦以及異常處理的API 序列推薦。在上下文代碼表示階段,DeepEHCR 首先提取異常位置的上下文代碼,即try 語句塊內(nèi)的代碼。然后將上下文代碼解析成抽象語法樹(abstract syntax tree,AST),并進(jìn)一步從AST 中提取API 調(diào)用樹。之后利用基于結(jié)構(gòu)的遍歷算法(structure-based traversal,SBT)[19]遍歷API 調(diào)用樹。將遍歷得到的序列與異常類型進(jìn)行拼接,得到最終的上下文代碼表示形式。在異常處理策略推薦階段,異常處理共分為三種策略:處理異常、記錄日志以及重新拋出。本文采用一種基于自注意力網(wǎng)絡(luò)[9,20]的分類模型來將上下文代碼映射到具體的異常處理策略中。在異常處理API 序列推薦階段,DeepEHCR 會(huì)針對(duì)處理異常這一策略,利用Transformer 模型[17]推薦具體的API 序列,這些API 序列可以輔助開發(fā)人員編寫正確的異常處理代碼。記錄日志策略和重新拋出策略不需要做進(jìn)一步API 序列推薦,因?yàn)檫@兩種策略的具體代碼形式相對(duì)簡單并且固定,不需要做出具體的代碼推薦。除此之外,記錄日志的方式有很多種,但是它們實(shí)現(xiàn)的功能是一樣的,用戶可以根據(jù)策略選擇自己特定的記錄日志方法來記錄日志。

圖1 DeepEHCR 整體框架圖Fig.1 Overall architecture of DeepEHCR
2.1.1 API調(diào)用樹
在做異常處理代碼推薦前,需要對(duì)上下文代碼進(jìn)行特征表示。目前關(guān)于代碼特征表示的研究大多是通過語法分析將代碼轉(zhuǎn)化成AST 的形式[7,21]。但是異常處理的代碼通常會(huì)和try 語句塊中特定的API 調(diào)用有關(guān),因此本文采用API 調(diào)用關(guān)系來表示上下文代碼。代碼塊是一種嵌套結(jié)構(gòu),它通過if、for 等控制流語句將代碼中的語句分為不同的層次。圖2 中的代碼示例包含了三個(gè)層次:方法體中的代碼塊為第一層;方法體中的for 循環(huán)語句為第二層;for 循環(huán)中的while 語句為第三層。根據(jù)這種層次關(guān)系,本文提出了一種新型的API 調(diào)用樹結(jié)構(gòu)。API 調(diào)用樹相比API調(diào)用序列的優(yōu)勢(shì)在于它充分考慮了代碼中的結(jié)構(gòu)特征,因此可以發(fā)掘出更多潛在的特征信息。

圖2 代碼示例Fig.2 Code example
API 調(diào)用樹由控制流節(jié)點(diǎn)和API 調(diào)用節(jié)點(diǎn)組成。本文將控制流節(jié)點(diǎn)分為while、do-while、for、foreach、switch、if六類。它們?cè)诖a中的對(duì)應(yīng)關(guān)系如圖3。

圖3 控制流與API調(diào)用節(jié)點(diǎn)對(duì)應(yīng)關(guān)系Fig.3 Correspondences between control flow and API call nodes
While 節(jié)點(diǎn)表示一個(gè)while 語句,它包含兩個(gè)子節(jié)點(diǎn):Expression 節(jié)點(diǎn)和Block 節(jié)點(diǎn)。其中Expression節(jié)點(diǎn)表示while 語句中的條件表達(dá)式,它的子節(jié)點(diǎn)為條件表達(dá)式中的API 調(diào)用序列;Block 節(jié)點(diǎn)表示循環(huán)代碼塊,它的子節(jié)點(diǎn)為循環(huán)代碼塊中的API 調(diào)用或者控制流語句對(duì)應(yīng)的節(jié)點(diǎn)。Do-while 節(jié)點(diǎn)、For 節(jié)點(diǎn)以及Foreach 節(jié)點(diǎn)和While 節(jié)點(diǎn)類似,都包含Expression和Block 兩個(gè)子節(jié)點(diǎn)。Switch 節(jié)點(diǎn)的子節(jié)點(diǎn)個(gè)數(shù)由其對(duì)應(yīng)的語句塊中case 語句的個(gè)數(shù)決定,每一個(gè)case語句對(duì)應(yīng)一個(gè)Case子節(jié)點(diǎn)。If節(jié)點(diǎn)表示if-else語句塊。它包含三個(gè)子節(jié)點(diǎn),其中Expression 節(jié)點(diǎn)表示條件表達(dá)式;Block 節(jié)點(diǎn)表示條件表達(dá)式為真時(shí)執(zhí)行的語句塊;Else節(jié)點(diǎn)為表示條件表達(dá)式為假時(shí)執(zhí)行的語句塊。
根據(jù)以上API 調(diào)用樹的定義規(guī)則,圖2 中的代碼塊可以表示成圖4 所示的層次結(jié)構(gòu)。

圖4 API調(diào)用樹結(jié)構(gòu)Fig.4 Structure of API call tree
本文利用JDT 工具(http://www.eclipse.org/jdt/)來對(duì)代碼進(jìn)行解析。JDT 為Eclipse 內(nèi)置工具,用于編譯代碼并生成對(duì)應(yīng)的AST。根據(jù)代碼的AST 模型,構(gòu)建上下文代碼對(duì)應(yīng)的API調(diào)用樹。
相對(duì)于AST 表示方法,API 調(diào)用樹能夠大量地減少樹中節(jié)點(diǎn)的個(gè)數(shù),還可以過濾掉大量無用的節(jié)點(diǎn)。這些無用的節(jié)點(diǎn)不僅會(huì)增加推薦模型的復(fù)雜度,而且會(huì)對(duì)模型的訓(xùn)練產(chǎn)生干擾。
2.1.2 API調(diào)用樹遍歷
為了將API 調(diào)用樹輸入到自注意力網(wǎng)絡(luò)中,需要通過遍歷API 調(diào)用樹的方式將API 調(diào)用樹轉(zhuǎn)化為序列形式。可以選擇先序遍歷這樣的經(jīng)典樹遍歷算法來做序列化處理,但是這樣得到的序列無法重構(gòu)API調(diào)用樹,因此損失了其中的部分結(jié)構(gòu)信息。本文采用Hu 等人[19]提出的SBT 算法,以保證在遍歷API 調(diào)用樹的過程中保留樹中的完整結(jié)構(gòu)信息。圖5 為SBT 的一個(gè)簡單示例,它使用一對(duì)括號(hào)以及根節(jié)點(diǎn)來標(biāo)識(shí)一棵樹,然后遞歸地處理子樹并將處理結(jié)果放入括號(hào)內(nèi)。

圖5 SBT 的簡單示例Fig.5 Simple example of SBT
最后,本文將上下文代碼對(duì)應(yīng)的異常類型與API調(diào)用樹對(duì)應(yīng)的序列進(jìn)行拼接,并通過分隔符進(jìn)行區(qū)分。拼接后的詞素序列被用來表示上下文代碼。
2.2.1 異常處理策略分類
異常處理策略指的是根據(jù)特定的編程上下文代碼應(yīng)當(dāng)采取的異常處理方式。Li 等人[10]將異常處理模式分為了四類:拋出異常(THROW)、處理異常(HANDLE)、記錄日志或者忽略(LOG&IGNORE)以及捕捉并重新拋出(WRAP&RETHROW)。由于DeepEHCR 是在捕捉了異常代碼塊后執(zhí)行的推薦,本文不考慮第一種情況,即拋出異常。除此之外,本文認(rèn)為代碼中捕捉了異常但不對(duì)異常做任何處理的方式是不合理的,因此對(duì)記錄日志或者忽略這一類別進(jìn)行了優(yōu)化,去除了忽略不做處理的情況。為了提高數(shù)據(jù)質(zhì)量,本文也將訓(xùn)練集中這樣的樣本剔除。最終本文將異常處理的策略分成表1 所示的三類。

表1 異常處理策略Table 1 Exception handling strategies
2.2.2 異常處理策略推薦模型構(gòu)建
本小節(jié)將詳細(xì)介紹異常處理策略推薦模型,該模型利用自注意力網(wǎng)絡(luò)從上一階段詞素序列中提取深層特征信息,對(duì)異常代碼進(jìn)行分類,最后將最優(yōu)的異常處理策略推薦給開發(fā)者。自注意力網(wǎng)絡(luò)[9]是為了解決傳統(tǒng)循環(huán)神經(jīng)網(wǎng)絡(luò)(recurrent neural network,RNN)對(duì)于過長序列輸入導(dǎo)致梯度爆炸以及梯度消失的問題。它可以很好地處理這種長依賴的情況[8]。除此之外,自注意力網(wǎng)絡(luò)可以采用并行計(jì)算的方式來提升訓(xùn)練的速度。
首先需要對(duì)輸入序列進(jìn)行處理,將其轉(zhuǎn)化為對(duì)應(yīng)的詞嵌入向量。詞嵌入技術(shù)是將自然語言輸入中的單詞嵌入到低維向量空間的方法[22]。近幾年,該方法也被應(yīng)用到源代碼處理的任務(wù)中,例如代碼推薦[23-24]、注釋生成[25-26]等。一般的做法是采用word2vec[27]詞嵌入方法預(yù)先將每個(gè)詞素轉(zhuǎn)化為向量,但是本文的輸入序列是從樹中遍歷得到,相鄰的詞素之間關(guān)系不大,因此將詞嵌入向量作為參數(shù)形式與自注意力網(wǎng)絡(luò)一同訓(xùn)練得到。由于自注意力網(wǎng)絡(luò)中沒有考慮詞素與詞素之間的位置關(guān)系,本文還加入了一個(gè)位置嵌入算法計(jì)算每個(gè)詞素對(duì)應(yīng)的位置向量。最后將詞嵌入向量與位置向量相加得到詞素序列對(duì)應(yīng)的輸入向量形式X=(x1,x2,…,xn)。
下面將對(duì)自注意力網(wǎng)絡(luò)[9]進(jìn)行詳細(xì)介紹。每層自注意力網(wǎng)絡(luò)都是由一個(gè)多頭自注意力子層以及一個(gè)前饋網(wǎng)絡(luò)子層構(gòu)成。多頭自注意力子層的結(jié)構(gòu)如圖6 所示。

圖6 多頭自注意力子層Fig.6 Multi-head self-attention sub-layer
多頭自注意力子層使用多個(gè)自注意力函數(shù)來學(xué)習(xí)不同位置的不同子空間下的特征信息。由于每頭只專注學(xué)習(xí)所在子空間的特征,效率和學(xué)習(xí)的效果都要比單頭自注意力高很多。多頭自注意力子層的計(jì)算公式如下:
其中,Q、K和V分別由輸入矩陣X經(jīng)過不同的線性變換XWQ、XWK和XWV得到,WQ、WK和WV為權(quán)重矩陣,dk為K的維度,KT為K的轉(zhuǎn)置;矩陣將Q、K、V映射到不同的子空間中;Concat(h1,h2,…,hh)函數(shù)表示將參數(shù)中的多個(gè)矩陣按照第二維度進(jìn)行拼接,例如hi∈,則Concat(h1,h2,…,hh)∈。
多頭自注意力子層的輸出會(huì)作為前饋網(wǎng)絡(luò)子層的輸入。前饋網(wǎng)絡(luò)子層可以是全連接神經(jīng)網(wǎng)絡(luò)也可以是卷積神經(jīng)網(wǎng)絡(luò)。本文采用全連接網(wǎng)絡(luò)作為前饋網(wǎng)絡(luò)子層。它由兩個(gè)線性變換以及一個(gè)ReLU 激活函數(shù)組成。
其中,W1、W2為權(quán)值矩陣,b1、b2為偏置項(xiàng)。
為了完成異常處理策略分類任務(wù),本文將自注意力網(wǎng)絡(luò)最后一層的輸出轉(zhuǎn)化為一維的向量,再經(jīng)過線性網(wǎng)絡(luò)以及Softmax 層輸出分類結(jié)果。
其中,view函數(shù)用來重組矩陣,按照第一維的順序?qū)⑵滢D(zhuǎn)化為一維向量。W為權(quán)值矩陣,b為偏置項(xiàng)。
2.2.3 模型訓(xùn)練
本文需要對(duì)訓(xùn)練數(shù)據(jù)集進(jìn)行處理。對(duì)于每個(gè)訓(xùn)練樣本Code,根據(jù)Code中的異常代碼(try 語句塊內(nèi)的代碼)以及異常類型構(gòu)建上下文信息的序列表示token_seq。異常處理代碼對(duì)應(yīng)的異常處理策略strategy則需要進(jìn)行人工標(biāo)記。為了保證標(biāo)記的準(zhǔn)確性,邀請(qǐng)了三名擁有多年Java 開發(fā)經(jīng)驗(yàn)的學(xué)生來完成。除此之外,為了保證一定的客觀性,本文還預(yù)先定義了一些標(biāo)準(zhǔn)。例如,如果異常處理代碼中只包含log相關(guān)函數(shù),則將其策略定義為記錄日志。最終訓(xùn)練數(shù)據(jù)Code被表示成二元組形式
如果推薦的異常處理策略是記錄日志或者重新拋出異常,用戶可以輕松地根據(jù)策略來編寫相對(duì)應(yīng)的異常處理代碼。但是,如果推薦的異常處理策略是處理異常,情況不會(huì)像前兩種那樣簡單,因?yàn)橛脩糁恢佬枰幚懋惓#⒉恢谰唧w的處理方法。為了更加全面地輔助用戶編寫具體處理異常的代碼,本文還設(shè)計(jì)了一種API 序列推薦模型。在上一步得到了具體的策略后,針對(duì)處理異常這一策略進(jìn)一步推薦具體的處理異常的API 序列,用戶可以根據(jù)API序列來構(gòu)建處理異常的代碼。
2.3.1 異常處理的API序列推薦模型構(gòu)建
異常處理的API 序列推薦模型使用的是Transformer模型[9]。該模型被廣泛應(yīng)用到自然語言處理等領(lǐng)域[28]。Transformer 模型主要由編碼器和解碼器兩部分組成。編碼器負(fù)責(zé)對(duì)輸入序列進(jìn)行編碼,并將其映射到固定的維度空間中;解碼器負(fù)責(zé)將編碼后的向量映射為目標(biāo)序列。Transformer 模型的示意圖如圖7 所示。

圖7 Transformer模型Fig.7 Transformer model
2.3.2 模型訓(xùn)練
本文首先需要對(duì)訓(xùn)練集進(jìn)行預(yù)處理。對(duì)于每一個(gè)訓(xùn)練數(shù)據(jù)Code,分離出異常代碼、異常類型以及異常處理代碼,構(gòu)成三元組形式
Transformer 模型首先將詞素序列token_seq作為輸入,利用編碼器對(duì)輸入序列進(jìn)行編碼。解碼器部分首先會(huì)生成特殊API
本文選擇從GitHub(https://www.github.com/)中下載原始代碼數(shù)據(jù)。為了保證代碼的質(zhì)量,利用代碼的獲贊數(shù)作為選擇的標(biāo)準(zhǔn),因?yàn)榇a的獲贊數(shù)反映了代碼的關(guān)注程度以及被其他編程人員認(rèn)可的程度。一個(gè)代碼的獲贊數(shù)越高,它的質(zhì)量往往也越高。本文只選擇獲贊數(shù)大于100 的Android 項(xiàng)目代碼。如表2 所示,本文最終選取了1 524 個(gè)Android 項(xiàng)目,其中包含了55 763 個(gè)含有異常處理的代碼塊。另外,本文還對(duì)每種策略對(duì)應(yīng)的代碼塊數(shù)以及所占的比例進(jìn)行了統(tǒng)計(jì)。其中處理異常策略為19 542個(gè),占總代碼塊數(shù)的35.0%;記錄日志策略為22 443個(gè),占總代碼塊數(shù)的40.2%;重新拋出策略為13 778個(gè),占總代碼塊數(shù)的24.8%。

表2 實(shí)驗(yàn)數(shù)據(jù)介紹Table 2 Introduction of experiment data
為了訓(xùn)練和評(píng)估異常處理策略分類模型,對(duì)全部的55 763 個(gè)代碼塊按9∶1 的比例隨機(jī)劃分為訓(xùn)練集和測(cè)試集,其中訓(xùn)練集包含50 187 個(gè)數(shù)據(jù),測(cè)試集包含5 576 個(gè)數(shù)據(jù)。由于異常處理API 序列生成模型針對(duì)的是處理異常這一策略,本文只選用處理異常策略的19 542 個(gè)代碼塊,同樣采用9∶1 的比例進(jìn)行劃分。最后得到17 588 個(gè)訓(xùn)練數(shù)據(jù)以及1 954 個(gè)測(cè)試數(shù)據(jù)。
本文的實(shí)驗(yàn)硬件環(huán)境為Intel i7-8700 3.2 GHz 處理器、GTX 1070 Ti的GPU以及32 GB內(nèi)存的工作站。DeepEHCR 模型選用Python 語言以及Pytorch(https://pytorch.org/)深度學(xué)習(xí)框架實(shí)現(xiàn)。為了訓(xùn)練模型,將訓(xùn)練數(shù)據(jù)隨機(jī)打亂并設(shè)置批次大小為64。本文選擇詞素出現(xiàn)的閾值為2,也就是說在訓(xùn)練集中出現(xiàn)次數(shù)大于等于2 的詞素會(huì)放入到詞匯表中。序列最大長度設(shè)置為100,對(duì)長度大于100 的序列進(jìn)行截?cái)啵瑢?duì)長度小于100 的序列采用填充的方式補(bǔ)齊。對(duì)于網(wǎng)絡(luò)模型的參數(shù),自注意力網(wǎng)絡(luò)的層數(shù)設(shè)置為3,自注意力共有4 個(gè)頭,嵌入向量的維度設(shè)置為256,前饋網(wǎng)絡(luò)的維度設(shè)置為了1 024。在模型中增加了Dropout機(jī)制使模型具有更好的泛化能力,Dropout被設(shè)置為0.1。表3 展示了詳細(xì)的參數(shù)設(shè)置。

表3 參數(shù)介紹Table 3 Parameter introduction
對(duì)于異常處理策略推薦,采用分類任務(wù)公認(rèn)的四個(gè)指標(biāo),即準(zhǔn)確率(Accuracy)、精確率(Precision)、召回率(Recall)以及F1 值(F1-score)來評(píng)估模型的有效性。
準(zhǔn)確率是指在整個(gè)測(cè)試數(shù)據(jù)中,模型分類正確的數(shù)量占總數(shù)量的百分比。
其中,Ntotal表示測(cè)試數(shù)據(jù)的總數(shù)量,Ncorrect表示測(cè)試數(shù)據(jù)中被分類正確的數(shù)量。
精確率和召回率作為評(píng)估二分類問題的性能指標(biāo),也可以擴(kuò)展到多分類問題中。在二分類問題中,精確率是指預(yù)測(cè)為正的樣本中實(shí)際也為正的樣本占被預(yù)測(cè)為正的樣本的比例。
其中,TP表示正樣本中預(yù)測(cè)為正的樣本個(gè)數(shù),F(xiàn)P表示負(fù)樣本中預(yù)測(cè)為正的樣本個(gè)數(shù)。
在n分類問題中,每次對(duì)一個(gè)分類計(jì)算,將這一類作為正樣本,其余類作為負(fù)樣本,執(zhí)行一次二分類的精確率計(jì)算,最后對(duì)n次精確率計(jì)算的結(jié)果求加權(quán)平均值得到多分類情況下的精確率。
其中,Ntotal表示測(cè)試樣本的總數(shù)量;Label表示所有類別的集合;Nl表示測(cè)試樣本中類別為l的樣本個(gè)數(shù);Precision2(l)表示類別l為正樣本時(shí)的二分類精確率。
召回率在二分類問題中表示正樣本中預(yù)測(cè)為正的樣本占所有正樣本的比例。
其中,TP表示正樣本中預(yù)測(cè)為正的樣本個(gè)數(shù),F(xiàn)N表示正樣本中預(yù)測(cè)為負(fù)的樣本個(gè)數(shù)。
在n分類問題中,每次對(duì)一個(gè)分類計(jì)算,將這一類作為正樣本,其余類作為負(fù)樣本,執(zhí)行一次二分類的召回率計(jì)算,最后對(duì)n次召回率計(jì)算的結(jié)果求加權(quán)平均值得到多分類情況下的召回率。
其中,Ntotal表示測(cè)試樣本的總數(shù)量;Label表示所有類別的集合;Nl表示測(cè)試樣本中類別為l的樣本個(gè)數(shù);Recall2(l)表示類別l為正樣本時(shí)的二分類召回率。
F1 值兼顧了分類模型的精確率和召回率,它可以看作模型精確率和召回率的一種調(diào)和平均值。
對(duì)于異常處理的API序列推薦,采用Hit@K(K=1,3,5)來評(píng)估模型的性能。
Hit@K(K=1,3,5)指的是推薦的K個(gè)結(jié)果中存在正確結(jié)果的樣本占總測(cè)試樣本的百分比。
其中,D表示測(cè)試數(shù)據(jù)集;rankd表示正確結(jié)果在推薦列表中的排名;I(·)函數(shù)為二值函數(shù),當(dāng)條件為真時(shí)值為1,否則值為0。
3.4.1 基準(zhǔn)實(shí)驗(yàn)
為了驗(yàn)證DeepEHCR 在異常處理策略推薦以及異常處理的API 序列推薦上的性能,本文構(gòu)建了基于LSTM 的異常處理推薦算法。LSTM 是一種循環(huán)神經(jīng)網(wǎng)絡(luò)計(jì)算法,常用于自然語言處理當(dāng)中。LSTM 通過引入輸入門、遺忘門以及輸出門來達(dá)到門控的目的,從而充分捕捉序列中的關(guān)聯(lián)信息。除此之外,為了驗(yàn)證API 調(diào)用樹提取的結(jié)構(gòu)特征對(duì)于模型性能帶來的優(yōu)勢(shì),本文還構(gòu)建了一個(gè)基于簡單API 序列的自注意力網(wǎng)絡(luò)模型來進(jìn)行對(duì)比。該模型沒有使用API調(diào)用樹信息,而是順序地從代碼塊中提取API 序列來表示。
最后,為了驗(yàn)證DeepEHCR 整體在異常代碼處理上的優(yōu)勢(shì),本文選擇了FuzzyCatch[6]和CarMiner[5]兩個(gè)當(dāng)前最新的異常代碼修復(fù)工具。FuzzyCatch[6]通過模糊邏輯算法預(yù)測(cè)代碼中可能存在的運(yùn)行時(shí)異常,并推薦相應(yīng)的異常處理API。它不同于DeepEHCR的地方在于它只推薦一個(gè)API 作為異常處理代碼。CarMiner[5]通過關(guān)聯(lián)關(guān)系挖掘技術(shù)挖掘try 語句塊中代碼與catch 語句塊中代碼的關(guān)聯(lián)規(guī)則,從而發(fā)現(xiàn)代碼中可能存在的關(guān)于異常的漏洞。為了體現(xiàn)實(shí)驗(yàn)的客觀性,本文選擇了FuzzyCatch[6]中使用的關(guān)于異常漏洞修復(fù)的437 個(gè)存在異常的真實(shí)代碼作為測(cè)試數(shù)據(jù)。對(duì)每一個(gè)存在異常代碼,DeepEHCR 為其推薦相關(guān)的異常處理策略或者異常處理的API 序列,如果推薦的結(jié)果正確,這表示該異常修復(fù)成功。
3.4.2 異常處理策略推薦性能評(píng)估
表4 展示了上文提及的3 種不同異常處理策略推薦模型在準(zhǔn)確率、精確率、召回率以及F1 值上的實(shí)驗(yàn)結(jié)果。LSTM 表示基于LSTM 的異常處理推薦算法,Self-Attention 表示未考慮結(jié)構(gòu)信息的自注意力網(wǎng)絡(luò)。DeepEHCR 在4 個(gè)指標(biāo)上的值都接近90%,說明DeepEHCR 在異常處理策略推薦上的整體性能高,平均每進(jìn)行10 次異常處理策略的推薦只會(huì)出現(xiàn)1 次錯(cuò)誤。準(zhǔn)確率方面,DeepEHCR 要比LSTM 和未考慮結(jié)構(gòu)信息的自注意力網(wǎng)絡(luò)分別高15.85 個(gè)百分點(diǎn)和4.52個(gè)百分點(diǎn);在F1 值方面,DeepEHCR 相較于LSTM 和未考慮結(jié)構(gòu)信息的自注意力網(wǎng)絡(luò)分別提升了17.6 個(gè)百分點(diǎn)以及4.54 個(gè)百分點(diǎn)。DeepEHCR 在異常處理策略推薦方面的性能要優(yōu)于其他方法的原因在于,它不僅充分考慮了上下文信息中的結(jié)構(gòu),而且利用自注意力網(wǎng)絡(luò)來挖掘API 調(diào)用之間的深層次的依賴關(guān)系。

表4 異常處理策略推薦實(shí)驗(yàn)結(jié)果Table 4 Exception handling strategy recommendation result 單位:%
表5 展示了3 種模型在時(shí)間性能上的對(duì)比結(jié)果。在推薦時(shí)間方面,3 個(gè)模型在5 576 個(gè)測(cè)試樣本上完成推薦的總時(shí)間差不多,其中DeepEHCR 平均完成一次推薦的時(shí)間只需0.038 s。說明DeepEHCR在提升準(zhǔn)確率的同時(shí)并沒有增加耗時(shí)。

表5 3 種異常處理策略推薦方法的時(shí)間性能Table 5 Time performance of 3 exception handling strategy recommendation methods 單位:s
3.4.3 異常處理的API序列推薦性能評(píng)估
表6 展示了3 種異常處理的API 序列推薦方法在Hit@K(K=1,3,5)上的實(shí)驗(yàn)結(jié)果。從表中的實(shí)驗(yàn)結(jié)果可知,自注意力網(wǎng)絡(luò)的性能要優(yōu)于LSTM。在充分考慮上下文中的結(jié)構(gòu)信息后,DeepEHCR在Hit@K上的得分相較于LSTM 以及未考慮結(jié)構(gòu)信息的自注意力網(wǎng)絡(luò)又有了明顯的提升。具體而言,DeepEHCR在Hit@1 上的得分達(dá)到了57.83%,相較于LSTM 和自注意力網(wǎng)絡(luò),分別提升了10.77 個(gè)百分點(diǎn)以及3.56個(gè)百分點(diǎn)。DeepEHCR 的性能優(yōu)于LSTM 的原因在于API 調(diào)用樹SBT 后的序列要比正常序列長一倍以上,LSTM 很難捕捉這種長序列中的依賴關(guān)系,而自注意力網(wǎng)絡(luò)通過多頭自注意力的計(jì)算避免了長依賴消失的問題。綜上所述,DeepEHCR在異常處理的API序列推薦任務(wù)上對(duì)比其余兩種方法有明顯的優(yōu)勢(shì)。

表6 3 種推薦方法在Hit@K 上的得分Table 6 Hit@K of 3 recommendation methods 單位:%
表7 展示了3 個(gè)模型在生成API 序列上的時(shí)間耗費(fèi)。在推薦時(shí)間方面,3 個(gè)模型在1 954 個(gè)測(cè)試樣本上完成推薦的總時(shí)間分別為271.1 s、301.5 s 以及314.2 s。平均每完成一次推薦花費(fèi)的時(shí)間分別為0.139 s、0.154 s 以及0.160 s,雖然DeepEHCR 最慢,但是這種差距幾乎可以忽略不計(jì)。

表7 3 種API序列推薦方法的時(shí)間性能Table 7 Time performance of 3 API sequence recommendation methods 單位:s
3.4.4 DeepEHCR 整體性能評(píng)估
本小節(jié)從DeepEHCR 整體出發(fā),分析其面對(duì)真實(shí)異常代碼的情況下推薦的性能。表8 展示了DeepEHCR 和其他兩個(gè)對(duì)比方法在修復(fù)真實(shí)的異常漏洞方面的實(shí)驗(yàn)結(jié)果。在所有439 個(gè)存在異常漏洞的代碼中,DeepEHCR 成功推薦了320 個(gè)異常處理代碼用于修復(fù)異常漏洞,修復(fù)的準(zhǔn)確率達(dá)到了72.8%,而CarMiner 和FuzzyCatch 分別成功修復(fù)了196 個(gè)(44.6%)異常漏洞以及287 個(gè)(65.4%)異常漏洞。實(shí)驗(yàn)表明DeepEHCR 在整體異常處理代碼推薦的性能方面明顯好于其余兩個(gè)對(duì)比方法。

表8 異常漏洞修復(fù)結(jié)果Table 8 Results in fixing exception bugs
針對(duì)API 庫的更新速度快、學(xué)習(xí)正確異常處理成本高的問題,本文提出了一種基于自注意力網(wǎng)絡(luò)的智能化異常處理代碼推薦方法DeepEHCR。該方法構(gòu)建API 調(diào)用樹來表示上下文信息,并利用自注意力網(wǎng)絡(luò)推薦相應(yīng)的異常處理策略。針對(duì)處理異常這一具體策略,利用Transformer 模型進(jìn)一步推薦處理異常相關(guān)的API 序列。實(shí)驗(yàn)表明DeepEHCR 能夠有效提取上下文信息并做出正確的異常處理代碼推薦。
在未來的工作中,會(huì)優(yōu)化推薦的異常處理代碼形式,在異常處理的代碼中增加結(jié)構(gòu)信息。除此之外,為了更全面地分析模型的優(yōu)勢(shì),會(huì)考慮構(gòu)建不同編程語言的推薦模型。