汪 昕,陳 馳,趙逸凡,彭 鑫,趙文耘
1(復(fù)旦大學(xué) 軟件學(xué)院,上海 201203)
2(上海市數(shù)據(jù)科學(xué)重點(diǎn)實(shí)驗(yàn)室(復(fù)旦大學(xué)),上海 201203)
在軟件開發(fā)過程中,開發(fā)人員經(jīng)常需要使用各種應(yīng)用程序編程接口(application programming interface,簡稱API)來復(fù)用已有的軟件框架、類庫,以節(jié)省軟件開發(fā)時間,提高軟件開發(fā)效率.但由于 API自身的復(fù)雜性、文檔資料的缺失或自身的疏漏等原因[1-4],開發(fā)人員經(jīng)常會誤用API.API的誤用情形多種多樣[5],例如多余的API調(diào)用、遺漏的API調(diào)用、錯誤的API調(diào)用參數(shù)、缺少前置條件判斷、忽略異常處理等.這些API誤用在實(shí)際項(xiàng)目中常常導(dǎo)致了功能性錯誤、性能問題、安全漏洞等代碼缺陷[5,6].例如在使用文件流API時,如果最后遺漏了對文件流進(jìn)行關(guān)閉的API調(diào)用,那么將導(dǎo)致內(nèi)存泄露問題.
為了自動檢測API誤用缺陷,需要獲得API使用規(guī)約[7],并根據(jù)規(guī)約對API使用代碼進(jìn)行檢測.然而,可用于自動檢測的API規(guī)約難以獲得,而人工編寫并維護(hù)的代價又很高.相關(guān)研究工作[8-10]關(guān)注于利用數(shù)據(jù)挖掘、統(tǒng)計(jì)語言模型等方法自動挖掘或?qū)W習(xí)API使用規(guī)約并用于缺陷檢測,但存在合成能力不足等問題.
本文將深度學(xué)習(xí)中的循環(huán)神經(jīng)網(wǎng)絡(luò)模型應(yīng)用于API使用規(guī)約的學(xué)習(xí)及API誤用缺陷的檢測.在大量的開源Java代碼基礎(chǔ)上,通過靜態(tài)分析構(gòu)造 API使用規(guī)約訓(xùn)練樣本,同時利用這些訓(xùn)練樣本搭建循環(huán)神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)學(xué)習(xí)API使用規(guī)約.在此基礎(chǔ)上,本文針對API使用代碼進(jìn)行基于上下文的語句預(yù)測,并通過預(yù)測結(jié)果與實(shí)際代碼的比較發(fā)現(xiàn)潛在的API誤用缺陷.本文對所提出的方法進(jìn)行實(shí)現(xiàn),并針對Java加密相關(guān)的API及其使用代碼進(jìn)行了實(shí)驗(yàn)評估,結(jié)果表明,該方法能夠在一定程度上實(shí)現(xiàn)API誤用缺陷的自動發(fā)現(xiàn).
本文第1節(jié)對本文使用的深度學(xué)習(xí)背景知識進(jìn)行說明.第2節(jié)介紹相關(guān)工作.第3節(jié)對本文所提出的方法及其實(shí)現(xiàn)進(jìn)行說明.第4節(jié)介紹本文中設(shè)計(jì)的兩個實(shí)驗(yàn)——深度學(xué)習(xí)模型訓(xùn)練實(shí)驗(yàn)和API誤用缺陷檢測實(shí)驗(yàn)的設(shè)計(jì)與結(jié)果分析.最后,在第5節(jié)對本文進(jìn)行總結(jié)與展望.
深度學(xué)習(xí)(deep learning,簡稱 DL)是一類通過連續(xù)多層變換的非線性處理單元對數(shù)據(jù)進(jìn)行復(fù)雜特征提取,并通過這些組合特征解決問題的機(jī)器學(xué)習(xí)算法[11].
循環(huán)神經(jīng)網(wǎng)絡(luò)(recurrent neural networks,簡稱RNN)屬于深度學(xué)習(xí)技術(shù)的一個重要分支.通過對數(shù)據(jù)中的時序信息的利用以及對數(shù)據(jù)中語義信息的深度表達(dá),RNN在處理和預(yù)測序列數(shù)據(jù)上實(shí)現(xiàn)了突破,并在語音識別、語言模型、機(jī)器翻譯等方面發(fā)揮出色[12-15].
RNN利用狀態(tài)值保存迭代計(jì)算時的歷史信息,利用時序信息輔助當(dāng)前決策.但簡單的 RNN存在長期依賴(long-term dependencies)問題.1997年,Hochreiter和 Schmidhuber提出的長短時記憶網(wǎng)絡(luò)(long short term memory,簡稱 LSTM)[16],經(jīng)過計(jì)算[16],能夠進(jìn)行前向傳播,并對歷史信息進(jìn)行遺忘,對輸入信息進(jìn)行狀態(tài)更新,有效解決了長期依賴問題.
深層循環(huán)神經(jīng)網(wǎng)絡(luò)(deep RNN)是RNN的一種變種,通過拓展淺層循環(huán)體,設(shè)置多個循環(huán)層,將每層RNN的輸出作為下一層RNN的輸入,進(jìn)一步處理抽象,增強(qiáng)RNN從輸入中提取高維度抽象信息的能力[12].
1.4.1 損失函數(shù)
損失函數(shù)定義了深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)模型的效果和優(yōu)化目標(biāo).本文將神經(jīng)網(wǎng)絡(luò)模型應(yīng)用于分類問題,用N個神經(jīng)元的輸出表示N個不同的API調(diào)用的評判概率.應(yīng)用于分類問題的常用損失函數(shù)是交叉熵(cross entropy),它表示了兩個概率分布之間的距離,可用于計(jì)算正確答案概率分布與預(yù)測答案概率分布之間的距離.在訓(xùn)練過程中,使用Softmax回歸處理層將神經(jīng)網(wǎng)絡(luò)的前向輸出轉(zhuǎn)換為大小為N的概率分布形式.
1.4.2 優(yōu)化算法
在神經(jīng)網(wǎng)絡(luò)模型中,優(yōu)化過程分為兩個階段:一是前向傳播階段,在這個階段,神經(jīng)網(wǎng)絡(luò)模型前向計(jì)算預(yù)測值,并通過預(yù)測值與目標(biāo)值計(jì)算損失函數(shù);二是反向傳播階段,在這個階段,采用梯度下降法(gradient descent)[17]的思想,通過損失函數(shù)對每個可優(yōu)化的參數(shù)進(jìn)行梯度計(jì)算,同時利用學(xué)習(xí)率進(jìn)行參數(shù)更新,從而達(dá)到優(yōu)化模型的目的.
在神經(jīng)網(wǎng)絡(luò)模型優(yōu)化過程中,選取適當(dāng)?shù)某跏贾?、學(xué)習(xí)率、迭代次數(shù),能進(jìn)一步調(diào)整模型的預(yù)測能力.同時,也要注意過擬合問題對模型預(yù)測能力的影響——當(dāng)模型進(jìn)入過擬合狀態(tài),模型的表達(dá)能力將局限在訓(xùn)練數(shù)據(jù)中,反而不利于模型的泛化預(yù)測能力.
不少研究工作關(guān)注于利用數(shù)據(jù)挖掘、統(tǒng)計(jì)語言模型等方法自動挖掘或?qū)W習(xí)API使用規(guī)約并用于缺陷檢測,但存在合成能力不足等問題.這方面有代表性的研究工作包括以下這些:
Li等人[8]研究了基于頻繁項(xiàng)集的 API使用規(guī)約挖掘技術(shù),并研發(fā)了一個挖掘和檢測工具 PR-Miner.PRMiner通過將代碼庫中的函數(shù)映射為數(shù)據(jù)子項(xiàng),并對代碼中頻繁共現(xiàn)的數(shù)據(jù)子項(xiàng)進(jìn)行統(tǒng)計(jì)和挖掘,根據(jù)數(shù)據(jù)子項(xiàng)的頻繁共現(xiàn)關(guān)系推斷API調(diào)用之間的關(guān)聯(lián)關(guān)系.最終,PR-Miner可以得到以API調(diào)用的關(guān)聯(lián)關(guān)系為基礎(chǔ)的使用規(guī)約.基于該工具,他們在Linux kernel,PostgreSQL等大型軟件系統(tǒng)的代碼中發(fā)現(xiàn)了23個確認(rèn)的Bug.
Zhong等人[9]研究了基于自然語言處理的 API使用規(guī)約提取技術(shù),并研發(fā)了一個 API使用規(guī)約提取工具Doc2Spec.Doc2Spec通過分析自然語言編寫的 API文檔,提取相關(guān)的 API使用規(guī)約.基于該工具,他們分析了J2SE4、J2EE5、JBoss6、iText7以及Oracle JDBC driver8這5個Java庫的Javadocs,提取出API使用規(guī)約,并基于這些API使用規(guī)約在真實(shí)代碼中人工發(fā)現(xiàn)了35個確認(rèn)的Bug.
Wang等人[10]研究了N-gram語言模型,并研發(fā)了一個缺陷檢測工具Bugram.Bugram利用N-gram語言模型,基于API調(diào)用token只與在它之前的n個token相關(guān)的假設(shè),對軟件項(xiàng)目中出現(xiàn)的API調(diào)用token序列進(jìn)行出現(xiàn)概率的計(jì)算,同時將較低概率的token序列的出現(xiàn)與程序中API的異常調(diào)用、誤用和特殊用法聯(lián)系起來,以此進(jìn)行缺陷的自動化檢測.基于該工具,他們在16個開源Java項(xiàng)目中發(fā)現(xiàn)了25個確認(rèn)的Bug以及17個可能存在的代碼重構(gòu)建議.
然而,這些研究中提出的技術(shù)方法存在一定的局限性.頻繁項(xiàng)集挖掘技術(shù)關(guān)注于挖掘代碼中數(shù)據(jù)子項(xiàng)的頻繁共現(xiàn)關(guān)系,但對API使用的順序信息沒有加以利用;而N-gram語言模型利用到了API使用的順序信息,但是對多樣和變長的代碼上下文存在模型的合成能力不足的問題.
本文將循環(huán)神經(jīng)網(wǎng)絡(luò)模型應(yīng)用于API使用規(guī)約的學(xué)習(xí)及API誤用缺陷的檢測.循環(huán)神經(jīng)網(wǎng)絡(luò)模型能有效提取序列數(shù)據(jù)中的抽象特征,以概率分布的形式進(jìn)行預(yù)測,能有效處理多樣和變長的上下文,具有較強(qiáng)的合成能力.改進(jìn)后的循環(huán)神經(jīng)網(wǎng)絡(luò)有效地解決了自然語言處理中的長期依賴問題,被廣泛應(yīng)用于模式識別等領(lǐng)域.因此,我們期望該模型也能有效學(xué)習(xí)API調(diào)用組合、順序及控制結(jié)構(gòu)等方面的使用規(guī)約.
本文所提出的API誤用缺陷檢測方法概覽如圖1所示,其中主要包括3個階段——訓(xùn)練數(shù)據(jù)構(gòu)造、模型訓(xùn)練與預(yù)測、缺陷檢測.
· 在訓(xùn)練數(shù)據(jù)構(gòu)造階段,基于大量開源Java代碼數(shù)據(jù)集,對源代碼進(jìn)行靜態(tài)分析,構(gòu)造抽象語法樹,并進(jìn)一步構(gòu)造為API語法圖,用于API調(diào)用序列的抽取,最后,將API調(diào)用序列構(gòu)造為深度學(xué)習(xí)模型需要的訓(xùn)練數(shù)據(jù).
· 在模型訓(xùn)練與預(yù)測階段,訓(xùn)練深度學(xué)習(xí)模型,基于該模型和 API使用的上下文對 API調(diào)用序列中某處的API調(diào)用進(jìn)行預(yù)測,得出該位置的API調(diào)用概率列表.
· 在缺陷檢測階段,從待檢測代碼中抽取用于預(yù)測的 API調(diào)用序列,基于模型訓(xùn)練與預(yù)測階段的模型預(yù)測對每個位置進(jìn)行API調(diào)用預(yù)測,并獲取每個位置的API調(diào)用概率列表,將原代碼中的API調(diào)用序列與預(yù)測得出的概率列表進(jìn)行比對,得出最終的API誤用檢測報(bào)告.
Fig.1 Overview of our approach圖1 本文方法概覽
在訓(xùn)練數(shù)據(jù)構(gòu)造階段,將數(shù)據(jù)從源代碼的形式依次構(gòu)造為抽象語法樹、API語法圖,并基于構(gòu)造得出的API語法圖提取出與API調(diào)用相對應(yīng)的API調(diào)用序列;隨后,將API調(diào)用序列構(gòu)造為可用于模型訓(xùn)練的訓(xùn)練數(shù)據(jù).如圖1所示,訓(xùn)練數(shù)據(jù)構(gòu)造階段分為4個子階段:靜態(tài)代碼分析、API語法圖構(gòu)造、API調(diào)用序列抽取、數(shù)據(jù)構(gòu)造.
3.2.1 靜態(tài)代碼分析
抽象語法樹(abstract syntax tree,簡稱AST)將Java源代碼映射成樹狀結(jié)構(gòu),是靜態(tài)解析源代碼的有效工具.在靜態(tài)代碼分析階段,本文使用JavaParser[18]對源代碼文件進(jìn)行解析,并獲取表示源代碼語法結(jié)構(gòu)的AST.
3.2.2 API語法圖構(gòu)造
在API語法圖構(gòu)造階段,在AST的基礎(chǔ)上進(jìn)行進(jìn)一步解析構(gòu)造,經(jīng)過靜態(tài)代碼分析和API語法圖構(gòu)造,代碼從源代碼形式轉(zhuǎn)化為API語法圖形式.如圖2所示,展示了本文中一個API語法圖抽取的例子.
Fig.2 Example for API syntax graph extraction圖2 API語法圖抽取例子
圖中的代碼通過Scanner API讀取每行文件內(nèi)容并打印到輸出控制臺中,最后關(guān)閉Scanner,釋放資源.在該代碼中使用到了java.io.File,java.util.Scanner,java.io.PrintStream這些API,經(jīng)過轉(zhuǎn)化,抽取出的API語法圖如圖中圖狀結(jié)構(gòu)所示.
在構(gòu)造API語法圖的過程中涉及到如下定義.
(1) 節(jié)點(diǎn)
節(jié)點(diǎn)分為3種類型.
· 方法節(jié)點(diǎn),表示API語法圖的來源(文件路徑、類、方法).
· API節(jié)點(diǎn),表示API對象創(chuàng)建、API方法調(diào)用、API類變量訪問.
· 控制節(jié)點(diǎn),表示控制結(jié)構(gòu)(如IF,ELSE,WHILE,FOR,FOREACH,TRY等).
(2) 邊
節(jié)點(diǎn)之間可以由有向邊連接,方向從父節(jié)點(diǎn)指向子節(jié)點(diǎn).邊分為兩種類型.
· 順序邊,表示流(控制流)的方向.
· 控制邊,表示控制結(jié)構(gòu)之間的順序關(guān)系(如IF→ELSE).
(3) API語法圖
一個根節(jié)點(diǎn),能構(gòu)成一個最簡的API語法圖.
邊從父節(jié)點(diǎn)指向子節(jié)點(diǎn),沒有子節(jié)點(diǎn)的節(jié)點(diǎn)稱為葉子節(jié)點(diǎn).
向API語法圖中的一個節(jié)點(diǎn)向該圖中另一個節(jié)點(diǎn)加一條邊,圖中不構(gòu)成環(huán)路,這個圖仍是一個API語法圖.
向API語法圖中加入一個節(jié)點(diǎn),由圖中任意一個節(jié)點(diǎn)向該節(jié)點(diǎn)加一條邊,圖中不構(gòu)成環(huán)路,這個圖仍是一個API語法圖.
兩個 API語法圖,從一個 API語法圖的葉子節(jié)點(diǎn)出發(fā),向另一API語法圖的根節(jié)點(diǎn)加邊,稱為移植.移植后,得到組合了兩個API語法圖的API語法圖.
根據(jù)定義進(jìn)行UML建模,設(shè)計(jì)API語法圖類以及相關(guān)類如圖3所示.
Fig.3 Class diagram of API syntax graph圖3 API語法圖相關(guān)類圖
其中,
· Node表示API語法圖上的節(jié)點(diǎn),分為3種節(jié)點(diǎn)類型,包括方法節(jié)點(diǎn)(MethodNode)、API節(jié)點(diǎn)(APINode)以及控制節(jié)點(diǎn)(ControlNode).
· Edge表示父節(jié)點(diǎn)連接子節(jié)點(diǎn)的邊,可以為順序類型邊或者控制結(jié)構(gòu)類型邊.假設(shè)從圖上從一個節(jié)點(diǎn)出發(fā),深度遍歷找到所有沒有子節(jié)點(diǎn)與之連接的節(jié)點(diǎn),這些節(jié)點(diǎn)為這個節(jié)點(diǎn)的葉子節(jié)點(diǎn)(leave).
· Graph表示API語法圖,該圖上的節(jié)點(diǎn)由Node表示,其中,一個特殊的根節(jié)點(diǎn)root表示一個API語法圖的開始.從Grapha的葉子節(jié)點(diǎn)到Graphb的根節(jié)點(diǎn)都加上邊,這個操作稱為移植(transplant).
在API語法圖構(gòu)造階段,基于AST以及控制流分析,將源代碼抽象表示為API語法圖,流程的具體步驟如下.
(1) 基于JavaParser,靜態(tài)分析代碼為AST.
(2) 基于 JavaParser對每個類中的方法進(jìn)行抽取,將信息存儲到方法節(jié)點(diǎn)中,并以該節(jié)點(diǎn)為根節(jié)點(diǎn),構(gòu)建API語法圖g.
(3) 對每個方法根據(jù)以下規(guī)則,迭代地構(gòu)建API語法圖g′.
a)如果當(dāng)前處理語句中有嵌套的語句,先構(gòu)造出嵌套在內(nèi)層的語句的 API語法圖,移植到g′上,再迭代地根據(jù)后續(xù)規(guī)則構(gòu)造出外層的語句的API語法圖,移植到g′上.
b)如果當(dāng)前處理語句是API對象創(chuàng)建(API object creation),則構(gòu)造API節(jié)點(diǎn),并以“完整類名.New([完整參數(shù)類類型])”的形式表示節(jié)點(diǎn),將以該節(jié)點(diǎn)為根節(jié)點(diǎn)的API語法圖移植到g′上.
c)如果當(dāng)前處理語句是API方法調(diào)用(API method call),則構(gòu)造API節(jié)點(diǎn),并以“完整方法名([完整參數(shù)類類型])”的形式表示節(jié)點(diǎn),將以該節(jié)點(diǎn)為根節(jié)點(diǎn)的API語法圖移植到g′上.
d)如果當(dāng)前處理語句是 API類變量訪問(API field access),則構(gòu)造 API節(jié)點(diǎn),并以“完整類名.類變量”的形式表示節(jié)點(diǎn),將以該節(jié)點(diǎn)為根節(jié)點(diǎn)的API語法圖移植到g′上.
e)如果當(dāng)前處理語句是控制結(jié)構(gòu),則根據(jù)以下主要規(guī)則,構(gòu)建相應(yīng)的API語法圖.
i “if”(判斷結(jié)構(gòu)),構(gòu)造控制節(jié)點(diǎn)“IF”,并設(shè)置為g′的根節(jié)點(diǎn).根據(jù)當(dāng)前處理語句的子結(jié)構(gòu),分別構(gòu)造以控制節(jié)點(diǎn)“CONDITION”、“THEN”、“ELSE”為根節(jié)點(diǎn)的 3個 API語法圖,并用控制邊將這3個API語法圖移植到g′上.
ii “while”(循環(huán)結(jié)構(gòu)),構(gòu)造控制節(jié)點(diǎn)“WHILE”,并設(shè)置為g′的根節(jié)點(diǎn).根據(jù)當(dāng)前處理語句的子結(jié)構(gòu),分別構(gòu)造以控制節(jié)點(diǎn)“CONDITION”、“BODY”為根節(jié)點(diǎn)的兩個 API語法圖,并用控制邊將這兩個API語法圖移植到g′上.
iii “try”(異常處理結(jié)構(gòu)),構(gòu)造控制節(jié)點(diǎn)“TRY”,并設(shè)置為g′的根節(jié)點(diǎn).根據(jù)當(dāng)前處理語句的子結(jié)構(gòu),分別構(gòu)造以控制節(jié)點(diǎn)“TRYBLOCK”、“CATCH”、“FINALLY”為根節(jié)點(diǎn)的 API語法圖,并用控制邊將這些API語法圖移植到g′上.
iv “for”(包含變量初始化、條件判斷、變量更新的循環(huán)結(jié)構(gòu)),構(gòu)造控制節(jié)點(diǎn)“FOR”,并設(shè)置為g′的根節(jié)點(diǎn).根據(jù)當(dāng)前處理語句的子結(jié)構(gòu),分別構(gòu)造以控制節(jié)點(diǎn)“INITIALIZATION”、“COMPARE”、“BODY”、“UPDATE”為根節(jié)點(diǎn)的 API語法圖,并用控制邊將這些 API語法圖移植到g′上.
v “for each”(循環(huán)集合訪問結(jié)構(gòu)),構(gòu)造控制節(jié)點(diǎn)“FOREACH”,并設(shè)置為g′的根節(jié)點(diǎn).根據(jù)當(dāng)前處理語句的子結(jié)構(gòu),分別構(gòu)造以控制節(jié)點(diǎn)“VARIABLE”、“ITERABLE”、“BODY”為根節(jié)點(diǎn)的API語法圖,并用控制邊將這些API語法圖移植到g′上.
(4) 將API語法圖g′移植到API語法圖g中,獲得每個類中方法對應(yīng)的API語法圖.最終獲取的API語法圖將作為下一階段的基礎(chǔ).
3.2.3 API調(diào)用序列抽取
定義API調(diào)用序列(API sequence)如下.
給定API語法圖,從根節(jié)點(diǎn)出發(fā),根據(jù)順序邊以及表示順序的控制邊對API語法圖進(jìn)行深度遍歷得到的節(jié)點(diǎn)標(biāo)簽序列,稱為這個 API語法圖上的 API調(diào)用序列.表示順序的控制邊有以下幾種:IF→CONDITION、WHILE→CONDITIO、TRY→TRYBLOCK、FOR→INITAILIZATION、FOREACH→VARIABLE.
例如在圖2的例子中,經(jīng)過API語法圖構(gòu)造以及API調(diào)用序列抽取,得到以下兩個API調(diào)用序列.
· “java.io.File.new(java.lang.String)→java.util.Scanner.new(java.io.File)→WHILE→CONDITION→java.util.Scanner.hasNextLine()→BODY→java.util.Scanner.nextLine()→java.io.PrintStream.println(java.lang.String)→java.util.Scanner.close()→EOS”.
· “java.io.File.new(java.lang.String)→java.util.Scanner.new(java.io.File)→WHILE→CONDITION→java.util.Scanner.hasNextLine()→java.util.Scanner.close()→EOS”.
在API調(diào)用序列抽取階段,基于API語法圖,抽取API語法圖中的API調(diào)用序列.主要思想是對API語法圖進(jìn)行深度遍歷,提取所有存在API調(diào)用節(jié)點(diǎn)的API調(diào)用序列作為與該API語法圖對應(yīng)的API調(diào)用序列集合.所有提取的API調(diào)用序列以EOS控制節(jié)點(diǎn)結(jié)束.
3.2.4 訓(xùn)練數(shù)據(jù)產(chǎn)生
訓(xùn)練數(shù)據(jù)構(gòu)造階段將 API調(diào)用序列轉(zhuǎn)換為訓(xùn)練數(shù)據(jù),該階段包括了詞匯表的建立以及訓(xùn)練數(shù)據(jù)的構(gòu)造兩個部分.
(1) 構(gòu)建詞匯表.為了將API調(diào)用序列轉(zhuǎn)化為深度學(xué)習(xí)模型能夠統(tǒng)一識別的序列輸入,將原始數(shù)據(jù)集中的代碼片段轉(zhuǎn)化為API調(diào)用序列后,經(jīng)過統(tǒng)計(jì)序列中出現(xiàn)的API調(diào)用詞頻,建立API調(diào)用與API調(diào)用編號一一對應(yīng)的詞匯表,并持久化到本地的詞匯表文件中.
(2) 構(gòu)造訓(xùn)練數(shù)據(jù).如圖4中構(gòu)造訓(xùn)練數(shù)據(jù)算法流程圖所示,基于詞匯表將API調(diào)用序列轉(zhuǎn)換為API調(diào)用編號序列,其中每個 API調(diào)用的編號與詞匯表中該調(diào)用的行號對應(yīng).對構(gòu)造好的編號序列進(jìn)行遍歷,構(gòu)造〈API調(diào)用前文編號序列,API調(diào)用編號〉形式的數(shù)據(jù),并作為訓(xùn)練數(shù)據(jù)持久化至本地訓(xùn)練數(shù)據(jù)文件中,方便之后的模型訓(xùn)練和預(yù)測.這里的API調(diào)用前文指的是在該序列中API調(diào)用之前的所有API調(diào)用組成的序列,API調(diào)用前文的編號序列形式稱為API調(diào)用前文編號序列.
Fig.4 Flowchart of training data build algorithm圖4 構(gòu)造訓(xùn)練數(shù)據(jù)算法流程圖
在模型訓(xùn)練與預(yù)測階段,基于TensorFlow框架,使用 Python3語言搭建深度學(xué)習(xí)模型對訓(xùn)練數(shù)據(jù)構(gòu)造中獲得的訓(xùn)練數(shù)據(jù)進(jìn)行學(xué)習(xí),得到訓(xùn)練好的深度學(xué)習(xí)模型,并利用該模型對特定時間步上的API調(diào)用進(jìn)行預(yù)測,得到該位置上的API調(diào)用概率列表.該階段主要分為模型訓(xùn)練和模型預(yù)測兩個階段.
3.3.1 模型訓(xùn)練
本文中采用的深度學(xué)習(xí)模型為深層循環(huán)網(wǎng)絡(luò)結(jié)構(gòu)配合長短時記憶網(wǎng)絡(luò)的應(yīng)用,這里簡稱為深層長短時記憶循環(huán)網(wǎng)絡(luò)(deep LSTM).結(jié)構(gòu)示意如圖5所示.示意圖中采用了雙層深層循環(huán)神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu),在每個循環(huán)體結(jié)構(gòu)中應(yīng)用LSTM模型,將每個時間步上的API調(diào)用序列的標(biāo)簽作為一個詞,Deep LSTM的輸入為包含 個時間步的詞向量,表示前t個API調(diào)用序列的輸入,輸出取第t個時間步的輸出,表示基于前文內(nèi)容,下一個API調(diào)用序列上的節(jié)點(diǎn)可能情況的概率分布.
Fig.5 Structure of deep LSTM圖5 Deep LSTM結(jié)構(gòu)
模型的搭建選取深度學(xué)習(xí)框架TensorFlow 1.6.0作為實(shí)現(xiàn)框架.
模型的計(jì)算流程如下.
(1) 經(jīng)過詞向量層(word embedding),將輸入的API調(diào)用前文編號序列中的每個API調(diào)用編號嵌入到一個實(shí)數(shù)向量中,以此降低輸入的維度,同時增加語義信息.
(2) 經(jīng)過詞向量層轉(zhuǎn)化的輸入向量經(jīng)過dropout層,丟棄部分信息以增強(qiáng)模型的健壯性.
(3) 使用TensorFlow提供的動態(tài)RNN接口實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)主要結(jié)構(gòu),其中包含了深層LSTM處理單元.
(4) 循環(huán)神經(jīng)網(wǎng)絡(luò)的輸出需要經(jīng)過一個全連接層(dense)轉(zhuǎn)化,與詞表大小保持一致,這個與詞表大小一致的輸出稱為logits;隨后,logits經(jīng)過Softmax處理以及交叉熵計(jì)算,可以分別得到API調(diào)用的概率分布以及損失值;TensorFlow將基于給定的優(yōu)化函數(shù),對參數(shù)進(jìn)行優(yōu)化.
(5) 同時,在訓(xùn)練過程中加入預(yù)測正確率(accuracy)作為訓(xùn)練時的反饋,同時也作為優(yōu)化訓(xùn)練模型的參考.至此,我們搭建并訓(xùn)練了一個應(yīng)用于API調(diào)用序列預(yù)測的深度學(xué)習(xí)模型.
3.3.2 模型預(yù)測
在模型預(yù)測階段,基于模型訓(xùn)練中的訓(xùn)練好的深度學(xué)習(xí)模型,并將用于預(yù)測的API調(diào)用前文序列作為輸入,預(yù)測下一位置上的 API調(diào)用的概率分布,將這個概率分布進(jìn)行排序,得出下一位置上的 API調(diào)用概率列表.該API調(diào)用概率列表將作為之后缺陷檢測的重要部分.
本階段的基礎(chǔ)是訓(xùn)練數(shù)據(jù)構(gòu)造階段和模型訓(xùn)練與預(yù)測階段.
如圖1所示,缺陷檢測階段經(jīng)過訓(xùn)練數(shù)據(jù)構(gòu)造階段的靜態(tài)代碼分析、API語法圖構(gòu)造、API調(diào)用序列抽取,得到原代碼中的API調(diào)用序列,基于模型訓(xùn)練與預(yù)測階段的模型預(yù)測,對API調(diào)用序列中除了具有固定語法順序搭配的控制流結(jié)構(gòu)(例如IF→CONDITION中IF后緊跟的CONDITION所在位置,WHILE→CONDITION中WHILE后緊跟的CONDITION所在位置等等)之外的每個位置進(jìn)行API調(diào)用預(yù)測并獲取每個位置的API調(diào)用概率列表,將原代碼中每個API調(diào)用序列上的每個位置的API調(diào)用與預(yù)測得出的API調(diào)用概率列表進(jìn)行比對,應(yīng)用定義好的代碼缺陷檢測規(guī)則,報(bào)告出候選的代碼缺陷,得出最終的API誤用檢測報(bào)告.
缺陷檢測實(shí)現(xiàn)部分涉及不同的實(shí)現(xiàn)語言——Java和 Python,為了模型之間較方便地協(xié)作調(diào)用,在本文的實(shí)現(xiàn)中采用如下技術(shù)框架進(jìn)行模塊之間的溝通:基于 Python語言中的 Web端框架 Flask[19]搭建 Web服務(wù)端,將API調(diào)用概率列表的預(yù)測包裝為Web API供客戶端調(diào)用.
本文中實(shí)現(xiàn)的缺陷檢測算法流程如圖6所示,算法中的具體步驟如下.
(1) 從待檢測的源代碼中抽取API語法圖.
(2) 從API語法圖中抽取API調(diào)用序列.
(3) 對每一個API調(diào)用序列,從第2個位置開始,進(jìn)行缺陷檢測.
(4) 基于檢測位置的API調(diào)用前文,預(yù)測該位置可能出現(xiàn)的API調(diào)用概率列表.
(5) 判斷該位置的API調(diào)用在API調(diào)用概率列表中的排名是否在可接受范圍(該范圍在算法開始前由人工定義)內(nèi):如果該API調(diào)用的出現(xiàn)在可接受范圍內(nèi),則繼續(xù)檢測下一位置;如果該 API調(diào)用的出現(xiàn)超出可接受范圍,則作為一個缺陷并進(jìn)行該位置的缺陷報(bào)告,缺陷報(bào)告包括了代碼缺陷在源代碼中的位置(起止行數(shù))以及對應(yīng)的API調(diào)用.
(6) 結(jié)束檢測后,可根據(jù)缺陷報(bào)告進(jìn)行進(jìn)一步的評估.
Fig.6 Flowchart of bug detection algorithm圖6 缺陷檢測算法流程圖
在實(shí)驗(yàn)過程中,將重點(diǎn)對算法中API調(diào)用在API調(diào)用概率列表中可接受范圍的設(shè)定進(jìn)行實(shí)驗(yàn).
本文中的實(shí)驗(yàn)評估分為兩部分:首先是深度學(xué)習(xí)模型訓(xùn)練,目的在于嘗試優(yōu)化深度學(xué)習(xí)模型對 API調(diào)用序列預(yù)測的準(zhǔn)確性和可靠性;其次是代碼缺陷檢測實(shí)驗(yàn),目的在于評估檢測深度學(xué)習(xí)模型在 API誤用缺陷檢測上的效果和可用性.
JCE(Java cryptography extension)是JDK提供的一組包,它們提供用于加密、密鑰生成和協(xié)商及消息驗(yàn)證碼(MAC)算法的框架和實(shí)現(xiàn),JCE提供的Java密碼學(xué)APIs(Java cryptography APIs)在javax.crypto包下.研究[20-22]說明:Java Cryptography APIs通過分離開發(fā)人員和API底層實(shí)現(xiàn)細(xì)節(jié),開發(fā)人員可以輕松地使用加解密技術(shù).這些API提供了多種模式和配置選項(xiàng),但因此,對開發(fā)人員來說使用和組合這些API組件的任務(wù)可能具有挑戰(zhàn)性.
本文中將實(shí)驗(yàn)對象聚焦于Java Cryptography APIs,展示深度學(xué)習(xí)模型在缺陷檢測上的效果和能力.
4.2.1 實(shí)驗(yàn)數(shù)據(jù)
Git是一個自由開源的分布式版本控制系統(tǒng),它的設(shè)計(jì)目的是無論小型還是大型的項(xiàng)目,都能通過Git快速高效地管理.GitHub是一個基于Git的大型開源代碼托管平臺以及版本控制系統(tǒng),GitHub上的開源代碼是挖掘API調(diào)用規(guī)約的一大數(shù)據(jù)來源[23,24].
在本文的深度學(xué)習(xí)模型訓(xùn)練實(shí)驗(yàn)中,以“javax.crypto”為關(guān)鍵詞,從GitHub上收集了14 422個java代碼文件,這些文件的最后修改時間在2018年1月1日之前(穩(wěn)定版本),這些文件組成的訓(xùn)練原始文件總大小為50MB.從訓(xùn)練原始文件中抽取38 602條API調(diào)用序列,構(gòu)造388 973條訓(xùn)練數(shù)據(jù),詞表大小為1 564.
4.2.2 實(shí)驗(yàn)設(shè)計(jì)
深度學(xué)習(xí)模型的搭建選取深度學(xué)習(xí)框架 TensorFlow 1.6.0作為實(shí)現(xiàn)框架,語言為 Python,開發(fā) IDE為Jupyter-notebook編輯器,搭建模型為深層循環(huán)神經(jīng)網(wǎng)絡(luò)和長短時記憶網(wǎng)絡(luò)的結(jié)合.
實(shí)驗(yàn)探究隱層大小(HIDDEN_SIZE)、深層循環(huán)網(wǎng)絡(luò)層數(shù)(NUM_LAYER)、學(xué)習(xí)率(LR)以及迭代次數(shù)(NUM_EPOCH)對模型效果的影響.對模型效果的評判效果以驗(yàn)證集上的分類準(zhǔn)確率(accuracy)為準(zhǔn),分類準(zhǔn)確率為模型預(yù)測 Top-1為目標(biāo)詞的準(zhǔn)確率.通過調(diào)整深度學(xué)習(xí)模型的部分參數(shù),對模型進(jìn)行調(diào)整訓(xùn)練,訓(xùn)練集為總數(shù)據(jù)集的90%,驗(yàn)證集為總數(shù)據(jù)集的10%.
4.2.3 實(shí)驗(yàn)?zāi)P头治?/p>
本次實(shí)驗(yàn)最終選擇的參數(shù)配置為HIDDEN_SIZE=250,NUM_LAYER=2,LR=0.002,NUM_EPOCH=20.如圖7所示,橫軸為訓(xùn)練迭代次數(shù)(epoch),縱軸為損失(loss).該模型在訓(xùn)練中的損失不斷下降趨于收斂.
Fig.7 Loss of DL model圖7 深度學(xué)習(xí)模型損失
該配置下,模型效果如圖8所示,橫軸為迭代次數(shù),縱軸為準(zhǔn)確率.
Fig.8 Accuracy of DL model (HIDDEN_SIZE=250,NUM_LAYER=2,LR=0.002)圖8 深度學(xué)習(xí)模型準(zhǔn)確率(HIDDEN_SIZE=250,NUM_LAYER=2,LR=0.002)
經(jīng)過20次迭代后,模型準(zhǔn)確率達(dá)到80.3%.該參數(shù)組合在驗(yàn)證集上效果最優(yōu),因此最終用于缺陷預(yù)測的模型參數(shù)為:HIDDEN_SIZE=250,NUM_LAYER=2,LR=0.002,NUM_EPOCH=20.
本節(jié)進(jìn)行基于深度學(xué)習(xí)模型的 API誤用相關(guān)代碼缺陷檢測實(shí)驗(yàn).進(jìn)行該部分實(shí)驗(yàn)前,對實(shí)驗(yàn)中的測試標(biāo)準(zhǔn)進(jìn)行定義.
· 定義TP為檢測文件API誤用缺陷位置正確的不重復(fù)缺陷報(bào)告數(shù).
· 定義FP為檢測文件API誤用缺陷位置錯誤的不重復(fù)缺陷報(bào)告數(shù).
· 定義FN為未進(jìn)行報(bào)告的缺陷報(bào)告數(shù).
1.查準(zhǔn)率(precision)定義為
2.召回率(recall)定義為
3.查準(zhǔn)率和召回率的調(diào)和均值(F1)定義為
查準(zhǔn)率和召回率往往體現(xiàn)的是實(shí)驗(yàn)結(jié)果的一個側(cè)面,比如召回率較高的模型在查準(zhǔn)率上不一定很高.因此在本文的實(shí)驗(yàn)中,采用二者的調(diào)和均值F1進(jìn)行API誤用缺陷檢測的實(shí)驗(yàn)評價.
4.3.1 實(shí)驗(yàn)數(shù)據(jù)
本次實(shí)驗(yàn)中,根據(jù)相關(guān)文獻(xiàn)中Java Cryptography API誤用的真實(shí)數(shù)據(jù)[5],選用了從SourceForge和GitHub上的優(yōu)質(zhì)項(xiàng)目(大于10星)中整理的關(guān)于Java Cryptography APIs的誤用代碼片段8個,其中包含API誤用14個,將這14個API誤用作為計(jì)算模型查準(zhǔn)率、召回率以及F1值的測試集.實(shí)驗(yàn)所用的測試集整理如表1所示,表格中標(biāo)明了每個測試用例的代碼來源、API誤用方法來源、誤用引入缺陷的具體 API以及 API誤用的說明,所有測試用例源碼可通過代碼來源在托管平臺上下載.
Table 1 Information of test case表1 測試用例信息
Table 1 Information of test case (Continued)表1 測試用例信息(續(xù))
這些測試用例中包含4種類型的API誤用(不互斥).
a.使用了多余的API調(diào)用;
b.使用了錯誤的API調(diào)用;
c.遺漏了關(guān)鍵的API調(diào)用;
d.忽略了對API調(diào)用中可能拋出的異常的處理.
其中,測試用例1使用了多余的API調(diào)用;測試用例2忽略了對API調(diào)用中可能拋出的異常的處理;測試用例3、測試用例5、測試用例7~測試用例9、測試用例11、測試用例13使用了錯誤的API調(diào)用;測試用例4、測試用例6、測試用例10、測試用例12、測試用例14遺漏了關(guān)鍵的API調(diào)用.
4.3.2 實(shí)驗(yàn)設(shè)計(jì)
人工整理API誤用測試用例,將每個 API誤用標(biāo)注成標(biāo)準(zhǔn)化的格式“文件路徑,方法,API誤用缺陷位置,誤用API”,用于計(jì)算查準(zhǔn)率、召回率和F1值.
在進(jìn)行API誤用缺陷檢測時,DeepLSTM將預(yù)測出API調(diào)用序列上某個位置上的API調(diào)用概率列表,為進(jìn)行缺陷檢測,定義 API調(diào)用在 API調(diào)用概率列表中可以接受的排名范圍,在本文中稱為可接受閾值(acceptable threshold).假設(shè)實(shí)驗(yàn)中的缺陷檢測模型的可接受水平設(shè)置為k,那么當(dāng) API調(diào)用在API調(diào)用概率列表中的排名在Top-1~Top-k之間時(包括Top-k),認(rèn)為該API調(diào)用在本次API誤用缺陷檢測中處于API調(diào)用的可接受范圍內(nèi);反之,認(rèn)為該API調(diào)用屬于API誤用代碼缺陷.
為驗(yàn)證模型的有效性,設(shè)置以下模型作為實(shí)驗(yàn)的對比模型.
(1) 基準(zhǔn)模型(baseline).該模型假設(shè)API調(diào)用序列上的每個位置都可能是潛在的API誤用,該假設(shè)覆蓋到所有的API誤用缺陷,召回率恒定為1,在測試集上的查準(zhǔn)率為0.088 6,F1值為0.163.
(2)N-gram檢測模型.Bugram[10]基于N-gram語言模型實(shí)現(xiàn)了對存在API誤用的異常序列的檢測.本文關(guān)注于對API調(diào)用序列上某一位置可能存在的API誤用的檢測,因此,本文實(shí)現(xiàn)Bugram應(yīng)用的N-gram預(yù)測模型作為對比實(shí)驗(yàn),使用與深度學(xué)習(xí)模型訓(xùn)練時相同的API調(diào)用序列,訓(xùn)練N-gram模型,基于前N個詞元預(yù)測下一個詞元.實(shí)驗(yàn)中取N={3,4,5}分別對應(yīng)3-gram模型、4-gram模型和5-gram模型,并進(jìn)行Jelinek-Mercer平滑處理[25].N-gram檢測模型應(yīng)用N-gram預(yù)測模型對API調(diào)用序列上某個位置的API調(diào)用概率列表進(jìn)行預(yù)測,同樣的,應(yīng)用可接受閾值(Top-k)進(jìn)行缺陷檢測.
在本實(shí)驗(yàn)中,比較不同模型的API誤用缺陷檢測效果,并探究可接受閾值的取值對代碼缺陷檢測模型的F1值的影響.
4.3.3 實(shí)驗(yàn)結(jié)果分析
經(jīng)測試,API誤用缺陷檢測的實(shí)驗(yàn)結(jié)果如圖9所示,圖中折線表示不同的實(shí)驗(yàn)?zāi)P?橫軸表示可接受閾值(top-k)的取值,圖9(a)是各模型的F1值,圖9(b)是各模型的查準(zhǔn)率,圖9(c)是各模型的召回率.
從結(jié)果中可以看出:
在一定范圍內(nèi),DeepLSTM的查準(zhǔn)率均高于基準(zhǔn)模型且召回率大于等于50%,有一定的缺陷檢測能力.本實(shí)驗(yàn)中4-gram模型較3-gram模型和5-gram模型缺陷檢測效果較好,但在本實(shí)驗(yàn)中,它們均不如基準(zhǔn)模型(查準(zhǔn)率最高約和基準(zhǔn)持平).而相較而言,DeepLSTM模型在本次實(shí)驗(yàn)所采用的模型中有一定的檢測能力且效果最好.
當(dāng)可接受閾值取到Top-8時,DeepLSTM缺陷檢測效果最好.
Fig.9 Result of API misuse bug detection experiment圖9 API誤用缺陷檢測實(shí)驗(yàn)結(jié)果
為了進(jìn)一步探究API誤用類型與模型能力的關(guān)系,實(shí)驗(yàn)中,將DeepLSTM模型對應(yīng)每個可接受閾值(top-k)的具體API誤用缺陷報(bào)告情況整理在表2中,表格中檢測正確的API誤用用“√”表示,未檢出的API誤用用“-”表示.
Table 2 API misuse bug detection reports statistics表2 API誤用缺陷檢測報(bào)告情況統(tǒng)計(jì)
基于整理后的API誤用缺陷檢測報(bào)告情況,按照測試集中API誤用的誤用類型對缺陷檢測報(bào)告情況進(jìn)行進(jìn)一步的可視化統(tǒng)計(jì)分析,如圖10所示.
縱軸表示了不同類型的 API誤用缺陷,橫軸表示檢測出以及未檢出的該類型 API誤用數(shù)量在該類型 API誤用總數(shù)中的占比,用顏色深藍(lán)色、淺綠色分別表示檢測出和未檢出的兩種情況,圖中有10個獨(dú)立的子圖來分別表示每個可接受閾值(top-k)對應(yīng)的API誤用缺陷檢測報(bào)告情況.
分析該圖,可以看出:
· 隨著k取值的增大,在某些API誤用的檢測上,模型的效果保持基本不變(錯誤的API調(diào)用),一定程度說明了本實(shí)驗(yàn)中采用的缺陷檢測方法對檢測該類API誤用的有效性:當(dāng)一個API誤用在API調(diào)用序列中不應(yīng)該出現(xiàn)(使用了錯誤的API)時,則應(yīng)用本實(shí)驗(yàn)中按位置預(yù)測并比較檢測API誤用是可行且有效的.
· 但是與之相反的,隨著k取值的增大,在某些API誤用的檢測上,模型效果不斷降低(遺漏API調(diào)用),這一定程度上也說明了本實(shí)驗(yàn)中采用的缺陷檢測方法對檢測該類API誤用的局限性:當(dāng)一個關(guān)鍵性API在 API調(diào)用序列中遺漏,僅靠當(dāng)前的 API調(diào)用序列作為預(yù)測的前文,很難推斷出下一位置是否遺漏了某個 API.例如,某個關(guān)鍵性 API可能在當(dāng)前位置更靠后幾個調(diào)用中出現(xiàn),這種情況對目前采用的僅對一個位置的API誤用缺陷進(jìn)行檢測的方法就具有很大的挑戰(zhàn)性.
· 隨著k取值的增大,在某些API誤用的檢測上,模型效果有較大的突變(多余的API調(diào)用、忽略異常處理),這與本實(shí)驗(yàn)中采用的測試集有一定的關(guān)系——多余的 API調(diào)用和忽略異常處理的測試用例都僅有1個.但是在k取到一定范圍(top-8)之前,本實(shí)驗(yàn)中采用的缺陷檢測方法對忽略異常處理類型的API誤用缺陷檢測效果還是不錯的.
· 就每個獨(dú)立子圖而言,也可以驗(yàn)證以上幾點(diǎn)結(jié)論:本實(shí)驗(yàn)采用的 API誤用缺陷檢測方法,對檢測錯誤的API調(diào)用類型的API誤用效果較好,而在發(fā)現(xiàn)遺漏API調(diào)用類型的API誤用上,能力稍有欠缺.
Fig.10 Analysis of relationship between acceptable thresholdand type of API misuse圖10 可接受閾值與API誤用類型關(guān)系分析
綜上,本實(shí)驗(yàn)應(yīng)用深度學(xué)習(xí)模型學(xué)習(xí)大量代碼中的API使用規(guī)約,并應(yīng)用在API誤用缺陷檢測上的方法.雖然由于大量開源代碼中可能存在些許誤用的 API代碼,影響到訓(xùn)練數(shù)據(jù)的質(zhì)量,對模型的效果產(chǎn)生著一定的影響,但是本實(shí)驗(yàn)中,模型在以檢測Java加密相關(guān)的API誤用代碼缺陷為例的實(shí)驗(yàn)中仍然起到了一定的效果,以可接受閾值為8時效果最佳(F1值、準(zhǔn)確率).在本實(shí)驗(yàn)中,模型對檢測錯誤的API調(diào)用類型的API誤用效果較好,而在發(fā)現(xiàn)遺漏API調(diào)用類型的API誤用上能力稍有欠缺,這與每種API誤用類型內(nèi)在的規(guī)律特征密不可分.
本文將深度學(xué)習(xí)中的循環(huán)神經(jīng)網(wǎng)絡(luò)模型應(yīng)用于API使用規(guī)約的學(xué)習(xí)及API誤用缺陷的檢測.將API調(diào)用組合、順序及控制結(jié)構(gòu)等方面的使用規(guī)約建模為API語法圖和API調(diào)用序列.在大量的開源Java代碼基礎(chǔ)上,對代碼進(jìn)行靜態(tài)分析,并構(gòu)造大量API使用規(guī)約訓(xùn)練樣本,對循環(huán)神經(jīng)網(wǎng)絡(luò)進(jìn)行訓(xùn)練.在實(shí)驗(yàn)中通過嘗試不同參數(shù)組合,選定并訓(xùn)練出較優(yōu)的循環(huán)神經(jīng)網(wǎng)絡(luò)模型,并用于基于前文的API調(diào)用預(yù)測,通過預(yù)測結(jié)果與實(shí)際代碼進(jìn)行比較來發(fā)現(xiàn)潛在的API誤用缺陷.隨后,在以檢測Java加密相關(guān)的API誤用代碼缺陷為例的實(shí)驗(yàn)中證明了該方法的有效性.實(shí)驗(yàn)表明,本方法檢測錯誤的API調(diào)用類型的API誤用時最為穩(wěn)定有效.
本文提出的方法在一定程度上能自動檢測API誤用缺陷,并在某類API誤用的檢測中較為有效,但是還存在一些不足和可改進(jìn)之處——在發(fā)現(xiàn)遺漏API調(diào)用類型的API誤用上,能力稍有欠缺.在未來研究中,可考慮對多個位置的API調(diào)用進(jìn)行預(yù)測比對,減少模型由于單個位置的預(yù)測帶來的API是否漏用的不確定性.