劉 霜 吳毅堅(jiān) 沈立煒 趙文耘
1(復(fù)旦大學(xué)軟件學(xué)院 上海 201203) 2(復(fù)旦大學(xué)計(jì)算機(jī)科學(xué)技術(shù)學(xué)院 上海 201203) 3(上海市數(shù)據(jù)科學(xué)重點(diǎn)實(shí)驗(yàn)室 上海 201203)
軟件系統(tǒng)的開發(fā)過程中,不可避免會(huì)出現(xiàn)代碼問題。在成千上萬甚至百萬行代碼的大型項(xiàng)目中找到問題并修復(fù)是一件困難的事情,因此,研究人員多年來致力于代碼問題自動(dòng)修復(fù)技術(shù)的研究。代碼問題自動(dòng)修復(fù)是在沒有人工干預(yù)的情況下自動(dòng)修復(fù)軟件問題。即對(duì)于不滿足正確約束的程序,生成滿足程序約束的補(bǔ)丁[1-2]。這種技術(shù)的基本目的是自動(dòng)生成正確的補(bǔ)丁,消除軟件程序中錯(cuò)誤的補(bǔ)丁程序[3-4]。工業(yè)界也很關(guān)注代碼問題自動(dòng)修復(fù)技術(shù),如Simfix、CBCD、PRECFIX[5]已步入應(yīng)用。
本文介紹的推薦方法適用于代碼靜態(tài)分析檢測到的問題(Issue)。靜態(tài)分析檢測到的問題一般有三類,包括編程錯(cuò)誤、違反標(biāo)準(zhǔn)的編碼、安全弱點(diǎn),后面簡稱為代碼問題。代碼靜態(tài)分析是一種通過在運(yùn)行程序之前檢查源代碼進(jìn)行調(diào)試的方法,通過針對(duì)一組(或多組)編碼規(guī)則分析一組代碼來完成,相關(guān)工具包括FindBugs、SonarQube、Helix QAC、Klocwork等。
靜態(tài)分析尤其擅長發(fā)現(xiàn)編碼問題,例如緩沖區(qū)溢出、內(nèi)存泄漏和空指針。當(dāng)程序開發(fā)人員碰到緩沖區(qū)溢出問題時(shí),由于提示信息往往僅包括問題類型和位置,可能無法馬上知道該如何修改。而同時(shí),該類問題有很多歷史修復(fù),它們對(duì)應(yīng)的問題類型、出問題的代碼及問題的代碼上下文可能是類似的,于是這些歷史修復(fù)可作為推薦方案。然而如何進(jìn)行方案推薦并不容易,首先,每種問題類型有著太多被正確修復(fù)過的源代碼文件,難以判定這些源代碼文件的歷史修復(fù)中哪個(gè)或哪些是我們需要的;再者,每種問題類型有著太多的歷史修復(fù),修復(fù)方式也是多種多樣,難以從如此多的歷史修復(fù)中有效率地推薦出一個(gè)有效的方案;最后,面對(duì)一個(gè)曾經(jīng)被正確修復(fù)過的源代碼文件,難以找到它針對(duì)該問題的修復(fù)方式。
針對(duì)這些問題,本文提出一種基于代碼上下文相似度分析的代碼問題修復(fù)推薦方法。該方法首先收集歷史版本中代碼問題的修復(fù)案例,建立問題修復(fù)資源庫,然后根據(jù)問題類型、問題代碼及上下文、修復(fù)代碼及上下文對(duì)修復(fù)案例進(jìn)行聚類,對(duì)每種不同類型的問題建立修復(fù)模板,最后通過對(duì)有同類問題的目標(biāo)代碼及其上下文進(jìn)行相似性分析,從而推薦具體的修復(fù)方式。基于上述方法,設(shè)計(jì)并實(shí)現(xiàn)一個(gè)基于代碼上下文相似度分析的代碼問題修復(fù)推薦工具。
眾所周知,修復(fù)軟件項(xiàng)目中的各種問題是一件非常困難且大量耗費(fèi)時(shí)間精力的事情。據(jù)統(tǒng)計(jì),修復(fù)軟件問題的時(shí)間占軟件維護(hù)時(shí)間成本的35.6%[6],軟件成本的絕大多數(shù)被認(rèn)為是軟件維護(hù)成本。開發(fā)人員花在修復(fù)問題上的時(shí)間占全部開發(fā)時(shí)間一半左右[7]。
靜態(tài)代碼分析是在軟件開發(fā)的早期,即運(yùn)行程序之前(例如,在編碼和單元測試之間)進(jìn)行的。靜態(tài)代碼分析可以盡早發(fā)現(xiàn)問題。靜態(tài)代碼分析可以通過創(chuàng)建自動(dòng)反饋循環(huán)來支持DevOps。開發(fā)人員將及早知道他們的代碼中是否存在問題,此時(shí)解決這些問題將更加容易。靜態(tài)分析相比于動(dòng)態(tài)分析更容易發(fā)現(xiàn)編碼錯(cuò)誤,因?yàn)槟承┚幋a錯(cuò)誤可能不會(huì)在單元測試期間發(fā)生。因此,靜態(tài)代碼分析(特別是在需要遵守行業(yè)標(biāo)準(zhǔn)的情況下)有不少優(yōu)勢(shì)。
目前學(xué)術(shù)界進(jìn)行問題修復(fù)的前提基于兩個(gè)假設(shè),測試用例的充分性假設(shè)(測試用例是否完全反映程序的正確性)和被修復(fù)代碼的高質(zhì)量假設(shè)[26],因此通過測試不總是等于完成修復(fù),存在精確性不夠高的問題。很多問題修復(fù)技術(shù)只針對(duì)某種類型的問題設(shè)計(jì)了修復(fù)算法。很多修復(fù)技術(shù)耗時(shí)長,計(jì)算資源消耗多。
針對(duì)自動(dòng)修復(fù)技術(shù)存在的各種問題,以及需要人工確認(rèn)的局限性,在工程上,我們可以采用修復(fù)方式推薦的策略來幫助程序開發(fā)人員更快更好地理解并修復(fù)代碼問題。因此本文提出基于代碼上下文相似度分析的代碼問題修復(fù)推薦方法,這是一種問題修復(fù)的通用技術(shù),它無須基于兩個(gè)假設(shè),生成的推薦方法能夠盡量避免引入其他的問題,且通過問題類型、問題代碼及上下文和修復(fù)代碼及上下文聚類方法提高了推薦效果及問題修復(fù)效率。
基于代碼上下文相似度分析的代碼問題修復(fù)推薦方法過程,主要包括以下兩個(gè)步驟。
(1) 問題修復(fù)案例(case)的歸類和存儲(chǔ)。圖1展示了該步驟,首先通過靜態(tài)分析工具(如FindBugs)獲取問題版本的問題類型、問題代碼及上下文,通過diff分析獲取修復(fù)版本的修復(fù)代碼及上下文,這些信息作為一條修復(fù)案例。然后根據(jù)問題類型、問題代碼及上下文、修復(fù)代碼及上下文對(duì)修復(fù)案例進(jìn)行聚類。最后,將修復(fù)案例抽象化,形成修復(fù)模板。將修復(fù)案例及模板存儲(chǔ)起來,作為問題修復(fù)資源庫。
圖1 問題修復(fù)案例的歸類和存儲(chǔ)
(2) 代碼問題修復(fù)推薦。圖2展示了該步驟,以目標(biāo)問題類型、問題代碼及其上下文代碼為輸入,結(jié)合問題修復(fù)資源庫中相應(yīng)類型的問題代碼及其上下文的相似度匹配,進(jìn)行問題修復(fù)案例推薦。本文提出基于代碼上下文相似度分析的代碼問題修復(fù)推薦方法,對(duì)輸入問題及其上下文代碼進(jìn)行Token化后的文本相似度分析,找出相似度最高的幾種修復(fù)方式,為用戶進(jìn)行問題修復(fù)推薦。
圖2 代碼問題修復(fù)推薦
1.3.1問題修復(fù)案例的歸類和存儲(chǔ)
通過解析靜態(tài)分析工具(如FindBugs)的結(jié)果文件,得到問題版本的問題類型、問題代碼及上下文。通過diff分析得到解決版本的修復(fù)代碼及上下文。本文將每個(gè)歷史已解決問題的類型、問題代碼及上下文、修復(fù)代碼及上下文構(gòu)造為一個(gè)修復(fù)案例,將它們保存在問題修復(fù)資源庫。
同種問題可能有多種不同的修復(fù)方式,但修復(fù)資源庫中針對(duì)同種問題的修復(fù)案例所采用的修復(fù)方式可能會(huì)存在較多重復(fù),所以需要對(duì)同種問題類型中的修復(fù)案例根據(jù)修復(fù)方式進(jìn)行聚類。具體對(duì)于某種問題的所有修復(fù)案例,其聚類方法是:首先,對(duì)于每個(gè)修復(fù)案例,將其問題代碼及上下文、修復(fù)代碼及上下文進(jìn)行分詞,這里主要按空格和調(diào)用符進(jìn)行劃分,接著采用詞袋模型將這些分詞構(gòu)建為詞頻向量。最后對(duì)詞頻向量進(jìn)行加權(quán)處理,以提升其中關(guān)鍵字的權(quán)重。最終每個(gè)修復(fù)案例被表示為一個(gè)加權(quán)的特征向量,其形式為:D=D(T1,W1;T2,W2;…;Tn,Wn),其中:T為分詞;W為權(quán)重;n為詞匯空間的大小,其由該問題所有修復(fù)案例的分詞種類數(shù)決定。最后使用凝聚層次聚類算法對(duì)這些特征向量進(jìn)行聚類,聚為一類的案例本文認(rèn)為有著相同的修復(fù)方式。
圖3展示了一個(gè)NP_NULL_ON_SOME_PATH問題類型的修復(fù)案例,其中:(a)展示的是問題修復(fù)前的代碼段,①②是問題代碼(FindBugs結(jié)果解析出的),其他的都是上下文代碼;(b)展示的是問題修復(fù)后的代碼段,①是修復(fù)代碼(問題代碼及上下文修復(fù)前后的diff),其他的都是上下文代碼。上下文代碼會(huì)用來計(jì)算代碼相似度,從而提高推薦效果。具有相似上下文的代碼問題會(huì)在推薦結(jié)果里排序更高。經(jīng)過分詞,統(tǒng)計(jì)出每個(gè)詞語的出現(xiàn)次數(shù),構(gòu)建其詞頻向量為:
{if,4;_injectableValues,4;null,4;return,4;findInjectableValue,2;valueId,3;this,4;forProperty,3;beanInstance,2;throw,2;new,2;IllegalStateException,2;No,1;injectableValues,1;configured,1;can,1;not,1;inject,1;value,1;with,1;id,1;}
圖3 NP_NULL_ON_SOME_PATH修復(fù)案例- 修復(fù)前后代碼段
雖然詞袋模型沒有考慮詞在代碼中的順序,但其簡單高效,并且經(jīng)過加權(quán)處理,在有限的修復(fù)案例中可以達(dá)到良好的效果。
使用上述修復(fù)案例聚類算法,發(fā)現(xiàn)每種代碼問題的修復(fù)方式一般分為一種或多種,例如NP_NULL_ON_SOME_PATH問題的修復(fù)方式一般有五種,且模式較為固定。
1) 對(duì)可能為空的變量進(jìn)行null值判斷的操作加上else分支,可能出現(xiàn)異常的操作放在else分支中運(yùn)行。
2) 先判斷變量是否為null值,如果為空直接拋異常。
3) 原問題代碼中沒有if-else語句的,在修復(fù)代碼中添加if-else,在if語句中對(duì)變量進(jìn)行null值判斷,可能出現(xiàn)異常的語句在else分支中運(yùn)行。
4) 先判斷變量是否為null值,如果為空直接進(jìn)行return操作。
5) 在變量賦值的語句中,變賦值為null為賦值一個(gè)實(shí)例。
圖3(b)展示了NP_NULL_ON_SOME_PATH問題修復(fù)案例的修復(fù)后代碼段,該案例使用了第二種修復(fù)方式,即先判斷變量是否為null值,如果為空直接拋異常的方式進(jìn)行修復(fù)。
修復(fù)案例聚類完成后,對(duì)其進(jìn)行抽象化處理,形成修復(fù)模板。抽象處理過程為:將變量名按其出現(xiàn)的次序替換為MYM1、MYM2等;將基本數(shù)據(jù)類型分別初始化為其默認(rèn)值;對(duì)于自定義的函數(shù)按其出現(xiàn)順序統(tǒng)一替換為f1、f2等;所有操作符、關(guān)鍵字保持不變;去掉注釋和多余的空行,統(tǒng)一縮進(jìn)。本文認(rèn)為每個(gè)修復(fù)案例抽象化后都是一個(gè)修復(fù)模板。一個(gè)修復(fù)模板包括問題類型、問題代碼及上下文(抽象化的)、修復(fù)代碼及上下文(抽象化的)。
1.3.2代碼問題修復(fù)推薦
本文提出的基于代碼上下文相似度分析的代碼問題修復(fù)推薦算法基于問題類型、問題代碼及其上下文的文本相似度(基于編輯距離),能夠?yàn)橛脩敉扑]與其問題最為相似的修復(fù)方式。問題類型指根據(jù)代碼靜態(tài)分析結(jié)果得到代碼問題的類型。問題代碼指根據(jù)代碼靜態(tài)分析得到的具體問題代碼行。問題代碼上下文指根據(jù)代碼靜態(tài)分析得到的與具體問題行相關(guān)的上下文代碼,它是根據(jù)具體問題類型的具體特點(diǎn)得到的、與問題發(fā)生原因緊密聯(lián)系的代碼內(nèi)容。以上所需可以通過代碼靜態(tài)分析的結(jié)果文件得到(例如通過分析FindBugs結(jié)果文件)。該算法將目標(biāo)問題代碼及其上下文與資源庫中的相同問題類型的問題記錄的問題代碼及其上下文進(jìn)行(基于有限自動(dòng)機(jī)的Token化后的)文本相似度匹配,選擇相似度高的、歷史出現(xiàn)次數(shù)多的修復(fù)方式進(jìn)行推薦。
基于本文方法,研發(fā)了基于代碼上下文相似度分析的代碼問題修復(fù)推薦工具。問題推薦工具可分析目標(biāo)項(xiàng)目內(nèi)的已修復(fù)問題的修復(fù)方式并對(duì)其進(jìn)行聚類和建模,構(gòu)建問題修復(fù)資源庫,然后根據(jù)目標(biāo)問題代碼及其上下文匹配資源庫內(nèi)的問題修復(fù)記錄,選擇相似度高的進(jìn)行修復(fù)推薦。
實(shí)驗(yàn)選取Github上的高星開源項(xiàng)目作為實(shí)驗(yàn)對(duì)象,代碼問題修復(fù)資源庫則選擇本文資源庫進(jìn)行實(shí)驗(yàn)。本文根據(jù)實(shí)驗(yàn)?zāi)康姆謩e設(shè)置不同的實(shí)驗(yàn),分析了工具的聚類效果及推薦效果的有效性,并且對(duì)本文代碼問題修復(fù)推薦方法的局限性進(jìn)行說明,最后將本文工具與“Nopol”進(jìn)行對(duì)比。
2.2.1聚類效果的有效性評(píng)估
在基于代碼相似度分析的問題修復(fù)推薦方法中,使用問題代碼及其上下文聚類、問題修復(fù)方式聚類方法進(jìn)行了優(yōu)化,提高了推薦的效果。本實(shí)驗(yàn)選取了Github上的高星開源項(xiàng)目的50條歷史修復(fù)案例,用聚類方法對(duì)其進(jìn)行聚類操作,對(duì)其實(shí)驗(yàn)結(jié)果進(jìn)行評(píng)估,驗(yàn)證其正確性、有效性。
這50條歷史修復(fù)案例分為七種問題類型,其中第一、第二、第五種問題類型只有一條修復(fù)案例,無須進(jìn)行聚類,將其剔除。剩余47條歷史修復(fù)案例的聚類實(shí)驗(yàn)結(jié)果如圖4所示。
圖4 聚類實(shí)驗(yàn)結(jié)果
47條修復(fù)案例分為4種問題類型。圖4展示了每種問題類型的案例條數(shù)。只有同種問題類型的案例才能進(jìn)行聚類。如問題類型三的20條修復(fù)案例,通過修復(fù)案例聚類將該20條修復(fù)案例聚為了4類,即該類型的代碼問題歷史上有4種修復(fù)方式。其余問題類型的聚類以此類推。
本文挑選了6位具有程序開發(fā)研究背景和經(jīng)驗(yàn)的碩士生,其中4位每人負(fù)責(zé)一種問題類型,對(duì)聚類結(jié)果的正確性進(jìn)行人工驗(yàn)證,其余2位對(duì)原始的47條修復(fù)案例分別進(jìn)行人工分析歸類。針對(duì)聚類效果評(píng)估實(shí)驗(yàn),參與者得到的信息包括50條修復(fù)案例的問題類型、問題代碼及上下文代碼、提交日期、修復(fù)方式代碼及上下文等。他們可以訪問修復(fù)推薦系統(tǒng),了解聚類方法的主要功能和流程,可以看到該方法的數(shù)據(jù)模型和源碼,也可以查看聚類方法的中間信息及結(jié)果信息。
實(shí)驗(yàn)發(fā)現(xiàn),驗(yàn)證類型三、類型四、類型六的碩士生表示他們認(rèn)為系統(tǒng)的聚類結(jié)果是正確的,即聚類方法對(duì)這三種問題類型的聚類類別個(gè)數(shù)以及每一類的修復(fù)案例劃分是正確的。驗(yàn)證類型七的碩士研究生表示這7個(gè)修復(fù)案例應(yīng)聚為4類而不是聚類所得的3類。即實(shí)驗(yàn)結(jié)果中的問題類型七的三種修復(fù)方式中的第1類(圖4中標(biāo)有星號(hào)的類別)應(yīng)該聚為兩類。這兩類中的2種修復(fù)方式的代碼非常類似,但是他認(rèn)為劃分為2種修復(fù)方式更為精確,其余兩類的聚類結(jié)果正確。其余2位實(shí)驗(yàn)參與者中的第一位的人工聚類結(jié)果與工具的聚類結(jié)果是相同的,第二位參與者的人工聚類結(jié)果與工具的聚類結(jié)果稍有差異,差異也是問題類型七中的第一類(圖4中標(biāo)有星號(hào)的類別)修復(fù)方式的劃分,他認(rèn)為該類劃分為兩類更為合適,其余類別的劃分?jǐn)?shù)量及每個(gè)類別中的修復(fù)案例與工具的聚類結(jié)果一致。
設(shè)置對(duì)比實(shí)驗(yàn)以驗(yàn)證問題聚類能有效提高對(duì)目標(biāo)問題修復(fù)推薦的效果。本實(shí)驗(yàn)挑選了2組具有程序開發(fā)經(jīng)驗(yàn)的碩士生,每組4人,2組參與者的程序開發(fā)能力相當(dāng)。從平臺(tái)上隨機(jī)選擇8個(gè)未修復(fù)且有推薦結(jié)果的問題,分配給2組的參與者,每人2個(gè)問題。第一組參與者根據(jù)聚類后的推薦修復(fù)案例結(jié)果進(jìn)行問題修復(fù),第二組參與者根據(jù)不聚類的推薦修復(fù)案例結(jié)果進(jìn)行問題修復(fù),分別記錄每位參與者對(duì)每個(gè)問題進(jìn)行出錯(cuò)原因排查、選定修復(fù)案例、問題修復(fù)所用的時(shí)間。最終每個(gè)問題都在正常時(shí)間內(nèi)被修復(fù)。實(shí)驗(yàn)發(fā)現(xiàn),第一組參與者的平均問題出錯(cuò)原因排查時(shí)間為1 min,平均選定修復(fù)案例時(shí)間為1 min,平均問題修復(fù)使用時(shí)間為3.2 min。第二組參與者的平均問題出錯(cuò)原因排查時(shí)間為2 min,平均選定修復(fù)案例時(shí)間為3.6 min,平均問題修復(fù)使用時(shí)間為7.8 min。從上述結(jié)果可知,問題聚類能有效提高對(duì)目標(biāo)問題修復(fù)推薦的效果。
由實(shí)驗(yàn)結(jié)果可知,本文的聚類結(jié)果基本正確,聚類方法較為合理,聚類效果較為有效。
2.2.2推薦方法的有效性評(píng)估
本實(shí)驗(yàn)從Github上隨機(jī)選擇4個(gè)高質(zhì)量項(xiàng)目中的23個(gè)待修復(fù)問題作為目標(biāo)問題,使用自己的歷史修復(fù)記錄資源庫中的問題修復(fù)案例對(duì)工具進(jìn)行有效性評(píng)估,工具中添加了來自Github的217個(gè)Java軟件項(xiàng)目,分析了這些項(xiàng)目近一年來的57 752個(gè)問題修復(fù)案例。選中的4個(gè)項(xiàng)目中有一個(gè)項(xiàng)目是平臺(tái)中的,隨機(jī)選擇該項(xiàng)目中的4個(gè)至今沒有被修復(fù)的問題作為目標(biāo)問題。其余3個(gè)項(xiàng)目不曾添加到平臺(tái)中,從這3個(gè)項(xiàng)目中隨機(jī)選擇19個(gè)至今未修復(fù)的問題作為目標(biāo)問題。本實(shí)驗(yàn)認(rèn)為對(duì)于一個(gè)待修復(fù)問題,推薦出的能夠幫助開發(fā)人員較快地理解并修復(fù)問題的修復(fù)案例叫作有參考價(jià)值的修復(fù)案例。
下面通過一個(gè)具體實(shí)例來說明怎樣叫做有參考價(jià)值的修復(fù)案例。
圖5展示了一個(gè)目標(biāo)問題的代碼段,①為問題代碼行。實(shí)驗(yàn)要求在不尋求其他任何幫助的情況下,僅依靠為它推薦出的代碼問題修復(fù)結(jié)果對(duì)問題進(jìn)行修復(fù)。
圖5 目標(biāo)問題代碼
本文工具為該目標(biāo)問題推薦出兩條修復(fù)案例,其中第一條更為相似。圖6以及圖7展示了為該問題推薦的第一條修復(fù)案例。其中,圖6展示的是問題代碼段,①為問題代碼行,其余部分為上下文代碼行。我們把該問題代碼段與目標(biāo)問題代碼段進(jìn)行比較,可以看出,這兩段問題代碼發(fā)生錯(cuò)誤的原因一致,且代碼非常相似,僅變量名不同,Token化后相似度很高,經(jīng)過推薦算法的計(jì)算,此記錄作為目標(biāo)問題的推薦結(jié)果很合適。圖7展示了推薦報(bào)告中的修復(fù)方式代碼段,①為修復(fù)代碼行,根據(jù)該推薦結(jié)果,無須尋求其他任何幫助,很容易對(duì)目標(biāo)問題進(jìn)行修復(fù),具有很高的參考價(jià)值。
圖6 問題修復(fù)推薦報(bào)告-問題代碼段
圖7 問題修復(fù)推薦報(bào)告-修復(fù)方式代碼段
本實(shí)驗(yàn)挑選了4位具有程序開發(fā)經(jīng)驗(yàn)的碩士生,讓他們分別對(duì)4個(gè)項(xiàng)目中的目標(biāo)問題的推薦結(jié)果的有效性進(jìn)行評(píng)估。實(shí)驗(yàn)認(rèn)為,如果在3小時(shí)內(nèi)修復(fù)推薦系統(tǒng)能夠推薦出修復(fù)案例、參與者完成問題修復(fù)、修復(fù)后的程序能夠通過FindBugs檢測,就認(rèn)為該目標(biāo)問題被成功修復(fù)(其中參與者進(jìn)行問題修復(fù)的過程不能超過15 min)。其中任何一項(xiàng)不能通過則認(rèn)為目標(biāo)問題沒有被成功修復(fù)。
本文認(rèn)為精確率是所有被成功修復(fù)的目標(biāo)問題數(shù)目除以所有成功生成推薦結(jié)果的目標(biāo)問題數(shù)目。
表1展示了23個(gè)目標(biāo)問題的推薦結(jié)果信息,參與者自己操作推薦系統(tǒng)獲取推薦結(jié)果,對(duì)于成功生成推薦結(jié)果的目標(biāo)問題,參與者從所有的推薦修復(fù)案例中選出一個(gè)最具有參考價(jià)值的修復(fù)案例,根據(jù)該修復(fù)案例對(duì)目標(biāo)問題進(jìn)行修復(fù),將修復(fù)后的程序進(jìn)行靜態(tài)分析。最終經(jīng)過實(shí)驗(yàn)驗(yàn)證,工具對(duì)23個(gè)目標(biāo)問題中的20個(gè)成功生成了推薦修復(fù)案例,這20個(gè)修復(fù)案例與目標(biāo)問題上下文匹配,是同種類型的問題。參與者對(duì)選定的最具有參考價(jià)值的20個(gè)修復(fù)案例進(jìn)行評(píng)估,認(rèn)為這20個(gè)選定的修復(fù)案例中17個(gè)是有效的、有參考價(jià)值的。即被成功修復(fù)的目標(biāo)問題數(shù)目為17,所有成功生成推薦結(jié)果的目標(biāo)問題數(shù)目為20,本文工具的精確率為85%。
表1 目標(biāo)問題推薦結(jié)果信息
設(shè)置對(duì)比實(shí)驗(yàn)以驗(yàn)證進(jìn)行問題修復(fù)推薦是有效的,可以提升開發(fā)效率。本實(shí)驗(yàn)挑選了2組具有程序開發(fā)經(jīng)驗(yàn)的碩士生,每組4人,2組參與者的程序開發(fā)能力相當(dāng)。從平臺(tái)上隨機(jī)選擇8個(gè)未修復(fù)問題,分配給2組的參與者,每人2個(gè)問題。第一組參與者根據(jù)問題修復(fù)推薦系統(tǒng)的推薦結(jié)果進(jìn)行問題修復(fù),第二組參與者沒有推薦結(jié)果作為參考,分別記錄每位參與者對(duì)每個(gè)問題進(jìn)行修復(fù)所用的時(shí)間。最終所有問題都被修復(fù)。實(shí)驗(yàn)發(fā)現(xiàn),第一組參與者的平均問題修復(fù)使用時(shí)間為3.7 min。第二組參與者的平均問題修復(fù)使用時(shí)間為16.2 min。從上述結(jié)果可知,問題修復(fù)推薦是有效的,可以提升開發(fā)效率。
上述兩個(gè)實(shí)驗(yàn)說明本文問題推薦方法以及推薦工具在實(shí)際的應(yīng)用中具有有效性。
然而,本文方法具有一定的局限性,由于本文方法是基于歷史修復(fù)的,因此修復(fù)推薦的結(jié)果的質(zhì)量依賴于修復(fù)資源庫中所收集的項(xiàng)目的質(zhì)量和數(shù)量。該方法僅針對(duì)靜態(tài)分析,只能修復(fù)代碼靜態(tài)分析檢測到的問題。由于該方法基于靜態(tài)檢測結(jié)果進(jìn)行分析,因此FindBugs的誤報(bào)漏報(bào)可能會(huì)對(duì)后續(xù)分析產(chǎn)生影響。由于目前僅使用FindBugs作為代碼靜態(tài)分析工具,只分析了Java項(xiàng)目,本文中的問題修復(fù)推薦方法目前僅適用于Java語言。
2.2.3與Nopol的區(qū)別
Nopol[8]是一種基于語義的問題修復(fù)工具,由于它也是代碼問題修復(fù)方面的工具,可以對(duì)Java程序中的問題進(jìn)行修復(fù),且是近年來較新的工具,因此選擇該工具進(jìn)行比較。我們從輸入輸出、問題類型、精確率、誤差幾方面將本文方法與其進(jìn)行對(duì)比。
輸入輸出:Nopol的輸入是一個(gè)帶有問題的程序以及相應(yīng)的測試集合,輸出是其生成的帶有條件表達(dá)式的補(bǔ)丁。測試集合包含可通過的測試用例以及至少一個(gè)失敗的測試用例??赏ㄟ^的測試用例用以定義程序預(yù)期的行為;失敗的測試用例用以定義要修復(fù)的問題。而本文工具的輸入是一個(gè)有問題的程序,輸出是與該問題最為相似的幾條修復(fù)記錄,包括問題代碼及其上下文、修復(fù)方式代碼及其上下文、問題類型等。
問題類型:Nopol可以修復(fù)的問題類型是if條件語句錯(cuò)誤及前置條件錯(cuò)誤,即針對(duì)條件語句問題進(jìn)行修復(fù)。本文工具的問題修復(fù)方法是通用的。
精確率:根據(jù)Xuan等[8]對(duì)Nopol的實(shí)驗(yàn)可知,在得到的17個(gè)合成補(bǔ)丁中,有13個(gè)是正確的,而本文的實(shí)驗(yàn)結(jié)果表明,在得到推薦結(jié)果的20個(gè)目標(biāo)問題中,17個(gè)被正確修復(fù)。
誤差:在實(shí)踐中,僅僅通過測試集合的補(bǔ)丁不一定是正確的,因?yàn)闇y試用例不一定能夠正確地定義程序行為。因此,當(dāng)測試集合的質(zhì)量不足時(shí),Nopol便無法保證能夠生成正確的補(bǔ)丁。而文本方法無需測試集合的支持,避免了該種情況的發(fā)生,且給出的已修復(fù)程序通過了FindBugs的靜態(tài)掃描,一定程度上避免了新問題的引入。
代碼問題修復(fù)在產(chǎn)業(yè)界可以輔助開發(fā)人員修復(fù)代碼,提高代碼質(zhì)量,在學(xué)術(shù)界更是實(shí)現(xiàn)軟件自動(dòng)化的有效途徑。由于其極大的產(chǎn)業(yè)意義和學(xué)術(shù)意義備受關(guān)注,越來越多的研究人員致力于問題修復(fù)技術(shù)的研究。
Genprog是早期的能夠自動(dòng)生成補(bǔ)丁的系統(tǒng),該系統(tǒng)的研究人員提出,可在應(yīng)用程序的其他位置找到補(bǔ)丁的代碼[9-10],除了發(fā)現(xiàn)可以重用的代碼存在于其他地方這一事實(shí)外,還發(fā)現(xiàn)了潛在的修復(fù)成分的上下文是有用的:通常,重用代碼部分的上下文類似于被修復(fù)的部分的代碼上下文[11-12]。生成候選補(bǔ)丁的一種方法是在原始程序上應(yīng)用變異運(yùn)算符。變異運(yùn)算符潛在地通過它的抽象語法樹表示形式或更粗粒度的表示形式(例如語句級(jí)或塊級(jí))操縱原始程序。較早的遺傳改良方法在語句級(jí)別運(yùn)行,并執(zhí)行簡單的刪除/替換操作,例如刪除現(xiàn)有語句或在同一源文件中用另一個(gè)語句替換現(xiàn)有語句[13-14]。最近的方法[15]在抽象語法樹級(jí)別使用了更細(xì)粒度的運(yùn)算符來生成更多組候選補(bǔ)丁。機(jī)器學(xué)習(xí)技術(shù)可以提高自動(dòng)問題修復(fù)系統(tǒng)的效率。SequenceR[16]對(duì)源代碼使用句到句學(xué)習(xí),以生成單行補(bǔ)丁,它定義了一種神經(jīng)網(wǎng)絡(luò)體系結(jié)構(gòu),可與源代碼很好地結(jié)合,并具有復(fù)制機(jī)制,該機(jī)制允許使用未包含在學(xué)習(xí)詞匯中的token生成補(bǔ)丁,這些token來自正在修復(fù)的Java類的代碼。
使用修復(fù)模板[17]是生成候選補(bǔ)丁的替代方法,用于修復(fù)特定類別的問題。也可以自動(dòng)挖掘修復(fù)模板[18]用于生成補(bǔ)丁并對(duì)其進(jìn)行驗(yàn)證。Osman等[19]提出的修復(fù)模式(patterns)獲取方法是先自動(dòng)地獲取修改模式,然后手動(dòng)地修改這些模式。他們的人工分析階段對(duì)深入了解問題背后的原因至關(guān)重要。他們的更改粒度在單行代碼級(jí)別,無須任何抽象就可以捕獲所有更改。該研究包含的軟件項(xiàng)目有717個(gè)。
代碼問題修復(fù)技術(shù)有許多應(yīng)用。在開發(fā)環(huán)境中,當(dāng)開發(fā)人員遇到問題時(shí),可以通過如單擊按鈕等操作來搜索補(bǔ)丁。當(dāng)自動(dòng)化到一定程度,IDE無須等待人工指示,主動(dòng)在后臺(tái)搜索潛在問題的解決方案。在持續(xù)集成服務(wù)器中,如果在持續(xù)集成過程中構(gòu)建失敗,則構(gòu)建失敗后可以立即搜索補(bǔ)丁[20]。實(shí)驗(yàn)表明,生成的補(bǔ)丁可以被開放源代碼的開發(fā)人員接受并合并到代碼庫中[21]。系統(tǒng)運(yùn)行時(shí),當(dāng)發(fā)生運(yùn)行故障,可以搜索二進(jìn)制補(bǔ)丁并在線應(yīng)用。這種修復(fù)系統(tǒng)的一個(gè)示例是ClearView[22],它使用x86二進(jìn)制補(bǔ)丁對(duì)x86代碼進(jìn)行修復(fù)。Itzal系統(tǒng)[20]與Clearview不同:修復(fù)搜索在運(yùn)行時(shí)進(jìn)行。而在生產(chǎn)中,所生產(chǎn)的補(bǔ)丁是源代碼級(jí)別的。BikiniProxy[23]系統(tǒng)可以在線修復(fù)瀏覽器中發(fā)生的JavaScript錯(cuò)誤。阿里巴巴為了解決內(nèi)部面臨的挑戰(zhàn)和難題,提出了PRECFIX[5]方法,主要分為三步,首先從代碼提交數(shù)據(jù)中提取“缺陷修復(fù)對(duì)”,然后將相似的“缺陷修復(fù)對(duì)”聚類,最后對(duì)聚類結(jié)果進(jìn)行模板提取,這個(gè)缺陷檢測和補(bǔ)丁推薦技術(shù)可以用于代碼評(píng)審、全庫離線掃描等。
然而,目前提出的問題修復(fù)方法還是存在很多問題,如精確率低、只能修復(fù)特定問題類型、對(duì)測試用例完備性的要求非常高、修復(fù)的耗時(shí)長、消耗的計(jì)算資源較多等。本文基于所提方法研發(fā)的基于代碼相似度分析的代碼問題修復(fù)方式推薦工具,是針對(duì)任意問題類型的,它不需要輸入測試用例,聚類效果較好,推薦結(jié)果較為具有參考價(jià)值,能夠提高程序開發(fā)人員修復(fù)問題的效率。
本文提出一種基于代碼上下文相似度分析的代碼問題修復(fù)推薦方法。該方法首先收集歷史版本中代碼問題的修復(fù)案例,建立問題修復(fù)資源庫,然后根據(jù)問題類型、問題代碼及上下文、修復(fù)代碼及上下文對(duì)修復(fù)案例進(jìn)行聚類,對(duì)每種不同類型的問題建立修復(fù)模板,最后通過對(duì)有同類問題的目標(biāo)代碼及其上下文進(jìn)行相似性分析,從而推薦具體的修復(fù)方式?;谝陨纤龇椒?,設(shè)計(jì)并實(shí)現(xiàn)一個(gè)基于代碼上下文相似度分析的代碼問題修復(fù)推薦工具,并基于Github上的真實(shí)項(xiàng)目設(shè)置多組實(shí)驗(yàn)對(duì)聚類效果的有效性和推薦方法的有效性進(jìn)行驗(yàn)證。
然而,本文方法的問題推薦目前是基于代碼問題修復(fù)方式資源庫的,因此資源庫中修復(fù)案例的質(zhì)量決定了修復(fù)推薦結(jié)果的質(zhì)量。另外,該方法僅針對(duì)靜態(tài)分析,因此只能修復(fù)代碼靜態(tài)分析檢測到的問題,對(duì)于靜態(tài)分析結(jié)果中可能產(chǎn)生的誤報(bào)漏報(bào)需要進(jìn)行進(jìn)一步處理,因此在實(shí)際應(yīng)用中代碼問題修復(fù)推薦的能力受此影響,對(duì)此,我們需要繼續(xù)進(jìn)行研究。